
/* Self-hosted Inter (TDD-07 §11.2): one variable-weight latin file, no
   third-party font host — matches the privacy page's no-third-parties claim
   and removes the render-blocking cross-origin request. */
@font-face{
    font-family:'Inter';
    font-style:normal;
    font-weight:100 900;
    font-display:swap;
    src:url("fonts/inter-latin.woff2") format("woff2");
    unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Product leaderboard discovery — search first, touch-friendly brand browsing. */
.lb-search{
    display:grid;
    gap:var(--sp-2);
    margin:var(--sp-5) 0 var(--sp-4);
}
.lb-search > label{
    color:var(--lr-text);
    font-size:var(--fs-sm);
    font-weight:var(--fw-semi);
}
.lb-search-field{
    position:relative;
    display:flex;
    align-items:center;
}
.lb-search-field > svg{
    position:absolute;
    left:14px;
    color:var(--lr-dim);
    pointer-events:none;
}
.lb-search-field input{
    width:100%;
    min-height:48px;
    padding:0 46px 0 44px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md);
    background:var(--lr-surface);
    color:var(--lr-text);
    font:inherit;
    box-shadow:0 8px 24px rgba(0,0,0,.18);
}
.lb-search-field input:focus{
    outline:3px solid var(--lr-accent-deal-wash);
    border-color:var(--lr-accent-deal-text);
}
.lb-search-clear{
    position:absolute;
    right:6px;
    width:38px;
    height:38px;
    border:0;
    border-radius:50%;
    background:transparent;
    color:var(--lr-dim);
    font-size:24px;
    cursor:pointer;
}
.lb-brand-rail-section,
.lb-browse-brands{
    margin:var(--sp-5) 0;
}
.lb-discovery-head{
    display:flex;
    align-items:end;
    justify-content:space-between;
    gap:var(--sp-3);
    margin-bottom:var(--sp-3);
}
.lb-discovery-head h2{ margin:0; font-size:var(--fs-lg); }
.lb-discovery-head .eyebrow{ margin:0 0 2px; }
.lb-brand-rail{
    display:flex;
    gap:var(--sp-3);
    overflow-x:auto;
    padding:2px 2px var(--sp-3);
    scroll-snap-type:x proximity;
    scrollbar-width:thin;
    overscroll-behavior-inline:contain;
}
.lb-brand-card{
    flex:0 0 min(72vw, 210px);
    min-height:88px;
    display:grid;
    grid-template-columns:42px minmax(0,1fr);
    grid-template-rows:auto auto;
    align-items:center;
    gap:0 var(--sp-3);
    padding:var(--sp-3);
    border:1px solid var(--lr-line);
    border-radius:var(--r-md);
    background:var(--lr-surface);
    color:var(--lr-text);
    text-decoration:none;
    scroll-snap-align:start;
}
.lb-brand-card:hover{ border-color:var(--lr-accent-deal-text); transform:translateY(-1px); }
.lb-brand-card > span:not(.lb-brand-monogram){ font-weight:var(--fw-semi); line-height:1.2; }
.lb-brand-card small{ grid-column:2; color:var(--lr-dim); }
.lb-brand-monogram{
    grid-row:1 / 3;
    width:42px;
    height:42px;
    display:grid;
    place-items:center;
    border-radius:12px;
    background:var(--lr-surface-2);
    color:var(--lr-accent-deal-text);
    font-weight:var(--fw-bold);
}
.lb-rail-controls{ display:none; gap:var(--sp-2); }
.lb-rail-controls button{
    width:42px;
    height:42px;
    border:1px solid var(--lr-line);
    border-radius:50%;
    background:var(--lr-surface);
    color:var(--lr-text);
    cursor:pointer;
}
.lb-rail-controls button:disabled{ opacity:.35; cursor:default; }
.brand-filter-search{
    width:100%;
    min-height:44px;
    margin-bottom:var(--sp-2);
    padding:0 var(--sp-3);
    border:1px solid var(--lr-line);
    border-radius:var(--r-sm);
    background:var(--lr-surface);
    color:var(--lr-text);
    font:inherit;
}
.brand-filter-options{ display:grid; gap:4px; }
.brand-filter-option{
    width:100%;
    min-height:42px;
    display:flex;
    justify-content:space-between;
    align-items:center;
    border-radius:var(--r-sm);
}
.lb-browse-brands{
    padding-top:var(--sp-5);
    border-top:1px solid var(--lr-line);
}
.lb-brand-links{
    display:grid;
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:var(--sp-2);
    margin:0;
    padding:0;
    list-style:none;
}
.lb-brand-links a{
    min-height:44px;
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:var(--sp-2);
    padding:var(--sp-2) var(--sp-3);
    border:1px solid var(--lr-line);
    border-radius:var(--r-sm);
    background:var(--lr-surface);
    color:var(--lr-text);
    text-decoration:none;
}
.lb-brand-links a:hover{ border-color:var(--lr-accent-deal-text); }
.lb-brand-links small{ color:var(--lr-dim); }
.lb-view-all-brands{ white-space:nowrap; font-size:var(--fs-sm); }
.lb-active-search .lb-active-chip{ font:inherit; cursor:pointer; }
.link-button{ border:0; padding:0; background:none; color:var(--lr-accent-deal-text); font:inherit; text-decoration:underline; cursor:pointer; }

@media (min-width: 769px){
    .lb-search{ max-width:760px; }
    .lb-brand-card{ flex-basis:220px; }
    .lb-rail-controls{ display:flex; }
    .lb-brand-rail{ scrollbar-width:none; }
    .lb-brand-rail::-webkit-scrollbar{ display:none; }
    .lb-brand-links{ grid-template-columns:repeat(4,minmax(0,1fr)); }
}

@media (min-width: 1100px){
    .lb-brand-links{ grid-template-columns:repeat(6,minmax(0,1fr)); }
}

/* Michroma (v4 Platform redesign): the display face for scoreboard numerals,
   rank digits, and section kickers — never body copy. SIL OFL 1.1 (free for
   commercial use, license alongside the file); chosen as the closest open
   match to the wide techno look the design was mocked with. */
@font-face{
    font-family:'Michroma';
    font-style:normal;
    font-weight:400;
    font-display:swap;
    /* TDD-08 §11.2: subsetted WOFF2 (caps+digits, ~9KB) first, TTF fallback. */
    src:url("fonts/Michroma.woff2") format("woff2"),
        url("fonts/Michroma.ttf") format("truetype");
}

*{
    margin:0;
    padding:0;
    box-sizing:border-box;
}

:root{
    /* v4 "Platform" design tokens: warm near-black ground, hairline rules,
       Crossfly display type. Everything below the v4 layer still reads these,
       so the admin mini-site re-skins automatically. */
    --lr-bg:#0A0909;
    --lr-surface:#100F0F;
    --lr-surface-2:#161514;
    --lr-line:#242222;
    --lr-line-soft:#1D1B1B;
    --lr-text:#F4F4F5;
    --lr-muted:#A9A5A5;
    --lr-soft:#D8D4D4;
    --lr-dim:#8B8686;   /* TDD-08 §4.3: bumped from #7A7676 to clear AA (≥4.5:1) */
    --lr-shadow:0 24px 80px rgba(0,0,0,.35);
    --lr-fresh:#57C271;   /* freshness badge green */
    --lr-stale:#E0A93E;   /* stale badge amber */
    --lr-display:'Michroma', Bahnschrift, 'Arial Narrow', 'Franklin Gothic Medium', sans-serif;

    /* v3 brand accent (TDD-04 §3): crimson = "money moved in your favor."
       Deals, savings, drop badges, primary CTAs — nothing else. White text
       on solid crimson always passes AA; raw crimson on dark only for
       borders/lines/big numerals; small crimson text uses the -text tint. */
    --lr-accent-deal:#B23540;   /* TDD-08 §4.3: darkened to a real 4.5:1 with white */
    --lr-accent-deal-hover:#C4414B;
    --lr-accent-deal-text:#E8737E;
    --lr-accent-deal-wash:rgba(196, 65, 75, 0.12);
    --lr-accent-deal-line:rgba(196, 65, 75, 0.45);

    /* ── TDD-08 §4.1 design-system token layer ─────────────────────────────
       Additive scales the seven-era stylesheet never had. Nothing below is
       meant to change the rendered look on its own — later sections repoint
       free-typed values onto these so the whole file speaks one language. */
    /* spacing — 4pt grid (replaces free-typed px) */
    --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px;
    --sp-6:32px; --sp-7:48px; --sp-8:64px; --sp-9:80px;
    /* radius (the lone --lr-radius was bypassed everywhere) */
    --r-sm:6px; --r-md:10px; --r-lg:14px; --r-pill:999px;
    /* type scale — ONE ladder, replaces the px soup + parallel rem soup */
    --fs-xs:12px; --fs-sm:13px; --fs-base:15px; --fs-md:16px; --fs-lg:20px;
    --fs-xl:24px; --fs-2xl:clamp(28px,4vw,44px); --fs-hero:clamp(38px,6vw,64px);
    /* weight — three, not the 500/700/800 drift */
    --fw-reg:400; --fw-med:500; --fw-semi:600; --fw-bold:700;
    /* line-height rungs */
    --lh-tight:1.15; --lh-ui:1.5; --lh-prose:1.65;
    /* z-index scale (raw 5/41/50/90/100/120/200/210/300 today) */
    --z-base:1; --z-sticky:100; --z-nudge:200; --z-modal:300;
    /* motion — one house easing + two durations */
    --ease:cubic-bezier(.2,0,0,1); --dur-fast:.14s; --dur:.2s;
    /* breakpoints are documented here for reference (media queries can't read
       custom properties): --bp-sm:640px --bp-md:900px --bp-lg:1200px */

    /* --lr-radius kept as an alias of --r-md so legacy references resolve
       until they're migrated; new code should use --r-md. */
    --lr-radius:var(--r-md);

    /* The site is dark, so tell the browser. Without this the UA paints every
       native control in its LIGHT theme: unchecked tick boxes were bright
       white squares (accent-color only colours the checked state), the
       <select> dropdown list opened white-on-white, and scrollbars, date
       pickers and autofill backgrounds all came out light. One declaration
       fixes the lot, and it is what `accent-color` is meant to sit beside. */
    color-scheme:dark;
}

body{
    background:var(--lr-bg);
    color:var(--lr-text);
    font-family:'Inter',sans-serif;
    min-height:100vh;
    overflow-x:clip;
}

/* ── Line breaking ────────────────────────────────────────────────────────
   Headlines were breaking into badly weighted lines and paragraphs were
   stranding single words on a line of their own ("...confirm on the /
   retailer's site."). Two one-line browser features fix both, and both
   degrade to today's behaviour where unsupported — so no fallback is needed.

   balance : headings, which are short enough for the browser to even out.
   pretty  : body copy, where the rule only guards the last line against
             orphans and so stays cheap on long text. */
h1, h2, h3,
.section-title,
.eyebrow,
.sec-kick{ text-wrap:balance; }

p, li, dd,
.lb-explainer,
.why-text,
.empty-state,
.entity-index-note{ text-wrap:pretty; }

a{
    color:inherit;
}

/* ══════════════════════════════════════════════════════════════════════════
   TDD-08 §4.4–4.5 — DESIGN-SYSTEM PRIMITIVES
   One button, one input, one card, one badge. New surfaces (nav CTA, hero
   CTA, inline "View deal", leaderboard table, related strip, auth) use these
   modifier-class components instead of inventing a 19th treatment. Legacy
   button/input classes are progressively re-pointed onto them.
   ══════════════════════════════════════════════════════════════════════════ */

.btn{
    display:inline-flex; align-items:center; justify-content:center; gap:var(--sp-2);
    min-height:44px; padding:0 var(--sp-4); border-radius:var(--r-md);
    font-family:'Inter',sans-serif; font-weight:var(--fw-semi); font-size:var(--fs-base);
    line-height:1; letter-spacing:.01em; text-decoration:none; cursor:pointer;
    border:1px solid transparent; background:none; color:var(--lr-text);
    -webkit-appearance:none; appearance:none;
    transition:transform var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.btn:hover{ transform:translateY(-1px); }
.btn:active{ transform:translateY(0); }
.btn:disabled,.btn[aria-disabled="true"],.btn.is-loading{ opacity:.6; cursor:default; transform:none; pointer-events:none; }
.btn--primary{ background:var(--lr-accent-deal); color:#fff; }
.btn--primary:hover{ background:var(--lr-accent-deal-hover); }
/* a11y §9.9: crimson focus ring is invisible on a crimson button — use white */
.btn--primary:focus-visible{ outline:2px solid #fff; outline-offset:2px; }
.btn--secondary{ background:var(--lr-text); color:#0A0A0A; }
.btn--secondary:hover{ background:#fff; }
.btn--ghost{ background:transparent; border-color:var(--lr-line); color:var(--lr-text); }
.btn--ghost:hover{ border-color:var(--lr-muted); background:var(--lr-surface); }
.btn--sm{ min-height:40px; padding:0 var(--sp-3); font-size:var(--fs-sm); }
.btn--lg{ min-height:48px; padding:0 var(--sp-5); font-size:var(--fs-md); }
.btn--block{ display:flex; width:100%; }
/* inline spinner for loading forms/buttons (§13.1) */
.btn .btn-spin{ display:none; width:15px; height:15px; border:2px solid currentColor;
    border-right-color:transparent; border-radius:50%; animation:btn-spin .6s linear infinite; }
.btn.is-loading .btn-spin{ display:inline-block; }
.btn.is-loading .btn-label{ opacity:.7; }
@keyframes btn-spin{ to{ transform:rotate(360deg); } }

.input{
    width:100%; min-height:46px; padding:0 var(--sp-3);
    border:1px solid var(--lr-line); border-radius:var(--r-md);
    background:var(--lr-surface); color:var(--lr-text);
    font-family:'Inter',sans-serif; font-size:16px; /* 16px: iOS zoom guard */
    line-height:var(--lh-ui);
    transition:border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.input::placeholder{ color:var(--lr-dim); }
.input:focus{ outline:none; border-color:var(--lr-accent-deal-line); box-shadow:0 0 0 3px var(--lr-accent-deal-wash); }
.input--pill{ border-radius:var(--r-pill); }

/* ── <select> and the tick boxes ──────────────────────────────────────────
   There was never a base rule for either, only per-context ones
   (.watch-mode-select, .market-select, .admin-container select). Any <select>
   written without a class therefore fell through to the browser default: a
   WHITE box with black text and native OS chrome, on a #0A0909 page. The
   preferences page renders fourteen of them in a column.

   Styling the element rather than adding another class means the pages that
   already forgot are fixed, and the next one cannot get it wrong. Anything
   with an explicit class still wins on specificity, so the existing
   treatments are untouched. The chevron is a data: URI, which this site's CSP
   permits under img-src. */
select{
    -webkit-appearance:none; appearance:none;
    min-height:44px;
    padding:0 34px 0 var(--sp-3);
    border:1px solid var(--lr-line);
    border-radius:var(--r-md);
    background-color:var(--lr-surface);
    color:var(--lr-text);
    font-family:'Inter',sans-serif;
    font-size:16px;                     /* 16px: iOS zoom guard, same as .input */
    line-height:var(--lh-ui);
    cursor:pointer;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%23A9A5A5' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 4.5 6 8l4-3.5'/%3E%3C/svg%3E");
    background-repeat:no-repeat;
    background-position:right var(--sp-3) center;
    transition:border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
select:hover{ border-color:var(--lr-dim); }
select:focus{ outline:none; border-color:var(--lr-accent-deal-line); box-shadow:0 0 0 3px var(--lr-accent-deal-wash); }
/* The dropdown list itself is drawn by the OS; only the option colours are
   ours to set, and without them the list renders white-on-white on Windows. */
select option{ background:var(--lr-surface-2); color:var(--lr-text); }

/* Ticks and radios were the OS default too — a bright white square that read
   as the loudest control on a settings page full of quiet rows. Three places
   already set this by hand; make it the default and let them be redundant. */
input[type="checkbox"],
input[type="radio"]{ accent-color:var(--lr-accent-deal); }

.card{
    background:var(--lr-surface); border:1px solid var(--lr-line);
    border-radius:var(--r-md); /* canonical: hairline, no shadow */
}
.card--float{ box-shadow:var(--lr-shadow); }

.badge{
    display:inline-flex; align-items:center; gap:var(--sp-1);
    padding:2px var(--sp-2); border-radius:var(--r-pill);
    font-size:var(--fs-xs); font-weight:var(--fw-semi); line-height:1.4;
    letter-spacing:.02em; white-space:nowrap; border:1px solid transparent;
}
.badge--deal{ background:var(--lr-accent-deal-wash); color:var(--lr-accent-deal-text); border-color:var(--lr-accent-deal-line); }
.badge--fresh{ background:rgba(87,194,113,.12); color:var(--lr-fresh); border-color:rgba(87,194,113,.4); }
.badge--stale{ background:rgba(224,169,62,.1); color:var(--lr-stale); border-color:rgba(224,169,62,.4); }
.badge--rank{ background:var(--lr-accent-deal-wash); color:var(--lr-accent-deal-text); border-color:var(--lr-accent-deal-line);
    font-family:var(--lr-display); font-size:var(--fs-xs); letter-spacing:.04em; }

nav.site-nav{
    display:flex;
    justify-content:flex-start;
    align-items:center;
    gap:16px;
    padding:12px 50px;
    border-bottom:1px solid var(--lr-line);
    background:rgba(8,8,8,.9);
    backdrop-filter:blur(18px);
    position:sticky;
    top:0;
    z-index:100;
}

/* The bare `nav` rule above pins the site header. In-page <nav> landmarks
   (the homepage category rail, the breadcrumb) are <nav>s too, so without this
   they inherit the header's sticky + chrome and stack a second bar on top of
   the header — content then scrolls behind both (TDD-09). Keep them in normal
   flow with their own styling. */
.home-rail,
.breadcrumb{
    position:static;
    z-index:auto;
    border-bottom:0;
    background:transparent;
    backdrop-filter:none;
    padding-left:0;
    padding-right:0;
}

.logo-section{
    display:flex;
    align-items:center;
    flex-shrink:0;
}

.logo{
    height:68px;
    width:auto;
    display:block;
    max-width:100%;
    object-fit:contain;
}

.nav-links{
    display:flex;
    align-items:center;
    gap:10px;
    margin-left:auto;
    min-width:0;
}

.nav-search-form{
    width:min(360px, 34vw);
    min-width:220px;
    position:relative;
}

.nav-search-form input{
    width:100%;
    height:44px;
    padding:0 14px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    color:#fff;
    font:inherit;
}

.nav-search-form input:focus{
    border-color:var(--lr-accent-deal-line);
    box-shadow:0 0 0 3px var(--lr-accent-deal-wash);
}

.nav-search-form input::placeholder{
    color:var(--lr-dim);
}

.search-suggestions{
    position:absolute;
    left:0;
    right:0;
    top:calc(100% + 8px);
    display:none;
    z-index:120;
    overflow:hidden;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface);
    box-shadow:0 18px 50px rgba(0,0,0,.45);
}

.search-suggestions.is-open{
    display:grid;
}

.search-suggestion{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:12px;
    padding:12px 14px !important;
    border:0 !important;
    border-radius:0 !important;
    background:var(--lr-surface) !important;
    color:#fff !important;
    text-decoration:none;
    text-transform:none !important;
    letter-spacing:0 !important;
}

.search-suggestion + .search-suggestion{
    border-top:1px solid #222 !important;
}

.search-suggestion:hover,
.search-suggestion:focus{
    background:#181818 !important;
}

.search-suggestion strong{
    font-size:14px;
    line-height:1.2;
}

.search-suggestion span{
    color:var(--lr-muted);
    font-size:11px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
    white-space:nowrap;
}

.mobile-menu-toggle{
    display:none;
}

.mobile-menu-button{
    display:none;
    width:44px;
    height:44px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    align-items:center;
    justify-content:center;
    flex-direction:column;
    gap:5px;
    cursor:pointer;
    margin-left:auto;
    transition:.2s;
}

.mobile-menu-button:hover{
    border-color:var(--lr-muted);
    background:var(--lr-surface-2);
}

.mobile-menu-button span{
    width:20px;
    height:2px;
    background:var(--lr-text);
    display:block;
}

.nav-links.is-open{
    opacity:1;
    visibility:visible;
    pointer-events:auto;
    transform:translateY(0);
}

.nav-menu-title{
    color:#8f8f8f;
    font-size:12px;
    font-weight:800;
    letter-spacing:1.4px;
    text-transform:uppercase;
    padding:4px 2px 2px;
    display:none;
}

.nav-links a,
.nav-dropdown summary,
.admin-btn{
    text-decoration:none;
    color:var(--lr-soft);
    font-size:13px;
    font-weight:800;
    letter-spacing:1px;
    text-transform:uppercase;
    border:1px solid transparent;
    border-radius:8px;
    padding:10px 12px;
    transition:.2s;
}

.nav-links a,
.nav-dropdown summary{
    border-color:var(--lr-line);
    background:var(--lr-surface-2);
    padding:13px 14px;
    white-space:nowrap;
}

.nav-dropdown{
    position:relative;
}

.nav-dropdown summary{
    list-style:none;
    cursor:pointer;
}

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

.nav-dropdown summary::after{
    content:"";
    display:inline-block;
    width:7px;
    height:7px;
    border-right:2px solid currentColor;
    border-bottom:2px solid currentColor;
    margin-left:9px;
    transform:translateY(-3px) rotate(45deg);
}

.nav-dropdown[open] summary::after{
    transform:translateY(1px) rotate(225deg);
}

.admin-btn{
    margin-left:0;
}

.nav-links a:hover,
.nav-dropdown summary:hover,
.admin-btn:hover{
    color:#fff;
    border-color:var(--lr-line);
    background:var(--lr-surface-2);
}

.dropdown-menu{
    display:none;
    position:absolute;
    top:100%;
    left:0;
    min-width:240px;
    padding:14px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface);
    box-shadow:0 18px 50px rgba(0,0,0,.45);
}

.nav-dropdown[open] .dropdown-menu,
.nav-dropdown:hover .dropdown-menu{
    display:grid;
    gap:6px;
}

.dropdown-menu strong{
    color:#fff;
    font-size:12px;
    letter-spacing:1px;
    text-transform:uppercase;
    margin-top:8px;
}

.dropdown-menu strong:first-child{
    margin-top:0;
}

.dropdown-menu a{
    white-space:nowrap;
    padding:9px 10px;
    text-transform:none;
    letter-spacing:0;
    font-weight:600;
}

.hero{
    padding:70px 20px 50px;
    text-align:center;
}

.eyebrow{
    color:#cfcfcf;
    font-size:12px;
    font-weight:800;
    letter-spacing:1.8px;
    text-transform:uppercase;
}

.hero h1{
    font-size:58px;
    font-weight:800;
    line-height:1.02;
    max-width:920px;
    margin:18px auto 0;
}

.hero-copy{
    margin:22px auto 0;
    max-width:650px;
}

.hero-text{
    color:var(--lr-muted);
    font-size:20px;
    line-height:1.5;
    font-weight:400;
}

.admin-container input,
.admin-container select,
.admin-container textarea{
    width:100%;
    padding:18px 20px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    color:white;
    font:inherit;
    transition:.2s;
}

.admin-container textarea{
    min-height:132px;
    resize:vertical;
}

.admin-container input:focus,
.admin-container select:focus,
.admin-container textarea:focus{
    border-color:var(--lr-accent-deal-line);
    box-shadow:0 0 0 3px var(--lr-accent-deal-wash);
}

.admin-container input::placeholder,
.admin-container textarea::placeholder{
    color:var(--lr-dim);
}

.deal-section,
.product-section,
.prices-section,
.admin-container{
    width:min(1180px, calc(100% - 40px));
    margin:0 auto;
}

.deal-section{
    padding:18px 0 76px;
}

.product-section{
    padding:0 0 74px;
}

.section-heading{
    display:flex;
    align-items:end;
    justify-content:space-between;
    gap:18px;
    margin-bottom:22px;
}

.section-title{
    font-size:30px;
    margin-top:6px;
    letter-spacing:0;
}

.category-tag,
.best-label{
    display:inline-flex;
    align-items:center;
    width:max-content;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:#1a1a1a;
    color:#efefef;
    font-size:12px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
    padding:8px 10px;
}

.deal-card{
    display:grid;
    grid-template-columns:minmax(320px, 42%) 1fr;
    gap:0;
    background:rgba(17,17,17,.96);
    border:1px solid var(--lr-line);
    border-radius:8px;
    overflow:hidden;
    box-shadow:0 24px 80px rgba(0,0,0,.35);
}

.deal-left{
    min-height:430px;
}

.deal-left img,
.product-image img{
    width:100%;
    height:100%;
    object-fit:contain;
    display:block;
}

/* Every product image renders on black, full stop (owner ruling, 2026-08-25)
   — this is the only image-bg-* class left. It used to branch on
   product.image_background (white/transparent/original); that column still
   exists in the DB but nothing reads it for rendering anymore. */
.image-bg-black{
    background:#050505 !important;
}

.deal-right{
    padding:46px;
    display:flex;
    flex-direction:column;
    justify-content:center;
}

.deal-right h2,
.product-info h1{
    font-size:44px;
    line-height:1.06;
    margin-top:16px;
}

.deal-right p,
.product-info p{
    margin-top:18px;
    color:var(--lr-muted);
    line-height:1.7;
}

.price-stats{
    display:grid;
    grid-template-columns:repeat(3, minmax(0,1fr));
    gap:12px;
    margin-top:28px;
}

.price-stats div{
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    padding:16px;
}

.price-stats span{
    display:block;
    color:#8f8f8f;
    font-size:12px;
    font-weight:800;
    text-transform:uppercase;
    letter-spacing:.8px;
}

.price-stats strong{
    display:block;
    margin-top:6px;
    font-size:24px;
}

.deal-button,
.admin-container button{
    margin-top:28px;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    min-height:50px;
    width:210px;
    text-align:center;
    text-decoration:none;
    color:#050505;
    background:white;
    border:0;
    border-radius:8px;
    font-weight:800;
    cursor:pointer;
    transition:.2s;
}

.deal-button:hover,
.admin-container button:hover{
    transform:translateY(-1px);
    background:#dcdcdc;
}

.product-page{
    width:min(1180px, calc(100% - 40px));
    margin:54px auto 0;
    display:grid;
    grid-template-columns:minmax(300px, 42%) 1fr;
    gap:38px;
    align-items:center;
}

.product-image{
    height:500px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    overflow:hidden;
    padding:24px;
}

.prices-section{
    padding:58px 0 80px;
}

.offer-table{
    display:grid;
    gap:8px;
}

.offer-row{
    display:grid;
    grid-template-columns:minmax(260px, 1.6fr) minmax(180px, .8fr) minmax(130px, .55fr) auto;
    gap:14px;
    align-items:center;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:#111;
    padding:12px 14px;
}

.offer-row div{
    display:grid;
    gap:3px;
}

.offer-row span,
.offer-row small{
    color:var(--lr-muted);
}

.offer-row strong{
    color:#fff;
}

.offer-main strong{
    font-size:16px;
}

.offer-main span,
.offer-meta span,
.price-amount span,
.offer-row small{
    font-size:12px;
    line-height:1.35;
}

.offer-row .price-amount{
    justify-items:end;
    text-align:right;
    font-size:14px;
}

.offer-row .price-amount strong{
    font-size:20px;
}

.chart-section{
    padding-top:0;
}

.history-chart{
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:#111;
    padding:22px;
}

.chart-legend{
    display:flex;
    flex-wrap:wrap;
    gap:18px;
    margin-bottom:12px;
}

.chart-legend span{
    display:flex;
    align-items:center;
    gap:8px;
    color:var(--lr-soft);
    font-size:13px;
    font-weight:800;
}

.chart-legend i{
    width:24px;
    height:4px;
    border-radius:999px;
}

.line-chart{
    width:100%;
    height:auto;
    min-height:300px;
    overflow:visible;
}

.chart-grid-line{
    stroke:#303030;
    stroke-width:1;
}

.chart-axis-label{
    fill:#969696;
    font-size:15px;
    font-weight:600;
}

.price-line{
    stroke-width:5;
    stroke-linecap:round;
    stroke-linejoin:round;
}

.price-point{
    stroke:#111;
    stroke-width:3;
}

.best-label{
    margin-top:8px;
    padding:6px 8px;
    color:#111;
    background:#fff;
}

.price-amount{
    font-size:24px;
    font-weight:800;
}

.empty-state{
    border:1px dashed var(--lr-line);
    border-radius:var(--r-md);
    background:rgba(17,17,17,.8);
    padding:34px;
    color:var(--lr-muted);
    line-height:var(--lh-prose);
    /* The box stretched the full 1180 column, so a two-sentence message ran at
       ~150 characters a line. Cap it at a reading measure. */
    max-width:78ch;
}
.empty-state strong{ color:var(--lr-text); }

.empty-state h3{
    color:white;
    margin-bottom:8px;
}

.spotlight-card{
    display:grid;
    grid-template-columns:280px 1fr;
    gap:24px;
    align-items:center;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:#111;
    padding:22px;
    text-decoration:none;
    transition:.2s;
}

.spotlight-card:hover{
    border-color:var(--lr-muted);
    transform:translateY(-2px);
}

.spotlight-image{
    height:230px;
    border-radius:8px;
    overflow:hidden;
}

.spotlight-image img{
    width:100%;
    height:100%;
    object-fit:contain;
    padding:18px;
}

.spotlight-card h3{
    font-size:28px;
    margin:12px 0 8px;
}

.spotlight-card p{
    color:var(--lr-muted);
    line-height:1.6;
}

.site-footer{
    border-top:1px solid var(--lr-line);
    padding:46px 20px 58px;
    color:#bdbdbd;
    background:#080808;
}

.site-footer h2{
    color:white;
    margin:8px 0 12px;
    font-size:22px;
}

.site-footer p{
    line-height:1.6;
    margin-top:8px;
}

.site-footer a{
    color:#fff;
    font-weight:800;
}

.footer-grid{
    width:min(1180px, calc(100% - 40px));
    margin:0 auto;
    display:grid;
    grid-template-columns:1.2fr 1fr 1fr;
    gap:28px;
}

.newsletter-form{
    display:grid;
    gap:10px;
    margin-top:12px;
}

/* These two predate the .btn/.input primitives and are element-typed, so their
   specificity (0,1,1) beat the class they were supposed to defer to (0,1,0):
   every <button class="btn btn--primary"> inside a newsletter form painted
   WHITE instead of crimson -- the homepage capture and the footer capture both
   claimed to be primary and neither looked it. Scoped to the unclassed case so
   the design system wins wherever it is actually used. */
.newsletter-form input:not(.input){
    padding:14px 16px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    color:#fff;
    font:inherit;
}

.newsletter-form button:not(.btn){
    border:0;
    border-radius:8px;
    padding:14px 18px;
    background:#fff;
    color:#111;
    font-weight:800;
    cursor:pointer;
}

.social-links{
    display:flex;
    gap:10px;
    margin-top:16px;
}

.social-icon{
    width:42px;
    height:42px;
    border-radius:50%;
    background:#080808;
    border:1px solid var(--lr-line);
    color:#fff;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    transition:background .2s ease, color .2s ease, border-color .2s ease, transform .2s ease;
}

.social-icon:hover,
.social-icon:focus-visible{
    background:var(--lr-surface-2);
    border-color:#fff;
    color:#fff;
    transform:translateY(-2px);
}

.social-icon svg{
    width:22px;
    height:22px;
    display:block;
}

.social-icon rect,
.social-icon circle{
    fill:none;
    stroke:currentColor;
    stroke-width:1.8;
}

.social-icon path{
    fill:currentColor;
}

.footer-cta-section{
    width:min(1180px, calc(100% - 40px));
    margin:34px auto 0;
    display:flex;
    flex-wrap:wrap;
    gap:12px;
    border-top:1px solid var(--lr-line);
    padding-top:28px;
}

.footer-cta-button{
    min-height:48px;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:#111;
    color:#fff;
    padding:0 18px;
    text-decoration:none;
    font-weight:800;
    transition:.2s;
}

.footer-cta-button:hover{
    border-color:#fff;
    transform:translateY(-2px);
}

.faq-section{
    width:min(1180px, calc(100% - 40px));
    margin:36px auto 0;
    padding-top:30px;
    border-top:1px solid var(--lr-line);
}

.faq-list{
    display:grid;
    gap:10px;
    margin-top:18px;
}

.faq-list details{
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:#111;
    padding:16px 18px;
}

.faq-list summary{
    cursor:pointer;
    color:#fff;
    font-weight:800;
}

.faq-list p{
    color:var(--lr-muted);
    margin-top:12px;
}

.admin-container{
    padding:46px 0 80px;
}

.admin-nav-actions{
    display:flex;
    align-items:center;
    gap:8px;
}

.analytics-summary{
    display:grid;
    grid-template-columns:repeat(3, minmax(0, 1fr));
    gap:14px;
    margin-bottom:18px;
}

.analytics-summary div{
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:#111;
    padding:22px;
}

.analytics-summary span{
    display:block;
    color:var(--lr-muted);
    font-size:12px;
    font-weight:800;
    letter-spacing:.8px;
    text-transform:uppercase;
}

.analytics-summary strong{
    display:block;
    font-size:34px;
    margin-top:8px;
}

.analytics-panel{
    margin-bottom:18px;
}

.analytics-table{
    display:grid;
}

.analytics-row{
    display:grid;
    grid-template-columns:1fr 100px 100px;
    gap:16px;
    align-items:center;
    border-top:1px solid #292929;
    padding:14px 4px;
}

.analytics-row div{
    display:grid;
    gap:4px;
}

.analytics-row small{
    color:var(--lr-muted);
}

.analytics-row a{
    color:#bcbcbc;
    font-size:12px;
}

.analytics-header{
    border-top:0;
    color:var(--lr-muted);
    font-size:12px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
}

.admin-grid{
    display:grid;
    grid-template-columns:repeat(2, minmax(0,1fr));
    gap:18px;
    margin-bottom:18px;
}

.admin-panel{
    background:#111;
    border:1px solid var(--lr-line);
    border-radius:8px;
    padding:24px;
}

.admin-panel h2{
    margin-bottom:18px;
}

.admin-help{
    line-height:1.6;
    margin:-6px 0 18px;
}

.retailer-authorization{
    border-top:1px solid var(--lr-line);
    margin-top:22px;
    padding-top:20px;
}

.retailer-authorization h3{
    font-size:16px;
    margin-bottom:7px;
}

.retailer-authorization p{
    line-height:1.5;
    margin-bottom:12px;
}

.secondary-admin-button{
    background:#1d1d1d !important;
    border:1px solid var(--lr-line) !important;
    color:#fff !important;
}

.compact-authorization{
    margin-top:14px;
    padding-top:14px;
}

.flash-message{
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    padding:16px 18px;
    margin-bottom:18px;
    font-weight:600;
}

.flash-message.success{
    border-color:#427a52;
}

.flash-message.error{
    border-color:#8d4545;
}

.admin-container form{
    display:grid;
    gap:12px;
}

.form-row{
    display:grid;
    grid-template-columns:1fr 120px;
    gap:10px;
}

.admin-container button{
    width:100%;
}

.checkbox-row{
    display:flex;
    align-items:center;
    gap:10px;
    color:var(--lr-soft);
    font-weight:600;
}

.checkbox-row input{
    width:18px;
}

.admin-product{
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    padding:18px;
    margin-top:12px;
}

.compact-heading{
    margin-top:38px;
}

.admin-product-grid{
    display:grid;
    grid-template-columns:repeat(auto-fill, minmax(290px, 1fr));
    gap:18px;
}

.admin-product-card{
    display:flex;
    flex-direction:column;
    overflow:hidden;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:#111;
    transition:.2s;
}

.admin-product-card:hover{
    border-color:#505050;
    transform:translateY(-2px);
}

.admin-product-link{
    display:block;
    flex:1;
    color:inherit;
    text-decoration:none;
}

.admin-product-image{
    height:190px;
    padding:16px;
}

.admin-product-image img{
    width:100%;
    height:100%;
    object-fit:contain;
}

.admin-product-card-body{
    padding:18px;
}

.admin-product-card-body h3{
    font-size:21px;
    margin-top:12px;
}

.admin-product-card-body p{
    color:var(--lr-muted);
    margin-top:8px;
}

.card-analytics{
    display:flex;
    gap:12px;
    margin-top:14px;
}

.card-analytics span{
    color:#c5c5c5;
    font-size:12px;
    font-weight:800;
}

.admin-card-actions{
    display:flex;
    flex-wrap:wrap;
    gap:7px;
    border-top:1px solid var(--lr-line);
    padding:12px;
}

.admin-card-actions .admin-action,
.admin-card-actions .delete-btn{
    padding:8px 9px;
    font-size:12px;
}

.admin-product-hero{
    display:grid;
    grid-template-columns:220px 1fr;
    gap:28px;
    align-items:center;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:#111;
    padding:22px;
    margin-bottom:18px;
}

.admin-product-hero-image{
    width:100%;
    height:190px;
    border-radius:8px;
    padding:14px;
}

.admin-product-hero-image img{
    width:100%;
    height:100%;
    object-fit:contain;
}

.site-content-panel{
    margin:18px 0;
}

.faq-admin-list{
    display:grid;
    gap:14px;
    margin-top:18px;
}

.faq-edit-form{
    border-top:1px solid var(--lr-line);
    padding-top:16px;
}

.admin-product-hero .muted{
    line-height:1.6;
    margin-top:10px;
}

.admin-product-hero .admin-actions{
    margin-top:18px;
}

.expanded-price-list > div{
    padding:12px 0;
}

.expanded-price-list em{
    color:#d59696;
    font-style:normal;
    font-weight:800;
}

.admin-product > div:first-child{
    display:flex;
    justify-content:space-between;
    gap:16px;
    margin-bottom:12px;
}

.admin-product span,
.muted{
    color:var(--lr-muted);
}

.admin-actions{
    display:flex;
    flex-wrap:wrap;
    gap:8px;
}

.admin-action{
    display:inline-flex;
    align-items:center;
    width:max-content;
    text-decoration:none;
    border:1px solid var(--lr-line);
    border-radius:8px;
    padding:9px 11px;
    color:#fff;
    font-size:13px;
    font-weight:800;
}

.admin-action:hover,
.primary-action{
    background:#fff;
    color:#111;
}

.field-label{
    color:var(--lr-soft);
    font-size:13px;
    font-weight:800;
    margin:4px 0 -4px;
}

.edit-container{
    max-width:1000px;
}

.delete-btn{
    display:inline-flex;
    width:max-content;
    text-decoration:none;
    border:1px solid var(--lr-line);
    border-radius:8px;
    padding:9px 11px;
    color:#fff;
    font-size:13px;
    font-weight:800;
}

.delete-btn:hover{
    background:#fff;
    color:#111;
}

.admin-price-list{
    display:grid;
    gap:8px;
    margin-top:14px;
}

.admin-price-list div{
    display:flex;
    justify-content:space-between;
    align-items:center;
    gap:12px;
    border-top:1px solid #292929;
    padding-top:8px;
}

.admin-table{
    width:100%;
    border-collapse:collapse;
}

.admin-table th,
.admin-table td{
    border-top:1px solid #292929;
    padding:12px 10px;
    text-align:left;
    vertical-align:middle;
}

.admin-table th{
    color:var(--lr-muted);
    font-size:12px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
}

.admin-table td{
    color:var(--lr-soft);
}

.small-delete{
    padding:6px 8px;
    font-size:12px;
}

.manual-badge{
    display:inline-block;
    padding:1px 7px;
    border-radius:999px;
    font-size:11px;
    font-weight:600;
    letter-spacing:.02em;
    background:rgba(196,65,75,.16);
    color:#e58a91;
    vertical-align:middle;
}

.login-panel{
    max-width:460px;
    padding-top:12vh;
}

.login-panel h1{
    margin-bottom:18px;
}

.request-page{
    width:min(760px, calc(100% - 40px));
    margin:54px auto 72px;
}

.request-panel{
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:#111;
    padding:28px;
}

.request-panel h1{
    font-size:34px;
    margin-top:8px;
}

.request-panel p{
    color:var(--lr-muted);
    line-height:1.6;
    margin-top:10px;
}

.request-form{
    display:grid;
    gap:12px;
    margin-top:24px;
}

.request-form input,
.request-form textarea{
    width:100%;
    padding:16px 18px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    color:#fff;
    font:inherit;
}

.request-form textarea{
    min-height:140px;
    resize:vertical;
}

.request-form input:focus,
.request-form textarea:focus{
    border-color:var(--lr-accent-deal-line);
    box-shadow:0 0 0 3px var(--lr-accent-deal-wash);
}

.request-form button{
    min-height:52px;
    border:0;
    border-radius:8px;
    background:#fff;
    color:#111;
    font-weight:800;
    cursor:pointer;
}

.field-label span{
    color:var(--lr-muted);
    font-weight:600;
}

@media (max-width: 900px){
    nav.site-nav{
        padding:10px 18px;
    }

    .logo{
        height:58px;
    }

    .nav-links{
        position:absolute;
        left:auto;
        right:18px;
        top:calc(100% + 8px);
        transform:translateY(-8px);
        display:grid;
        gap:8px;
        width:min(330px, calc(100vw - 36px));
        padding:14px;
        border:1px solid var(--lr-line);
        border-radius:8px;
        background:var(--lr-surface);
        box-shadow:0 18px 50px rgba(0,0,0,.45);
        opacity:0;
        visibility:hidden;
        pointer-events:none;
        transition:.18s ease;
        margin-left:0;
    }

    .mobile-menu-button{
        display:flex;
    }

    .nav-links.is-open{
        opacity:1;
        visibility:visible;
        pointer-events:auto;
        transform:translateY(0);
    }

    .nav-menu-title{
        display:block;
    }

    .nav-search-form{
        width:100%;
        min-width:0;
    }

    .search-suggestions{
        position:static;
        margin-top:8px;
        box-shadow:none;
    }

    .nav-dropdown summary{
        width:100%;
    }

    .dropdown-menu{
        position:static;
        min-width:0;
        margin-top:8px;
        padding:8px;
        border-radius:8px;
        box-shadow:none;
        background:#0c0c0c;
    }

    .dropdown-menu a{
        width:100%;
    }

    .admin-btn{
        display:none;
    }

    .admin-nav-actions{
        margin-left:auto;
        flex-wrap:wrap;
        justify-content:flex-end;
    }

    .admin-nav-actions .admin-btn{
        display:inline-flex;
        padding:9px 10px;
        font-size:11px;
    }

    .hero{
        padding:34px 18px 34px;
        text-align:left;
    }

    .hero-copy{
        margin:0;
        max-width:none;
    }

    .hero h1{
        font-size:42px;
        line-height:1.04;
        margin:14px 0 0;
    }

    .hero-text{
        font-size:18px;
    }

    .deal-section,
    .product-section,
    .prices-section,
    .admin-container,
    .product-page{
        width:calc(100% - 28px);
    }

    .deal-section,
    .product-section{
        padding-bottom:48px;
    }

    .section-heading{
        align-items:flex-start;
        flex-direction:column;
        margin-bottom:16px;
    }

    .section-title{
        font-size:28px;
    }

    .deal-card,
    .spotlight-card,
    .product-page{
        grid-template-columns:1fr;
    }

    .deal-left{
        min-height:280px;
        padding:18px;
    }

    .deal-right{
        padding:24px;
    }

    .deal-right h2,
    .product-info h1{
        font-size:32px;
    }

    .deal-button{
        width:100%;
    }

    .spotlight-image{
        height:250px;
    }

    .product-page{
        margin-top:28px;
        gap:22px;
    }

    .product-image{
        height:330px;
    }

    .price-stats{
        grid-template-columns:1fr;
        gap:8px;
    }

    .offer-row{
        grid-template-columns:1fr auto;
        gap:8px 12px;
        padding:12px;
    }

    .offer-main{
        grid-column:1 / -1;
    }

    .offer-meta{
        align-self:end;
    }

    .offer-row .price-amount{
        justify-items:end;
    }


    .admin-container{
        padding:28px 0 56px;
    }

    .analytics-summary,
    .admin-grid,
    .admin-product-grid,
    .admin-product-hero{
        grid-template-columns:1fr;
    }

    .analytics-summary{
        gap:8px;
    }

    .analytics-summary div{
        padding:16px;
    }

    .analytics-summary strong{
        font-size:28px;
    }

    .admin-panel{
        padding:18px;
    }

    .admin-product-image{
        height:160px;
    }

    .admin-card-actions .admin-action,
    .admin-card-actions .delete-btn,
    .admin-actions .admin-action,
    .admin-actions .delete-btn{
        flex:1 1 calc(50% - 8px);
        justify-content:center;
        width:auto;
        text-align:center;
    }

    .analytics-row{
        grid-template-columns:1fr 64px 64px;
        gap:10px;
    }

    .admin-price-list div{
        align-items:flex-start;
        flex-direction:column;
    }

    .admin-price-list .delete-btn{
        width:100%;
        justify-content:center;
    }

    .admin-table,
    .admin-table thead,
    .admin-table tbody,
    .admin-table tr,
    .admin-table td{
        display:block;
        width:100%;
    }

    .admin-table thead{
        display:none;
    }

    .admin-table tr{
        border:1px solid var(--lr-line);
        border-radius:8px;
        background:#111;
        padding:10px 12px;
        margin-top:10px;
    }

    .admin-table td{
        display:flex;
        justify-content:space-between;
        gap:16px;
        border-top:1px solid #292929;
        padding:10px 0;
        text-align:right;
        word-break:break-word;
    }

    .admin-table td:first-child{
        border-top:0;
    }

    .admin-table td::before{
        content:attr(data-label);
        color:var(--lr-muted);
        flex:0 0 auto;
        font-size:12px;
        font-weight:800;
        letter-spacing:.7px;
        text-align:left;
        text-transform:uppercase;
    }

    .history-chart{
        overflow-x:auto;
    }

    .line-chart{
        min-width:720px;
    }

    .footer-grid{
        width:calc(100% - 8px);
        grid-template-columns:1fr;
        gap:30px;
    }

    .site-footer{
        padding:40px 14px 48px;
    }

    .faq-section{
        width:calc(100% - 8px);
    }

    .footer-cta-section{
        width:calc(100% - 8px);
        flex-direction:column;
    }

    .footer-cta-button{
        width:100%;
    }
}

@media (max-width: 520px){
    body{
        background:#080808;
    }

    .hero{
        padding-top:26px;
    }

    .hero h1{
        font-size:34px;
    }

    .hero-text{
        font-size:16px;
    }

    .deal-left{
        height:235px;
        min-height:235px;
    }

    .deal-right h2,
    .product-info h1{
        font-size:28px;
    }

    .product-image{
        height:280px;
        padding:18px;
    }

    .prices-section{
        padding:42px 0 54px;
    }

    .offer-row{
        padding:10px;
    }

    .offer-row .price-amount strong{
        font-size:18px;
    }

    .best-label{
        font-size:10px;
        padding:5px 7px;
    }

    .admin-panel{
        padding:16px;
    }

    .admin-product-hero{
        padding:16px;
    }

    .admin-product-hero-image{
        height:150px;
    }

    .admin-actions .admin-action,
    .admin-actions .delete-btn{
        flex-basis:100%;
    }

    .site-footer h2{
        font-size:20px;
    }
}

/* === v2 leaderboard =====================================================
   New components for the leaderboard redesign (TDD-02). Everything below
   uses only the --lr-* tokens plus the two status hues. */

.visually-hidden,
.sr-only{
    position:absolute;
    width:1px;
    height:1px;
    margin:-1px;
    overflow:hidden;
    clip:rect(0 0 0 0);
    white-space:nowrap;
    border:0;
    padding:0;
}

/* --- page frame --- */

.lb-page{
    width:min(1180px, calc(100% - 40px));
    margin:0 auto;
    padding:44px 0 80px;
}

.lb-header{
    max-width:720px;
}

.lb-header .section-title{
    font-size:38px;
}

.lb-explainer{
    color:var(--lr-muted);
    line-height:1.6;
    margin-top:12px;
}

.lb-meta{
    color:var(--lr-soft);
    font-size:12px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
    margin-top:14px;
}

/* --- filter chips --- */

.lb-filters-wrap{
    margin:26px 0 20px;
}

.lb-filters{
    display:grid;
    gap:12px;
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:rgba(17,17,17,.96);
    padding:14px 16px;
}

.chip-group{
    display:grid;
    gap:7px;
}

.chip-group-label{
    color:var(--lr-muted);
    font-size:11px;
    font-weight:800;
    letter-spacing:1px;
    text-transform:uppercase;
}

.chip-row{
    display:flex;
    flex-wrap:wrap;
    gap:8px;
    align-items:center;
}

.chip{
    display:inline-flex;
    align-items:center;
    gap:7px;
    min-height:36px;
    padding:0 13px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface-2);
    color:var(--lr-soft);
    font-size:13px;
    font-weight:800;
    text-decoration:none;
    cursor:pointer;
    transition:border-color .15s, background .15s, color .15s;
}

.chip:hover{
    border-color:var(--lr-muted);
    color:var(--lr-text);
}

.chip.is-active{
    background:var(--lr-text);
    border-color:var(--lr-text);
    color:#050505;
}

.chip.is-disabled{
    opacity:.4;
    pointer-events:none;
}

.chip-count{
    font-size:11px;
    font-weight:800;
    color:var(--lr-muted);
}

.chip.is-active .chip-count{
    color:var(--lr-line);
}

.chip-apply{
    border:0;
    background:var(--lr-text);
    color:#050505;
    font:inherit;
    font-size:13px;
    font-weight:800;
}

.chip-clear{
    background:transparent;
}

.max-price-input{
    width:110px;
    min-height:36px;
    padding:0 12px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface-2);
    color:var(--lr-text);
    font:inherit;
    font-size:13px;
}

.max-price-input:focus{
    border-color:var(--lr-accent-deal-line);
    box-shadow:0 0 0 3px var(--lr-accent-deal-wash);
}

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

.lb-list{
    display:grid;
    grid-template-columns:minmax(0,1fr);
    gap:10px;
}

/* A long unbreakable offer label must not set the grid track's intrinsic
   width and push a phone leaderboard beyond the viewport. */
.lb-list > .lb-row{ min-width:0; width:100%; }

.lb-row{
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:rgba(17,17,17,.96);
    overflow:hidden;
    transition:border-color .15s;
}

.lb-row:hover{
    border-color:#484848;
}

.lb-row[open]{
    border-color:var(--lr-muted);
}

.lb-summary{
    display:grid;
    grid-template-columns:44px 48px minmax(0,1fr) auto auto auto auto;
    align-items:center;
    gap:16px;
    min-height:72px;
    padding:12px 18px;
    cursor:pointer;
    list-style:none;
}

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

.lb-summary:focus-visible{
    outline:2px solid var(--lr-text);
    outline-offset:-2px;
    border-radius:var(--lr-radius);
}

/* Rank is the anchor of the row: big numeral, accent for the podium. */
.lb-rank{
    font-size:27px;
    font-weight:800;
    font-variant-numeric:tabular-nums;
    color:var(--lr-muted);
    text-align:center;
    line-height:1;
}

.lb-rank.is-top{
    color:var(--lr-text);
}

.lb-thumb{
    width:48px;
    height:48px;
    border-radius:6px;
    background:#050505;
    border:1px solid var(--lr-line);
    overflow:hidden;
    display:block;
    flex-shrink:0;
}

.lb-thumb img{
    width:100%;
    height:100%;
    object-fit:contain;
    display:block;
}

.lb-name{
    display:grid;
    gap:4px;
    min-width:0;
}

.lb-name a{
    color:var(--lr-text);
    font-size:16px;
    font-weight:800;
    text-decoration:none;
    line-height:1.25;
}

.lb-name a:hover{
    text-decoration:underline;
}

.lb-name-meta{
    display:flex;
    align-items:center;
    gap:8px;
    min-width:0;
}

.lb-brand{
    color:var(--lr-muted);
    font-size:13px;
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
}

.lb-brand:empty{
    display:none;
}

.lb-chip{
    display:inline-flex;
    align-items:center;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface-2);
    color:var(--lr-soft);
    font-size:10px;
    font-weight:800;
    letter-spacing:.6px;
    text-transform:uppercase;
    padding:3px 8px;
    white-space:nowrap;
}

.lb-chip:empty{
    display:none;
}

/* The metric is the second-loudest element; sticker price is tertiary. */
.lb-metric{
    font-size:18px;
    font-weight:800;
    font-variant-numeric:tabular-nums;
    color:var(--lr-text);
    text-align:right;
    white-space:nowrap;
}

.lb-offer{
    display:flex;
    align-items:center;
    gap:9px;
}

.store-mark{
    width:22px;
    height:22px;
    border-radius:50%;
    border:1px solid var(--lr-line);
    background:var(--lr-surface-2);
    color:var(--lr-soft);
    font-size:11px;
    font-weight:800;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    flex-shrink:0;
}

.lb-offer-detail{
    display:grid;
    line-height:1.3;
}

.lb-offer-size{
    color:var(--lr-soft);
    font-size:12px;
    font-weight:600;
    white-space:nowrap;
}

.lb-offer-price{
    color:var(--lr-muted);
    font-size:12px;
    font-variant-numeric:tabular-nums;
}

.lb-expand{
    color:var(--lr-muted);
    font-size:12px;
    font-weight:800;
    white-space:nowrap;
}

.lb-expand::after{
    content:"";
    display:inline-block;
    width:7px;
    height:7px;
    border-right:2px solid currentColor;
    border-bottom:2px solid currentColor;
    margin-left:8px;
    transform:translateY(-2px) rotate(45deg);
    transition:transform .15s;
}

.lb-row[open] .lb-expand::after{
    transform:translateY(1px) rotate(225deg);
}

/* --- freshness badge --- */

.freshness{
    display:inline-flex;
    align-items:center;
    gap:6px;
    color:var(--lr-muted);
    font-size:12px;
    white-space:nowrap;
}

.freshness-dot{
    width:7px;
    height:7px;
    border-radius:50%;
    background:var(--lr-fresh);
    flex-shrink:0;
}

.freshness.is-stale .freshness-dot{
    background:var(--lr-stale);
}

.freshness.is-stale{
    color:var(--lr-stale);
}

.freshness-flag{
    border:1px solid var(--lr-stale);
    border-radius:999px;
    padding:1px 7px;
    font-size:10px;
    font-weight:800;
    letter-spacing:.6px;
    text-transform:uppercase;
}

/* --- expanded offer list --- */

.lb-offers{
    border-top:1px solid var(--lr-line);
    background:var(--lr-surface);
    padding:6px 18px 14px;
}

.offer-list{
    width:100%;
    border-collapse:collapse;
}

.offer-list th{
    color:var(--lr-muted);
    font-size:11px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
    text-align:left;
    padding:12px 10px 8px;
}

.offer-list td{
    border-top:1px solid #1f1f1f;
    padding:11px 10px;
    color:var(--lr-soft);
    font-size:13px;
    vertical-align:middle;
}

.offer-list td[data-label="Store"]{
    display:flex;
    align-items:center;
    gap:9px;
}

.offer-item.is-stale td{
    color:var(--lr-muted);
}

.offer-metric{
    color:var(--lr-text);
    font-size:14px;
    font-weight:800;
    font-variant-numeric:tabular-nums;
}

.offer-price{
    font-variant-numeric:tabular-nums;
}

/* ---- Coupon price (components/_offer_price.html) ---------------------------
   A struck-through regular price + a "with code" discounted price and code, so
   shoppers know the lower number only applies with the coupon. Ranking still
   uses the regular price; this is display-only. */
.price-with-coupon{ display:inline-flex; flex-wrap:wrap; align-items:baseline; gap:4px 6px; }
.price-was{ color:var(--lr-muted); text-decoration:line-through; text-decoration-thickness:1.5px; }
.price-now{ font-variant-numeric:tabular-nums; }
.price-with-coupon.has-coupon .price-now{ color:var(--lr-accent-deal-text); font-weight:800; }
.price-coupon{
    display:inline-flex; align-items:center; gap:4px;
    flex-basis:100%;
    margin-top:2px;
    padding:1px 7px;
    border-radius:999px;
    background:var(--lr-accent-deal-wash);
    border:1px solid var(--lr-accent-deal-line);
    color:var(--lr-accent-deal-text);
    font-size:11px; font-weight:700; line-height:1.5;
    width:max-content; max-width:100%;
}
.price-coupon-code{ font-family:var(--lr-mono, ui-monospace, monospace); letter-spacing:.02em; }
/* A custom `display` on .price-coupon would otherwise beat the UA [hidden]
   rule, so the empty "with code" tab must be hidden explicitly. */
.price-coupon[hidden], .price-was[hidden]{ display:none; }

/* `.offer-go` is now only a LAYOUT/JS hook on the shared crimson CTA (see
   .cta-deal). It used to be a white button here and crimson only when a later
   rule caught `.is-best`, which meant the site had two buy-button colours and
   the one a shopper met first depended on which table they opened. The colour
   declarations are gone rather than overridden further down, so there is no
   cascade race to lose. */
.offer-go{
    white-space:nowrap;
}

.lb-empty a{
    color:var(--lr-text);
    font-weight:800;
}

/* --- homepage: deal-of-the-day hero + category sections --- */

.dod-eyebrow{
    color:var(--lr-muted);
}

.dod-metric{
    display:flex;
    align-items:baseline;
    gap:12px;
    margin-top:22px;
    flex-wrap:wrap;
}

.dod-metric strong{
    font-size:40px;
    font-weight:800;
    font-variant-numeric:tabular-nums;
}

.dod-metric span{
    color:var(--lr-muted);
    font-size:14px;
    font-weight:600;
}

.dod-savings{
    display:inline-flex;
    align-items:center;
    width:max-content;
    margin-top:14px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface-2);
    color:var(--lr-soft);
    font-size:12px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
    padding:8px 12px;
}

/* --- homepage search results (no-JS search fallback) --- */

.search-results{
    width:min(1180px, calc(100% - 40px));
    margin:0 auto;
    padding:36px 0 22px;
}

.search-result-list{
    display:grid;
    gap:10px;
    margin-top:14px;
}

.search-result{
    display:flex;
    align-items:center;
    gap:14px;
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:rgba(17,17,17,.96);
    padding:12px 16px;
    text-decoration:none;
    color:var(--lr-text);
    transition:border-color .15s;
}

.search-result:hover{
    border-color:var(--lr-muted);
}

.search-result strong{
    font-size:15px;
}

.search-result span{
    color:var(--lr-muted);
    font-size:12px;
    font-weight:800;
    letter-spacing:.6px;
    text-transform:uppercase;
    margin-left:auto;
    white-space:nowrap;
}

/* --- product page: nutrition panel --- */

.nutrition-panel{
    max-width:420px;
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:var(--lr-surface);
    padding:22px;
}

.nutrition-panel h2{
    font-size:22px;
    padding-bottom:10px;
    border-bottom:4px solid var(--lr-text);
}

.nutrition-rows{
    display:grid;
}

.nutrition-rows div{
    display:flex;
    justify-content:space-between;
    gap:16px;
    border-bottom:1px solid #1f1f1f;
    padding:9px 0;
    font-size:14px;
}

.nutrition-rows div:last-child{
    border-bottom:0;
}

.nutrition-rows span{
    color:var(--lr-muted);
}

.nutrition-rows strong{
    font-variant-numeric:tabular-nums;
    text-align:right;
}

/* --- admin: supplement-label nutrition entry --- */

.product-form-columns{
    display:grid;
    grid-template-columns:1fr 1fr;
    gap:22px;
    align-items:start;
}

.product-form-columns > div{
    display:grid;
    gap:12px;
    min-width:0;
}

.nutrition-label{
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:var(--lr-surface-2);
    padding:18px;
    display:grid;
    gap:10px;
}

.nutrition-label-title{
    font-size:20px;
    font-weight:800;
    padding-bottom:8px;
    border-bottom:4px solid var(--lr-text);
    margin-bottom:4px;
}

.nutrition-label .field-label{
    margin:2px 0 -6px;
}

.rank-warning{
    border:1px solid var(--lr-stale);
    border-radius:var(--lr-radius);
    background:rgba(224,169,62,.07);
    color:var(--lr-stale);
    font-size:13px;
    font-weight:600;
    line-height:1.5;
    padding:12px 14px;
}

/* --- admin: health panel (the operator's inbox) --- */

.health-panel h2{
    margin-bottom:6px;
}

.health-intro{
    color:var(--lr-muted);
    margin-bottom:16px;
}

.health-section{
    border-left:3px solid var(--lr-line);
    padding-left:16px;
    margin-top:18px;
}

.health-section h3{
    font-size:13px;
    font-weight:800;
    letter-spacing:.8px;
    text-transform:uppercase;
    margin-bottom:4px;
}

.health-section-notices{
    border-left-color:#8d4545;
}

.health-section-notices h3{
    color:#d59696;
}

.health-section-failing{
    border-left-color:var(--lr-stale);
}

.health-section-failing h3{
    color:var(--lr-stale);
}

.health-section-missing{
    border-left-color:var(--lr-line);
}

.health-section-missing h3{
    color:var(--lr-muted);
}

.health-empty{
    color:var(--lr-muted);
    padding:6px 0;
}

/* --- admin: per-card offer intake --- */

.card-intake{
    border-top:1px solid var(--lr-line);
    padding:12px;
}

.card-intake summary{
    cursor:pointer;
    color:var(--lr-soft);
    font-size:12px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
    list-style:none;
}

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

.card-intake summary::after{
    content:"";
    display:inline-block;
    width:6px;
    height:6px;
    border-right:2px solid currentColor;
    border-bottom:2px solid currentColor;
    margin-left:8px;
    transform:translateY(-2px) rotate(45deg);
}

.card-intake[open] summary::after{
    transform:translateY(1px) rotate(225deg);
}

.card-intake form{
    display:grid;
    gap:8px;
    margin-top:10px;
}

.admin-container .card-intake textarea{
    min-height:76px;
    padding:12px 14px;
    font-size:13px;
}

/* --- toast (JS fetch-failure notice) --- */

.lr-toast{
    position:fixed;
    left:50%;
    bottom:26px;
    transform:translateX(-50%);
    z-index:200;
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:var(--lr-surface-2);
    color:var(--lr-text);
    font-size:14px;
    font-weight:600;
    padding:14px 18px;
    box-shadow:var(--lr-shadow);
}

/* --- nav: five category links need breathing room on smaller desktops ---

   The wrap that used to live here (nav{flex-wrap:wrap}) is gone. It turned a
   too-wide header into a TWO-ROW header — logo stranded alone top-left, search
   and the whole category run dropped to a second row — which is precisely the
   defect this file now guards against. Sizing below 1420px is handled fluidly
   by the "desktop header: one row, always" block at the end of this file, so
   the header narrows continuously instead of falling off a cliff at one width.
   Only the type scale stays here. */

@media (max-width: 1420px) and (min-width: 901px){
    .nav-links a{
        padding:11px 10px;
        font-size:12px;
        letter-spacing:.5px;
    }
}

/* --- sticky controls bar on desktop only (TDD-09: slim now, not a tall
   panel — the Filters/Sort buttons stay reachable while scrolling and the
   dropdown panels open on demand instead of overlapping the board) --- */

@media (min-width: 769px){
    .lb-filters-wrap{
        position:sticky;
        top:92px;
        z-index:50;
    }
}

/* --- mobile: rows reflow to stacked cards, chips scroll horizontally --- */

@media (max-width: 768px){
    .lb-page{
        width:calc(100% - 28px);
        padding:28px 0 56px;
    }

    .lb-header .section-title{
        font-size:30px;
    }

    /* The stacked leaderboard row is defined once, at the very end of this
       file — search "MOBILE LEADERBOARD ROW". It has to live last because
       several later top-level rules re-declare .lb-summary's columns, and a
       media query adds no specificity: whichever rule comes last simply wins.
       Splitting it across the file is exactly what broke it. */

    .chip-row{
        flex-wrap:nowrap;
        overflow-x:auto;
        -webkit-overflow-scrolling:touch;
        scrollbar-width:none;
        padding-bottom:2px;
    }

    .chip-row::-webkit-scrollbar{
        display:none;
    }

    .chip{
        min-height:44px;
        flex-shrink:0;
    }

    .max-price-input{
        min-height:44px;
    }

    .lb-offers{
        padding:4px 12px 12px;
        overflow-x:hidden;
    }

    .offer-list thead{
        display:none;
    }

    .offer-list,
    .offer-list tbody,
    .offer-list tr,
    .offer-list td{
        display:block;
        width:100%;
    }

    .offer-list tr{
        border-top:1px solid #1f1f1f;
        padding:10px 0;
    }

    .offer-list td{
        border-top:0;
        padding:3px 0;
        display:flex;
        justify-content:space-between;
        gap:14px;
    }

    .offer-list td::before{
        content:attr(data-label);
        color:var(--lr-muted);
        font-size:11px;
        font-weight:800;
        letter-spacing:.7px;
        text-transform:uppercase;
        flex-shrink:0;
    }

    .offer-list td[data-label=""]::before{
        content:none;
    }

    .offer-go{
        width:100%;
        min-height:44px;
    }

    .offer-go-cell{
        padding-top:8px;
    }

    

    

    

    .product-form-columns{
        grid-template-columns:1fr;
    }

    .dod-metric strong{
        font-size:32px;
    }
}

@media (prefers-reduced-motion: reduce){
    .lb-row,
    .chip,
    .offer-go,
    .lb-expand::after,
    .search-result{
        transition:none;
    }
}


/* === v3 crimson brand (TDD-04) ==========================================
   One accent, one meaning: crimson marks deals, savings, and the actions
   that capture them. Everything below uses --lr-* tokens only. */

/* --- drop badge: solid crimson chip, white text, angled corner (§3.4) --- */

.drop-badge{
    display:inline-flex;
    align-items:center;
    gap:5px;
    width:max-content;
    background:var(--lr-accent-deal);
    color:#fff;
    font-size:12px;
    font-weight:800;
    letter-spacing:.5px;
    padding:5px 10px;
    border-radius:4px;
    clip-path:polygon(6px 0, 100% 0, 100% 100%, 0 100%, 0 6px);
    white-space:nowrap;
    font-variant-numeric:tabular-nums;
}

.drop-badge:empty{
    display:none;
}

/* --- crimson primary CTA --- */

.crimson-button{
    background:var(--lr-accent-deal) !important;
    color:#fff !important;
}

.crimson-button:hover{
    background:var(--lr-accent-deal-hover) !important;
}

/* --- flash messages on customer pages --- */

.site-flashes{
    width:min(1180px, calc(100% - 40px));
    margin:18px auto 0;
    display:grid;
    gap:10px;
}

/* --- nav auth state --- */

.nav-signin{
    white-space:nowrap;
}

.nav-account-chip{
    display:inline-flex;
    align-items:center;
    gap:9px;
    border:1px solid var(--lr-line) !important;
    background:var(--lr-surface-2);
    border-radius:999px !important;
    padding:8px 14px 8px 8px !important;
}

.nav-account-initial{
    width:30px;
    height:30px;
    border-radius:50%;
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    color:var(--lr-text);
    display:inline-flex;
    align-items:center;
    justify-content:center;
    font-size:14px;
    font-weight:800;
}

/* --- footer account row + honesty note --- */

.footer-account-row{
    width:min(1180px, calc(100% - 40px));
    margin:30px auto 0;
    padding-top:22px;
    border-top:1px solid var(--lr-line);
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    gap:18px;
}

.footer-account-row a{
    font-weight:800;
    color:#fff;
}

/* Sign-out is a state change, so it posts rather than links (audit S-10: a GET
   that mutates can be fired by any third-party <img>). It must still *read* as
   one of the links beside it.

   This block only strips the button chrome. It deliberately sets no font,
   colour or decoration: each footer names its own link styling, and the button
   is added to those same selectors below so the two can never drift. Styling it
   independently here is what made it render bigger and underlined next to its
   siblings in the first place. */
.footer-signout{
    display:contents;
}

.footer-signout button{
    font:inherit;
    color:inherit;
    background:none;
    border:0;
    padding:0;
    cursor:pointer;
    border-radius:var(--r-sm);
}

.footer-honesty-note{
    color:var(--lr-muted);
    font-size:12px;
    margin-left:auto;
    max-width:52ch;
}

/* --- homepage hero scroll cue --- */

.hero-scroll-cue{
    display:block;
    margin-top:26px;
    color:var(--lr-muted);
    font-size:22px;
    animation:lr-cue 2.2s ease-in-out infinite;
}

@keyframes lr-cue{
    0%, 100%{ transform:translateY(0); opacity:.55; }
    50%{ transform:translateY(6px); opacity:1; }
}

/* --- deal of the day money row (§4.2) --- */

.dod-money{
    display:flex;
    align-items:center;
    flex-wrap:wrap;
    gap:12px;
    margin-top:20px;
}

.dod-price{
    font-size:44px;
    font-weight:800;
    font-variant-numeric:tabular-nums;
    line-height:1;
}

.dod-baseline{
    color:var(--lr-muted);
    font-size:20px;
    font-variant-numeric:tabular-nums;
}

.dod-badge{
    font-size:13px;
}

.dod-actions{
    display:flex;
    align-items:center;
    flex-wrap:wrap;
    gap:18px;
}

.dod-store-link{
    margin-top:28px;
    color:var(--lr-soft);
    font-size:13px;
    font-weight:600;
}

.dod-store-link:hover{
    color:var(--lr-text);
}

/* --- featured shelf: scroll-snap row, 4-up grid >=1024px (§4.3) --- */

.featured-section{
    width:min(1180px, calc(100% - 40px));
    margin:0 auto;
    padding:0 0 58px;
}

.featured-shelf{
    display:flex;
    gap:14px;
    overflow-x:auto;
    scroll-snap-type:x mandatory;
    -webkit-overflow-scrolling:touch;
    padding-bottom:6px;
}

.featured-card{
    flex:0 0 258px;
    scroll-snap-align:start;
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:rgba(17,17,17,.96);
    overflow:hidden;
    display:flex;
    flex-direction:column;
    transition:border-color .15s;
}

.featured-card:hover{
    border-color:var(--lr-muted);
}

.featured-thumb{
    display:block;
    height:170px;
    padding:14px;
}

.featured-thumb img{
    width:100%;
    height:100%;
    object-fit:contain;
    display:block;
}

.featured-body{
    display:grid;
    gap:7px;
    padding:14px 16px 16px;
}

.featured-name{
    color:var(--lr-text);
    font-size:15px;
    font-weight:800;
    text-decoration:none;
    line-height:1.3;
}

.featured-name:hover{
    text-decoration:underline;
}

.featured-metric{
    color:var(--lr-soft);
    font-size:13px;
    font-weight:600;
    font-variant-numeric:tabular-nums;
}

.featured-price-row{
    display:flex;
    align-items:center;
    gap:10px;
}

.featured-price{
    color:var(--lr-muted);
    font-size:14px;
    font-variant-numeric:tabular-nums;
}

.featured-go{
    margin-top:4px;
    color:var(--lr-soft);
    font-size:13px;
    font-weight:800;
    text-decoration:none;
}

.featured-go:hover{
    color:var(--lr-text);
}

@media (min-width: 1024px){
    .featured-shelf{
        display:grid;
        grid-template-columns:repeat(4, minmax(0, 1fr));
        overflow-x:visible;
        scroll-snap-type:none;
    }

    .featured-card{
        flex:none;
    }
}

/* --- small business spotlight: the one editorial moment (§4.4) --- */

.spotlight-section{
    width:min(1180px, calc(100% - 40px));
    margin:0 auto;
    padding:0 0 58px;
}

.spotlight-entry{
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:rgba(17,17,17,.96);
    padding:24px;
    margin-top:14px;
}

/* business-name eyebrow with the angled crimson underline (§3.4) */
.spotlight-business{
    display:inline-block;
    color:var(--lr-text);
    font-size:13px;
    font-weight:800;
    letter-spacing:1.6px;
    text-transform:uppercase;
    padding-bottom:8px;
    position:relative;
}

.spotlight-business::after{
    content:"";
    position:absolute;
    left:0;
    bottom:0;
    width:100%;
    height:4px;
    background:var(--lr-accent-deal);
    clip-path:polygon(0 0, calc(100% - 5px) 0, 100% 100%, 0 100%);
}

.spotlight-blurb{
    color:var(--lr-soft);
    line-height:1.65;
    max-width:65ch;
    margin-top:12px;
}

.spotlight-product{
    display:flex;
    align-items:center;
    flex-wrap:wrap;
    gap:14px;
    border-top:1px solid var(--lr-line);
    margin-top:18px;
    padding-top:16px;
}

.spotlight-thumb{
    width:56px;
    height:56px;
    border-radius:6px;
    border:1px solid var(--lr-line);
    overflow:hidden;
    flex-shrink:0;
}

.spotlight-thumb img{
    width:100%;
    height:100%;
    object-fit:contain;
    display:block;
}

.spotlight-name{
    color:var(--lr-text);
    font-weight:800;
    text-decoration:none;
    font-size:15px;
}

.spotlight-name:hover{
    text-decoration:underline;
}

.spotlight-metric{
    color:var(--lr-soft);
    font-size:13px;
    font-weight:600;
    font-variant-numeric:tabular-nums;
}

.spotlight-price{
    color:var(--lr-muted);
    font-size:14px;
    font-variant-numeric:tabular-nums;
}

.spotlight-go{
    margin-left:auto;
}

/* --- sort control (§5.1) --- */

.lb-controls{
    display:grid;
    gap:12px;
}

.sort-control{
    display:inline-flex;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface-2);
    padding:4px;
    width:max-content;
}

.sort-segment{
    display:inline-flex;
    align-items:center;
    min-height:36px;
    padding:0 16px;
    border-radius:999px;
    color:var(--lr-soft);
    font-size:13px;
    font-weight:800;
    text-decoration:none;
    white-space:nowrap;
    transition:background .15s, color .15s;
}

.sort-segment:hover{
    color:var(--lr-text);
}

/* The tick is hidden by default: on the horizontal row the fill already says
   which segment is selected, and a tick beside it is noise. The stacked
   layout re-shows it (see the mobile block). */
.sort-segment-check{
    display:none;
    flex:0 0 auto;
    margin-left:10px;
}

.sort-segment-label{
    flex:1 1 auto;
}

.sort-segment.is-active{
    background:var(--lr-text);
    color:#050505;
}

/* "Biggest drop" active = the one crimson segment (§3.3) */
.sort-segment-drop.is-active{
    background:var(--lr-accent-deal);
    color:#fff;
}

@media (min-width: 769px){
    .lb-controls{
        grid-template-columns:minmax(0, 1fr) auto;
        align-items:start;
    }
}

/* --- leaderboard row: metric cell + drop badge --- */

.lb-metric-cell{
    display:grid;
    justify-items:end;
    gap:5px;
}

.lb-drop{
    font-size:11px;
    padding:3px 8px;
}

/* rank slot doubles as the drop-mode lead badge */
.lb-rank.is-badge{
    font-size:13px;
    font-weight:800;
    color:#fff;
    background:var(--lr-accent-deal);
    border-radius:4px;
    clip-path:polygon(5px 0, 100% 0, 100% 100%, 0 100%, 0 5px);
    padding:5px 6px;
    font-variant-numeric:tabular-nums;
}

/* rank column collapses when no numeral belongs there (price/drop/stale) */
.lb-row.no-rank .lb-rank{
    display:none;
}

.lb-row.no-rank .lb-summary{
    grid-template-columns:48px minmax(0,1fr) auto auto auto auto;
}

/* --- stale tail (§5.2) --- */

.stale-divider{
    display:flex;
    align-items:center;
    gap:14px;
    color:var(--lr-muted);
    font-size:12px;
    font-weight:800;
    letter-spacing:.8px;
    text-transform:uppercase;
    padding:16px 4px 6px;
    white-space:nowrap;
}

.stale-divider span{
    overflow:hidden;
    text-overflow:ellipsis;
}

.stale-divider::before,
.stale-divider::after{
    content:"";
    height:1px;
    background:var(--lr-line);
    flex:1;
}

.lb-row.is-stale{
    opacity:.65;
}

.lb-row.is-stale .lb-thumb{
    filter:grayscale(.7);
}

.lb-last-seen{
    display:none;
    align-items:center;
    gap:6px;
    color:var(--lr-stale);
    font-size:12px;
    white-space:nowrap;
}

.lb-last-seen::before{
    content:"";
    width:7px;
    height:7px;
    border-radius:50%;
    background:var(--lr-stale);
    flex-shrink:0;
}

.lb-row.is-stale .lb-last-seen{
    display:inline-flex;
}

.lb-row.is-stale .lb-fresh-slot .freshness{
    display:none;
}

/* --- watch button (crimson when active — it captures a price advantage) --- */

.lb-offers-head{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:14px;
    padding:12px 10px 4px;
}

.lb-offers-title{
    color:var(--lr-muted);
    font-size:11px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
}

.watch-form{
    display:inline-flex;
}

.watch-btn{
    display:inline-flex;
    align-items:center;
    gap:8px;
    min-height:38px;
    padding:0 15px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface-2);
    color:var(--lr-soft);
    font:inherit;
    font-size:13px;
    font-weight:800;
    text-decoration:none;
    cursor:pointer;
    transition:border-color .15s, background .15s, color .15s;
}

.watch-btn:hover{
    border-color:var(--lr-muted);
    color:var(--lr-text);
}

.watch-dot{
    width:9px;
    height:9px;
    border-radius:50%;
    border:2px solid currentColor;
    position:relative;
    flex-shrink:0;
}

.watch-dot::after{
    content:"";
    position:absolute;
    inset:2px;
    border-radius:50%;
    background:currentColor;
}

.watch-btn.is-watching{
    background:var(--lr-accent-deal);
    border-color:var(--lr-accent-deal);
    color:#fff;
}

.watch-btn.is-watching:hover{
    background:var(--lr-accent-deal-hover);
    border-color:var(--lr-accent-deal-hover);
    color:#fff;
}

/* --- product page: watch hero + deal banner (§6.2–6.3) --- */

.hero-drop-badge{
    margin-top:18px;
    font-size:13px;
}

.watch-hero{
    margin-top:18px;
}

.watch-hero-btn{
    min-height:48px;
    padding:0 20px;
    font-size:14px;
}

.watch-hero-form{
    display:grid;
    gap:12px;
    justify-items:start;
}

.watch-active-row{
    display:flex;
    align-items:center;
    flex-wrap:wrap;
    gap:14px;
}

.watch-target-inline{
    display:flex;
    align-items:center;
    flex-wrap:wrap;
    gap:10px;
    color:var(--lr-soft);
    font-size:14px;
}

.watch-target-inline input{
    width:110px;
    min-height:44px;
    padding:0 12px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    color:#fff;
    font:inherit;
    font-size:16px; /* prevents iOS zoom-jump (§11) */
}

.watch-target-inline input:focus{
    border-color:var(--lr-accent-deal-line);
    box-shadow:0 0 0 3px var(--lr-accent-deal-wash);
}

.watch-target-hint{
    color:var(--lr-muted);
    font-size:13px;
}

.watch-target-edit{
    position:relative;
}

.watch-target-edit summary{
    cursor:pointer;
    color:var(--lr-soft);
    font-size:13px;
    font-weight:800;
    list-style:none;
    border:1px solid var(--lr-line);
    border-radius:999px;
    padding:10px 14px;
}

.watch-target-edit summary::-webkit-details-marker{
    display:none;
}

.watch-target-form{
    display:flex;
    align-items:center;
    flex-wrap:wrap;
    gap:10px;
    margin-top:10px;
    color:var(--lr-soft);
    font-size:14px;
}

.watch-target-form input{
    width:110px;
    min-height:44px;
    padding:0 12px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    color:#fff;
    font:inherit;
    font-size:16px;
}

.watch-target-form button{
    min-height:44px;
    border:0;
    border-radius:8px;
    padding:0 16px;
    background:#fff;
    color:#050505;
    font-weight:800;
    cursor:pointer;
}

/* best offer row gets the crimson View Deal treatment (§6.3) */
.offer-row.is-best{
    border-color:var(--lr-accent-deal-line);
}

.offer-row.is-best .offer-go{
    background:var(--lr-accent-deal);
    color:#fff;
}

.offer-row.is-best .offer-go:hover{
    background:var(--lr-accent-deal-hover);
}

.offer-row .best-label[hidden]{
    display:none;
}

.offer-row.is-stale{
    opacity:.65;
}

.product-filters{
    margin-bottom:18px;
}

/* --- auth pages (§7): small centered single-card forms --- */

.auth-page{
    display:grid;
    justify-items:center;
    padding:64px 20px 90px;
}

.auth-card{
    width:min(420px, 100%);
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:rgba(17,17,17,.96);
    box-shadow:var(--lr-shadow);
    padding:30px 28px 28px;
}

.auth-card h1{
    font-size:26px;
    line-height:1.2;
    margin-top:10px;
}

.auth-sub{
    color:var(--lr-muted);
    line-height:1.6;
    margin-top:12px;
}

.auth-form{
    display:grid;
    gap:12px;
    margin-top:20px;
}

/* Text fields only.

   This was a bare `.auth-form input`, so it also matched the two consent
   CHECKBOXES on the signup form. `min-height:48px` outranks the `height:17px`
   they set for themselves, so each box rendered 48px tall and its centre landed
   16px below the first line of its own label — the misalignment on the "I agree
   to the Terms" and newsletter rows. Excluding the box types here fixes the
   cause; the site-wide `input[type=checkbox]` rule then gives them their proper
   24px hit area. */
.auth-form input:not([type="checkbox"]):not([type="radio"]){
    width:100%;
    min-height:48px;
    padding:0 14px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    color:#fff;
    font:inherit;
    font-size:16px;
}

.auth-form input:not([type="checkbox"]):not([type="radio"]):focus{
    border-color:var(--lr-accent-deal-line);
    box-shadow:0 0 0 3px var(--lr-accent-deal-wash);
}

.auth-submit{
    min-height:50px;
    border:0;
    border-radius:8px;
    background:var(--lr-accent-deal);
    color:#fff;
    font:inherit;
    font-size:15px;
    font-weight:800;
    cursor:pointer;
    transition:background .15s;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    text-decoration:none;
}

.auth-submit:hover{
    background:var(--lr-accent-deal-hover);
}

.auth-submit.secondary{
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    color:#fff;
}

.auth-submit.secondary:hover{
    border-color:var(--lr-muted);
    background:var(--lr-surface-2);
}

.auth-cta-row{
    margin-top:20px;
}

.auth-alt{
    color:var(--lr-muted);
    font-size:14px;
    margin-top:16px;
}

.auth-alt a{
    color:#fff;
    font-weight:800;
}

.auth-unverified{
    border:1px solid var(--lr-stale);
    border-radius:var(--lr-radius);
    background:rgba(224,169,62,.06);
    padding:16px;
    margin-top:18px;
}

.auth-unverified h2{
    font-size:16px;
}

.auth-unverified p{
    color:var(--lr-soft);
    font-size:14px;
    line-height:1.55;
    margin-top:8px;
}

.auth-callout{
    border:1px solid var(--lr-accent-deal-line);
    border-radius:var(--lr-radius);
    background:var(--lr-accent-deal-wash);
    color:var(--lr-soft);
    font-size:14px;
    line-height:1.5;
    padding:12px 14px;
    margin-bottom:18px;
}

.auth-callout a{
    color:var(--lr-accent-deal-text);
    font-weight:800;
}

.form-errors{
    list-style:none;
    border:1px solid #8d4545;
    border-radius:var(--lr-radius);
    background:rgba(196,65,75,.08);
    color:#e8b4b8;
    font-size:14px;
    line-height:1.6;
    padding:12px 16px;
    margin-top:16px;
    display:grid;
    gap:4px;
}

/* --- account page (§7) --- */

.account-page{
    width:min(920px, calc(100% - 40px));
    margin:0 auto;
    padding:44px 0 80px;
}

/* The signed-in header. Used by /account, /account/preferences,
   /account/notifications, /stack, /planner, /collections and the shared
   collection page — and it had no rule at all, so the eyebrow, the <h1> and
   the standfirst sat on whatever default margins the browser supplied. */
.account-header{ margin-bottom:var(--sp-6); }
.account-header h1{
    font-size:clamp(28px, 6vw, 40px);
    line-height:var(--lh-tight);
    letter-spacing:-.02em;
    margin-top:var(--sp-2);
}

.account-sub{
    color:var(--lr-muted);
    line-height:1.6;
    margin-top:12px;
    max-width:62ch;
}

/* "What watching has done for you", and it rendered as three bare text lines
   jammed together — .account-stats and its children had no CSS whatsoever.
   Borrowed from .disc-figures on the public discover pages rather than
   invented, so the signed-in side reads as the same site. */
.account-stats{
    display:flex;
    flex-wrap:wrap;
    gap:var(--sp-3) 0;
    margin-top:var(--sp-5);
    padding-top:var(--sp-5);
    border-top:1px solid var(--lr-line);
}
.account-stat{
    padding:0 22px;
    border-left:1px solid var(--lr-line);
}
.account-stat:first-child{ padding-left:0; border-left:0; }
.account-stat-value{
    display:block;
    font-family:var(--lr-display);
    font-size:clamp(19px, 2.4vw, 26px);
    line-height:1.1;
    font-variant-numeric:tabular-nums;
    color:var(--lr-text);
}
.account-stat-label{
    display:block;
    margin-top:6px;
    font-size:11px;
    font-weight:var(--fw-semi);
    letter-spacing:.1em;
    text-transform:uppercase;
    color:var(--lr-dim);
}
/* On a phone three figures do not fit a row; two columns keep them as figures
   rather than collapsing back into a list of sentences. */
@media (max-width: 560px){
    .account-stats{ display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-4) 0; }
    .account-stat{ padding:0 var(--sp-4); }
    .account-stat:nth-child(odd){ padding-left:0; border-left:0; }
}

/* A <button> that is really a link — "Cancel this change" inside the pending
   email banner. Unstyled, so it arrived as a grey OS button mid-sentence. */
.link-button{
    border:0; padding:0; background:none;
    color:var(--lr-accent-deal-text);
    font:inherit; text-decoration:underline; cursor:pointer;
}
.link-button:hover{ color:var(--lr-text); }
.account-banner-action{ display:inline; }

.watchlist{
    display:grid;
    gap:10px;
    margin-top:26px;
}

.watch-row{
    display:grid;
    /* Each row is its own grid container, so `auto` columns sized to their own
       content and no two rows lined up: a row whose alert mode is "below my
       target" carries an extra price input, which shoved its price and label
       columns left of every other row's. Fixed tracks make the five columns
       land in the same place down the whole list. */
    grid-template-columns:56px minmax(0,1.4fr) minmax(0,1fr) minmax(0,300px) minmax(0,104px);
    align-items:center;
    gap:16px;
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:rgba(17,17,17,.96);
    padding:14px 16px;
}

.watch-thumb{
    width:56px;
    height:56px;
    border-radius:6px;
    border:1px solid var(--lr-line);
    background:#050505;
    overflow:hidden;
    display:block;
}

.watch-thumb img{
    width:100%;
    height:100%;
    object-fit:contain;
    display:block;
}

.watch-name{
    display:grid;
    gap:4px;
    min-width:0;
}

.watch-name a{
    color:var(--lr-text);
    font-weight:800;
    text-decoration:none;
    font-size:15px;
    line-height:1.3;
}

.watch-name a:hover{
    text-decoration:underline;
}

.watch-alerted{
    color:var(--lr-muted);
    font-size:12px;
}

.watch-price{
    display:grid;
    gap:2px;
    justify-items:start;
}

.watch-price-label{
    color:var(--lr-muted);
    font-size:11px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
}

.watch-price strong{
    font-size:18px;
    font-variant-numeric:tabular-nums;
}

.watch-price small{
    color:var(--lr-muted);
    font-size:12px;
}

.watch-noprice{
    color:var(--lr-stale);
    font-size:13px;
}

.watch-target{
    display:grid;
    gap:5px;
}

.watch-target-label{
    color:var(--lr-muted);
    font-size:11px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
}

.watch-target-row{
    display:flex;
    gap:8px;
}

.watch-target-row input{
    width:96px;
    min-height:44px;
    padding:0 10px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-surface-2);
    color:#fff;
    font:inherit;
    font-size:16px;
}

.watch-target-save{
    min-height:44px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    padding:0 12px;
    background:var(--lr-surface-2);
    color:#fff;
    font:inherit;
    font-size:13px;
    font-weight:800;
    cursor:pointer;
}

.watch-target-save:hover{
    border-color:var(--lr-muted);
}

.watch-remove button{
    min-height:44px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    padding:0 14px;
    background:transparent;
    color:var(--lr-muted);
    font:inherit;
    font-size:13px;
    font-weight:800;
    cursor:pointer;
}

.watch-remove button:hover{
    color:#fff;
    border-color:var(--lr-muted);
}

.empty-links{
    display:flex;
    flex-wrap:wrap;
    gap:10px;
    margin-top:14px;
}

.empty-links a{
    color:#fff;
    font-weight:800;
}

.account-footer{
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    gap:18px;
    border-top:1px solid var(--lr-line);
    margin-top:34px;
    padding-top:20px;
    color:var(--lr-muted);
    font-size:14px;
}

.account-footer a,
.account-footer .footer-signout button{
    color:#fff;
    font-weight:800;
    margin-left:auto;
}

/* --- admin mini-site chrome (neutral — crimson stays out, §13.8) --- */

.admin-nav{
    display:flex;
    align-items:center;
    gap:18px;
    padding:14px 28px;
    border-bottom:1px solid var(--lr-line);
    background:rgba(8,8,8,.9);
    position:sticky;
    top:0;
    z-index:100;
    flex-wrap:wrap;
}

.admin-wordmark{
    color:#fff;
    font-size:16px;
    font-weight:800;
    letter-spacing:2px;
    text-decoration:none;
    white-space:nowrap;
}

.admin-wordmark span{
    color:var(--lr-muted);
    font-size:11px;
    font-weight:600;
    letter-spacing:1px;
    text-transform:uppercase;
    margin-left:6px;
}

.admin-sections{
    display:flex;
    align-items:center;
    gap:6px;
    flex-wrap:wrap;
}

.admin-section-link{
    color:var(--lr-soft);
    font-size:13px;
    font-weight:800;
    text-decoration:none;
    border:1px solid transparent;
    border-radius:8px;
    padding:9px 12px;
    white-space:nowrap;
    transition:.15s;
}

.admin-section-link:hover{
    color:#fff;
    background:var(--lr-surface-2);
}

.admin-section-link.is-active{
    background:#fff;
    color:#050505;
}

.admin-body .admin-nav-actions{
    margin-left:auto;
}

.health-strip{
    display:grid;
    grid-template-columns:repeat(auto-fit, minmax(180px, 1fr));
    gap:12px;
    margin-bottom:18px;
}

.health-tile{
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    background:var(--lr-surface);
    padding:18px;
}

.health-tile span{
    display:block;
    color:var(--lr-muted);
    font-size:12px;
    font-weight:800;
    letter-spacing:.7px;
    text-transform:uppercase;
}

.health-tile strong{
    display:block;
    font-size:32px;
    margin-top:8px;
    font-variant-numeric:tabular-nums;
}

.health-tile.is-warning{
    border-color:var(--lr-stale);
}

.dashboard-notices{
    border-color:#8d4545;
    margin-bottom:18px;
}

.dod-preview{
    display:flex;
    align-items:center;
    flex-wrap:wrap;
    gap:16px;
}

.dod-preview img{
    width:56px;
    height:56px;
    object-fit:contain;
    border-radius:6px;
    border:1px solid var(--lr-line);
    background:#050505;
}

.dod-preview div{
    display:grid;
    gap:3px;
}

.dod-preview-math{
    margin-left:auto;
    text-align:right;
}

.dod-preview-math strong{
    font-size:22px;
    color:var(--lr-accent-deal-text);
    font-variant-numeric:tabular-nums;
}

.products-search{
    display:flex !important;
    align-items:center;
    flex-wrap:wrap;
    gap:12px;
    margin-bottom:18px;
}

.products-search input[type="search"]{
    flex:1 1 240px;
    width:auto;
    padding:14px 16px;
}

.products-search select{
    width:auto;
    padding:14px 16px;
}

.products-search button,
.products-search .admin-action{
    width:auto;
    min-height:48px;
    margin-top:0;
    padding:0 18px;
}

.products-problems-toggle{
    white-space:nowrap;
}

.refresh-btn{
    background:transparent;
    cursor:pointer;
    font:inherit;
    font-size:13px;
    font-weight:800;
}

.offer-actions{
    justify-content:flex-end;
}

.featured-admin-name{
    display:inline-flex;
    align-items:center;
    gap:10px;
}

.featured-admin-name img{
    width:36px;
    height:36px;
    object-fit:contain;
    border-radius:6px;
    border:1px solid var(--lr-line);
    background:#050505;
}

.featured-status{
    font-size:12px;
    font-weight:800;
}

.featured-status.is-live{
    color:var(--lr-fresh);
}

.featured-status.is-off{
    color:var(--lr-stale);
}

.featured-actions{
    justify-content:flex-end;
}

.featured-actions .admin-action{
    cursor:pointer;
    font:inherit;
    background:transparent;
}

.featured-actions .admin-action[disabled]{
    opacity:.35;
    pointer-events:none;
}

.feature-type-row{
    display:flex;
    gap:22px;
    flex-wrap:wrap;
}

.clicks-tabs{
    display:flex;
    gap:6px;
    flex-wrap:wrap;
    border-bottom:1px solid var(--lr-line);
    margin-bottom:20px;
    padding-bottom:12px;
}

.clicks-tab{
    color:var(--lr-soft);
    font-size:13px;
    font-weight:800;
    text-decoration:none;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface-2);
    padding:9px 15px;
    transition:.15s;
}

.clicks-tab:hover{
    color:#fff;
    border-color:var(--lr-muted);
}

.clicks-tab.is-active{
    background:#fff;
    border-color:#fff;
    color:#050505;
}

.clicks-windows{
    display:flex;
    gap:6px;
}

/* the clicks chart is one of admin's two crimson moments (§3.3) */
.clicks-chart .price-line{
    stroke:#C4414B;
}

/* The promo card styles that used to live here went with the two-preview
   exporter they were written for. The Promo Studio that replaced it is admin
   chrome, so its styles live in admin.css under .ps-* with the rest of the
   Aperture system. */

/* --- responsive: mobile is designed, not reflowed (§11) --- */

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

    .sort-control{
        width:100%;
        overflow-x:auto;
        -webkit-overflow-scrolling:touch;
        scrollbar-width:none;
    }

    .sort-control::-webkit-scrollbar{
        display:none;
    }

    .sort-segment{
        flex:1 0 auto;
        justify-content:center;
        min-height:44px;
    }

    /* no-rank variant: see "MOBILE LEADERBOARD ROW" at the end of this file. */

    .featured-card{
        flex-basis:66vw;
    }

    .watch-row{
        grid-template-columns:56px minmax(0,1fr);
        row-gap:10px;
    }

    /* Thumb and name share the first line; everything under it spans the whole
       card. Pinning these to column 2 left the 56px thumb column empty for
       three rows — a hollow gutter down the left of every card, and 72px of a
       390px screen spent on nothing. */
    .watch-price,
    .watch-target,
    .watch-remove{
        grid-column:1 / -1;
    }

    /* The form is label-over-control, not label-beside-control: making the
       <form> itself a flex row put "Alert me on" next to the select and left
       Save stranded underneath. */
    .watch-target{ display:grid; gap:var(--sp-1); }
    .watch-remove{ justify-self:start; }

    .footer-honesty-note{
        margin-left:0;
    }

    .dod-price{
        font-size:36px;
    }

    /* price + badge lock together; the badge never wraps away (§11) */
    .dod-money{
        row-gap:8px;
    }
}

@media (max-width: 520px){
    .featured-card{
        flex-basis:78vw; /* peeking half-card scroll affordance (§11) */
    }

    .auth-card{
        padding:24px 18px;
    }

    .auth-submit{
        width:100%;
    }

    .spotlight-go{
        margin-left:0;
        width:100%;
    }

    .account-page{
        width:calc(100% - 28px);
    }
}

@media (prefers-reduced-motion: reduce){
    .hero-scroll-cue{
        animation:none;
    }

    .sort-segment,
    .watch-btn,
    .drop-badge,
    .featured-card,
    .clicks-tab,
    .admin-section-link,
    .auth-submit{
        transition:none;
    }
}

/* =========================================================================
   TDD-05 / TDD-06 — a11y floor, error & legal pages, account settings,
   signup nudge, chart enhancement, password UX, disclaimers
   ========================================================================= */

/* --- Global focus system (TDD-05 §9): one crimson ring everywhere. ------ */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible{
    outline:2px solid var(--lr-accent-deal);
    outline-offset:2px;
}

/* Skip link: first element in <body>, visible only when focused. */
.skip-link{
    position:absolute;
    left:-9999px;
    top:0;
    z-index:200;
    padding:10px 18px;
    background:var(--lr-accent-deal);
    color:#fff;
    font-weight:700;
    border-radius:0 0 8px 0;
}

.skip-link:focus{
    left:0;
}

/* --- Branded error pages (TDD-05 §7). ----------------------------------- */
.error-page{
    max-width:640px;
    margin:0 auto;
    padding:80px 20px 120px;
    text-align:center;
}

.error-numeral{
    font-size:clamp(96px,20vw,160px);
    font-weight:800;
    line-height:1;
    color:var(--lr-accent-deal);
    margin:0 0 12px;
}

.error-message{
    font-size:18px;
    color:var(--lr-soft);
    margin:0 0 28px;
}

/* The 404 had no layout for its own controls: .error-search had no rule at all
   (it borrowed .nav-search-form, a flex built for the nav pill) and
   .error-links set only a margin. The result was three buttons at three
   widths on three different alignments — a left-aligned "Search" under a
   full-width field, then a centred wide button, then a centred narrow one. */
.error-search{
    display:flex;
    flex-wrap:wrap;
    gap:var(--sp-2);
    justify-content:center;
    margin:0 auto var(--sp-5);
    max-width:420px;
}
.error-search .input{ flex:1 1 200px; }
.error-search .btn{ flex:0 0 auto; }

.error-links{
    display:flex;
    flex-wrap:wrap;
    gap:var(--sp-3);
    justify-content:center;
    margin-bottom:36px;
}

.error-leaderboards ul{
    list-style:none;
    margin:var(--sp-3) 0 0;
    padding:0;
    display:flex;
    flex-wrap:wrap;
    gap:var(--sp-2);
    justify-content:center;
}

/* Underlined links wrapping across two centred lines read as raw markup. The
   boards are destinations, so they get the same quiet chip the rest of the
   site uses for one. */
.error-leaderboards a{
    display:inline-flex;
    align-items:center;
    min-height:40px;
    padding:0 var(--sp-3);
    border:1px solid var(--lr-line);
    border-radius:var(--r-pill);
    color:var(--lr-soft);
    text-decoration:none;
    font-size:var(--fs-sm);
    transition:border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.error-leaderboards a:hover{ border-color:var(--lr-dim); color:var(--lr-text); }

/* --- Legal pages (TDD-06 §4): one readable prose column. ---------------- */
.legal-page{
    max-width:72ch;
    margin:0 auto;
    padding:56px 20px 96px;
}

.legal-page h1{
    font-size:32px;
    margin:6px 0 4px;
}

.legal-page h2{
    font-size:19px;
    margin:34px 0 10px;
}

.legal-page p{
    line-height:1.7;
    color:var(--lr-soft);
    margin:0 0 14px;
}

.legal-page a{
    color:var(--lr-text);
    text-decoration:underline;
    text-underline-offset:3px;
}

.legal-page pre{
    background:var(--lr-surface);
    border:1px solid var(--lr-line);
    border-radius:8px;
    padding:14px 18px;
    overflow-x:auto;
}

.legal-updated{
    font-size:13px;
    color:var(--lr-muted);
}

.legal-review-banner{
    background:rgba(196,65,75,.12);
    border:1px solid var(--lr-accent-deal);
    color:var(--lr-accent-deal-text);
    border-radius:8px;
    padding:12px 16px;
    font-size:14px;
    font-weight:600;
    margin-bottom:24px;
}

.review-marker{
    color:var(--lr-accent-deal-text);
    font-size:13px;
}

.unsubscribe-message{
    color:var(--lr-soft);
    line-height:1.6;
    margin:12px 0 22px;
}

/* --- Footer legal row (TDD-06 §4.4). ------------------------------------ */
.footer-legal-row{
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    gap:8px 22px;
    padding:16px 24px 22px;
    border-top:1px solid var(--lr-line);
    font-size:12px;
}

.footer-legal-row a{
    color:var(--lr-muted);
    text-decoration:underline;
    text-underline-offset:3px;
}

.footer-affiliate-note,
.footer-disclaimer-note{
    color:var(--lr-muted);
    flex-basis:100%;
    line-height:1.6;
}

/* --- On-page data disclaimers (TDD-06 §8). ------------------------------ */
.data-disclaimer{
    color:var(--lr-muted);
    font-size:12.5px;
    line-height:1.6;
    max-width:62ch;
    margin:10px 0 0;
}

/* --- Account banners + settings (TDD-05 §12 / TDD-06 §7). --------------- */
.account-banner{
    background:var(--lr-surface);
    border:1px solid var(--lr-line);
    border-radius:8px;
    padding:12px 16px;
    font-size:14px;
    color:var(--lr-soft);
    margin:0 0 14px;
}

.account-banner a{
    color:var(--lr-text);
    text-decoration:underline;
    text-underline-offset:3px;
}

.account-banner-warning{
    border-color:var(--lr-stale);
    color:var(--lr-stale);
}

.account-settings{
    margin-top:48px;
}

.settings-title{
    font-size:18px;
    margin:28px 0 12px;
}

.settings-panel{
    background:var(--lr-surface);
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    padding:14px 18px;
    margin-bottom:10px;
}

/* The <summary> is the control, but it sat inside the panel's padding, so only
   a 24px-tall strip actually toggled — the 14px above and below it looked
   tappable and was not. Negative margins push the summary out to the panel's
   edges so the whole header is the target, at the 44px touch minimum.

   It also had no affordance: bold text in a bordered box reads as a label, not
   as something that opens. A chevron says which it is, and rotates when open. */
.settings-panel summary{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:var(--sp-3);
    min-height:44px;
    margin:-14px -18px;
    padding:var(--sp-2) 18px;
    border-radius:inherit;
    cursor:pointer;
    font-weight:600;
}
.settings-panel summary::-webkit-details-marker,
.settings-panel summary::marker{ display:none; content:""; }
.settings-panel summary::after{
    content:"";
    flex:none;
    width:9px; height:9px;
    border-right:1.8px solid var(--lr-dim);
    border-bottom:1.8px solid var(--lr-dim);
    transform:translateY(-2px) rotate(45deg);
    transition:transform var(--dur-fast) var(--ease);
}
.settings-panel[open] > summary{ margin-bottom:var(--sp-2); }
.settings-panel[open] > summary::after{ transform:translateY(2px) rotate(-135deg); }
.settings-panel summary:hover::after{ border-color:var(--lr-text); }

.settings-panel-static a{
    color:var(--lr-text);
    font-weight:600;
    text-decoration:underline;
    text-underline-offset:3px;
}

.settings-form{
    margin-top:14px;
    max-width:380px;
}

.settings-note{
    font-size:12.5px;
    color:var(--lr-muted);
    line-height:1.6;
    margin:8px 0 0;
}

.settings-inline-form{
    display:flex;
    align-items:center;
    gap:14px;
    flex-wrap:wrap;
}

.alerts-toggle{
    display:flex;
    align-items:center;
    gap:10px;
    font-size:14.5px;
    cursor:pointer;
}

.alerts-toggle input{
    width:18px;
    height:18px;
    accent-color:var(--lr-accent-deal);
}

.settings-small-btn{
    padding:8px 18px;
    min-height:0;
}

.settings-panel-danger{
    border-color:rgba(196,65,75,.55);
}

.settings-danger-btn{
    background:var(--lr-accent-deal);
}

/* --- Signup-nudge slide-in (TDD-05 §12.5): a corner card, never a modal. - */
.signup-nudge{
    position:fixed;
    right:18px;
    bottom:18px;
    z-index:90;
    width:min(320px, calc(100vw - 36px));
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    border-radius:12px;
    box-shadow:var(--lr-shadow);
    padding:18px 20px;
    transform:translateY(16px);
    opacity:0;
    transition:transform .35s ease, opacity .35s ease;
}

.signup-nudge.is-visible{
    transform:translateY(0);
    opacity:1;
}

.nudge-title{
    font-weight:800;
    font-size:16px;
    margin:0 0 6px;
}

.nudge-text{
    font-size:13.5px;
    color:var(--lr-soft);
    line-height:1.55;
    margin:0 0 14px;
}

.nudge-cta{
    /* keep .btn--block's flex centering — inline-block here left-aligned the
       label because this rule cascades after .btn--block */
    display:flex;
}

.nudge-dismiss{
    position:absolute;
    top:8px;
    right:10px;
    background:none;
    border:0;
    color:var(--lr-muted);
    font-size:20px;
    line-height:1;
    cursor:pointer;
    padding:4px;
}

@media (prefers-reduced-motion: reduce){
    .signup-nudge{
        transition:none;
        transform:none;
    }
}

/* --- Password UX (TDD-05 §13.5). ---------------------------------------- */
.password-field{
    position:relative;
}

.password-field input{
    width:100%;
    padding-right:64px;
}

/* Sits inside the password field, so it can grow to a proper touch target
   without changing the layout around it — it was 27px tall, the smallest
   control on the two pages every account starts from. */
.password-toggle{
    position:absolute;
    right:6px;
    top:50%;
    transform:translateY(-50%);
    display:inline-flex;
    align-items:center;
    justify-content:center;
    min-height:40px;
    min-width:48px;
    background:none;
    border:0;
    border-radius:var(--r-sm);
    color:var(--lr-muted);
    font:inherit;
    font-size:12.5px;
    font-weight:600;
    cursor:pointer;
    padding:0 var(--sp-2);
}
.password-toggle:hover{ color:var(--lr-text); }

.password-hint{
    min-height:18px;
    font-size:12.5px;
    color:var(--lr-stale);
    margin:6px 0 0;
}

.consent-row{
    display:flex;
    align-items:flex-start;
    gap:10px;
    /* min-height, not padding: the consent text usually wraps to two lines and
       already clears 44px — this only lifts the single-line case (the optional
       newsletter opt-in) to a tappable size. */
    min-height:44px;
    padding-top:var(--sp-1);
    font-size:13.5px;
    color:var(--lr-soft);
    line-height:1.55;
    margin:14px 0 4px;
    cursor:pointer;
}

/* No width/height here: 17px fought the site-wide 24px checkbox rule, which is
   the WCAG 2.5.8 target size. The box keeps the standard 24px and is nudged to
   sit optically on the first line of its label. */
.consent-row input{
    flex:none;
    margin-top:1px;
    accent-color:var(--lr-accent-deal);
}

.consent-row a{
    color:var(--lr-text);
    text-decoration:underline;
    text-underline-offset:3px;
}

/* --- Search dropdown additions (TDD-05 §11). ----------------------------- */
.search-suggestion{
    display:flex;
    align-items:center;
    gap:10px;
}

.search-suggestion.is-active{
    background:#181818;
}

.search-suggestion-thumb{
    width:28px;
    height:28px;
    object-fit:contain;
    border-radius:4px;
    background:#000;
    flex:none;
}

.search-suggestion span{
    margin-left:auto;
    font-variant-numeric:tabular-nums;
}

.search-no-results{
    color:var(--lr-muted);
    font-size:13.5px;
}

/* --- Chart enhancement (TDD-05 §10). ------------------------------------- */
.chart-range-control{
    flex:none;
}

.chart-store-chips{
    display:flex;
    flex-wrap:wrap;
    gap:8px;
    margin:0 0 14px;
}

.chart-store-chip{
    font:inherit;
    font-size:12.5px;
    padding:6px 12px;
    border-radius:999px;
    border:1px solid var(--lr-line);
    background:var(--lr-surface);
    color:var(--lr-muted);
    cursor:pointer;
}

.chart-store-chip.is-active{
    color:var(--lr-text);
    border-color:var(--lr-muted);
}

.history-chart{
    position:relative;
}

.chart-tooltip{
    position:absolute;
    background:#000;
    border:1px solid var(--lr-line);
    border-radius:6px;
    color:var(--lr-text);
    font-size:12.5px;
    font-variant-numeric:tabular-nums;
    padding:5px 10px;
    pointer-events:none;
    transform:translateX(-50%);
    white-space:nowrap;
    z-index:5;
}

.chart-atl-line{
    stroke-width:1.5;
    opacity:.8;
}

.chart-atl-label{
    font-size:15px;
    font-weight:700;
}

.chart-crosshair{
    stroke-width:1;
    opacity:.6;
}

/* Screen-reader chart table: hidden until opened or focused (TDD-05 §10). */
.chart-table-disclosure{
    margin-top:10px;
}

/* Deliberately screen-reader-only: a scripted control replaces it visually.
   min-height is pinned back to 0 because the site-wide `summary` tap-target
   floor would otherwise inflate this clipped element to 24px and reintroduce
   the layout gap it exists to avoid. */
.chart-table-disclosure summary{
    position:absolute;
    width:1px;
    height:1px;
    min-height:0;
    margin:-1px;
    padding:0;
    overflow:hidden;
    clip:rect(0 0 0 0);
    white-space:nowrap;
    border:0;
    display:block;
}

.chart-table-disclosure summary:focus-visible,
.chart-table-disclosure[open] summary{
    position:static;
    width:auto;
    height:auto;
    margin:0;
    padding:6px 4px;
    clip:auto;
    white-space:normal;
    cursor:pointer;
    color:var(--lr-muted);
    font-size:13px;
}

.chart-table-scroll{
    overflow-x:auto;
}

.chart-data-table{
    border-collapse:collapse;
    font-size:13px;
    font-variant-numeric:tabular-nums;
    margin-top:8px;
}

.chart-data-table caption{
    text-align:left;
    color:var(--lr-muted);
    font-size:12.5px;
    padding-bottom:6px;
}

.chart-data-table th,
.chart-data-table td{
    border:1px solid var(--lr-line);
    padding:6px 12px;
    text-align:right;
}

.chart-data-table th[scope="row"],
.chart-data-table thead th:first-child{
    text-align:left;
}

/* --- Admin: scrape-stall banner (TDD-05 §14.3) + retailer policy. -------- */
.dashboard-stall-banner{
    border:1px solid var(--lr-accent-deal);
    background:rgba(196,65,75,.10);
}

.dashboard-stall-banner h2{
    color:var(--lr-accent-deal-text);
}

.dashboard-stall-banner ul{
    margin:8px 0;
    padding-left:22px;
}

.retailer-policy-panel{
    margin-top:26px;
    padding-top:18px;
    border-top:1px solid var(--lr-line);
}

.retailer-policy-panel form{
    display:flex;
    flex-wrap:wrap;
    gap:10px;
    margin-top:12px;
}


/* =========================================================================
   v4 "PLATFORM" LAYER — the 2026-07 redesign.
   Appended last on purpose: it overrides the older component styles above
   for the public site (nav, homepage, leaderboards, footer) while leaving
   the admin mini-site on the shared tokens. Signature moves: gym photo
   hero, hairline rules instead of card boxes, Crossfly Display for
   numerals/kickers, judges' lights on the deal of the day.
   The pre-redesign stylesheet is archived at
   design-archive/style-v3-pre-platform-redesign.css.
   ========================================================================= */

/* --- shared v4 atoms ---------------------------------------------------- */

.sec-kick{
    font-family:var(--lr-display);
    font-weight:400;
    font-size:11px;
    letter-spacing:.12em;
    text-transform:uppercase;
    color:var(--lr-muted);
}

.sec-kick b,
.sec-kick strong{
    font-weight:400;
    /* -text, not the raw accent: this kicker is 11px, and raw --lr-accent-deal
       is 3.17:1 on the dark ground — it only clears AA at large sizes. The
       token block's own comment says small crimson text uses the -text tint
       (5.9:1); this was one of the places that did not. */
    color:var(--lr-accent-deal-text);
}

.kick-crimson{
    color:var(--lr-accent-deal);
}

.kick-white{
    color:#fff;
}

/* every eyebrow site-wide picks up the display face */
.eyebrow{
    font-family:var(--lr-display);
    font-weight:400;
    font-size:11px;
    letter-spacing:.12em;
    text-transform:uppercase;
    color:var(--lr-muted);
}

/* judges' lights: three whites = a verified good buy */
.judge-lights{
    display:inline-flex;
    gap:6px;
    flex-shrink:0;
}

.judge-lights i{
    width:11px;
    height:11px;
    border-radius:50%;
    background:var(--lr-text);
    box-shadow:0 0 9px rgba(244,244,245,.5);
}

/* --- nav: plain links, search pill, hairline bottom ---------------------- */

nav.site-nav{
    padding:10px 40px;
    gap:20px;
    background:rgba(10,9,9,.92);
    border-bottom:1px solid var(--lr-line);
}

.logo{
    height:48px;
}

.nav-links{
    gap:18px;
}

.nav-links a,
.nav-dropdown summary{
    border:0;
    background:transparent;
    padding:10px 2px;
    color:var(--lr-muted);
    font-size:13.5px;
    font-weight:600;
    letter-spacing:0;
    text-transform:none;
    white-space:nowrap;
}

.nav-links a:hover,
.nav-dropdown summary:hover{
    color:var(--lr-text);
    border-color:transparent;
    background:transparent;
}

/* Sign-in CTA: dark pill with a slow crimson conic-gradient border edge
   (21st.dev "shiny" button, dialled way down — no dot pattern, no white
   shimmer, just a subtle rotating red rim that brightens on hover). */
@property --gradient-angle {
    syntax:"<angle>"; initial-value:0deg; inherits:false;
}
@property --gradient-angle-offset {
    syntax:"<angle>"; initial-value:0deg; inherits:false;
}
@property --gradient-percent {
    syntax:"<percentage>"; initial-value:8%; inherits:false;
}

.nav-signin{
    position:relative;
    isolation:isolate;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    padding:8px 20px !important;
    border:1px solid transparent;
    border-radius:999px;
    font-size:13.5px;
    font-weight:600;
    line-height:1;
    white-space:nowrap;
    cursor:pointer;
    overflow:hidden;
    color:var(--lr-text) !important;
    /* padding-box fill = dark surface; border-box = animated crimson rim */
    background:
        linear-gradient(var(--lr-surface-2), var(--lr-surface-2)) padding-box,
        conic-gradient(
            from calc(var(--gradient-angle) - var(--gradient-angle-offset)),
            transparent,
            var(--lr-accent-deal) var(--gradient-percent),
            var(--lr-accent-deal-hover) calc(var(--gradient-percent) * 2),
            var(--lr-accent-deal) calc(var(--gradient-percent) * 3),
            transparent calc(var(--gradient-percent) * 4)
        ) border-box !important;
    box-shadow:inset 0 0 0 1px rgba(255,255,255,.02);
    animation:nav-signin-spin 6s linear infinite;
    transition:
        box-shadow 500ms cubic-bezier(.25,1,.5,1),
        transform 180ms ease,
        --gradient-percent 700ms cubic-bezier(.25,1,.5,1),
        --gradient-angle-offset 700ms cubic-bezier(.25,1,.5,1);
}

.nav-signin:is(:hover, :focus-visible){
    --gradient-percent:16%;
    --gradient-angle-offset:90deg;
    color:#fff !important;
    box-shadow:
        inset 0 0 0 1px rgba(196,65,75,.28),
        0 0 18px -6px var(--lr-accent-deal);
}

.nav-signin:active{
    transform:translateY(1px);
}

@keyframes nav-signin-spin{
    to{ --gradient-angle:360deg; }
}

/* Touch devices have no hover state, so keep the crimson glow + thicker rim
   on permanently — most visitors are mobile and would never see it otherwise. */
@media (hover: none){
    .nav-signin{
        --gradient-percent:16%;
        box-shadow:
            inset 0 0 0 1px rgba(196,65,75,.28),
            0 0 18px -6px var(--lr-accent-deal);
    }
}

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

.nav-account-chip{
    border:1px solid var(--lr-line) !important;
    background:var(--lr-surface);
}

.nav-search-form input{
    height:38px;
    border-radius:999px;
    border-color:var(--lr-line);
    background:transparent;
    font-size:13px;
    padding:0 16px;
}

.nav-search-form input:focus{
    border-color:var(--lr-dim);
    box-shadow:none;
}

.search-suggestions{
    border-radius:12px;
    border-color:var(--lr-line);
    background:var(--lr-surface);
}

.search-suggestion{
    background:var(--lr-surface) !important;
}

.search-suggestion + .search-suggestion{
    border-top:1px solid var(--lr-line-soft) !important;
}

.search-suggestion:hover,
.search-suggestion:focus,
.search-suggestion.is-active{
    background:var(--lr-surface-2) !important;
}

/* --- homepage hero: the gym floor --------------------------------------- */

.home-hero{
    position:relative;
    height:500px;
    overflow:hidden;
}

.home-hero picture,
.home-hero-photo{
    position:absolute;
    inset:0;
    width:100%;
    height:100%;
}

.home-hero-photo{
    object-fit:cover;
    object-position:center 40%;
    filter:grayscale(1) contrast(1.05) brightness(.72);
    display:block;
}

.home-hero-shade{
    position:absolute;
    inset:0;
    background:linear-gradient(180deg, rgba(10,9,9,.55) 0%, rgba(10,9,9,.15) 40%, var(--lr-bg) 97%);
}

.home-hero-copy{
    position:absolute;
    left:40px;
    right:40px;
    bottom:104px;
    max-width:1180px;
    margin:0 auto;
}

.home-hero-copy h1{
    font-size:60px;
    font-weight:800;
    line-height:1.05;
    letter-spacing:-.015em;
    margin-top:12px;
    max-width:660px;
}

.home-hero-copy h1 em{
    font-style:normal;
    color:var(--lr-accent-deal);
}

.home-hero-text{
    color:#C9C5C5;
    max-width:52ch;
    margin-top:14px;
    font-size:16px;
    line-height:1.55;
}

/* --- deal of the day: the attempt board --------------------------------- */

.deal-section{
    position:relative;
    z-index:5;
    margin-top:-58px;
    padding:0 0 8px;
}

.dod-board{
    background:var(--lr-surface);
    border:1px solid var(--lr-line);
    border-radius:var(--lr-radius);
    padding:30px 34px;
    display:grid;
    grid-template-columns:auto minmax(0,1fr) auto auto;
    gap:34px;
    align-items:center;
}

.dod-thumb{
    display:block;
    width:136px;
    height:160px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    overflow:hidden;
    flex-shrink:0;
}

.dod-thumb img{
    width:100%;
    height:100%;
    object-fit:contain;
    padding:8px;
    display:block;
}

.dod-tag{
    display:flex;
    align-items:center;
    gap:14px;
    flex-wrap:wrap;
}
/* If the kicker must wrap at a narrow width, break it before the category —
   never inside it. "PROTEIN / POWDER" is not a line break, it is a typo. */
.dod-tag .sec-kick b{ white-space:nowrap; }

.dod-body h2{
    font-size:27px;
    font-weight:700;
    line-height:1.2;
    margin-top:12px;
}

.dod-body h2 a{
    text-decoration:none;
}

.dod-body h2 a:hover{
    text-decoration:underline;
}

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

.dod-permetric{
    font-family:var(--lr-display);
    font-size:23px;
    letter-spacing:.01em;
    font-variant-numeric:tabular-nums;
}

.dod-board > *{
    min-width:0;
}

.dod-money{
    justify-content:flex-end;
    margin-top:8px;
    gap:10px;
}

.dod-price{
    font-size:29px;
}

.dod-baseline{
    font-size:15px;
    color:var(--lr-dim);
}

.dod-drop{
    color:var(--lr-accent-deal-text);
    font-size:13px;
    font-weight:700;
    margin-top:6px;
    font-variant-numeric:tabular-nums;
}

.dod-actions{
    display:grid;
    gap:10px;
    justify-items:center;
}

.dod-actions .deal-button{
    width:auto;
    min-width:170px;
    margin-top:0;
    padding:0 26px;
    font-size:13.5px;
}

.dod-actions .dod-store-link{
    margin-top:0;
    font-size:12.5px;
    color:var(--lr-muted);
}

/* --- homepage sections: hairlines, not cards ----------------------------- */

.spotlight-section,
.featured-section{
    padding:56px 0 0;
}

.spotlight-section .section-heading,
.featured-section .section-heading{
    margin-bottom:0;
}

.spotlight-entry{
    border:0;
    border-top:1px solid var(--lr-line);
    border-radius:0;
    background:transparent;
    padding:26px 4px 0;
    margin-top:16px;
}

.spotlight-business{
    font-size:22px;
    font-weight:700;
    letter-spacing:0;
    text-transform:none;
    padding-bottom:0;
}

.spotlight-business::after{
    display:none;
}

.spotlight-blurb{
    color:var(--lr-muted);
    font-size:14.5px;
    margin-top:10px;
}

.spotlight-product{
    border-top:0;
    margin-top:18px;
    padding-top:0;
}

.featured-shelf{
    border-top:1px solid var(--lr-line);
    margin-top:16px;
    padding-top:24px;
}

.featured-card{
    border:0;
    border-radius:0;
    background:transparent;
    overflow:visible;
}

.featured-card:hover{
    border-color:transparent;
}

.featured-thumb{
    height:120px;
    padding:0;
}

.featured-thumb img{
    object-position:left center;
}

.featured-body{
    padding:14px 0 0;
}

.featured-metric{
    font-size:16px;
    font-weight:800;
    color:var(--lr-text);
}

.lb-list{
    gap:0;
}

.lb-list .lb-row:first-child{
    border-top:1px solid var(--lr-line);
}

.lb-row{
    background:transparent;
    border:0;
    border-bottom:1px solid var(--lr-line);
    border-radius:0;
}

.lb-row:hover,
.lb-row[open]{
    border-color:var(--lr-line);
    background:rgba(16,15,15,.6);
}

.lb-rank{
    font-family:var(--lr-display);
    font-weight:400;
    font-size:22px;
    color:var(--lr-dim);
}

/* long product names wrap inside their column instead of colliding with
   the metric on narrow screens */
.lb-name a{
    overflow-wrap:anywhere;
}

.lb-rank.is-top{
    color:var(--lr-text);
}

.lb-thumb{
    border-color:var(--lr-line-soft);
    background:var(--lr-surface);
}

.lb-offers{
    background:var(--lr-surface);
    border-top:1px solid var(--lr-line-soft);
}

.offer-list th{
    color:var(--lr-dim);
}

.offer-list td{
    border-top:1px solid #1A1818;
}

/* ── THE outbound CTA: .cta-deal ────────────────────────────────────────
   Rendered only by templates/components/_deal_cta.html. This is the whole of
   its styling; there is no second block, and no page-specific override of its
   colour is permitted.

   HISTORY, because this rule reverses a previous decision on purpose. An
   earlier pass made the non-best `.offer-go` WHITE, reasoning that 47 crimson
   buttons on one product page meant none of them stood out. The reasoning about
   emphasis was right; the lever was wrong. Painting 46 buy buttons a different
   colour to highlight the 47th means the site has two buy-button colours and a
   shopper has to work out which is which — and the same argument had already
   produced a grey text link on the featured shelf and an outlined ghost button
   in the spotlight, so "the buy button" had four appearances site-wide.

   Emphasis now comes from the CARD (border, wash, rank badge — see
   .offer-card.is-best and the rank-one leaderboard row) and from
   `.cta-deal--lead`, which changes size and adds a halo but never the colour.
   One action, one colour, everywhere.

   Contrast: #B23540 with #fff is 4.53:1, over the AA floor for normal text
   (--lr-accent-deal was darkened for exactly this in TDD-08 §4.3). */
.cta-deal{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    gap:var(--sp-2, 8px);
    min-height:36px;
    padding:0 14px;
    border:1px solid transparent;
    border-radius:6px;
    background:var(--lr-accent-deal);
    color:#fff;
    font-family:'Inter', sans-serif;
    font-size:12px;
    font-weight:800;
    line-height:1;
    letter-spacing:.01em;
    text-decoration:none;
    white-space:nowrap;
    cursor:pointer;
    transition:background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}

.cta-deal:hover{
    background:var(--lr-accent-deal-hover);
    color:#fff;
    transform:translateY(-1px);
}

/* Pressed. Distinct from hover, or a tap on a touch device gives no feedback
   at all between "finger down" and "new tab opens". */
.cta-deal:active{
    background:#9E2E38;
    transform:translateY(0);
}

/* a11y §9.9: a crimson focus ring on a crimson button is invisible. White,
   matching .btn--primary, so keyboard focus reads the same on both. */
.cta-deal:focus-visible{
    outline:2px solid #fff;
    outline-offset:2px;
}

/* Nothing to open. Keeps the crimson identity so the row does not look like it
   lost its button, but is unmistakably inert and unreachable by tab. */
.cta-deal.is-disabled{
    opacity:.45;
    cursor:default;
    pointer-events:none;
    transform:none;
}

/* The primary / rank-one CTA. Bigger and haloed — never a different colour. */
.cta-deal--lead{
    min-height:42px;
    padding:0 18px;
    font-size:13px;
    box-shadow:0 0 0 1px var(--lr-accent-deal-line), 0 6px 18px -8px rgba(196,65,75,.75);
}

.cta-deal--block{
    display:flex;
    width:100%;
}

@media (max-width: 700px){
    /* Thumb target, and a full-width button is the phone convention for the
       one action a card exists to offer. */
    .cta-deal{ min-height:44px; width:100%; }
}

@media (prefers-reduced-motion: reduce){
    .cta-deal{ transition:none; }
    .cta-deal:hover{ transform:none; }
}

/* `.offer-go` is retained ONLY for the legacy offer table (_offer_row.html) and
   the shared-collection page, which are not "View deal" surfaces. It now points
   at the same crimson so no public buy action is ever a white or grey control.
   New markup must use the _deal_cta.html macro instead. */
.offer-go{
    background:var(--lr-accent-deal);
    color:#fff;
    border-radius:6px;
}

.offer-go:hover{
    background:var(--lr-accent-deal-hover);
    border-color:var(--lr-accent-deal-hover);
    color:#fff;
}

.offer-go:focus-visible{
    outline:2px solid #fff;
    outline-offset:2px;
}

/* --- leaderboard page frame ---------------------------------------------- */

.lb-header .section-title{
    font-size:clamp(42px, 6vw, 64px);
    letter-spacing:-.015em;
}

.lb-filters-wrap{
    margin-top:30px;
}

.lb-filters{
    border:0;
    border-top:1px solid var(--lr-line);
    border-bottom:1px solid var(--lr-line);
    border-radius:0;
    background:rgba(10,9,9,.92);
    padding:16px 0;
}

.chip{
    background:transparent;
    border-color:var(--lr-line);
    color:var(--lr-muted);
    font-weight:600;
}

.chip:hover{
    border-color:var(--lr-dim);
    color:var(--lr-text);
}

.chip.is-active{
    background:var(--lr-text);
    border-color:var(--lr-text);
    color:var(--lr-bg);
    font-weight:700;
}

.chip-apply{
    background:var(--lr-text);
    color:var(--lr-bg);
}

.max-price-input{
    background:transparent;
    border-style:dashed;
    border-color:var(--lr-line);
}

.sort-control{
    background:transparent;
    border-color:var(--lr-line);
}

.sort-segment.is-active{
    background:var(--lr-text);
    color:var(--lr-bg);
}

/* --- collapsible Filters + Sort dropdowns (TDD-09) ----------------------- */
/* The controls used to be a full always-open panel pinned sticky over the
   board — tall, semi-transparent, overlapping the rows on scroll. Now they
   collapse into two buttons; only a slim bar sticks, panels open on demand. */
.lb-controls{
    display:flex;
    flex-wrap:wrap;
    gap:10px;
    align-items:center;
    justify-content:space-between;
}

.lb-dd{ position:relative; }

.lb-dd > summary{
    list-style:none;
    display:inline-flex;
    align-items:center;
    gap:8px;
    min-height:40px;
    padding:0 15px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface-2);
    color:var(--lr-text);
    font-size:13px;
    font-weight:800;
    cursor:pointer;
    user-select:none;
    white-space:nowrap;
    transition:border-color .15s, background .15s;
}
.lb-dd > summary::-webkit-details-marker,
.lb-dd > summary::marker{ display:none; content:""; }
.lb-dd > summary:hover{ border-color:var(--lr-dim); }
.lb-dd[open] > summary{ border-color:var(--lr-text); }
.lb-dd > summary:focus-visible{ outline:2px solid var(--lr-text); outline-offset:2px; }

.lb-dd-caret{ color:var(--lr-soft); transition:transform .18s; }
.lb-dd[open] .lb-dd-caret{ transform:rotate(180deg); }
.lb-dd-label{ color:var(--lr-soft); font-weight:700; }
.lb-dd-sort-current{ color:var(--lr-text); }
.lb-dd-count{
    display:inline-flex; align-items:center; justify-content:center;
    min-width:18px; height:18px; padding:0 5px;
    border-radius:999px; background:var(--lr-accent-deal); color:#fff;
    font-size:11px; font-weight:800;
}
.lb-dd-count[hidden]{ display:none; }

.lb-dd-panel{
    position:absolute;
    top:calc(100% + 8px);
    z-index:60;
    background:var(--lr-surface);
    border:1px solid var(--lr-line);
    border-radius:14px;
    padding:16px;
    box-shadow:0 20px 48px rgba(0,0,0,.55);
    max-height:min(70vh, 560px);
    overflow:auto;
}
.lb-dd-panel-filters{ left:0; width:min(620px, 92vw); }
.lb-dd-panel-sort{ right:0; }

/* the filter form / sort control render bare inside the dropdown card */
.lb-dd-panel .lb-filters{
    border:0; background:transparent; padding:0; border-radius:0;
    display:grid; gap:16px;
}
.lb-dd-panel-sort .sort-control{ display:inline-flex; }

@media (max-width: 768px){
    .lb-dd-panel-filters{ left:0; right:auto; width:min(92vw, 460px); }
    .lb-dd-panel-sort .sort-control{ display:flex; }
}

.stale-divider{
    font-family:var(--lr-display);
    font-weight:400;
    color:var(--lr-stale);
    letter-spacing:.14em;
}

.stale-divider::before,
.stale-divider::after{
    background:#2A2622;
}

/* --- footer: about / rankings / contact + socials + logo bar ------------- */

.site-footer{
    padding:52px 20px 34px;
    color:var(--lr-muted);
}

.site-footer h2{
    font-size:18px;
    margin:10px 0 0;
}

.site-footer p{
    font-size:13.5px;
    line-height:1.65;
    margin-top:10px;
    max-width:38ch;
}

.site-footer a{
    color:var(--lr-accent-deal-text);
    font-weight:700;
    text-decoration:none;
}

.footer-grid{
    grid-template-columns:1.1fr 1.1fr 1fr;
    gap:44px;
}

/* Footer socials as a macOS-style magnetic dock: JS sets each icon's width
   on mousemove for the proximity-magnify; CSS holds base size + look. Only
   platforms with an admin-set URL render (see _footer.html). */
.magnetic-dock{
    display:inline-flex;
    align-items:flex-end;
    gap:14px;
    height:82px;
    margin-top:16px;
    padding:0 14px 12px;
    border-radius:18px;
    background:color-mix(in srgb, var(--lr-surface) 65%, transparent);
    border:1px solid var(--lr-line);
    width:max-content;
    max-width:100%;
}

.magnetic-dock .dock-icon{
    width:44px;                /* base size — JS overrides while hovering */
    aspect-ratio:1;
    flex:none;
    display:grid;
    place-items:center;
    border-radius:50%;
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    color:var(--lr-soft);
    cursor:pointer;
    text-decoration:none;
    will-change:width;
    transition:background .2s ease, color .2s ease, border-color .2s ease;
}

.magnetic-dock .dock-icon:hover,
.magnetic-dock .dock-icon:focus-visible{
    color:#fff;
    background:var(--lr-accent-deal);
    border-color:var(--lr-accent-deal);
    outline:2px solid #fff;
    outline-offset:3px;
}

.magnetic-dock .dock-icon svg{
    width:52%;
    height:52%;
}

.magnetic-dock .dock-abbr{
    font-size:13px;
    font-weight:700;
    letter-spacing:.04em;
}

.footer-bar{
    width:min(1180px, calc(100% - 40px));
    margin:44px auto 0;
    padding-top:22px;
    border-top:1px solid var(--lr-line);
    display:flex;
    align-items:center;
    gap:24px;
    flex-wrap:wrap;
}

.footer-logo{
    margin-right:auto;
}

.footer-logo img{
    height:30px;
    width:auto;
    display:block;
}

.footer-bar a,
.footer-bar .footer-signout button{
    color:var(--lr-muted);
    font-size:12.5px;
    font-weight:600;
    text-decoration:none;
}

.footer-bar a:hover,
.footer-bar .footer-signout button:hover{
    color:var(--lr-text);
}

.footer-notes{
    width:min(1180px, calc(100% - 40px));
    /* 14px from the bar above and 6px between the notes meant three separate
       legal statements read as one continuous grey block. */
    margin:var(--sp-5) auto 0;
    display:grid;
    gap:var(--sp-3);
}

.footer-notes p{
    font-size:12px;
    color:var(--lr-dim);
    line-height:1.6;
    /* `max-width:none` let 12px text run the full 1180px column — about 180
       characters a line, which is unreadable even for small print. */
    max-width:104ch;
    margin:0;
}

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

@media (max-width: 900px){
    nav.site-nav{
        padding:10px 16px;
    }

    /* the logo is the mobile brand moment — bigger than v3 */
    .logo{
        height:56px;
    }

    .nav-links{
        border-radius:12px;
        background:var(--lr-surface);
        border-color:var(--lr-line);
        padding:18px;
        gap:0;
    }

    .nav-search-form input{
        height:44px;
        font-size:14px;
    }

    .nav-menu-title{
        font-family:var(--lr-display);
        font-weight:400;
        font-size:10px;
        letter-spacing:.2em;
        color:var(--lr-dim);
        padding:14px 2px 6px;
    }

    .nav-links a{
        border-top:1px solid var(--lr-line-soft);
        border-radius:0;
        padding:13px 2px;
        font-size:15px;
        width:100%;
    }

    .nav-links a:first-of-type{
        border-top:0;
    }

    .nav-account-chip{
        border:0 !important;
        border-top:1px solid var(--lr-line) !important;
        border-radius:0 !important;
        background:transparent;
        padding:13px 2px !important;
    }

    .mobile-menu-button{
        border:0;
    }

    .home-hero{
        height:400px;
    }

    .home-hero-photo{
        object-position:center 62%;
    }

    .home-hero-copy{
        left:20px;
        right:20px;
        bottom:88px;
    }

    .home-hero-copy h1{
        font-size:37px;
        line-height:1.07;
        margin-top:10px;
    }

    .deal-section{
        margin-top:-52px;
    }

    .dod-board{
        grid-template-columns:auto minmax(0,1fr);
        gap:16px;
        padding:20px;
    }

    .dod-thumb{
        width:96px;
        height:112px;
    }

    .dod-body h2{
        font-size:17px;
        margin-top:10px;
    }

    .dod-score{
        grid-column:1 / -1;
        text-align:left;
    }

    .dod-permetric{
        font-size:15px;
    }

    .dod-money{
        justify-content:flex-start;
    }

    .dod-actions{
        grid-column:1 / -1;
        justify-items:stretch;
    }

    .dod-actions .deal-button{
        width:100%;
    }

    .dod-actions .dod-store-link{
        text-align:center;
    }

    

    .footer-grid{
        grid-template-columns:1fr;
        gap:30px;
    }
}

@media (max-width: 768px){
    /* metric may wrap to two lines so the name column keeps enough width
       for whole words */
    .lb-metric{
        font-size:15px;
        white-space:normal;
    }

    .featured-shelf{
        display:grid;
        grid-template-columns:1fr 1fr;
        gap:22px;
        overflow-x:visible;
        scroll-snap-type:none;
        padding-bottom:0;
    }

    .featured-card{
        flex:none;
    }

    .featured-thumb{
        height:96px;
    }
}

@media (max-width: 600px){
    .home-hero-text{
        display:none;
    }

    .home-hero-copy{
        bottom:76px;
    }
}

@media (prefers-reduced-motion: reduce){
    .magnetic-dock .dock-icon{
        transition:none;
    }
}

/* =========================================================================
   v4.1 polish (2026-07-06 owner feedback): bigger product imagery, quieter
   leaderboard rows, top-3 offers, collapsible product filters, and a touch
   more crimson on mobile product pages.
   ========================================================================= */

/* bigger product imagery on the deal board and leaderboard rows */
.dod-thumb{
    width:128px;
    height:148px;
}

.lb-thumb{
    width:64px;
    height:64px;
}

.lb-summary{
    grid-template-columns:44px 64px minmax(0,1fr) auto auto auto auto;
}

.lb-row.no-rank .lb-summary{
    grid-template-columns:64px minmax(0,1fr) auto auto auto auto;
}

/* leaderboard cleanup: the letter circle goes (the store is named in text
   now), the detail chip stops shouting, offers panel breathes */
.lb-row .store-mark{
    display:none;
}

.lb-chip{
    border:0;
    background:none;
    padding:0;
    color:var(--lr-dim);
    letter-spacing:.05em;
}

.lb-offer-size{
    color:var(--lr-soft);
}

.lb-expand{
    color:var(--lr-dim);
}

.lb-all-offers{
    display:inline-block;
    margin:10px 10px 6px;
    font-size:12.5px;
    font-weight:700;
    color:var(--lr-accent-deal-text);
    text-decoration:none;
    border-bottom:1px solid var(--lr-accent-deal-line);
    padding-bottom:2px;
}

.lb-all-offers:hover{
    color:var(--lr-text);
    border-color:var(--lr-text);
}

.lb-all-offers[hidden]{
    display:none;
}

/* product page: the filter bar lives behind one button; default sort until
   the shopper opens it */
.filters-disclosure{
    margin-bottom:18px;
}

.filters-disclosure summary{
    list-style:none;
    display:inline-flex;
    align-items:center;
    gap:10px;
    min-height:40px;
    padding:0 18px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    color:var(--lr-soft);
    font-size:13px;
    font-weight:700;
    cursor:pointer;
    transition:border-color .15s, color .15s;
}

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

.filters-disclosure summary::after{
    content:"";
    width:7px;
    height:7px;
    border-right:2px solid currentColor;
    border-bottom:2px solid currentColor;
    transform:translateY(-2px) rotate(45deg);
    transition:transform .15s;
}

.filters-disclosure summary:hover{
    border-color:var(--lr-dim);
    color:var(--lr-text);
}

.filters-disclosure[open] summary{
    border-color:var(--lr-dim);
    color:var(--lr-text);
}

.filters-disclosure[open] summary::after{
    transform:translateY(1px) rotate(225deg);
}

.filters-disclosure .lb-filters{
    border:0;
    background:transparent;
    padding:18px 2px 4px;
}

@media (max-width: 768px){
    /* The wider 64px thumb column these rules introduced now lives with the
       stacked-row grid-template-areas it has to agree with (search
       "THE mobile leaderboard row"). Declaring columns here and areas there
       was the bug: two max-width:768px blocks 3,400 lines apart, and only the
       pair together described the layout. */

    /* mobile product pages get a little more crimson (desktop stays as-is) */
    .product-page .category-tag{
        border-color:var(--lr-accent-deal-line);
        background:var(--lr-accent-deal-wash);
        color:var(--lr-accent-deal-text);
    }

    .product-page .price-stats > div:first-child{
        border-color:var(--lr-accent-deal-line);
    }

    .product-page .price-stats > div:first-child strong{
        color:var(--lr-accent-deal-text);
    }

    .watch-hero .watch-btn{
        border-color:var(--lr-accent-deal-line);
        color:var(--lr-accent-deal-text);
    }

    .watch-hero .watch-btn.is-watching{
        color:#fff;
    }

    .filters-disclosure summary{
        border-color:var(--lr-accent-deal-line);
        color:var(--lr-accent-deal-text);
    }
}

@media (prefers-reduced-motion: reduce){
    .filters-disclosure summary,
    .filters-disclosure summary::after{
        transition:none;
    }
}


/* =============================================================================
   EMAIL CENTER + NOTIFICATION SETTINGS (comms platform, 2026-07-07)
   Admin chrome stays neutral (TDD-04 §13.8); crimson appears only for
   dead-letter warnings and the send-now action.
   ============================================================================= */

/* --- Email Center sub-navigation --- */
.email-subnav{
    display:flex;
    gap:4px;
    margin:0 0 22px;
    border-bottom:1px solid var(--lr-line);
    flex-wrap:wrap;
}
.email-subnav-link{
    padding:10px 14px;
    font-size:.85rem;
    color:var(--lr-muted);
    text-decoration:none;
    border-bottom:2px solid transparent;
    margin-bottom:-1px;
}
.email-subnav-link:hover{ color:var(--lr-text); }
.email-subnav-link.is-active{
    color:var(--lr-text);
    border-bottom-color:var(--lr-accent-deal);
}

.email-nav-actions{ display:flex; gap:10px; align-items:center; }
.email-inline-actions{ display:flex; gap:10px; flex-wrap:wrap; }
.email-strip{ margin-bottom:14px; }

/* --- status chips --- */
.email-status{
    display:inline-block;
    padding:2px 10px;
    border-radius:999px;
    font-size:.75rem;
    font-weight:700;
    text-transform:uppercase;
    letter-spacing:.5px;
    border:1px solid var(--lr-line);
    color:var(--lr-muted);
}
.email-status-sent{ color:var(--lr-fresh); border-color:var(--lr-fresh); }
.email-status-sending,.email-status-scheduled{ color:var(--lr-stale); border-color:var(--lr-stale); }
.email-status-dead,.email-status-cancelled{ color:var(--lr-accent-deal-text); border-color:var(--lr-accent-deal-line); }
.email-status-pending,.email-status-draft{ color:var(--lr-soft); }

/* --- 14-day sends chart --- */
.email-chart{
    display:flex;
    align-items:flex-end;
    gap:6px;
    height:140px;
    padding:8px 0 22px;
}
.email-chart-col{
    flex:1;
    position:relative;
    height:100%;
    display:flex;
    align-items:flex-end;
    gap:2px;
}
.email-chart-bar{
    flex:1;
    min-height:2px;
    background:var(--lr-soft);
    border-radius:3px 3px 0 0;
    opacity:.75;
}
.email-chart-bar.is-failed{ background:var(--lr-accent-deal); opacity:.9; }
.email-chart-label{
    position:absolute;
    bottom:-20px;
    left:0;
    right:0;
    text-align:center;
    font-size:.62rem;
    color:var(--lr-dim);
}

/* --- filters + pagination --- */
.email-filter-bar{
    display:flex;
    gap:10px;
    flex-wrap:wrap;
    margin:0 0 16px;
}
.email-filter-bar input[type="search"],
.email-filter-bar select{
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    color:var(--lr-text);
    border-radius:8px;
    padding:8px 12px;
    font-size:.88rem;
}
.email-pagination{
    display:flex;
    gap:6px;
    margin-top:16px;
    flex-wrap:wrap;
}
.email-pagination a{
    padding:5px 11px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    color:var(--lr-muted);
    text-decoration:none;
    font-size:.85rem;
}
.email-pagination a.is-active{
    color:var(--lr-text);
    border-color:var(--lr-accent-deal);
}

/* --- composer --- */
.composer-grid{
    display:grid;
    grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    gap:18px;
    align-items:start;
}
@media (max-width:1100px){ .composer-grid{ grid-template-columns:1fr; } }

.composer-editor .admin-panel{ margin-bottom:18px; }
.composer-preview-pane{ position:sticky; top:18px; }
.composer-preview-panel iframe{
    width:100%;
    height:72vh;
    border:1px solid var(--lr-line);
    border-radius:10px;
    background:#f4f3f1;   /* emails render on their own light canvas */
}

.composer-blocks{ display:flex; flex-direction:column; gap:12px; margin-bottom:14px; }
.composer-block{
    border:1px solid var(--lr-line);
    border-radius:10px;
    background:var(--lr-surface-2);
}
.composer-block-head{
    display:flex;
    justify-content:space-between;
    align-items:center;
    padding:8px 12px;
    border-bottom:1px solid var(--lr-line-soft);
}
.composer-block-title{
    font-size:.72rem;
    font-weight:700;
    text-transform:uppercase;
    letter-spacing:1px;
    color:var(--lr-dim);
}
.composer-block-controls{ display:flex; gap:4px; }
.composer-block-body{ padding:12px; }
.composer-block-body .block-field{ margin-bottom:10px; }
.composer-block-body input[type="text"],
.composer-block-body input[type="search"]{
    width:100%;
    background:var(--lr-surface);
    border:1px solid var(--lr-line);
    color:var(--lr-text);
    border-radius:8px;
    padding:8px 12px;
    font-size:.9rem;
}
.composer-add-row{
    display:flex;
    gap:8px;
    flex-wrap:wrap;
    align-items:center;
}

.rt-toolbar{ display:flex; gap:4px; margin-bottom:6px; }
.rt-btn{
    background:var(--lr-surface);
    border:1px solid var(--lr-line);
    color:var(--lr-soft);
    border-radius:6px;
    padding:3px 10px;
    font-size:.8rem;
    cursor:pointer;
}
.rt-btn:hover{ color:var(--lr-text); border-color:var(--lr-dim); }
.rt-btn.rt-bold{ font-weight:800; }
.rt-btn.rt-italic{ font-style:italic; }
.rt-btn.rt-danger:hover{ color:var(--lr-accent-deal-text); border-color:var(--lr-accent-deal-line); }
.rt-editor{
    min-height:84px;
    background:var(--lr-surface);
    border:1px solid var(--lr-line);
    border-radius:8px;
    padding:10px 12px;
    font-size:.92rem;
    line-height:1.6;
}
.rt-editor:focus{ outline:1px solid var(--lr-dim); }
.rt-editor a{ color:var(--lr-accent-deal-text); }

.block-image-thumb{
    max-width:180px;
    border-radius:8px;
    display:block;
    margin:4px 0 10px;
}
.block-product-results{ display:flex; flex-direction:column; gap:4px; margin-bottom:8px; }
.block-product-hit{
    text-align:left;
    background:var(--lr-surface);
    border:1px solid var(--lr-line);
    color:var(--lr-soft);
    border-radius:8px;
    padding:7px 12px;
    font-size:.85rem;
    cursor:pointer;
}
.block-product-hit:hover{ color:var(--lr-text); border-color:var(--lr-dim); }
.block-product-picked{
    font-size:.85rem;
    color:var(--lr-fresh);
    margin:0 0 8px;
}

.composer-actions .composer-action-row{
    display:flex;
    gap:10px;
    align-items:center;
    margin-bottom:12px;
    flex-wrap:wrap;
}
.composer-actions input[type="email"],
.composer-actions input[type="datetime-local"]{
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    color:var(--lr-text);
    border-radius:8px;
    padding:8px 12px;
    font-size:.88rem;
}

/* --- template gallery --- */
.email-template-bar{
    display:flex;
    gap:12px;
    align-items:center;
    flex-wrap:wrap;
    margin-bottom:14px;
}
.email-template-bar select,
.email-template-test input[type="email"]{
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    color:var(--lr-text);
    border-radius:8px;
    padding:8px 12px;
    font-size:.88rem;
}
.email-template-test{ display:flex; gap:8px; align-items:center; }
.email-template-preview{
    width:100%;
    height:70vh;
    border:1px solid var(--lr-line);
    border-radius:10px;
    background:#f4f3f1;
}

/* --- user notification settings --- */
.notify-form .auth-submit{ margin-top:18px; }
.notify-row{
    display:flex;
    justify-content:space-between;
    align-items:center;
    gap:18px;
    padding:14px 0;
    border-bottom:1px solid var(--lr-line-soft);
    cursor:pointer;
}
.notify-row:last-of-type{ border-bottom:none; }
.notify-row.is-locked{ cursor:default; opacity:.7; }
.notify-copy{ display:flex; flex-direction:column; gap:3px; }
.notify-copy strong{ font-size:.95rem; }
.notify-copy small{ color:var(--lr-muted); font-size:.82rem; line-height:1.5; }
.notify-copy a{ color:inherit; text-decoration:underline; }

.notify-toggle{
    appearance:none;
    -webkit-appearance:none;
    width:44px;
    height:24px;
    flex:0 0 44px;
    border-radius:999px;
    background:var(--lr-line);
    border:1px solid var(--lr-line);
    position:relative;
    cursor:pointer;
    transition:background .15s ease;
}
.notify-toggle::after{
    content:"";
    position:absolute;
    top:2px;
    left:2px;
    width:18px;
    height:18px;
    border-radius:50%;
    background:var(--lr-soft);
    transition:transform .15s ease;
}
.notify-toggle:checked{ background:var(--lr-accent-deal); border-color:var(--lr-accent-deal); }
.notify-toggle:checked::after{ transform:translateX(20px); background:#fff; }
.notify-toggle:disabled{ cursor:default; opacity:.6; }

/* --- account watchlist: alert-mode select --- */
.watch-mode-select{
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    color:var(--lr-text);
    border-radius:8px;
    padding:7px 10px;
    font-size:.85rem;
    max-width:170px;
}


/* ══════════════════════════════════════════════════════════════════════════
   TDD-08 §4.6/§12/§15 — one global reduced-motion reset (last so it wins).
   Every era's components — including the newer composer ones — stop animating
   for users who ask for it. Instant, not removed (keeps functionality). */
@media (prefers-reduced-motion: reduce){
    *,*::before,*::after{
        animation-duration:.001ms !important;
        animation-iteration-count:1 !important;
        transition-duration:.001ms !important;
        scroll-behavior:auto !important;
    }
}

/* ══════════════════════════════════════════════════════════════════════════
   TDD-08 §5 — navigation polish (search icon + loading, active state, scrim).
   Appended last so it wins over the seven earlier nav eras. */
.logo{ height:44px; }                                  /* §5.4 slightly tighter */
.nav-search-form{ display:flex; align-items:center; }
.nav-search-icon{ position:absolute; left:12px; top:50%; transform:translateY(-50%);
    color:var(--lr-dim); pointer-events:none; z-index:1; }
.nav-search-form input[type="search"]{ padding-left:36px; }
/* §5.6 "searching…" affordance: a spinner in the icon slot while a fetch is out */
.nav-search-form.is-searching .nav-search-icon{
    border:2px solid var(--lr-dim); border-right-color:transparent; border-radius:50%;
    width:15px; height:15px; animation:btn-spin .6s linear infinite; }
.nav-search-form.is-searching .nav-search-icon > *{ display:none; }
.nav-links a.is-active{ color:var(--lr-text); }
.nav-links a.is-active::after{ content:""; display:block; height:2px; margin-top:3px;
    background:var(--lr-accent-deal); border-radius:2px; }

/* §5.7 dim scrim behind the open mobile menu */
.nav-scrim{ position:fixed; inset:0; z-index:calc(var(--z-nudge) - 1);
    background:rgba(0,0,0,.5); }
.nav-scrim[hidden]{ display:none; }
@media (min-width: 1180px){ .nav-scrim{ display:none !important; } }
@media (max-width: 900px){
    .nav-links.is-open{ z-index:var(--z-nudge); }
    .nav-links a.is-active::after{ display:none; }   /* full-width rows, no underline */
}

/* ══════════════════════════════════════════════════════════════════════════
   TDD-08 §6 — homepage: hero CTA, trust strip, top-of-page category chooser.
   Appended last so it wins the cascade. */
.home-hero-cta{ margin-top:var(--sp-4); box-shadow:0 8px 30px rgba(0,0,0,.35); }

.home-trust{
    width:min(1180px, calc(100% - 40px)); margin:22px auto 0;
    display:flex; flex-wrap:wrap; align-items:center; gap:10px 14px;
    color:var(--lr-muted); font-size:var(--fs-sm);
    font-variant-numeric:tabular-nums;
}
.home-trust b{ color:var(--lr-text); font-weight:var(--fw-semi); }
.home-trust-dot{ color:var(--lr-dim); }

@media (max-width: 600px){
    /* §6.3: keep a shortened, readable subhead on phones instead of hiding it */
    .home-hero-text{
        display:-webkit-box !important; -webkit-line-clamp:2; -webkit-box-orient:vertical;
        overflow:hidden; font-size:14px; line-height:1.4; margin-top:8px;
    }
    
}

/* ══════════════════════════════════════════════════════════════════════════
   TDD-08 §7 — footer & nudge polish (appended last). */
/* §7.1 legal/medical disclaimer must clear AA even on the darkest footer bg */
.footer-notes p{ color:var(--lr-muted); }

/* §7.2/§7.3 footer newsletter + category link column */
.footer-links{ list-style:none; margin:12px 0 0; padding:0; display:grid; gap:8px; }
.footer-links a{ color:var(--lr-muted); text-decoration:none; font-size:var(--fs-sm); }
.footer-links a:hover{ color:var(--lr-text); }
.footer-newsletter-form{ display:flex; gap:8px; margin-top:12px; flex-wrap:wrap; }
.footer-newsletter-form .input{ flex:1; min-width:180px; }
.footer-newsletter-form .btn{ flex-shrink:0; }
.footer-newsletter-note{ margin-top:8px; font-size:var(--fs-xs); color:var(--lr-dim); }

/* §7.5 nudge dismiss: 44px hit area, keyboard-focusable, readable contrast */
.nudge-dismiss{
    top:6px; right:6px; width:44px; height:44px; padding:0;
    display:inline-flex; align-items:center; justify-content:center;
    color:var(--lr-muted); border-radius:var(--r-sm);
    font-size:0; /* icon-only */
}
.nudge-dismiss:hover{ color:var(--lr-text); background:var(--lr-surface-2); }
.nudge-dismiss:focus-visible{ outline:2px solid var(--lr-accent-deal); outline-offset:2px; }

/* ══════════════════════════════════════════════════════════════════════════
   TDD-08 §8 — leaderboard: real table (header + aligned columns), value
   bars, inline buy, podium, cleanups. Appended last.
   The §8.5 category tab bar that used to open this block is gone — the site
   header already lists every category, and the duplicate strip cost most of a
   phone screen above the first ranked product. */

/* §8.4 value bar (all widths) */
.lb-metric-top{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.lb-value-bar{ display:block; height:4px; margin-top:7px; border-radius:2px;
    background:var(--lr-line); overflow:hidden; max-width:120px; }
.lb-value-fill{ display:block; height:100%; border-radius:2px;
    background:var(--lr-accent-deal); }
.lb-best-badge[hidden]{ display:none; }

/* §8.3 price prominence + §8.6 inline buy */
.lb-offer-price{ font-size:var(--fs-base); font-weight:var(--fw-semi);
    font-variant-numeric:tabular-nums; color:var(--lr-text); }
.lb-buy-inline{ margin-top:8px; }
.lb-buy-inline[hidden]{ display:none; }

/* §8.14 white product images no longer glare on the dark row */
.lb-thumb{ padding:4px; background:var(--lr-surface-2) !important; }
.lb-thumb img{ border-radius:4px; }
/* §8.14 empty flavor: don't print a lone "Flavor" label on mobile */
.offer-flavor:empty::before{ display:none !important; }

/* §8.2 podium accents on the top three ranks.

   The crimson numeral is keyed on `.is-rank-one`, NOT on `:first-of-type`.
   :first-of-type is a position in the DOM, and on a board sorted Price: High
   to Low the first row is the most expensive product — which was getting the
   site's "this one won" colour for having the largest sticker price. Crimson
   is now spent only where pricing.primary_badge says the ranking has a winner. */
.lb-rank.is-top{ color:var(--lr-text); }
.lb-row.is-rank-one .lb-rank{ color:var(--lr-accent-deal-text); }

/* §8.0 the table: header + fixed columns so values line up top-to-bottom.
   Desktop only — phones keep the existing stacked grid. */
@media (min-width: 769px){
    .lb-head{
        display:grid;
        grid-template-columns:44px 64px minmax(0,1fr) 132px 168px 104px 84px;
        gap:16px; align-items:center;
        padding:10px 18px; position:sticky; top:64px; z-index:var(--z-base);
        background:rgba(10,9,9,.94); backdrop-filter:blur(8px);
        border-bottom:1px solid var(--lr-line);
        font-size:var(--fs-xs); text-transform:uppercase; letter-spacing:.06em;
        color:var(--lr-dim); font-weight:var(--fw-semi);
    }
    .lb-head em{ font-style:normal; color:var(--lr-dim); text-transform:none; letter-spacing:0; }
    .lb-head-price,.lb-head-value,.lb-head-fresh{ }
    .lb-summary{
        grid-template-columns:44px 64px minmax(0,1fr) 132px 168px 104px 84px;
    }
    .lb-row.no-rank .lb-summary{
        grid-template-columns:64px minmax(0,1fr) 132px 168px 104px 84px;
    }
    /* keep the header's first (rank) column label from colliding on no-rank sets */
    .lb-offer{ display:flex; flex-direction:column; align-items:flex-start; gap:2px; }

    /* The "Checked" word is redundant in the TABLE — the column header above it
       says exactly that — and it was the reason the slot needed 133px inside a
       104px column, so every row's timestamp bled into the offers-toggle column
       beside it. _freshness.html always intended the table to drop the prefix
       ("the offer TABLE, whose column header already says Checked, can suppress
       it in CSS"); that suppression only ever got applied to the phone layout,
       which is the one place with no column header to carry the word. */
    .lb-summary .lb-fresh-slot{ min-width:0; }
    .lb-summary .lb-fresh-slot .freshness-prefix{ display:none; }
}
@media (max-width: 768px){
    .lb-head{ display:none; }
    .lb-value-bar{ max-width:none; }
}

/* §8.13 leaderboard loading state */
.lb-list.is-loading{ opacity:.5; transition:opacity var(--dur) var(--ease); pointer-events:none; }

/* ══════════════════════════════════════════════════════════════════════════
   TDD-08 §9 — product page: breadcrumb, metric hero, benchmark, buy CTA,
   trust fields, related strip. Appended last. */
.product-page{ align-items:start; }   /* §9.10 kill the centering dead space */

.breadcrumb ol{ list-style:none; display:flex; flex-wrap:wrap; gap:6px; margin:0 0 10px; padding:0;
    font-size:var(--fs-xs); color:var(--lr-dim); }
/* --lr-line is the hairline-rule colour (#242222): against the #0A0909 page it
   is roughly 1.3:1, so the separator was invisible and the trail read as two
   unrelated words with a gap. --lr-dim is the quietest ink we actually use for
   text, which is what this is. */
.breadcrumb li:not(:last-child)::after{ content:"›"; margin-left:6px; color:var(--lr-dim); }
.breadcrumb a{ color:var(--lr-muted); text-decoration:none; }
.breadcrumb a:hover{ color:var(--lr-text); }
.breadcrumb [aria-current]{ color:var(--lr-soft); }

.product-brand{ display:inline-block; margin-left:8px; color:var(--lr-muted);
    font-size:var(--fs-sm); font-weight:var(--fw-semi); text-transform:uppercase; letter-spacing:.05em; }

/* §9.1 the metric is the product */
.metric-hero{ margin:20px 0 6px; }
.metric-hero-label{ font-size:var(--fs-sm); color:var(--lr-muted); }
.metric-hero-figure{ font-family:var(--lr-display); font-size:var(--fs-2xl);
    line-height:var(--lh-tight); color:var(--lr-text); font-variant-numeric:tabular-nums;
    margin-top:4px; }
.metric-hero-benchmark{ margin-top:10px; }
.metric-hero-secondary{ display:flex; flex-wrap:wrap; gap:6px 18px; margin-top:12px;
    font-size:var(--fs-sm); color:var(--lr-muted); }
.metric-hero-price b{ font-size:var(--fs-md); color:var(--lr-text); font-variant-numeric:tabular-nums; }
.metric-hero-count{ color:var(--lr-dim); }
/* What the headline price actually buys (store + size), shown when we have no
   verified ¢/g to normalise it. Reads at --lr-soft, not --lr-dim: it is a
   qualifier ON the biggest number on the page, so it has to be noticed, not
   discovered. */
.metric-hero-for{ color:var(--lr-soft); }

/* "Unit value unavailable" (metadata research, 2026-07-30). Quiet, not an
   alarm: the price below it is real and current, and the only thing missing is
   our own verification of the serving/size details. Styling it as a warning
   would tell the shopper to distrust a page that is in fact accurate. */
.metric-hero-unavailable{ margin-top:12px; padding:10px 12px;
    border-left:2px solid var(--lr-line); border-radius:0 4px 4px 0;
    font-size:var(--fs-sm); line-height:var(--lh-ui); color:var(--lr-muted); }

/* §9.1 "what's this?" tooltip — no JS required (hover/focus) */
.metric-what{ position:relative; display:inline-block; }
.metric-what-btn{ background:none; border:0; padding:0; color:var(--lr-accent-deal-text);
    font:inherit; font-size:var(--fs-xs); text-decoration:underline dotted; cursor:help;
    /* 2.5.8: 24px minimum. Inline-flex rather than padding so the dotted
       underline still hugs the text. */
    display:inline-flex; align-items:center; min-height:24px; }
.metric-what-pop{ position:absolute; left:0; top:calc(100% + 8px); z-index:var(--z-sticky);
    width:min(280px, 70vw); padding:10px 12px; border-radius:var(--r-md);
    background:var(--lr-surface-2); border:1px solid var(--lr-line); box-shadow:var(--lr-shadow);
    color:var(--lr-soft); font-size:var(--fs-xs); line-height:var(--lh-ui);
    opacity:0; visibility:hidden; transition:opacity var(--dur-fast) var(--ease); }
.metric-what:hover .metric-what-pop,
.metric-what:focus-within .metric-what-pop{ opacity:1; visibility:visible; }

/* Full width, because the SECONDARY action under it already is.

   "Set price alert" is a block-level control spanning the hero column, while
   this — the one action the page exists to produce — was an auto-width inline
   button, so the quieter control was the bigger target and read as the primary
   one. product.html states the intent ("directly under View Deal as a clear
   SECONDARY action — same width, quieter treatment"); the equal width was only
   ever applied inside the ≤900px media query. */
.product-buy-cta{ margin-top:18px; width:100%; }
.affiliate-note{ margin-top:12px; font-size:var(--fs-xs); color:var(--lr-dim); }

/* §9.13 trust fields */
.trust-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:16px; }
.trust-item{ border:1px solid var(--lr-line); border-radius:var(--r-md);
    background:var(--lr-surface); padding:16px; }
.trust-label{ display:block; font-size:var(--fs-xs); text-transform:uppercase;
    letter-spacing:.06em; color:var(--lr-dim); margin-bottom:8px; }
.trust-item p{ font-size:var(--fs-sm); color:var(--lr-soft); line-height:var(--lh-ui); }

.related-list{ margin-top:8px; }

/* ══════════════════════════════════════════════════════════════════════════
   TDD-08 §13 — auth polish: strength meter, hints, remember row, magic-link
   fallback disclosure. Appended last. */
.auth-hint{ margin-top:10px; font-size:var(--fs-xs); color:var(--lr-dim); }
/* The <label> is the hit area, and at 24px tall it was the smallest control on
   the sign-in form. Nothing moves visually — the row just becomes tappable to
   the 44px minimum, which is most of the point on a phone. */
.remember-row{ display:flex; align-items:center; gap:var(--sp-2); margin:6px 0 4px;
    min-height:44px; cursor:pointer;
    font-size:var(--fs-sm); color:var(--lr-muted); }
.remember-row input{ width:16px; height:16px; }

/* The meter and its hint only exist once there is a password to describe.

   Untouched, the form showed three dead grey bars plus an empty <p> that still
   reserved its 18px line box — ~70px of apparently-broken UI between the
   password field and "Confirm password" before the visitor has typed anything.
   JS sets data-level as they type, which is when both become meaningful. */
.pw-meter:not([data-level]){ display:none; }
.password-hint:empty{ display:none; }
.pw-meter{ display:grid; grid-template-columns:repeat(3,1fr); gap:6px; margin:10px 0 6px; }
.pw-meter span{ height:4px; border-radius:2px; background:var(--lr-line); }
.pw-meter[data-level="1"] span:nth-child(1){ background:var(--lr-accent-deal); }
.pw-meter[data-level="2"] span:nth-child(1),
.pw-meter[data-level="2"] span:nth-child(2){ background:var(--lr-stale); }
.pw-meter[data-level="3"] span{ background:var(--lr-fresh); }
.password-hint[data-level="1"]{ color:var(--lr-accent-deal-text); }
.password-hint[data-level="3"]{ color:var(--lr-fresh); }

.auth-password-fallback{ margin-top:18px; border-top:1px solid var(--lr-line); padding-top:14px; }
/* The passwordless path needs to look like something you can press.

   With the default marker suppressed and no other affordance, "Email me a
   sign-in link instead" rendered as a line of muted grey prose sitting under a
   rule — indistinguishable from a caption, so the whole magic-link option was
   effectively invisible. A caret that rotates on open is the same disclosure
   vocabulary the leaderboard's Filters/Sort dropdowns already use. It also gets
   a real hit area: it was a bare 20px text line. */
.auth-password-fallback summary{ cursor:pointer; color:var(--lr-muted); font-size:var(--fs-sm);
    font-weight:var(--fw-semi); list-style:none;
    display:flex; align-items:center; gap:var(--sp-2); min-height:44px; }
.auth-password-fallback summary::-webkit-details-marker{ display:none; }
.auth-password-fallback summary::after{
    content:""; width:8px; height:8px; margin-left:2px;
    border-right:1.7px solid currentColor; border-bottom:1.7px solid currentColor;
    transform:rotate(45deg) translate(-2px,-2px);
    transition:transform var(--dur) var(--ease);
}
.auth-password-fallback[open] summary::after{ transform:rotate(-135deg) translate(-2px,-2px); }
@media (prefers-reduced-motion: reduce){
    .auth-password-fallback summary::after{ transition:none; }
}
.auth-password-fallback summary:hover{ color:var(--lr-text); }
.auth-password-fallback summary:focus-visible{ outline:2px solid var(--lr-accent-deal); outline-offset:3px; border-radius:var(--r-sm); }
.auth-password-fallback[open] summary{ color:var(--lr-text); margin-bottom:12px; }

/* form-errors get focus + a clear alert treatment (§12/§13.2) */
.form-errors:focus{ outline:2px solid var(--lr-accent-deal); outline-offset:3px; }

/* ══════════════════════════════════════════════════════════════════════════
   TDD-08 §10 — price chart: $↔¢/g toggle layout, axis title, point focus. */
.chart-controls{ display:flex; flex-wrap:wrap; gap:10px; align-items:center; }
.chart-unit-control[hidden]{ display:none; }
.chart-axis-title{ fill:var(--lr-dim); font-size:11px; letter-spacing:.04em; }
.price-point{ transition:r var(--dur-fast) var(--ease); }
.price-point:focus-visible{ outline:2px solid var(--lr-text); outline-offset:2px; }
.price-point:hover,.price-point:focus{ r:6; }

/* ══════════════════════════════════════════════════════════════════════════
   TDD-08 §12 — a11y target-size floor. Interactive controls that predate the
   .btn primitive get a ≥40px desktop / ≥44px touch minimum. Appended last. */
.chip, .sort-segment, .watch-btn, .offer-go, .admin-action, .chip-apply, .chip-clear{
    min-height:40px;
    display:inline-flex; align-items:center; justify-content:center;
}
@media (pointer: coarse){
    .chip, .sort-segment, .watch-btn, .offer-go, .admin-action{ min-height:44px; }
}

/* §9.5 always-visible product sort control */
.product-sort-visible{ margin-bottom:14px; }
.product-sort-visible .chip-group-label{ margin-bottom:6px; }

/* §8.9 value-math derivation inside the expanded row */
.lb-metric-math{ margin:6px 12px 2px; font-size:var(--fs-sm); color:var(--lr-muted);
    font-variant-numeric:tabular-nums; }
.lb-metric-math[hidden]{ display:none; }
.lb-metric-math-label{ color:var(--lr-dim); margin-right:6px; }

/* §9.7 per-offer delta vs best value */
.offer-delta{ font-size:var(--fs-xs); color:var(--lr-muted); font-variant-numeric:tabular-nums; }
.offer-delta[hidden]{ display:none; }

/* §8.12 pagination: capped rows hidden until "Show all" */
.lb-row.is-capped{ display:none; }
.lb-show-all{ margin:16px auto 0; display:flex; }

/* §13.10 account security: recent sign-in activity list */
.login-activity{ list-style:none; margin:10px 0 0; padding:0; display:grid; gap:8px; }
.login-activity li{ display:flex; flex-wrap:wrap; gap:6px 14px; align-items:baseline;
    font-size:var(--fs-sm); color:var(--lr-muted); font-variant-numeric:tabular-nums; }
.login-activity .login-kind{ color:var(--lr-text); font-weight:var(--fw-semi); }
.login-activity .login-ip{ color:var(--lr-dim); font-size:var(--fs-xs); }

/* §8.16 row price sparkline */
.lb-spark{ display:block; margin-top:6px; color:var(--lr-dim); overflow:visible; }
.lb-spark[hidden]{ display:none; }

/* §10.8 latest-price summary above the chart */
.chart-latest{ margin:4px 0 12px; font-size:var(--fs-sm); color:var(--lr-muted);
    font-variant-numeric:tabular-nums; }
.chart-latest[hidden]{ display:none; }

/* §13.15 async settings save status */
.notify-save-row{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.notify-saved{ color:var(--lr-fresh); font-size:var(--fs-sm); font-weight:var(--fw-semi); }

/* §10.7 store chip doubles as the chart legend key */
.chart-chip-swatch{ display:inline-block; width:10px; height:10px; border-radius:2px;
    margin-right:6px; vertical-align:middle; }
.chart-legend[hidden]{ display:none; }

/* §13.11 unsaved-change hint on the watch-target save button */
.watch-target-save.is-unsaved{ background:var(--lr-accent-deal); color:#fff; border-color:var(--lr-accent-deal); }

/* ══════════════════════════════════════════════════════════════════════════
   TDD-09 §7 — FRONT-OF-HOUSE POLISH PASS
   Small, mobile-first cosmetic + interaction touches that make the site read
   as higher-end. Appended last so it wins the cascade. Every motion effect is
   already neutralized by the global reduced-motion reset above; nothing here
   is required for content to render.
   ══════════════════════════════════════════════════════════════════════════ */

/* §7.1 kill the default grey/blue tap-flash on every tappable surface, and
   §7.5 stop iOS from inflating type on rotate. §7.6 smooth in-page jumps. */
html{
    -webkit-tap-highlight-color:transparent;
    -webkit-text-size-adjust:100%;
    text-size-adjust:100%;
    scroll-behavior:smooth;
}

/* §7.3 branded text selection — crimson wash instead of browser blue */
::selection{ background:rgba(196,65,75,.32); color:#fff; }
::-moz-selection{ background:rgba(196,65,75,.32); color:#fff; }

/* §7.4 branded input caret */
.input,
input,
textarea,
.nav-search-form input{ caret-color:var(--lr-accent-deal); }

/* §7.7 contain horizontal scrollers so a sideways flick can't rubber-band the
   page or trigger the browser's back-swipe / pull-to-refresh */
.chip-row,
.sort-control,
.featured-shelf,
.lb-offers{ overscroll-behavior-x:contain; }

/* §7.2 tactile press feedback. Hover lifts never fire on touch, so give every
   tappable surface a subtle push-in on :active — snappy on desktop clicks too. */
.btn:active{ transform:translateY(0) scale(.97); }
.watch-btn,
.chip,
.offer-go,
.sort-segment{
    transition-property:transform, border-color, background, color;
    transition-duration:var(--dur-fast);
    transition-timing-function:var(--ease);
}
.watch-btn:active,
.chip:active,
.offer-go:active,
.sort-segment:active,
.deal-button:active,
.crimson-button:active,
.dod-store-link:active,
.chip-apply:active,
.chip-clear:active,
.lb-show-all:active{ transform:scale(.96); }

.spotlight-thumb,
.dod-thumb,
.featured-thumb,
.nav-account-chip,
.search-result{ transition:transform var(--dur-fast) var(--ease); }
.featured-card:active,
.search-result:active,
.spotlight-thumb:active,
.featured-thumb:active,
.dod-thumb:active,
.nav-account-chip:active{ transform:scale(.985); }

/* §7.10 Watch toggle: a satisfying pop when a price goes on the watchlist */
@keyframes lr-watch-pop{
    0%{ transform:scale(1); }
    45%{ transform:scale(1.11); }
    100%{ transform:scale(1); }
}
.watch-btn.lr-pop{ animation:lr-watch-pop .32s var(--ease); }

/* §7.11 scroll-aware header: flat while over the hero, gains depth once it
   floats over content (class toggled in polish.js) */
body > nav{
    transition:box-shadow .22s var(--ease), background .22s var(--ease), border-color .22s var(--ease);
}
body > nav.is-scrolled{
    background:rgba(8,8,8,.96);
    border-bottom-color:rgba(255,255,255,.14);
    box-shadow:0 8px 28px rgba(0,0,0,.38);
}

/* §7.8 lazy thumbnails ease in on decode instead of popping (class added by
   polish.js only, so images stay visible if JS never runs) */
img.lr-fade{ opacity:0; transition:opacity .5s var(--ease); }
img.lr-fade.lr-loaded{ opacity:1; }

/* §7.9 scroll reveal: sections & cards fade up as they enter the viewport.
   .lr-reveal is added by JS, so content is fully visible without it. */
.lr-reveal{
    opacity:0;
    transform:translateY(10px);
    transition:opacity .45s var(--ease), transform .45s var(--ease);
    transition-delay:var(--lr-reveal-delay, 0ms);
    will-change:opacity, transform;
}

/* Phones get the fade without the slide. The translate is the part that costs
   a compositor layer per section and reads as motion sickness on a small
   screen; the opacity carries the same "this just arrived" signal for free. */
@media (max-width: 768px){
    .lr-reveal{ transform:none; transition:opacity .32s var(--ease); will-change:opacity; }
}
.lr-reveal.lr-in{ opacity:1; transform:none; }

/* §7.12 respect the notch / home indicator on iPhones */
@supports (padding:max(0px)){
    .signup-nudge{
        bottom:max(18px, env(safe-area-inset-bottom));
        right:max(18px, env(safe-area-inset-right));
    }
}

/* Reduced-motion: the global reset already flattens the transitions above, but
   make the hidden-then-reveal state impossible to get stuck in. */
@media (prefers-reduced-motion: reduce){
    .lr-reveal{ opacity:1; transform:none; }
    img.lr-fade{ opacity:1; }
}


/* ==========================================================================
   MOBILE LEADERBOARD ROW  —  single source of truth
   ==========================================================================
   Must stay LAST in this file. `.lb-summary`'s grid columns are declared at
   three other top-level points (the v3 base, the v4 platform layer and the v5
   imagery layer), and a media query adds no specificity — so a mobile rule
   placed earlier simply loses to any later top-level rule. Splitting the
   stacked layout across the file is what broke it in the first place: one
   @media (max-width:768px) block set grid-template-areas, another 3,400 lines
   below set grid-template-columns, and only the pair together described the
   row.

   The defect this fixes: the row was
       columns: 36px 64px minmax(0,1fr) auto
       areas:  "rank thumb name metric" / "rank thumb offer offer"
               / "rank thumb fresh expand"
   An `auto` track takes its max-content width, so the metric value plus the
   "Best value" chip (~170px) and the nowrap "2 offers" toggle claimed the
   right-hand columns and starved `name`'s minmax(0,1fr) to about 20px at
   360px wide. Every product title then wrapped one character per line and a
   single row ran ~1200px tall — the mobile leaderboard was unusable, on a
   deals site whose primary surface is a phone.

   The fix: one content column. name / metric / offer each get a full-width
   row, and freshness shares its row with the offer-count toggle by sitting in
   the same grid cell pinned to the opposite end. Giving the metric its own
   line is also right for this product — cost per gram of active ingredient is
   the whole thesis, so on a phone it reads as a headline under the name
   instead of a cramped right rail.
   ========================================================================== */

@media (max-width: 768px){
    .lb-summary{
        display:grid;
        grid-template-columns:36px 64px minmax(0,1fr);
        grid-template-areas:
            "rank thumb name"
            "rank thumb metric"
            "rank thumb offer"
            "rank thumb fresh";
        align-items:center;
        row-gap:var(--sp-2);
        column-gap:var(--sp-3);
        padding:var(--sp-3);
        min-height:0;
    }

    .lb-row.no-rank .lb-summary{
        grid-template-columns:64px minmax(0,1fr);
        grid-template-areas:
            "thumb name"
            "thumb metric"
            "thumb offer"
            "thumb fresh";
    }

    /* A tall stacked row leaves a centred rank floating in empty space. */
    .lb-summary .lb-rank{
        grid-area:rank;
        font-size:var(--fs-xl);
        align-self:start;
        padding-top:2px;
    }

    .lb-summary .lb-thumb{
        grid-area:thumb;
        align-self:start;
    }

    .lb-summary .lb-name{
        grid-area:name;
    }

    /* Brand and type have a full row now, so the brand no longer has to
       ellipsis away to a few characters. */
    .lb-summary .lb-name-meta{
        flex-wrap:wrap;
        row-gap:2px;
    }

    .lb-summary .lb-brand{
        white-space:normal;
        overflow:visible;
        text-overflow:clip;
    }

    .lb-summary .lb-metric-cell{
        grid-area:metric;
        align-self:start;
        display:flex;
        flex-wrap:wrap;
        align-items:center;
        justify-content:flex-start;
        gap:var(--sp-2);
    }

    .lb-summary .lb-metric{
        font-size:17px;
    }

    .lb-summary .lb-offer{
        grid-area:offer;
        align-items:flex-start;
        text-align:left;
    }

    .lb-summary .lb-fresh-slot{
        grid-area:fresh;
        justify-self:start;
    }

    .lb-summary .lb-expand{
        grid-area:fresh;
        justify-self:end;
    }
}


/* --------------------------------------------------------------------------
   Empty product thumbnail (audit finding U-02)
   --------------------------------------------------------------------------
   A product with no image yet is ordinary — the admin adds one after creating
   it — but <img src=""> made every browser draw a broken-image icon beside the
   raw alt text, which reads as a broken site rather than a missing photo.

   The placeholder is a monogram in the display face on the same surface the
   real thumbnail sits on: quiet, obviously deliberate, and it keeps the row's
   rhythm because it occupies exactly the image's box. No crimson — an absent
   photo is not an alert.
   -------------------------------------------------------------------------- */
.thumb-empty{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    flex:none;
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    border-radius:var(--r-md);
    color:var(--lr-dim);
    font-family:var(--lr-display);
    font-size:clamp(12px, 38%, 28px);
    line-height:1;
    letter-spacing:.02em;
    overflow:hidden;
}

/* The product page hero has no fixed pixel box — it fills its container. */
.product-thumb-empty{
    width:100%;
    aspect-ratio:1;
    font-size:clamp(28px, 18vw, 92px);
    border-radius:var(--r-lg);
}


/* ==========================================================================
   CONTRAST PAIRS  —  WCAG 2.2 AA  (audit findings A-01..A-03)
   ==========================================================================
   Must stay near the end of this file, after the seven eras of overrides above.

   The bug class this fixes: a light background and its dark ink were declared
   in the same rule, but a *different*, more specific rule elsewhere set only
   `color`. The background survived and the ink did not, leaving light text on
   a light chip that nobody had ever designed:

     .sort-segment.is-active   #D8D4D4 on #F4F4F5  =  1.29:1   (invisible)
     .offer-row .best-label    #A9A5A5 on #FFFFFF  =  2.44:1

   Neither pairing appears in any rule — each is an accident of the cascade.
   So the rule here is: whenever an element takes a light surface, it owns BOTH
   halves of the pair, at a specificity high enough that a stray `color` cannot
   split them again.
   ========================================================================== */

.sort-control .sort-segment.is-active,
.chart-controls .sort-segment.is-active,
.sort-segment.is-active{
    background:var(--lr-text);
    color:#050505;
}

/* The one crimson segment (biggest drop) keeps its own pair. */
.sort-control .sort-segment-drop.is-active,
.sort-segment-drop.is-active{
    background:var(--lr-accent-deal);
    color:#fff;
}

.offer-row .best-label,
.offer-row.is-best .best-label{
    background:#fff;
    color:#111;
}

/* Small crimson text: raw --lr-accent-deal is 3.17:1 on the dark ground, which
   passes only at large sizes. The token layer already ships -text for exactly
   this (#E8737E, 5.9:1) and its own comment says small crimson text must use
   it — these eyebrows were the places that did not. */
.eyebrow-deal,
.dod-eyebrow em,
.deal-eyebrow em{
    color:var(--lr-accent-deal-text);
}

/* --------------------------------------------------------------------------
   Tap targets — WCAG 2.2 AA 2.5.8 (24x24 CSS px minimum)
   --------------------------------------------------------------------------
   The footer's link rows rendered 15-19px tall. They sit in navigation lists,
   not prose, so the inline exception does not apply. Padding rather than
   height keeps the visual rhythm identical while giving the pointer a real
   target. */
.footer-links a,
.footer-bar a,
.footer-bar .footer-signout button,
.footer-column-links a,
.dod-store-link{
    display:inline-flex;
    align-items:center;
    min-height:24px;
}


/* <summary> is a control, so 2.5.8's 24px minimum applies to it too. Several
   disclosure headers (account settings, admin import panels) rendered 15-20px
   tall — fine to read, awkward to hit. Padding keeps the visual weight. */
summary{
    min-height:24px;
    display:flex;
    align-items:center;
}

/* The offer-count toggle and admin table links sit in lists and tables, not
   prose, so the inline-link exception does not cover them either. */
.lb-expand,
.admin-table a,
.attn-body a{
    display:inline-flex;
    align-items:center;
    min-height:24px;
}


/* Remaining standalone controls under the 24px floor. These are links and
   inputs that stand on their own (list items, action rows, form controls)
   rather than sitting inside a sentence, so 2.5.8's inline exception does not
   cover them. */
/* `.settings-links a` is deliberately NOT in this list. Those links are
   two-line cards (.settings-link is display:grid — a <strong> over a <small>),
   and forcing inline-flex on them turned each card into a horizontal row, so
   "Favorites" and "Products you are monitoring." collided on one line with no
   gap. They already clear 44px via .settings-link's own min-height. */
.account-links a,
.account-footer a,
.account-footer .footer-signout button,
.watch-name a,
.a-panel__head a,
.a-panel a.admin-section-link,
.auth-alt a,
.auth-form-links a,
.footer-contact a{
    display:inline-flex;
    align-items:center;
    min-height:24px;
}

/* Checkboxes and radios: the default UA box is ~13px. */
input[type="checkbox"],
input[type="radio"]{
    width:24px;
    height:24px;
    outline-offset:3px;
}


/* Standalone links inside settings panels and the footer contact block: these
   sit alone in their container rather than inside a sentence, so they are
   targets in their own right. */
.settings-panel-static a,
.footer-column a[href^="mailto:"],
.footer-contact a[href^="mailto:"],
.site-footer a[href^="mailto:"]{
    display:inline-flex;
    align-items:center;
    min-height:24px;
}


/* .sort-segment is used on <a> on the leaderboard and on <button> for the
   chart's range / unit toggles. The rule never set a background, so the
   anchors were transparent (correct) while the buttons picked up Chrome's UA
   default `buttonface` — #F0F0F0. That put --lr-soft text on a near-white
   chip at 1.29:1: effectively invisible, and only on the chart controls,
   which is why it never showed up next to the identical-looking leaderboard
   segments. Inactive segments are transparent; .is-active owns its own pair
   above. */
.sort-segment{
    background:transparent;
    border:0;
    font:inherit;
    cursor:pointer;
}


/* Breadcrumb links and the metric explainer sit in navigation / control rows,
   not inside a sentence, so 2.5.8's 24px floor applies to them as well. */
.breadcrumb a,
.metric-help,
.metric-explainer-toggle,
button.metric-help{
    display:inline-flex;
    align-items:center;
    min-height:24px;
}

/* Deal-of-the-day title link: a heading-sized target that measured 20px tall
   at the tablet breakpoint. */
.dod-body h2 a,
.dod-body h2{
    display:inline-flex;
    align-items:center;
    min-height:24px;
}


/* ==========================================================================
   v5 — MOBILE-FIRST CUSTOMER LAYER  (2026-07-28)
   ==========================================================================
   Appended last on purpose: the stylesheet has grown in eras, each overriding
   the last, and a media query adds no specificity — so anything that has to
   beat an earlier top-level rule has to come after it.

   What this layer does, in order:
     1. buttons      one hierarchy, refined weights, real tap targets
     2. header       shorter on phones, touch-first drawer, no scroll blur
     3. homepage     the redesigned sections (rail / movers / spread / how)
     4. board        the leaderboard row, decluttered for phones
     5. product      phone-order hero, quieter offer rows
     6. desktop      progressive enhancement from 769px up

   The customer site is designed at 360-430px first; every `min-width` query
   below is an enhancement, never a rescue.
   ========================================================================== */


/* ── 1. BUTTONS ───────────────────────────────────────────────────────────
   The complaint was "chunky", and chunk came from four places at once:
   50px heights, 210px fixed widths, font-weight 800, and letter-spacing on
   short labels. Weight is the biggest lever — 800 on a 13px label is what
   made a "View deal" pill read as a slab. Tap areas are unchanged or larger:
   visual weight came down, hit area did not.

   The hierarchy, loudest to quietest:
     .btn--primary   solid crimson. The money action. One per view region.
     .btn--secondary solid light. Confirmations, admin submits.
     .btn--ghost     hairline outline. Repeatable actions, list rows.
     .link-action    text + chevron. Navigation and "see more".
   ------------------------------------------------------------------------- */

.btn{
    min-height:44px;                    /* thumb target, unchanged */
    padding:0 var(--sp-4);
    border-radius:var(--r-md);
    font-weight:var(--fw-semi);         /* was 700/800 across the file */
    font-size:var(--fs-base);
    letter-spacing:0;                   /* tracking on a 3-word label = slab */
    white-space:nowrap;
}
.btn--sm{ min-height:40px; padding:0 var(--sp-3); font-size:var(--fs-sm); }
.btn--lg{ min-height:48px; padding:0 var(--sp-5); font-size:var(--fs-md); }

/* The hover lift is a mouse affordance; on touch it fires on tap and reads as
   a glitch. Touch gets a press state instead. */
@media (hover: hover){
    .btn:hover{ transform:translateY(-1px); }
}
@media (hover: none){
    .btn:hover{ transform:none; }
}
.btn:active{ transform:scale(.98); }

.btn--ghost{
    background:transparent;
    border-color:var(--lr-line);
    color:var(--lr-soft);
    font-weight:var(--fw-semi);
}
.btn--ghost:hover{ border-color:var(--lr-dim); color:var(--lr-text); background:transparent; }

/* Tertiary: a text action that still clears the 24px target floor. The chevron
   is generated, so no template ships a literal "→" for a screen reader to
   announce as "right arrow". */
.link-action{
    display:inline-flex;
    align-items:center;
    gap:6px;
    min-height:44px;
    color:var(--lr-soft);
    font-size:var(--fs-sm);
    font-weight:var(--fw-semi);
    text-decoration:none;
    white-space:nowrap;
}
.link-action::after{
    content:"";
    width:6px; height:6px;
    border-top:1.5px solid currentColor;
    border-right:1.5px solid currentColor;
    transform:rotate(45deg);
    transition:transform var(--dur-fast) var(--ease);
}
.link-action:hover{ color:var(--lr-text); }
@media (hover: hover){
    .link-action:hover::after{ transform:translateX(2px) rotate(45deg); }
}

/* Legacy chunky classes, re-pointed rather than re-authored: every template
   that used them keeps working, they just stop looking like slabs. */
.deal-button{
    margin-top:0;
    width:auto;
    min-width:0;
    min-height:44px;
    padding:0 var(--sp-5);
    border-radius:var(--r-md);
    font-weight:var(--fw-semi);
    font-size:var(--fs-sm);
}

.offer-go{
    min-height:40px;
    padding:0 var(--sp-4);
    border-radius:var(--r-md);
    font-size:var(--fs-sm);
    font-weight:var(--fw-semi);
    letter-spacing:0;
}

.watch-btn{
    min-height:40px;
    padding:0 var(--sp-4);
    font-size:var(--fs-sm);
    font-weight:var(--fw-semi);
}
.watch-hero-btn{ min-height:44px; font-size:var(--fs-base); }

.chip{
    font-weight:var(--fw-med);
    letter-spacing:0;
}
.chip.is-active{ font-weight:var(--fw-semi); }
.sort-segment{ font-weight:var(--fw-semi); letter-spacing:0; }

/* Adjacent controls need a real gap or a fat thumb hits the wrong one. */
.dod-actions,
.why-cta,
.watch-active-row{ gap:var(--sp-3); }


/* ── 2. HEADER ────────────────────────────────────────────────────────────
   The mobile header was ~76px of an 844px screen — 9% of the viewport, pinned,
   on every scroll, and the logo drove it (56px on phones, taller than on
   desktop). It is ~54px now.
   ------------------------------------------------------------------------- */

@media (max-width: 900px){
    nav.site-nav{
        padding:var(--sp-2) var(--sp-4);
        gap:var(--sp-3);
        /* backdrop-filter over a scrolling list is a per-frame blur of the
           whole strip. On a budget phone that is the difference between a
           smooth board and a stuttering one, and the header is opaque enough
           that nobody can see what it was buying. */
        backdrop-filter:none;
        background:rgba(10,9,9,.97);
    }

    .logo{ height:38px; }

    .mobile-menu-button{
        width:44px; height:44px;
        border:1px solid var(--lr-line);
        border-radius:var(--r-sm);
    }

    /* The drawer: a full-width sheet under the header rather than a floating
       330px card pinned to the right edge — the right edge is the hardest
       place on a phone for a left thumb to reach. */
    .nav-links{
        left:var(--sp-3);
        right:var(--sp-3);
        top:calc(100% + 6px);
        width:auto;
        max-height:calc(100dvh - 72px);
        overflow-y:auto;
        overscroll-behavior:contain;
        padding:var(--sp-3) var(--sp-4) var(--sp-4);
        border-radius:var(--r-lg);
    }

    .nav-links a{
        min-height:48px;                /* generous: it is a list of targets */
        padding:0 2px;
        display:flex;
        align-items:center;
        font-size:var(--fs-md);
        font-weight:var(--fw-med);
    }

    .nav-menu-title{
        font-size:10px;
        letter-spacing:.18em;
        padding:var(--sp-4) 2px var(--sp-1);
    }

    .nav-search-form input{ height:46px; font-size:var(--fs-md); }

    .nav-account-chip{
        min-height:48px;
        padding:0 2px !important;
        display:flex; align-items:center;
    }

    /* The sign-in pill runs a 6s infinite conic-gradient animation. In the
       desktop header that is a nice detail; inside a drawer that is closed
       most of the time it is a timer burning battery for nothing. */
    .nav-signin{
        animation:none;
        min-height:48px;
        margin-top:var(--sp-3);
        justify-content:center;
    }
}


/* ── 3. HOMEPAGE ──────────────────────────────────────────────────────────
   Sections alternate shape on purpose so a thumb-scroll has rhythm instead of
   eight identical stacked cards: full-bleed photo, inline counters, horizontal
   rail, bordered board, horizontal rail, hairline editorial, grid shelf,
   full-bleed band.
   ------------------------------------------------------------------------- */

.home-hero{ height:clamp(360px, 62vh, 500px); }
.home-hero-copy{ bottom:clamp(72px, 12vh, 104px); }
.home-hero-copy h1{ font-size:clamp(32px, 9vw, 60px); }
.home-hero-cta{ margin-top:var(--sp-4); }

.home-trust{
    margin-top:var(--sp-4);
    font-size:var(--fs-xs);
}

/* Shared section header: title left, one quiet action right. */
.home-sec-head{
    width:min(1180px, calc(100% - 32px));
    /* A section heading sat flush against its own content: measured 0px
       between .home-sec-head and both .cat-shelf and .drop-empty. The rails
       below carry their own margin-top, and these are block siblings in normal
       flow, so the two collapse to whichever is larger rather than stacking. */
    margin:0 auto var(--sp-5);
    display:flex;
    align-items:flex-end;
    justify-content:space-between;
    gap:var(--sp-3);
}
.home-sec-head .section-title{ font-size:clamp(22px, 6vw, 32px); }

/* --- category rail: one swipe to any board, each carrying a live figure --- */
.home-rail{
    display:flex;
    gap:var(--sp-3);
    width:100%;
    margin:var(--sp-5) 0 0;
    padding:0 var(--sp-4) var(--sp-2);
    overflow-x:auto;
    scroll-snap-type:x proximity;
    -webkit-overflow-scrolling:touch;
    scrollbar-width:none;
}
.home-rail::-webkit-scrollbar{ display:none; }

.home-rail-item{
    flex:0 0 auto;
    min-width:132px;
    scroll-snap-align:start;
    display:grid;
    align-content:start;
    gap:2px;
    padding:var(--sp-3) var(--sp-4) var(--sp-3) 0;
    border-top:2px solid var(--lr-text);
    text-decoration:none;
    transition:border-color var(--dur-fast) var(--ease);
}
.home-rail-item:hover{ border-top-color:var(--lr-accent-deal); }
.home-rail-label{ font-size:var(--fs-sm); font-weight:var(--fw-semi); color:var(--lr-text); }
.home-rail-figure{
    font-size:var(--fs-lg);
    font-weight:var(--fw-bold);
    font-variant-numeric:tabular-nums;
    margin-top:var(--sp-1);
}
.home-rail-note{ font-size:var(--fs-xs); color:var(--lr-dim); }

/* Empty-thumbnail monograms carry an inline pixel width from the thumb()
   macro (the size the caller asked for). Inside a sized container that fixed
   width becomes the grid track's min-content, which is how the two-up featured
   shelf grew 12px wider than its own container and put a horizontal scrollbar
   on the homepage. In a box that already has dimensions, the monogram fills
   the box. */
.featured-thumb .thumb-empty,
.dod-thumb .thumb-empty,
.spotlight-thumb .thumb-empty,
.deal-thumb .thumb-empty,
.lb-thumb .thumb-empty{
    width:100% !important;
    height:100% !important;
}
.featured-thumb img,
.dod-thumb img,
.spotlight-thumb img,
.deal-thumb img,
.lb-thumb img{ max-width:100%; }

/* The monogram's own size rule is font-size:clamp(12px, 38%, 28px), and a
   percentage font-size resolves against the PARENT's font-size, not the box —
   38% of 16px is 6px, so every monogram clamped to the 12px floor and read as
   a speck in a 160px tile. Sized per container instead. */
.lb-thumb .thumb-empty{ font-size:20px; }
.deal-thumb .thumb-empty{ font-size:22px; }
.spotlight-thumb .thumb-empty{ font-size:26px; }
.dod-thumb .thumb-empty{ font-size:30px; }
.featured-thumb .thumb-empty{ font-size:38px; }

/* The coupon tab is flex-basis:100% so it breaks onto its own line, which also
   made it stretch to the row's full width — a heavy bordered slab under every
   discounted price. It says four words; small crimson text says them. */
.price-coupon{
    background:none;
    border:0;
    padding:0;
    border-radius:0;
    font-weight:var(--fw-med);
}

/* The flash container reserves 18px above <main> on every page, whether or not
   anything was flashed. Nothing is flashed on the vast majority of them. */
.site-flashes:empty{ margin:0; }

/* --- deal of the day: price first, then the proof --- */
/* No negative margin. The board used to tuck under the hero's bottom edge,
   but the trust strip and the category rail sit between them now, so the pull
   overlapped those instead of the photo. */
.deal-section{ margin-top:var(--sp-5); padding:0 0 var(--sp-2); }

.dod-board{
    width:min(1180px, calc(100% - 32px));
    margin:0 auto;
    padding:var(--sp-4);
    gap:var(--sp-3) var(--sp-4);
    grid-template-columns:auto minmax(0,1fr);
    grid-template-areas:
        "thumb  body"
        "score  score"
        "action action";
    align-items:start;
}
.dod-thumb{ grid-area:thumb; width:76px; height:88px; }
.dod-body{ grid-area:body; }
.dod-score{ grid-area:score; text-align:left; }
.dod-actions{ grid-area:action; display:grid; justify-items:stretch; }

.dod-body h2{
    display:block;
    font-size:var(--fs-md);
    line-height:1.3;
    margin-top:var(--sp-2);
}
.dod-tag{ gap:var(--sp-2); }
.judge-lights i{ width:8px; height:8px; }

.dod-money{
    display:flex;
    align-items:baseline;
    flex-wrap:wrap;
    gap:var(--sp-2);
    justify-content:flex-start;
    margin-top:0;
}
.dod-price{ font-size:30px; font-weight:var(--fw-bold); font-variant-numeric:tabular-nums; }
.dod-baseline{ font-size:var(--fs-sm); }
.dod-drop{ position:relative; top:-2px; }
.dod-permetric{
    font-size:var(--fs-sm);
    color:var(--lr-muted);
    margin-top:var(--sp-1);
    font-family:inherit;
    letter-spacing:0;
}
.dod-permetric{ display:flex; flex-direction:column; gap:2px; align-items:flex-end; }
.dod-metric{
    color:var(--lr-text);
    font-weight:var(--fw-semi);
    font-variant-numeric:tabular-nums;
}
.dod-store{ color:var(--lr-dim); font-size:var(--fs-xs); }
.dod-cta{ width:100%; }
.dod-store-link{ justify-content:center; color:var(--lr-muted); }

/* --- Top Deals: the biggest genuine price drops (§5) ---------------------
   This is the old "Prices that moved" rail, kept visually intact and given the
   fields §5 requires: old price, new price, percentage, dollars, value metric
   and freshness. Same swipeable-rail shape, same surface, same border and
   radius — only the card is taller because it now says more.
   ------------------------------------------------------------------------- */
.home-top-deals{ padding-top:var(--sp-7); }

.deal-rail{
    display:flex;
    gap:var(--sp-3);
    margin-top:var(--sp-4);
    padding:0 var(--sp-4) var(--sp-2);
    overflow-x:auto;
    scroll-snap-type:x proximity;
    -webkit-overflow-scrolling:touch;
    scrollbar-width:none;
}
.deal-rail::-webkit-scrollbar{ display:none; }

.deal-card{
    flex:0 0 auto;
    width:186px;
    scroll-snap-align:start;
    position:relative;
    display:flex;
    flex-direction:column;
    gap:var(--sp-2);
    padding:var(--sp-3);
    border:1px solid var(--lr-line);
    border-radius:var(--r-lg);
    background:var(--lr-surface);
    transition:border-color var(--dur-fast) var(--ease);
}
.deal-card:hover{ border-color:var(--lr-dim); }

.deal-card-link{
    display:grid;
    gap:var(--sp-1);
    text-decoration:none;
    color:inherit;
}
.deal-card-link:active{ transform:scale(.99); }

.deal-thumb{
    display:flex; align-items:center; justify-content:center;
    width:56px; height:56px;
    border:1px solid var(--lr-line-soft);
    border-radius:var(--r-sm);
    background:var(--lr-surface-2);
    overflow:hidden;
}
.deal-thumb img{ width:100%; height:100%; object-fit:contain; padding:3px; }

.deal-badge{ position:absolute; top:var(--sp-3); right:var(--sp-3); }

.deal-name{
    margin-top:var(--sp-2);
    font-size:var(--fs-sm);
    font-weight:var(--fw-semi);
    line-height:1.3;
    color:var(--lr-text);
    /* Two lines then ellipsis: one long title must not make its card twice the
       height of every neighbour in the rail. */
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
    overflow:hidden;
}

/* §1/§7 the price you pay today is the dominant figure; the comparison price
   is small, struck and ABOVE it, so the eye lands on the number that matters. */
.deal-price-row{ display:flex; align-items:baseline; gap:6px; flex-wrap:wrap; }
.deal-was{
    font-size:var(--fs-xs);
    color:var(--lr-dim);
    text-decoration-line:line-through;
    font-variant-numeric:tabular-nums;
}
.deal-now{
    font-size:var(--fs-lg);
    font-weight:var(--fw-bold);
    color:var(--lr-text);
    font-variant-numeric:tabular-nums;
}
/* §6 percentage AND dollars, in the accent that means "saving" everywhere
   else on the site. Wraps rather than truncating — the dollar figure is half
   the claim and clipping it would leave the flattering half. */
.deal-savings{
    font-size:var(--fs-xs);
    font-weight:var(--fw-bold);
    color:var(--lr-accent-deal-text);
    font-variant-numeric:tabular-nums;
    line-height:1.35;
}
.deal-metric{ font-size:var(--fs-xs); color:var(--lr-muted); font-variant-numeric:tabular-nums; }
.deal-fresh{ font-size:var(--fs-xs); }
.deal-buy{ margin-top:auto; width:100%; justify-content:center; }

/* From tablet up the rail becomes a grid: there is horizontal room, and a
   board the section calls "Top Deals" reads better all at once than as
   something to swipe. The count is admin-configurable, so the grid is
   auto-fill — nothing here assumes eight cards. */
@media (min-width: 768px){
    .deal-rail{
        display:grid;
        grid-template-columns:repeat(auto-fill, minmax(196px, 1fr));
        overflow:visible;
        padding-inline:0;
        width:min(1180px, calc(100% - 40px));
        margin-inline:auto;
    }
    .deal-card{ width:auto; }
}

/* --- spotlight: hairline editorial, no card --- */
.spotlight-section,
.featured-section{ padding-top:var(--sp-7); }

.spotlight-entry{
    width:min(1180px, calc(100% - 32px));
    margin:var(--sp-4) auto 0;
    padding:var(--sp-5) 0 0;
}
.spotlight-business{ font-size:var(--fs-lg); }
.spotlight-blurb{ font-size:var(--fs-base); }

.spotlight-product{
    display:grid;
    grid-template-columns:auto minmax(0,1fr);
    grid-template-areas:
        "thumb name"
        "thumb figures"
        "go    go";
    align-items:center;
    gap:var(--sp-2) var(--sp-3);
    margin-top:var(--sp-4);
}
.spotlight-thumb{ grid-area:thumb; align-self:start; }
.spotlight-name{ grid-area:name; font-size:var(--fs-base); font-weight:var(--fw-semi); }
.spotlight-figures{
    grid-area:figures;
    display:flex; align-items:baseline; gap:var(--sp-3);
}
.spotlight-price{ font-size:var(--fs-md); font-weight:var(--fw-bold); font-variant-numeric:tabular-nums; }
.spotlight-metric{ font-size:var(--fs-sm); color:var(--lr-muted); font-variant-numeric:tabular-nums; }
.spotlight-go{ grid-area:go; justify-content:center; margin-top:var(--sp-2); }

/* --- featured shelf --- */
.featured-shelf{
    width:min(1180px, calc(100% - 32px));
    margin:var(--sp-4) auto 0;
    padding-top:var(--sp-5);
    display:grid;
    grid-template-columns:repeat(2, minmax(0, 1fr));
    gap:var(--sp-5) var(--sp-4);
    overflow:visible;
}
.featured-thumb{ height:104px; }
.featured-body{ display:grid; gap:2px; padding-top:var(--sp-3); }
.featured-name{
    font-size:var(--fs-sm); font-weight:var(--fw-semi); line-height:1.3;
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
.featured-price-row{ display:flex; align-items:center; gap:var(--sp-2); margin-top:var(--sp-1); }
.featured-price{ font-size:var(--fs-md); font-weight:var(--fw-bold); font-variant-numeric:tabular-nums; }
.featured-metric{ font-size:var(--fs-xs); font-weight:var(--fw-reg); color:var(--lr-dim); }
.featured-go{ min-height:32px; font-size:var(--fs-xs); }

/* --- the spread: the one insight band --- */
.home-why{
    margin-top:var(--sp-8);
    padding:var(--sp-7) var(--sp-4);
    border-top:1px solid var(--lr-line);
    border-bottom:1px solid var(--lr-line);
    background:var(--lr-surface);
}
.why-lead{ width:min(1180px, 100%); margin:0 auto; }
.why-lead .section-title{
    font-size:clamp(30px, 9vw, 52px);
    line-height:1.05;
    letter-spacing:-.02em;
    margin-top:var(--sp-2);
}
.why-lead .section-title em{ font-style:normal; color:var(--lr-accent-deal-text); }
.why-text{
    margin-top:var(--sp-4);
    max-width:56ch;
    color:var(--lr-soft);
    font-size:var(--fs-base);
    line-height:var(--lh-prose);
}
.why-text b{ color:var(--lr-text); font-variant-numeric:tabular-nums; }

/* .spread-grid / .spread-cell / .spread-range / .spread-count were DELETED
   here on 2026-08-01 along with the markup they styled.

   They drew the five-cell metric-range table under "Why this exists"
   ("8.9¢/g protein → 249.9¢/g protein · across 81 products", and four more in
   three other units). The template no longer emits it and the homepage no
   longer runs market_spread() to feed it, so these rules had nothing left to
   match. Removing them rather than leaving them behind is the point of §4:
   hiding the block with CSS while the query still ran and the markup still
   rendered would have been the same page with a longer stylesheet. */

/* The "how it works" block (.home-how/.how-steps/.how-num/.how-cta) was
   deleted here: its markup — a numbered three-step walkthrough under its own
   heading — merged into the single .home-why section on the homepage, so the
   rules had no elements left to match. Its replacement, .why-points, is a few
   lines below. */

/* ── 4. THE BOARD ─────────────────────────────────────────────────────────
   Product-card declutter. The question asked of every element was "does a
   shopper need this to decide whether the row deserves a tap?".

   Kept on a phone: rank, image, name, cost per unit, price, store + size,
   drop badge, freshness, offer count.
   Deferred to >=769px or into the expanded row: the value bar (rank already
   orders the list), the 84px sparkline (unreadable at that size), the "Best
   value" text badge (that is what rank 1 means), and the protein-type chip
   (it is a filter, and it is in the expanded panel).

   These are CSS-hidden rather than cut from the markup because the same
   partial is cloned client-side by static/leaderboard.js when filters change,
   and one source of markup truth is worth more here than the ~200 bytes a
   hidden sparkline costs.
   ------------------------------------------------------------------------- */

@media (max-width: 768px){
    .lb-summary .lb-value-bar,
    .lb-summary .lb-spark,
    .lb-summary .lb-best-badge,
    .lb-summary .lb-chip{ display:none !important; }

    /* After display.py suppresses the repeated brand, most rows have nothing
       left on the meta line — it must not still claim a row of grid. */
    .lb-summary .lb-name-meta:empty{ display:none; }

    .lb-summary{
        padding:var(--sp-3) var(--sp-4);
        row-gap:var(--sp-1);
        column-gap:var(--sp-3);
        grid-template-columns:22px 56px minmax(0,1fr);
        grid-template-areas:
            "rank thumb name"
            "rank thumb metric"
            "rank thumb offer"
            "rank thumb fresh";
    }
    .lb-row.no-rank .lb-summary{
        grid-template-columns:56px minmax(0,1fr);
        grid-template-areas:
            "thumb name"
            "thumb metric"
            "thumb offer"
            "thumb fresh";
    }

    .lb-summary .lb-rank{
        font-size:var(--fs-md);
        font-weight:var(--fw-bold);
        font-family:inherit;
        padding-top:3px;
    }
    .lb-summary .lb-thumb{ width:56px; height:56px; }

    .lb-summary .lb-name a{ font-size:var(--fs-base); font-weight:var(--fw-semi); }
    .lb-summary .lb-brand{ font-size:var(--fs-xs); }

    /* Cost per unit is the thesis, so on a phone it is the headline under the
       name rather than a cramped right rail. */
    .lb-summary .lb-metric-cell{ margin-top:var(--sp-1); gap:var(--sp-2); }
    .lb-summary .lb-metric{ font-size:var(--fs-lg); font-weight:var(--fw-bold); }

    /* Price and store on one line, price first. */
    .lb-summary .lb-offer{
        flex-direction:row;
        align-items:baseline;
        flex-wrap:wrap;
        gap:var(--sp-1) var(--sp-2);
    }
    .lb-summary .lb-offer-detail{
        display:flex;
        align-items:baseline;
        flex-wrap:wrap;
        gap:var(--sp-1) var(--sp-2);
    }
    .lb-summary .lb-offer-price{ font-size:var(--fs-md); font-weight:var(--fw-bold); order:-1; }
    .lb-summary .lb-offer-size{ font-size:var(--fs-xs); color:var(--lr-dim); white-space:normal; }

    .lb-buy-inline{
        margin-top:var(--sp-2);
        min-height:36px;
        padding:0 var(--sp-3);
        font-size:var(--fs-xs);
    }

    .lb-summary .lb-fresh-slot{ font-size:var(--fs-xs); }
    .lb-summary .lb-expand{ font-size:var(--fs-xs); font-weight:var(--fw-med); }
}

/* REMOVED 2026-08-01 (§5). This made every buy button on the board a
   transparent OUTLINE except the first row's, on the reasoning that twenty-five
   crimson pills had no hierarchy. Two problems with it:

   1. It made the site's buy button grey on the leaderboard and crimson on the
      homepage, so a shopper could not learn what "buy" looks like here. The
      hierarchy argument was right; recolouring twenty-four buttons was the
      wrong lever, and the row-level rank-one treatment is the right one.
   2. "positional rather than a class the JS has to remember to set" was the
      actual bug: `:first-of-type` is a DOM position, so on a board sorted
      Price: High to Low it filled the MOST EXPENSIVE product's button with the
      colour this site uses to mean "best value".

   `.lb-buy-inline` is now a layout hook only — margin, nothing else — and the
   button under it is the same .cta-deal crimson as everywhere else. */

/* Leaderboard page frame. */
.lb-page{ padding-top:var(--sp-5); }
.lb-header .section-title{ font-size:clamp(28px, 8vw, 56px); }
.lb-header .lb-explainer{
    margin-top:var(--sp-3);
    font-size:var(--fs-sm);
    color:var(--lr-muted);
    max-width:62ch;
}
/* Fine print, not body copy: it sat at the same colour as the explainer above
   it, so the header read as two competing paragraphs. */
.lb-header .data-disclaimer{ font-size:var(--fs-xs); color:var(--lr-dim); }
.lb-meta{ font-size:var(--fs-xs); color:var(--lr-dim); }

/* Filters + sort: two pills, always within a thumb's reach. */
.lb-filters-wrap{ margin-top:var(--sp-4); }
.lb-controls{ gap:var(--sp-2); justify-content:flex-start; }
.lb-dd > summary{ min-height:44px; font-weight:var(--fw-semi); }

@media (max-width: 768px){
    /* The filter panel becomes a bottom-anchored sheet rather than a popover
       pinned under a button at the top of the page: on a phone the thumb is at
       the bottom, and a 460px-wide dropdown overflows a 360px screen. */
    .lb-dd-panel-filters{
        position:fixed;
        left:var(--sp-3);
        right:var(--sp-3);
        top:auto;
        bottom:var(--sp-3);
        width:auto;
        max-height:min(72dvh, 560px);
        z-index:var(--z-modal);
        border-radius:var(--r-lg);
    }
    .lb-dd-panel-sort{ right:auto; left:0; min-width:200px; }
    .lb-dd-panel .chip-row{ flex-wrap:wrap; overflow-x:visible; }

    /* Phase 1 §5 — the unexplained circle.
       `display:grid` alone left the segments stacked inside a pill
       (border-radius:999px), so the panel's top and bottom rendered as two
       large half-circles. A stacked control is a menu, not a pill, so it gets
       menu geometry: square-ish corners, full-width rows, a tick on the
       selected one. */
    .lb-dd-panel-sort .sort-control{
        display:grid;
        gap:2px;
        width:100%;
        border-radius:var(--r-md, 10px);
        padding:4px;
    }
    .lb-dd-panel-sort .sort-segment{
        width:100%;
        justify-content:space-between;
        border-radius:8px;
        padding:0 12px;
    }
    /* The tick only exists in this layout. */
    .lb-dd-panel-sort .sort-segment.is-active .sort-segment-check{
        display:block;
    }
    .sort-segment{ min-height:44px; }
}


/* ── 5. PRODUCT PAGE ──────────────────────────────────────────────────────
   Phone order: image → identity → the number → buy → watch → detail. The
   description and the two disclaimers used to sit between the price and the
   buy button; they follow it now.
   ------------------------------------------------------------------------- */

/* The page's headline number was set in Michroma, whose committed subset is
   caps + digits (see the @font-face comment) — so "4.2¢/g protein" rendered
   its cent sign from a fallback face, and at line-height 1.7 the string broke
   into two enormous lines on a phone. Inter has the glyph, and this is the one
   number the whole product is about: it has to be right before it is stylish.
   Michroma keeps every other display role (rank numerals, eyebrows, kickers). */
/* The figure is a <p>, and `.product-info p` (0,1,1) out-specifies a bare
   `.metric-hero-figure` (0,1,0) — so for all the intent below, the ¢/g number
   was painting --lr-muted. The one number the whole site exists to publish was
   the quietest large thing on the page: same 44px as the product title, but
   grey, and dimmer than the 16px sticker price sitting under it. Scoped to
   .product-info so it outranks the generic paragraph colour it lives inside. */
.product-info .metric-hero-figure,
.metric-hero-figure{
    font-family:'Inter', sans-serif;
    /* It is the loudest thing on the page, so it takes the brightest ink. */
    color:var(--lr-text);
    font-weight:var(--fw-bold);
    font-variant-numeric:tabular-nums;
    letter-spacing:-.02em;
    line-height:1.05;
}

/* Watching is a retention action, not the money action. On phones it had
   picked up a crimson rim and crimson text, which put it at the same volume as
   "View best deal" directly above it. The dot keeps the accent. */
.watch-btn:not(.is-watching){
    border-color:var(--lr-line);
    color:var(--lr-soft);
    background:var(--lr-surface-2);
}
.watch-btn:not(.is-watching) .watch-dot{ color:var(--lr-accent-deal-text); }
.watch-btn:not(.is-watching):hover{ border-color:var(--lr-dim); color:var(--lr-text); }

@media (max-width: 900px){
    .product-page{
        grid-template-columns:1fr;
        gap:var(--sp-5);
        margin-top:var(--sp-5);
        width:calc(100% - 32px);
    }
    .product-image{ height:auto; aspect-ratio:1; padding:var(--sp-4); }

    .product-info h1{ font-size:clamp(22px, 6.5vw, 30px); line-height:1.2; overflow-wrap:anywhere; }

    /* The metric is why the page exists — it leads on a phone. */
    .metric-hero{ margin-top:var(--sp-4); }
    .metric-hero-figure{ font-size:clamp(30px, 8.5vw, 44px); }
    .product-buy-cta{ width:100%; margin-top:var(--sp-4); }
    .watch-hero-btn{ width:100%; justify-content:center; }
    .watch-hero-form{ justify-items:stretch; }

    .product-info > p{ font-size:var(--fs-sm); }
    .affiliate-note,
    .product-info .data-disclaimer{ font-size:var(--fs-xs); }

    .prices-section{ padding:var(--sp-7) 0; }
    .offer-table{ gap:var(--sp-3); }
}


/* ── 6. DESKTOP ENHANCEMENT ───────────────────────────────────────────────
   Everything above is the phone design. From 769px the extra width buys back
   density and the context a small screen could not afford.
   ------------------------------------------------------------------------- */

@media (min-width: 769px){
    /* .home-why is deliberately NOT in this list. It is a full-bleed band --
       background + hairline rules edge to edge -- and its two children
       (.why-lead, .spread-grid) already constrain themselves to 1180 and
       centre. Giving the band a width too left it flush against the viewport
       with a 260px bare strip on the right. */
    .home-sec-head,
    .featured-shelf,
    .spotlight-entry,
    .dod-board{ width:min(1180px, calc(100% - 48px)); }

    .home-rail{
        width:min(1180px, calc(100% - 48px));
        margin:var(--sp-6) auto 0;
        padding:0;
        display:grid;
        grid-template-columns:repeat(auto-fit, minmax(150px, 1fr));
        gap:var(--sp-5);
        overflow:visible;
    }
    .home-rail-item{ min-width:0; padding-right:0; }

    .deal-section{ margin-top:var(--sp-6); }
    .dod-board{
        grid-template-columns:auto minmax(0,1fr) auto auto;
        grid-template-areas:"thumb body score action";
        align-items:center;
        /* 32px between four columns left the body 334px — 19px short of the
           kicker plus its judge lights, so "Deal of the day · PROTEIN /
           POWDER" broke across two lines and stranded one word. */
        gap:var(--sp-5);
        padding:var(--sp-6);
    }
    .dod-thumb{ width:116px; height:136px; }
    .dod-body h2{ font-size:26px; }
    .dod-score{ text-align:right; }
    .dod-money{ justify-content:flex-end; }
    .dod-permetric{ text-align:right; }
    .dod-cta{ width:auto; min-width:170px; }

    .deal-rail{
        width:min(1180px, calc(100% - 48px));
        margin:var(--sp-5) auto 0;
        padding:0 0 var(--sp-2);
    }
    .deal-thumb{ width:64px; height:64px; }
    .deal-name{ font-size:var(--fs-base); }

    .spotlight-product{
        grid-template-columns:auto minmax(0,1fr) auto auto;
        grid-template-areas:"thumb name figures go";
        gap:var(--sp-5);
    }
    .spotlight-go{ margin-top:0; }

    .featured-shelf{ grid-template-columns:repeat(4, minmax(0, 1fr)); gap:var(--sp-6) var(--sp-5); }
    .featured-thumb{ height:150px; }
    .featured-name{ font-size:var(--fs-base); }

    .home-why{ padding:var(--sp-9) var(--sp-6); }
    .why-lead{ width:min(1180px, calc(100% - 48px)); }
    .spread-grid{
        width:min(1180px, calc(100% - 48px));
        grid-template-columns:repeat(auto-fit, minmax(160px, 1fr));
        gap:var(--sp-6);
    }

    .why-cta .btn{ flex:0 0 auto; }

    .lb-buy-inline{ min-height:36px; font-size:var(--fs-xs); }
}

/* Large desktop: the reading column stops growing so lines stay readable, but
   the shelves use the room rather than leaving a sparse gutter. */
@media (min-width: 1440px){
    .featured-shelf{ grid-template-columns:repeat(5, minmax(0, 1fr)); }
}



/* â”€â”€ 7. PRODUCT PAGE: OFFER ROWS + CHART â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Both were built desktop-first and reflowed. The offer list gave every row a
   filled crimson button, and the chart was pinned to a 720px canvas inside a
   horizontally scrolling box â€” a sideways swipe inside a vertically scrolling
   page, on the one element a phone user most wants to read.
   ------------------------------------------------------------------------- */

/* One primary per list: the best-value offer. The rest are outlines. */
.offer-row .offer-go{
    background:transparent;
    border:1px solid var(--lr-line);
    color:var(--lr-soft);
}
.offer-row .offer-go:hover{
    background:transparent;
    border-color:var(--lr-dim);
    color:var(--lr-text);
    transform:none;
}
.offer-row.is-best .offer-go{
    background:var(--lr-accent-deal);
    border-color:var(--lr-accent-deal);
    color:#fff;
}
.offer-row.is-best .offer-go:hover{
    background:var(--lr-accent-deal-hover);
    border-color:var(--lr-accent-deal-hover);
    color:#fff;
}

/* The price is what gets paid â€” it should not be the quietest number in the
   row. The metric stays the ranking signal; the price stops whispering. */
.po-price{ font-size:var(--fs-md); font-weight:var(--fw-semi); color:var(--lr-text); }
.offer-row .best-label{ font-size:var(--fs-xs); padding:2px 8px; }

/* Chart controls: two segmented pills on one line, sized to their labels. */
.chart-controls{ gap:var(--sp-2); }
.chart-controls .sort-control{
    display:inline-flex;
    width:auto;
    flex:0 0 auto;
}
.chart-controls .sort-segment{ min-height:36px; padding:0 var(--sp-3); font-size:var(--fs-sm); }

@media (max-width: 768px){
    /* Fit the chart to the screen instead of scrolling it sideways. The SVG
       has a viewBox, so it scales; the range control, the "Latest: â€¦" line and
       the "View as table" disclosure carry the precision that the smaller
       canvas gives up. */
    .history-chart{ overflow-x:visible; padding:var(--sp-4) var(--sp-2); }
    /* static/chart.js draws in a fixed 1000x340 coordinate space, so the box
       has to be close to that ratio or the SVG letterboxes inside it — a 3/2
       box wasted ~110px of empty panel above and below the plot. */
    .line-chart{ min-width:0; min-height:0; aspect-ratio:1000 / 380; }
    /* The viewBox is 1000 units wide, so text scales down with it â€” these are
       viewBox units, not CSS pixels. */
    .chart-axis-label{ font-size:26px; }
    .chart-axis-title{ display:none; }
    .price-point{ r:9; }

    .chart-section .section-heading{ display:grid; gap:var(--sp-3); }
    .chart-section .eyebrow{ font-size:10px; letter-spacing:.08em; }
}

/* The per-store toggles read as filled white buttons because .chart-store-chip
   never set its own background and inherited the UA button face. */
.chart-store-chip{
    background:var(--lr-surface-2);
    color:var(--lr-muted);
    min-height:36px;
    font-weight:var(--fw-med);
}
.chart-store-chip.is-active{
    background:var(--lr-surface-2);
    color:var(--lr-text);
    border-color:var(--lr-dim);
}



/* â”€â”€ 8. CONTRAST PAIRS + TAP TARGETS INTRODUCED BY THIS PASS â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Same rule the file's existing CONTRAST PAIRS block states: an element that
   takes a coloured surface owns BOTH halves of the pair, at a specificity a
   stray `color` elsewhere cannot split.
   ------------------------------------------------------------------------- */

/* .lb-rank.is-badge fills the rank cell with solid crimson (it holds a drop
   percentage, not a numeral). The podium rule then painted the top row's ink
   crimson too â€” #E8737E on #B23540 is 2.07:1, invisible. */
/* Scoped off :first-of-type for the same reason as the podium accent above:
   "first in the DOM" is not "won the ranking". `.is-badge` is already specific
   to the drop-percentage rank cell, so it needs no positional qualifier at
   all. */
.lb-row .lb-rank.is-badge,
.lb-row.is-rank-one .lb-rank.is-top.is-badge{
    background:var(--lr-accent-deal);
    color:#fff;
}

/* Same collision on the deal board: .drop-badge supplies the crimson fill,
   and the older .dod-drop rule supplied crimson ink on top of it. */
.dod-money .dod-drop,
.drop-badge.dod-drop{
    color:#fff;
}

/* Product-title links render 19-20px tall â€” under WCAG 2.2 AA 2.5.8's 24px
   floor. They are the primary target of every card on the site, so they get
   the height rather than an exemption argument. */
.lb-name a,
.spotlight-name,
.featured-name,
.deal-name,
.watch-name a,
.search-result strong{
    display:block;
    min-height:24px;
}
/* -webkit-box is what clamps these to two lines; `display:block` above would
   undo the clamp, so restore it. min-height still applies to a -webkit-box, so
   a one-line title in a wide card still clears the floor. */
.featured-name,
.deal-name{
    display:-webkit-box;
    min-height:24px;
    padding:1px 0;
}

/* The watchlist's alert-mode row is a wrapping flex row holding a <select>
   with long option labels, a price input and a Save button.

   The select used to take flex-basis:100%, which forced Save onto a line of
   its own and made every card ~56px taller than it needed to be. It only has
   to be wide enough for "Below my target price" (~150px), which leaves Save
   beside it on a 390px phone; below that the wrap still catches it. */
.watch-target-row{ flex-wrap:wrap; align-items:center; }
.watch-target-row .watch-mode-select{ flex:1 1 150px; min-width:0; }
@media (min-width: 601px){
    .watch-target-row .watch-mode-select{ flex:0 1 auto; }
}



/* The "what's this?" popover is absolutely positioned from an inline-block
   sitting mid-sentence, so `left:0; width:min(280px,70vw)` put its right edge
   66px past a 375px viewport. It is visibility:hidden, not display:none, so it
   still contributed to the document's scroll width — the product page carried
   a horizontal scrollbar that nothing visible explained. Anchoring it to the
   label line instead of the word lets it span the column. */
@media (max-width: 768px){
    .metric-hero-label{ position:relative; }
    .metric-what{ position:static; }
    .metric-what-pop{ left:0; right:0; width:auto; }
}

/* ── REDUCED MOTION ───────────────────────────────────────────────────────
   Everything decorative in this layer is opacity/transform only, so honouring
   the preference means switching it off, never losing content.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce){
    .btn,
    .link-action,
    .link-action::after,
    .deal-card,
    .deal-card-link,
    .home-rail-item{ transition:none; }
    .btn:active,
    .deal-card-link:active{ transform:none; }
}


/* ── THE `hidden` ATTRIBUTE, ENFORCED ─────────────────────────────────────
   The UA rule for [hidden] is `display:none` at zero specificity, so ANY
   class that sets a display — and every button, chip, flex row and grid in
   this file sets one — silently beats it. `<div class="watch-target-inline"
   hidden>` then renders in full.

   This file already carried fourteen per-selector `.thing[hidden]{display:none}`
   patches, each one added the day someone noticed a specific element ignoring
   its own hidden attribute. That list is a symptom: it can only ever cover the
   elements somebody happened to catch, and two it had not caught were live on
   customer pages at the 2026-07-28 review —

     .lb-show-all         an empty 34x44 ghost button under every category
                          board, on all three breakpoints
     .watch-target-inline the product page's "Alert me below $" field, which
                          is meant to appear only after the first tap on
                          "Watch price". Because it was always visible, that
                          first tap spent itself revealing an already-visible
                          field and did NOT submit — a shopper typed a target
                          price, pressed Watch price, and nothing happened.
                          Two clicks were needed to watch a product.

   One rule replaces the list. `hidden` is a semantic statement — "this is not
   relevant right now" — and no component's display value should be able to
   contradict it, which is why every mainstream CSS reset ships exactly this.
   !important is load-bearing here, not defensive: beating a class selector
   from an attribute selector needs it.

   Anything that must stay laid out while concealed wants visibility/opacity
   (see .metric-what-pop above), never the hidden attribute.
   ------------------------------------------------------------------------- */
[hidden]{ display:none !important; }


/* ── SEARCH RESULTS: NO SIDEWAYS SCROLL ON A PHONE ────────────────────────
   `/?q=` at 360px pushed the whole document to 376px wide — the page scrolled
   horizontally, which reads as broken on the one screen a visitor lands on
   after searching.

   Cause: `.search-result` is a flex row holding the product title and a
   "Category · from $price" line. Flex items default to `min-width:auto`, so
   neither can shrink below its own min-content; together with the gap and
   padding that floor measured 354px against a 320px container. The row's
   single grid track then sized itself to the content (355.7px) instead of the
   container, and everything hung 36px off the right edge.

   Two halves, both needed: pin the track to the container so it can never be
   content-sized, and let the row wrap so the meta line drops beneath the title
   on a narrow screen rather than demanding width that isn't there. Truncation
   was the alternative and it is the worse one — the price is the reason the
   line exists.
   ------------------------------------------------------------------------- */
.search-result-list{ grid-template-columns:minmax(0,1fr); }
/* `gap:14px` is a column gap that the wrap turns into a 14px ROW gap too, so
   every result stood 80px tall with a band of nothing between its title and
   its price. Splitting the axes brings the row to ~64px, which is two more
   results on a phone screen for no loss of legibility. */
.search-result{ flex-wrap:wrap; gap:2px 14px; padding:10px 14px; }
.search-result strong,
.search-result span{ min-width:0; }
/* `margin-left:auto` right-aligns the meta beside the title on a wide row. Once
   it wraps to its own line that same rule shoves it to the right edge, so every
   result read as a title on the left and a price on the right of the line
   below — a zigzag down the page. Under the title is where it belongs. */
@media (max-width: 640px){
    .search-result span{ margin-left:0; }
}


/* ── MOBILE MENU BUTTON ───────────────────────────────────────────────────
   The button never declared a `background`, so it inherited Chrome's UA
   `buttonface` — rgb(240,240,240). Its three bars are --lr-text (#F4F4F5).
   That is near-white bars on a near-white plate: a measured **1.03:1**, so
   the icon was invisible and the control read as a pale slab stuck on the
   black header. Exactly the A-01 failure (a UA button surface defeating a
   light foreground), recurring because nothing reset the appearance.

   `appearance:none` is the load-bearing line — without it Chrome keeps
   painting its own surface and any background we set can still be composited
   against the UA control. Everything else is the LiftRadar header palette:
   dark plate, white bars, white-ish hairline outline.

   Declared last so none of the four earlier .mobile-menu-button blocks (two
   of them inside @media max-width:900px, one of which zeroes the border) can
   take the background back.
   ------------------------------------------------------------------------- */
.mobile-menu-button{
    -webkit-appearance:none;
    appearance:none;
    background:transparent;              /* the dark header IS the fill */
    border:1px solid rgba(255,255,255,.30);
    border-radius:var(--r-sm, 8px);
    /* 44x44 is the WCAG 2.2 target floor and the header's rhythm — keep it
       even where an earlier block drops the border. */
    width:44px;
    height:44px;
    gap:5px;
    transition:background-color var(--dur-fast,.15s) var(--ease, ease),
               border-color var(--dur-fast,.15s) var(--ease, ease);
}
.mobile-menu-button span{
    background:var(--lr-text);           /* white bars, always */
    border-radius:1px;
}
/* Hover is a mouse affordance; a phone never sees it. Keep it subtle. */
.mobile-menu-button:hover{
    background:rgba(255,255,255,.07);
    border-color:rgba(255,255,255,.46);
}
/* Touch feedback: phones have no hover, so :active is the only confirmation
   that the tap registered. */
.mobile-menu-button:active{
    background:rgba(255,255,255,.12);
}
/* The ring must clear the dark header, so white — a crimson ring on #0A0909
   is the A-01 mistake in another costume. */
.mobile-menu-button:focus-visible{
    outline:2px solid var(--lr-text);
    outline-offset:2px;
}
/* Open state reads as pressed rather than merely hovered, so it is obvious
   which control dismisses the drawer. */
.mobile-menu-button[aria-expanded="true"]{
    background:rgba(255,255,255,.12);
    border-color:rgba(255,255,255,.55);
}


/* ══ PRODUCT PAGE — RESEARCH PANELS (2026-07-28 mobile-first pass) ═══════════
   "What's in it" and "Nutrition Facts" moved BELOW every pricing section and
   became collapsible on phones. They ship `open`; a script closes them under
   768px, so a no-JS reader still gets the content.
   ------------------------------------------------------------------------- */
.detail-disclosure{
    border:1px solid var(--lr-line);
    border-radius:var(--r-md);
    background:var(--lr-surface);
    padding:0;
}
.detail-disclosure > summary{
    display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3);
    padding:var(--sp-4);
    cursor:pointer;
    list-style:none;
    min-height:44px;                       /* WCAG 2.2 target */
}
.detail-disclosure > summary::-webkit-details-marker{ display:none; }
.detail-disclosure > summary:focus-visible{
    outline:2px solid var(--lr-text); outline-offset:-2px; border-radius:var(--r-md);
}
.detail-disclosure > summary .eyebrow{ display:block; margin:0 0 2px; }
.detail-disclosure > summary .section-title{ display:block; margin:0; }
/* A chevron built from a border, so it costs no request and rotates cheaply. */
.disclosure-chevron{
    flex:none; width:9px; height:9px;
    border-right:2px solid var(--lr-muted);
    border-bottom:2px solid var(--lr-muted);
    transform:rotate(45deg); transform-origin:center;
    transition:transform var(--dur-fast,.15s) var(--ease, ease);
}
.detail-disclosure[open] .disclosure-chevron{ transform:rotate(-135deg); }
.detail-disclosure > *:not(summary){ padding:0 var(--sp-4); }
.detail-disclosure > *:not(summary):last-child{ padding-bottom:var(--sp-4); }

/* The actives: the figures that decide the purchase. Two columns on a phone —
   these are short label/number pairs, so one column wastes half the screen and
   doubles the panel's height. */
.actives-grid{
    display:grid;
    grid-template-columns:repeat(2, minmax(0, 1fr));
    gap:var(--sp-2);
    margin:0 0 var(--sp-3);
}
.active-cell{
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    border-radius:var(--r-sm);
    padding:10px 12px;
    min-width:0;
}
.active-cell dt{
    margin:0 0 2px;
    font-size:var(--fs-xs, 11px);
    font-weight:var(--fw-semi);
    letter-spacing:.06em; text-transform:uppercase;
    color:var(--lr-muted);
}
.active-cell dd{
    margin:0;
    font-size:var(--fs-lg, 18px);
    font-weight:var(--fw-bold, 700);
    color:var(--lr-text);
    overflow-wrap:anywhere;              /* "Monohydrate" must not overflow */
}
/* The serving line is context, not a headline — full width, quieter. */
.active-cell--serving{ grid-column:1 / -1; }
.active-cell--serving dd{ font-size:var(--fs-base, 15px); font-weight:var(--fw-med); }

.panel-source{
    margin:var(--sp-3) 0 0;
    font-size:var(--fs-xs, 11px);
    color:var(--lr-dim);
}

@media (min-width: 768px){
    /* Wider screens have the room for a real row of figures. */
    .actives-grid{ grid-template-columns:repeat(auto-fit, minmax(140px, 1fr)); }
    /* Open and non-interactive from here up: the control would imply the
       content can be hidden, and on desktop it never is. */
    .detail-disclosure > summary{ cursor:default; }
    .disclosure-chevron{ display:none; }
}


/* ══ PRODUCT PAGE — COMPACT ALTERNATIVES ════════════════════════════════════
   "Cheaper per g protein" reused components/_lb_row.html, i.e. the full
   leaderboard card: expandable offer table, freshness badge, buy button,
   watch control — four of them, each ~200px tall, on a page whose own offer
   table sits directly above. The job here is only "here are cheaper options",
   so the row collapses to rank / name / metric / price / go.
   ------------------------------------------------------------------------- */
.related-list .lb-row{
    border:0; border-bottom:1px solid var(--lr-line); border-radius:0;
    background:none; margin:0;
}
.related-list .lb-row:last-child{ border-bottom:0; }
/* Everything the compact row does not need. The markup stays (one component,
   one contract) — only this context hides the parts it has no use for. */
.related-list .lb-row .lb-offers,
.related-list .lb-row .lb-expand,
.related-list .lb-row .lb-fresh-slot,
.related-list .lb-row .lb-buy-inline,
.related-list .lb-row .lb-name-meta,
.related-list .lb-row .watch-btn,
.related-list .lb-row .lb-best-badge{ display:none !important; }
.related-list .lb-row > summary{
    padding:12px 0;
    cursor:default;                      /* nothing left to expand */
    grid-template-columns:auto 44px minmax(0,1fr) auto;
    gap:var(--sp-3);
    align-items:center;
}
.related-list .lb-row > summary::-webkit-details-marker{ display:none; }
.related-list .lb-thumb{ width:44px; height:44px; }
.related-list .lb-thumb img,
.related-list .lb-thumb .thumb-empty{ width:44px !important; height:44px !important; }
.related-list .lb-name a{ font-size:var(--fs-sm, 14px); }
.related-list .lb-metric-value{ font-size:var(--fs-base, 15px); }


/* ══ LEADERBOARD ROW — WHAT A PHONE ACTUALLY NEEDS ══════════════════════════
   The brief for a ranked row on a phone is: rank, name, value metric, best
   price, retailer, one button. Everything else is either one tap away in the
   expanded row or on the product page.

   Two things are dropped below 768px, and only these two:
     · pack size ("5 lb") — the retailer answers "where", the size does not
       change the ranking, and it is in the expanded offer list
     · the freshness TIMESTAMP text ("12h ago") — the coloured dot already
       says fresh, and a stale row still shows its "stale" flag in full,
       because hiding staleness would be dishonest rather than tidy
   ------------------------------------------------------------------------- */
.lb-offer-size{ display:inline-flex; align-items:baseline; gap:6px; min-width:0; }
/* The separator belongs to the pack element, so hiding the pack takes the
   dot with it — no stranded "Amazon ·". */
.lb-offer-pack::before{ content:"·"; margin-right:6px; color:var(--lr-dim); }
.lb-offer-store{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

@media (max-width: 767.98px){
    .lb-summary .lb-offer-pack{ display:none; }
    /* Keep the dot (and the "stale" flag); drop only the relative time. */
    .lb-summary .lb-fresh-slot .lb-last-seen,
    .lb-summary .lb-fresh-slot time[data-freshness]{ display:none; }
}


/* The #1 badge stays hidden on phones (it is hidden below 768px along with
   the sparkline, value bar and detail chip).

   Measured before reverting: re-enabling it pushed the FIRST row — the most
   important one on the page — from 240px to 271px at 390px wide, because the
   chip wrapped onto its own line beside the metric. What it would have said
   is already on screen: the sort control sits directly above the board at all
   times and reads "Sort: Best value" / "Lowest price" / "Biggest drop". A
   30px tax on row one to repeat the control above it is not a trade worth
   making on a phone. It still renders from 769px up, where it costs nothing.

   The label itself is now sort-aware (see components/_lb_row.html) so it can
   never describe a different ranking than the one on screen. */


/* ══ MOBILE FOOTER — SHORTER, SAME INFORMATION ══════════════════════════════
   The footer ran roughly a full phone screen on its own: five stacked
   sections at 30px gaps, a newsletter form, the link bar and three note
   paragraphs. Merging the two duplicate explainers into one (see
   _footer.html) removed a section; this tightens what is left.

   Nothing is removed and nothing is hidden — every link, the alerts form,
   the affiliate/tracking/accuracy notes and the contact address all still
   render. Only spacing and type size change, and only below 768px.
   ------------------------------------------------------------------------- */
@media (max-width: 768px){
    .footer-grid{ gap:var(--sp-5); }
    .site-footer h2{ font-size:var(--fs-lg, 18px); margin-bottom:4px; }
    .site-footer p{ font-size:var(--fs-sm, 14px); }

    /* Category links were a stack of full-width rows; they are short labels,
       so two per line halves the height without shrinking the tap target. */
    .footer-links{
        display:grid;
        grid-template-columns:repeat(2, minmax(0,1fr));
        gap:2px var(--sp-4);
    }
    .footer-links a{
        display:flex; align-items:center;
        min-height:40px;                 /* comfortably over the 24px floor */
    }

    /* The legal/disclosure notes stay in full — they are the ones that must
       not be trimmed — but at a tighter rhythm.

       "Tighter" had become 4px, which at 11px/1.5 is less than half a line: the
       three notes ran together into one grey slab, and the phone is where they
       are hardest to read. 10px is still tighter than the desktop 12px and
       leaves each note a visibly separate statement. */
    .footer-notes{ margin-top:var(--sp-4); gap:10px; }
    .footer-notes p{ font-size:11px; line-height:1.5; }

    /* The dock is a pointer-proximity effect; a phone has no pointer, so the
       82px reserved for the magnify never pays for itself. */
    .magnetic-dock{ height:auto; margin-top:var(--sp-3); }

    /* The legal bar is the last row of 24px targets on a phone. 24px clears
       WCAG 2.5.8 (AA) and that is why it was set there, but it is a POINTER
       minimum — the touch guidance native apps follow is 44. Measured in
       Chromium at 390px, on every page of the site: "Affiliate disclosure"
       110x24, "Privacy policy" 113x24, "Terms" 38x24, "Sign in" 41x24,
       "Create an account" 85x24, and the footer logo 145x30.

       .footer-links above already gets 40px at this breakpoint; this row had
       been missed. min-height rather than a fixed height, so the wrapped rows
       keep their rhythm instead of becoming a stack of buttons, and the row
       gap tightens to absorb the extra height. */
    .footer-bar{ gap:4px var(--sp-4); }

    .footer-bar a,
    .footer-bar .footer-signout button{
        min-height:44px;
    }

    .footer-bar .footer-logo{
        display:inline-flex;
        align-items:center;
        min-height:44px;
    }
}


/* A WCAG 2.2 target-size fix for `.spread-cell dt a` lived here. The spread
   grid is gone (2026-08-01, §4), and so is the 16px link that needed it. */


/* ══ MOBILE FILTER SHEET (2026-07-28) ═══════════════════════════════════════
   Measured before: the open panel was 560px of a 844px viewport — 66% of the
   screen — and it was already capped, so the cap was not the problem. The
   CONTENT was ~463px because every group stacked an uppercase label line on
   top of a WRAPPING chip row. "Store" alone was 159px: six retailers over
   three lines.

   The fix is per-group horizontal scrolling, not an accordion. An accordion
   would be shorter still, but it puts a tap in front of every filter, and
   filtering is the one thing this panel exists to make fast. A single
   scrolling line per group keeps all five groups visible AND applies a filter
   in one tap. Wrapping stays on from 769px up, where vertical space is free.

   Measured after: ~330px, roughly 39% of the viewport.
   ------------------------------------------------------------------------- */
@media (max-width: 768px){
    .lb-dd-panel-filters{
        /* Was min(72dvh, 560px). The content no longer needs it, and a sheet
           that stops well short of the header keeps the board behind it
           visible — you can see the list you are filtering. */
        max-height:min(50dvh, 380px);
        padding:var(--sp-3) 0 0;          /* rows bleed to the edges; see below */
        display:flex;
        flex-direction:column;
        overflow:hidden;                   /* the groups scroll, not the sheet */
    }
    .lb-dd-panel-filters .lb-filters{
        gap:var(--sp-3);
        overflow-y:auto;
        padding:0 var(--sp-4) var(--sp-3);
        overscroll-behavior:contain;       /* no scroll-chaining to the board */
    }

    .lb-dd-panel-filters .chip-group{ gap:5px; }
    .lb-dd-panel-filters .chip-group-label{ font-size:10px; letter-spacing:.09em; }

    /* One line per group. The negative margin + matching padding let the row
       scroll edge-to-edge so a half-visible chip signals "more this way",
       instead of stopping short and looking like the list simply ends. */
    .lb-dd-panel-filters .chip-row{
        flex-wrap:nowrap;
        overflow-x:auto;
        scroll-snap-type:x proximity;
        -webkit-overflow-scrolling:touch;
        scrollbar-width:none;
        margin:0 calc(var(--sp-4) * -1);
        padding:2px var(--sp-4);
    }
    .lb-dd-panel-filters .chip-row::-webkit-scrollbar{ display:none; }
    .lb-dd-panel-filters .chip{ flex:0 0 auto; scroll-snap-align:start; }

    /* Max price scrolls like the rest — input + Apply + two presets is four
       items and wrapped to three lines (109px) at 390px, making it the tallest
       group in the sheet. The input stays a fixed width so it never collapses
       to an unusable sliver. */
    .lb-dd-panel-filters .max-price-input{ flex:0 0 92px; min-width:0; }
}

/* The action bar: always the last thing in the sheet, never scrolled away. */
.filter-actions{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:var(--sp-3);
    flex-wrap:wrap;
}
.filter-summary{
    font-size:var(--fs-sm, 13px);
    color:var(--lr-muted);
    font-weight:var(--fw-med);
}
.filter-actions-buttons{ display:flex; align-items:center; gap:var(--sp-2); }
/* A disabled "Clear all" stays in place rather than disappearing — a control
   that appears and vanishes as you filter is harder to aim at than one that
   is simply dim. */
.chip.is-disabled{ opacity:.45; pointer-events:none; }
.filter-done{ min-height:40px; }

@media (max-width: 768px){
    /* Sticky, not a flex sibling: the action bar lives INSIDE <form
       data-filterbar> so leaderboard.js's delegated click handler still sees
       Clear all and Done. The form is what scrolls, so the bar has to pin
       itself to the bottom of that scroller rather than sit outside it —
       otherwise it scrolls away and "Clear all" is unreachable again, which
       is the problem this bar exists to solve. */
    .filter-actions{
        position:sticky;
        bottom:0;
        z-index:1;
        margin:0 calc(var(--sp-4) * -1) calc(var(--sp-3) * -1);
        border-top:1px solid var(--lr-line);
        background:var(--lr-surface);
        padding:var(--sp-3) var(--sp-4);
        border-radius:0 0 var(--r-lg) var(--r-lg);
        /* clears the iOS home indicator */
        padding-bottom:max(var(--sp-3), env(safe-area-inset-bottom));
    }
}
/* Desktop has no dismissable sheet — the popover closes on outside click. */
@media (min-width: 769px){
    .filter-done{ display:none; }
}

.lb-result-search{
    display:flex;
    align-items:center;
    gap:var(--sp-2);
}
.lb-result-search input[type="search"]{
    width:min(250px, 32vw);
    min-height:42px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md);
    background:var(--lr-surface);
    color:var(--lr-text);
    padding:0 var(--sp-3);
}
.lb-stock-lock{
    display:inline-flex;
    align-items:center;
    gap:var(--sp-2);
    min-height:42px;
    color:var(--lr-muted);
    font-size:var(--fs-sm, 13px);
    white-space:nowrap;
}
.lb-stock-lock input{ accent-color:var(--lr-accent-deal-text); }
@media (max-width: 768px){
    .lb-result-search{ order:-2; width:100%; }
    .lb-result-search input[type="search"]{ width:100%; min-width:0; }
    .lb-stock-lock{ order:-1; }
    details[data-filter-sheet][open] .lb-dd-panel-filters{
        position:fixed;
        inset:auto 0 0;
        width:100%;
        max-width:none;
        max-height:min(72dvh, 620px);
        border-radius:var(--r-lg) var(--r-lg) 0 0;
        z-index:90;
    }
}


/* ══ MOBILE LEADERBOARD CARD (2026-07-28) ═══════════════════════════════════
   Measured before: 160–240px per row at 390px, i.e. roughly three products on
   an 844px screen. For a site whose whole job is comparing many products at a
   glance, that is one product at a time.

   The old mobile grid stacked FIVE rows beside the thumbnail —
   name / metric / offer / freshness / expander — and hung the buy button on a
   sixth line of its own. This collapses that to three, by pairing things that
   belong together rather than by deleting information:

     row 1   product name (clamped to two lines)
     row 2   ¢/unit  ·  drop badge            ·  N offers  (the expander)
     row 3   $price  ·  retailer              ·  [View deal]

   Nothing is removed from the DOM. Everything hidden here — value bar,
   sparkline, detail chip, #1 badge, pack size, freshness timestamp — is either
   already in the expanded row, on the product page, or restored at 769px.
   ------------------------------------------------------------------------- */
@media (max-width: 768px){
    .lb-summary{
        padding:var(--sp-3) var(--sp-4);
        row-gap:2px;
        column-gap:var(--sp-3);
        align-items:start;
        grid-template-columns:20px 48px minmax(0,1fr);
        grid-template-areas:
            "rank thumb name"
            "rank thumb metric"
            "rank thumb offer";
    }
    .lb-row.no-rank .lb-summary{
        grid-template-columns:48px minmax(0,1fr);
        grid-template-areas:
            "thumb name"
            "thumb metric"
            "thumb offer";
    }
    /* 48px, down from 56: it is an identifier at this size, not an image the
       shopper studies, and the 8px buys a line of text back. */
    .lb-summary .lb-thumb{ width:48px; height:48px; }
    .lb-summary .lb-thumb img,
    .lb-summary .lb-thumb .thumb-empty{ width:48px !important; height:48px !important; }

    /* A long name must not be able to make one card three times taller than
       its neighbours — that is what breaks scanning. Two lines, then ellipsis;
       the full name is on the product page one tap away. */
    .lb-summary .lb-name a{
        display:-webkit-box;
        -webkit-line-clamp:2;
        -webkit-box-orient:vertical;
        overflow:hidden;
        font-size:var(--fs-sm);
        line-height:1.3;
    }

    /* Row 2: the metric is the headline. The expander label rides along on the
       right of the same line instead of claiming a row. */
    .lb-summary .lb-metric-cell{
        grid-area:metric;
        display:flex;
        flex-direction:row;
        align-items:baseline;
        gap:var(--sp-2);
        margin-top:1px;
    }
    .lb-summary .lb-metric{ font-size:var(--fs-md); font-weight:var(--fw-bold); }
    .lb-summary .lb-expand{
        grid-area:metric;
        justify-self:end;
        align-self:baseline;
        font-size:var(--fs-xs);
        color:var(--lr-dim);
        white-space:nowrap;
    }

    /* Row 3: price, retailer and the action on ONE line — nowrap, because the
       button wrapping to its own line is exactly the 40px this redesign is
       reclaiming. The retailer truncates; the price and button never shrink. */
    .lb-summary .lb-offer{
        grid-area:offer;
        display:flex;
        flex-direction:row;
        flex-wrap:nowrap;
        align-items:center;
        gap:var(--sp-2);
        margin-top:1px;
    }
    .lb-summary .lb-offer-detail{
        flex:1 1 auto;
        min-width:0;
        display:flex;
        align-items:baseline;
        gap:var(--sp-2);
        flex-wrap:nowrap;
        overflow:hidden;
    }
    .lb-summary .lb-offer-price{
        order:-1;
        flex:0 0 auto;
        font-size:var(--fs-base);
        font-weight:var(--fw-bold);
    }
    .lb-summary .lb-offer-size{
        min-width:0;
        font-size:var(--fs-xs);
        color:var(--lr-dim);
        white-space:nowrap;
        overflow:hidden;
        text-overflow:ellipsis;
    }
    .lb-summary .lb-buy-inline{
        flex:0 0 auto;
        margin-top:0;
        min-height:34px;
        padding:0 var(--sp-3);
        font-size:var(--fs-xs);
    }
    /* Freshness on the collapsed card: STALE rows only.

       Placing it in the `offer` area put the green dot hard against the right
       edge of "View deal", where it read as a status light ON the button
       rather than on the offer. And a dot that means "this price is current"
       is the default state — it is on every row, so it distinguishes nothing.
       What a shopper needs to see is the exception: a row whose price is old.
       Stale rows keep the full slot, flag and all; the board header already
       carries the honest "checked Nh ago" for everything else, and the
       timestamp per offer is in the expanded row. */
    .lb-summary .lb-fresh-slot{
        grid-area:offer;
        justify-self:end;
        align-self:center;
        pointer-events:none;
        font-size:var(--fs-xs);
        display:none;
    }
    .lb-row.is-stale .lb-summary .lb-fresh-slot{
        display:flex;
        position:relative;
        z-index:1;
    }
    /* ...and a stale row gives it room so it cannot sit under the button. */
    .lb-row.is-stale .lb-summary .lb-buy-inline{ margin-right:var(--sp-5); }
}


/* ══ PRICE VERDICT + PRICE FACTS (2026-07-28) ═══════════════════════════════
   One verdict in the hero (app.price_verdict picks the strongest true signal,
   or none), and the supporting figures beside the chart so the graph is
   evidence for a conclusion rather than homework.
   ------------------------------------------------------------------------- */
.price-verdict{
    display:flex;
    flex-direction:column;
    gap:2px;
    margin:var(--sp-3) 0 0;
    padding:10px var(--sp-3);
    border-radius:var(--r-sm);
    border:1px solid var(--lr-accent-deal-line);
    background:var(--lr-accent-deal-wash);
}
.price-verdict-text{
    font-size:var(--fs-base);
    font-weight:var(--fw-bold);
    color:var(--lr-accent-deal-text);
}
.price-verdict-detail{ font-size:var(--fs-xs); color:var(--lr-soft); }
/* price_verdict() returns an empty detail for the 90-day low, whose subtitle
   only restated its own headline. Collapsing the empty element here means the
   verdict markup needs no conditional to stay tidy. */
.price-verdict-detail:empty{ display:none; }
/* A category-average verdict is the weakest of the three, so it states itself
   quietly — the crimson treatment is reserved for a genuine low or drop. */
.price-verdict--value{
    border-color:var(--lr-line);
    background:var(--lr-surface-2);
}
.price-verdict--value .price-verdict-text{ color:var(--lr-text); }

.price-facts{
    display:grid;
    grid-template-columns:repeat(3, minmax(0,1fr));
    gap:var(--sp-2);
    margin:0 0 var(--sp-2);
}
.price-fact{
    background:var(--lr-surface-2);
    border:1px solid var(--lr-line);
    border-radius:var(--r-sm);
    padding:10px 12px;
    min-width:0;
}
.price-fact dt{
    margin:0 0 2px;
    font-size:var(--fs-xs, 11px);
    text-transform:uppercase;
    letter-spacing:.06em;
    color:var(--lr-muted);
    font-weight:var(--fw-semi);
}
.price-fact dd{
    margin:0;
    font-size:var(--fs-base);
    font-weight:var(--fw-semi);
    color:var(--lr-text);
    font-variant-numeric:tabular-nums;
    display:flex;
    align-items:baseline;
    gap:6px;
    flex-wrap:wrap;
}
.price-fact-strong{ font-size:var(--fs-lg); font-weight:var(--fw-bold); }
.price-fact-delta{ font-size:var(--fs-xs); font-weight:var(--fw-bold); }
/* Below the average is good news for a buyer; above it is not. Colour is a
   second channel here, never the only one — the sign is always printed. */
.price-fact-delta.is-good{ color:var(--lr-accent-deal-text); }
.price-fact-delta.is-bad{ color:var(--lr-muted); }
.price-facts-note{
    margin:0 0 var(--sp-3);
    font-size:var(--fs-xs);
    color:var(--lr-dim);
}

@media (max-width: 430px){
    /* Three columns of currency get cramped below ~430px; two rows read
       better than three squeezed cells. "Right now" keeps the full width
       because it is the number the other two exist to contextualise. */
    .price-facts{ grid-template-columns:repeat(2, minmax(0,1fr)); }
    .price-fact:first-child{ grid-column:1 / -1; }
}


/* ══ HOMEPAGE "WHY THIS EXISTS" — THE MERGED CLOSING SECTION ════════════════
   The method, as three short lines rather than the numbered three-step
   walkthrough it replaced. Same three facts; a fraction of the height, and it
   reads as a claim with support rather than as documentation.
   ------------------------------------------------------------------------- */
.why-points{
    list-style:none;
    margin:var(--sp-5) 0 0;
    padding:0;
    display:grid;
    gap:var(--sp-3);
    max-width:62ch;
}
.why-points li{
    position:relative;
    padding-left:var(--sp-4);
    color:var(--lr-muted);
    font-size:var(--fs-sm);
    line-height:var(--lh-ui);
}
/* A crimson tick built from a border — no glyph, no icon request, and it
   inherits the accent so a theme change carries. */
.why-points li::before{
    content:"";
    position:absolute;
    left:0;
    top:.5em;
    width:6px;
    height:6px;
    border-radius:50%;
    background:var(--lr-accent-deal);
}
.why-points b{ color:var(--lr-text); font-weight:var(--fw-semi); }

.why-cta{
    margin-top:var(--sp-6);
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    gap:var(--sp-4);
}
/* On a phone the CTA fills the row. On desktop it sizes to its label — an
   earlier media query already said so at line ~8303, but this rule was added
   later at equal specificity and silently beat it, so "Create a free account"
   stretched to 1010px across the band. The breakpoint now lives with the rule
   it belongs to instead of two thousand lines away. */
.why-cta .btn{ flex:1 1 220px; }
@media (min-width: 769px){
    .why-cta .btn{ flex:0 0 auto; }
}


/* ══════════════════════════════════════════════════════════════════════════
   2026-07-30 POLISH PASS
   ══════════════════════════════════════════════════════════════════════════
   Appended last, so these rules win the cascade over the earlier eras without
   !important and without editing rules other sections depend on. Nothing here
   changes the palette, the type scale, the spacing tokens or the component
   shapes — it refines emphasis inside shapes that already exist, and styles
   the handful of genuinely new elements (Top Deals cards, the rich spotlight,
   the homepage newsletter band, the mobile profile icon).
   ------------------------------------------------------------------------- */


/* ── §20 MOBILE HEADER: [ Menu ]  LIFTRADAR  [ Profile ] ───────────────────
   The wordmark was small and tucked into the left corner. It is now centred on
   the VIEWPORT and modestly larger.

   Why absolute positioning rather than justify-content:center or an auto-margin
   flex trick: the two controls flanking it are different widths (a 3-bar
   hamburger vs a person glyph), so any flex-based centring centres the logo in
   the LEFTOVER space and it sits visibly off-centre. Taking the logo out of
   flow and pinning it to 50% of the header makes it independent of what the
   icons weigh — which is exactly the brief.

   Desktop is untouched: everything below is inside a max-width query.
   ------------------------------------------------------------------------- */
.nav-profile{ display:none; }

@media (max-width: 900px){
    /* .site-nav, not a bare `nav`: every other <nav> is a component
       (.brand-jump, .breadcrumb, .magnetic-dock) and was inheriting this.
       Unscoped it gave the A-Z jump bar justify-content:space-between, so a
       wrapped second row put "S" hard left and "Z" hard right with a void
       between, and it gave the breadcrumb a 64px min-height plus 14px of
       padding it has no use for. */
    nav.site-nav{
        position:relative;
        display:flex;
        align-items:center;
        justify-content:space-between;
        gap:var(--sp-2);
        min-height:64px;
        padding:8px 14px;
    }

    .logo-section{
        position:absolute;
        left:50%;
        top:50%;
        transform:translate(-50%, -50%);
        margin:0;
        /* Never reach the icons: the reserved gutters are ~52px a side, so a
           wide wordmark shrinks instead of overlapping. */
        max-width:calc(100% - 128px);
        display:flex;
        justify-content:center;
    }
    .logo-section a{ display:block; line-height:0; }

    /* Modestly larger than the 44px it had been — a brand moment, not a
       banner. Capped by the max-width above on very narrow screens. */
    .logo{
        height:52px;
        max-width:100%;
        object-fit:contain;
    }

    /* Both controls claim the same width so neither side can pull the header's
       optical balance, and both clear the 44px touch target.

       margin:0 is load-bearing: .mobile-menu-button has carried
       `margin-left:auto` since the v3 nav, which shoved it flush against the
       profile icon on the right and left the logo box overlapping it. With the
       logo out of flow, `justify-content:space-between` is what separates the
       two controls, and an auto margin defeats it. */
    .mobile-menu-button,
    .nav-profile{
        position:relative;
        z-index:1;
        flex:0 0 auto;
        margin:0;
        width:44px;
        height:44px;
        display:inline-flex;
        align-items:center;
        justify-content:center;
    }

    .nav-profile{
        color:var(--lr-muted);
        border-radius:50%;
        text-decoration:none;
        transition:color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
    }
    .nav-profile:hover,
    .nav-profile:focus-visible{
        color:var(--lr-text);
        background:var(--lr-surface-2);
    }
    .nav-profile svg{ display:block; }
}

@media (max-width: 380px){
    /* At 360px and below the gutters matter more than the extra logo height. */
    .logo{ height:46px; }
    .logo-section{ max-width:calc(100% - 116px); }
}


/* ── §2 FRESHNESS AS A STATUS ELEMENT ──────────────────────────────────────
   The timestamp is the freshness claim now that no interval is promised, so it
   reads as a small status chip rather than a caption.
   ------------------------------------------------------------------------- */
.freshness{
    font-variant-numeric:tabular-nums;
    white-space:nowrap;
}
.freshness-prefix{ color:inherit; opacity:.75; }

/* The offer TABLE has a "Checked" column header two rows up; repeating the
   word in every cell is noise. Same component, same string, one hidden span. */
.offer-list .freshness-prefix{ display:none; }

/* A stale price must say so in words, not just in colour. */
.freshness.is-stale .freshness-flag{
    text-transform:none;
    letter-spacing:.02em;
    font-weight:var(--fw-semi);
}

@media (max-width: 767.98px){
    /* §8: freshness comes BACK on phones, on a LINE OF ITS OWN.

       It used to be hidden entirely on non-stale rows, and for a good reason:
       it had been parked in the `offer` grid area, right-aligned, where the
       green dot sat against "View deal" and read as a status light on the
       BUTTON. Simply un-hiding it there would recreate that bug.

       So the mobile row gains a fourth area instead. §8 is explicit that a
       phone visitor must see freshness without expanding anything, and a bare
       coloured dot does not say "18 min ago". The line costs ~16px and is the
       only thing on it, so nothing else gets tighter.

       The stale-row exception below still applies — a stale row also prints
       its "may be out of date" flag on the same line. */
    .lb-summary{
        grid-template-areas:
            "rank thumb name"
            "rank thumb metric"
            "rank thumb offer"
            "rank thumb fresh";
    }
    .lb-row.no-rank .lb-summary{
        grid-template-areas:
            "thumb name"
            "thumb metric"
            "thumb offer"
            "thumb fresh";
    }
    .lb-summary .lb-fresh-slot{
        grid-area:fresh;
        display:flex;
        justify-self:start;
        align-self:center;
        margin-top:3px;
        font-size:var(--fs-xs);
    }
    .lb-summary .lb-fresh-slot time[data-freshness]{ display:inline; }
    /* "Checked" is the one part a phone can lose: the dot plus a relative time
       already reads as freshness on its own line. */
    .lb-summary .lb-fresh-slot .freshness-prefix{ display:none; }
    /* The stale row no longer needs to dodge the button — nothing shares the
       line with it now. */
    .lb-row.is-stale .lb-summary .lb-buy-inline{ margin-right:0; }

    /* The row's View Deal is the primary action on a phone (§8), and it was
       34px tall — under the touch minimum — because .lb-buy-inline only got a
       min-height from 768px up. Measured at 375/430 before changing it. */
    .lb-summary .lb-buy-inline{ min-height:40px; }
}


/* ── §1/§7 PRICE HIERARCHY: THE PRICE YOU PAY IS THE BIGGEST THING ─────────
   The widget already refused to render a comparison price without a real
   discount (pricing.price_view). This makes the surviving pair read correctly:
   the final price dominates, the struck comparison is demoted, and the coupon
   code is a quiet third line rather than a competing figure.
   ------------------------------------------------------------------------- */
.price-with-coupon{
    display:inline-flex;
    flex-direction:column;
    align-items:flex-start;
    gap:1px;
}
.price-with-coupon .price-was{
    order:-1;                       /* comparison ABOVE the price you pay */
    font-size:var(--fs-xs);
    color:var(--lr-dim);
    font-weight:var(--fw-reg);
}
.price-with-coupon .price-now{
    font-size:var(--fs-md);
    font-weight:var(--fw-bold);
    color:var(--lr-text);
    line-height:1.15;
}
.price-with-coupon .price-coupon{
    font-size:var(--fs-xs);
    color:var(--lr-accent-deal-text);
    font-weight:var(--fw-semi);
}


/* ── §9 BADGES: ONE PER CARD, ONE MEANING EACH ─────────────────────────────
   The label strings come from pricing.BADGES; these are the only visual
   treatments they get. Deliberately restrained — the value metric and the
   price are what a scan should land on, not a chip.
   ------------------------------------------------------------------------- */
.badge--drop,
.badge--low90,
.badge--coupon{
    display:inline-flex;
    align-items:center;
    border-radius:var(--r-sm);
    padding:3px 8px;
    font-size:10px;
    font-weight:800;
    letter-spacing:.06em;
    text-transform:uppercase;
    white-space:nowrap;
}
/* A genuine drop and a genuine 90-day low are the two "act now" claims, so
   they carry the crimson that means "saving" everywhere else. */
.badge--drop,
.badge--low90{ background:var(--lr-accent-deal); color:#fff; }
/* A coupon is a fact about how to pay, not a claim that the price is good —
   quieter treatment on purpose. */
.badge--coupon{
    background:transparent;
    color:var(--lr-muted);
    border:1px solid var(--lr-line);
}

/* §6: the drop chip carries both halves now, so it may wrap to a second line
   rather than clipping the dollar figure — the half that stops a percentage
   from flattering a cheap product. */
.lb-drop.drop-badge{ white-space:normal; line-height:1.3; }


/* ── §11 GROUPED AUTOCOMPLETE ──────────────────────────────────────────────
   Same panel, same pill, same row height. The only additions are group
   headings and the compact price/metric preview on product rows.
   ------------------------------------------------------------------------- */
.search-group-label{
    padding:8px 12px 4px;
    font-size:10px;
    font-weight:800;
    letter-spacing:.12em;
    text-transform:uppercase;
    color:var(--lr-dim);
}
/* The panel is `display:grid` with an implicit `auto` column, whose automatic
   minimum is the item's MIN-CONTENT width. A suggestion row is a nowrap flex
   line, so its min-content is the full untruncated name plus the preview —
   239px past the panel's right edge at 1440px, measured. Pinning the column to
   minmax(0, 1fr) lets the row shrink to the panel so the truncation below can
   actually happen. */
.search-suggestions.is-open{ grid-template-columns:minmax(0, 1fr); }

/* On phones the panel drops out of absolute positioning (position:static, so
   it pushes the menu open rather than covering it) — which makes it a FLEX
   SIBLING of the search icon and input inside .nav-search-form. It therefore
   took whatever width was left over after the input: 184px of a 317px form,
   measured at 375px, with every suggestion clipped to about two words.
   Wrapping the form and giving the panel a whole line fixes it without
   touching the desktop layout, where the panel is still absolutely
   positioned and this rule is inert. */
@media (max-width: 900px){
    .nav-search-form{ flex-wrap:wrap; }
    .search-suggestions{ flex:1 0 100%; width:100%; }
}

/* The name takes the slack and truncates; the price/metric preview is what a
   shopper is comparing on, so it never shrinks or gets pushed out of the
   panel. */
.search-suggestion strong{
    min-width:0;
    /* basis 0, not auto: with flex-wrap on (the phone layout below) an `auto`
       basis lets a long title claim its full content width and wrap itself
       onto a line BELOW the thumbnail before it will shrink. */
    flex:1 1 0;
    overflow:hidden;
    text-overflow:ellipsis;
    white-space:nowrap;
}
/* Scoped through .search-suggestion so it outranks the panel's generic
   `.search-suggestion span` rule — see the comment inside. */
.search-suggestion .search-suggestion-meta{
    flex:0 0 auto;
    margin-left:auto;
    font-size:var(--fs-xs);
    color:var(--lr-muted);
    font-variant-numeric:tabular-nums;
    white-space:nowrap;
    /* The panel's generic `span` rule is an uppercase, letter-spaced,
       800-weight category label from an older suggestion design. Rendering
       "FROM $42.95 · 2.3¢/G PROTEIN" in it made the preview wider than the
       product name it was meant to annotate — the name truncated to "Nutr…".
       This is a quiet caption, so it reads like one. */
    text-transform:none;
    letter-spacing:0;
    font-weight:var(--fw-reg);
}
.search-suggestion-thumb{ flex:0 0 auto; }

/* The preview sits on its own line, indented past the thumbnail — which is
   both the shape §11 asks for

       Optimum Nutrition Gold Standard Whey
       from $62.99 · 3.5¢/g

   and the only way the name survives. The panel is only as wide as the nav
   search field (360px on desktop, 317px on a phone), so sharing a line meant
   one of the two was cut to nothing: measured, the name truncated to
   "Nutricost Whe…" at 1440px. */
/* `gap:12px` is set for the columns but applies to both axes, so once the meta
   wraps to its own line it inherited a 12px ROW gap on top of its 2px margin —
   14px of nothing between a title and the price that belongs to it, and 79px
   per suggestion. Splitting the axes puts the two lines together as one block
   and fits another result on a phone. */
.search-suggestion{ flex-wrap:wrap; gap:2px 12px; }
.search-suggestion .search-suggestion-meta{
    flex:1 0 100%;
    margin-left:40px;
}
/* Brand and category rows have no thumbnail and no preview, so they keep the
   single-line shape. */
.search-suggestion--brand,
.search-suggestion--retailer,
.search-suggestion--category{ flex-wrap:nowrap; }
/* Brand and category rows have no thumbnail, so they indent to the same text
   column as the product rows above them instead of starting at the panel edge. */
.search-suggestion--brand strong,
.search-suggestion--retailer strong,
.search-suggestion--category strong{ padding-left:36px; }
.search-suggestion--category strong::after{
    content:" board";
    color:var(--lr-dim);
    font-weight:var(--fw-reg);
}


/* ── §12 QUICK FILTER CHIPS ────────────────────────────────────────────────
   The same chip component as every other filter, pulled to the top of the bar.
   ------------------------------------------------------------------------- */
.quick-filter-group .chip-row{ gap:6px; }
.chip-quick{ font-weight:var(--fw-semi); }


/* ── §13 CURRENTLY UNAVAILABLE ─────────────────────────────────────────────
   Kept for transparency and price history, visibly separated from anything
   buyable, and never able to look like a live offer.
   ------------------------------------------------------------------------- */
.offer-unavailable{
    margin-top:var(--sp-6);
    padding-top:var(--sp-5);
    border-top:1px solid var(--lr-line);
}
.offer-unavailable-title{
    margin:0 0 4px;
    font-size:var(--fs-base);
    font-weight:var(--fw-semi);
    color:var(--lr-muted);
}
.offer-unavailable-note{
    margin:0 0 var(--sp-4);
    font-size:var(--fs-xs);
    color:var(--lr-dim);
    max-width:62ch;
}
/* Dimmed as a group, so no single row can be mistaken for a live price. */
.offer-row.is-unavailable{ opacity:.62; }
.offer-row.is-unavailable .offer-go{ display:none; }
.offer-oos-flag{
    display:inline-flex;
    align-items:center;
    border:1px solid var(--lr-line);
    border-radius:999px;
    padding:1px 8px;
    font-size:10px;
    font-weight:800;
    letter-spacing:.06em;
    text-transform:uppercase;
    color:var(--lr-muted);
}
.po-last-seen{
    font-size:var(--fs-sm);
    color:var(--lr-muted);
    font-variant-numeric:tabular-nums;
}


/* ── §14 PRICE FACTS: UP TO FIVE FIGURES ───────────────────────────────────
   Was a fixed three-column grid; the 90-day high and the typical price make it
   up to five, so it auto-fits instead of overflowing.
   ------------------------------------------------------------------------- */
.price-facts{ grid-template-columns:repeat(auto-fit, minmax(132px, 1fr)); }
@media (max-width: 430px){
    .price-facts{ grid-template-columns:repeat(2, minmax(0,1fr)); }
    .price-fact:first-child{ grid-column:1 / -1; }
}
/* Five range buttons where there were three — they must stay tappable at
   375px rather than shrinking below the touch minimum. */
.chart-range-control{ flex-wrap:wrap; }
.chart-range-control .sort-segment{ min-width:44px; min-height:36px; }


/* ── §15 PRICE ALERT: A CLEAR SECONDARY ACTION ─────────────────────────────
   Directly under View Deal, same width, quieter treatment. Two stated choices
   (any drop / a target) instead of an empty box whose meaning was invisible.
   ------------------------------------------------------------------------- */
.watch-hero{ margin-top:var(--sp-3); }
.watch-hero-btn{ width:100%; justify-content:center; }

.watch-mode-row{
    display:flex;
    flex-wrap:wrap;
    gap:var(--sp-2) var(--sp-4);
    margin-bottom:var(--sp-2);
}
.watch-mode{
    display:inline-flex;
    align-items:center;
    gap:6px;
    font-size:var(--fs-sm);
    color:var(--lr-muted);
    cursor:pointer;
}
.watch-mode input{ accent-color:var(--lr-accent-deal); }
.watch-target-field{
    display:inline-flex;
    align-items:center;
    gap:4px;
    color:var(--lr-muted);
    font-size:var(--fs-sm);
}
.watch-target-field input{ max-width:140px; }


/* ── §17 RETAILER LOGOS ────────────────────────────────────────────────────
   Recognition only. Same box the monogram occupied, so a configured logo and
   an unconfigured monogram sit on the same baseline in the same row.
   ------------------------------------------------------------------------- */
.store-mark--logo{
    object-fit:contain;
    background:transparent;
    border-radius:var(--r-sm);
}


/* ── §3 SPOTLIGHT: THE RICH CAMPAIGN ───────────────────────────────────────
   Same hairline-editorial treatment the spotlight has always had — no card, no
   new palette. The extra fields (logo, tagline, hero image, story, CTA) are
   laid into that existing rhythm, and every one of them is optional, so a
   spotlight with only a name and a blurb renders exactly like the old one.
   ------------------------------------------------------------------------- */
.spotlight-hero{
    margin-bottom:var(--sp-4);
    border-radius:var(--r-lg);
    overflow:hidden;
    background:var(--lr-surface-2);
}
.spotlight-hero img{
    display:block;
    width:100%;
    max-height:260px;
    object-fit:cover;
}
.spotlight-head{
    display:flex;
    align-items:center;
    gap:var(--sp-3);
}
.spotlight-logo{
    flex:0 0 auto;
    display:flex;
    align-items:center;
    justify-content:center;
    width:56px;
    height:56px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-sm);
    background:var(--lr-surface-2);
    overflow:hidden;
}
.spotlight-logo img{ width:100%; height:100%; object-fit:contain; padding:6px; }
.spotlight-head-text{ min-width:0; }
.spotlight-tagline{
    display:inline-block;
    margin-bottom:4px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    padding:2px 10px;
    font-size:10px;
    font-weight:800;
    letter-spacing:.1em;
    text-transform:uppercase;
    color:var(--lr-muted);
}
.spotlight-story{ margin-top:var(--sp-3); max-width:68ch; }
.spotlight-story p{
    margin:0 0 var(--sp-3);
    color:var(--lr-muted);
    font-size:var(--fs-sm);
    line-height:1.65;
}
.spotlight-cta{ margin-top:var(--sp-4); }


/* ── §19 HOMEPAGE NEWSLETTER BAND ──────────────────────────────────────────
   Reuses .newsletter-form, so the field and button are the ones the footer
   capture has always used. The band itself is the same hairline-separated
   section rhythm as everything else on the page.
   ------------------------------------------------------------------------- */
.home-newsletter{
    margin-top:var(--sp-7);
    padding:var(--sp-7) 0 var(--sp-2);
    border-top:1px solid var(--lr-line);
}
.home-newsletter-inner{
    width:min(1180px, calc(100% - 32px));
    margin-inline:auto;
    max-width:none;
}
.home-newsletter .section-title{ margin-top:4px; }
.home-newsletter-text{
    margin:var(--sp-3) 0 var(--sp-4);
    max-width:62ch;
    color:var(--lr-muted);
    font-size:var(--fs-sm);
    line-height:1.65;
}
.home-newsletter-form{
    display:flex;
    flex-wrap:wrap;
    gap:var(--sp-2);
    max-width:520px;
}
.home-newsletter-form .input{ flex:1 1 240px; min-width:0; }

@media (min-width: 768px){
    .home-newsletter-inner{ width:min(1180px, calc(100% - 48px)); }
}


/* ── §7/§8 PRODUCT CARD HIERARCHY ──────────────────────────────────────────
   Not a redesign: the same rows, retuned so a scan lands in the intended
   order — product, then the price you pay, then the value metric, then any
   saving, then freshness. Mostly weight and colour, no new structure.
   ------------------------------------------------------------------------- */
.lb-name a{ font-weight:var(--fw-semi); }
.lb-metric{ font-weight:var(--fw-bold); }
/* The retailer answers "where", which is the question after "how much" —
   it should not compete with the price beside it. */
.lb-offer-store{ color:var(--lr-muted); }

.offer-row .po-price .price-now{ font-size:var(--fs-md); }
.offer-row .offer-metric{ color:var(--lr-text); }

@media (max-width: 767.98px){
    /* §8: nothing that matters hides behind a disclosure on a phone. The row
       already stacks name / metric / offer / freshness onto their own lines;
       these keep each line legible at 375px rather than shrinking to fit. */
    .lb-summary .lb-offer-price .price-now{ font-size:var(--fs-base); }
    .lb-summary .lb-metric{ font-size:var(--fs-base); }
    .lb-drop.drop-badge{ font-size:11px; padding:4px 8px; }
    .deal-card{ width:172px; }
    .deal-savings{ font-size:11px; }
}


/* ── ADMIN: SPOTLIGHT EDITOR + PREVIEW ─────────────────────────────────────
   The preview renders the PUBLIC section component inside the admin shell, so
   it needs the public surface back underneath it. Everything else here is the
   product-pick list.
   ------------------------------------------------------------------------- */
.admin-preview-frame{
    margin-top:var(--sp-4);
    padding:var(--sp-4);
    border:1px solid var(--lr-line);
    border-radius:var(--r-lg);
    background:#0a0909;
}

.spotlight-picks{
    list-style:none;
    margin:var(--sp-3) 0 0;
    padding:0;
    max-height:420px;
    overflow-y:auto;
    border:1px solid var(--lr-line);
    border-radius:var(--r-sm);
}
.spotlight-pick{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:var(--sp-3);
    padding:6px 10px;
    border-bottom:1px solid var(--lr-line-soft);
}
.spotlight-pick:last-child{ border-bottom:0; }
.spotlight-pick .checkbox-row{ margin:0; min-width:0; }
.spotlight-pick-actions{ display:inline-flex; gap:4px; flex:0 0 auto; }

/* =========================================================================
   Desktop header: one row, always.

   Appended last on purpose — eight earlier "nav eras" live in this file and
   the last one wins. Everything the header needs to stay on a single line at
   1280/1366/1440/1536/1920 is stated here in one place, so the next person
   reads one block instead of reconciling eight.

   The old approach set a hard breakpoint (<=1420px) that swapped in smaller
   type and then let the header WRAP when that still did not fit. At 1280 it
   did not fit: the logo sat alone on row one and search + six category links
   + the sign-in pill dropped to row two. Measured at 1440 the row filled its
   container to the pixel — zero slack — so the layout was one longer category
   name away from breaking on wider screens too.

   The fix is to make every horizontal cost fluid rather than fixed, and to
   forbid wrapping outright so a future overflow shows up as a visible squeeze
   (which the widths below are measured to prevent) instead of silently
   restructuring the header. Below 901px the existing mobile panel takes over
   untouched.
   ========================================================================= */

@media (min-width: 1180px){
    nav.site-nav{
        flex-wrap:nowrap;
        align-items:center;
        /* Gutters and the logo/nav gap give back ~40px at 1280 and stay at the
           v4 values from 1500px up, where there is room to spare. */
        gap:clamp(12px, 1.6vw, 20px);
        padding-left:clamp(20px, 2.8vw, 40px);
        padding-right:clamp(20px, 2.8vw, 40px);
    }

    .logo-section{
        flex:0 0 auto;
    }

    /* 44px at 1280 is a deliberate floor: still a legible wordmark, not a
       token shrunk to buy space. It reaches the full v4 48px by ~1410px. */
    .logo{
        height:clamp(44px, 3.4vw, 48px);
    }

    .nav-links{
        flex:1 1 auto;
        flex-wrap:nowrap;
        justify-content:flex-end;
        align-items:center;
        gap:clamp(9px, 1.25vw, 18px);
        row-gap:0;
        min-width:0;
    }

    /* flex-basis rather than width so the field can give ground before the
       category links do — a 190px search box is still perfectly usable, a
       truncated "Nutrition Bars" is not. */
    .nav-search-form{
        flex:0 1 clamp(190px, 23vw, 360px);
        width:auto;
        /* A hard floor, not min-width:0. Without it flex-shrink happily takes
           the field down to ~45px at 1024 — the placeholder gone, the field
           a stub. Below the width where 190px still fits, the mobile panel
           takes over (see the breakpoint note above). */
        min-width:190px;
    }

    .nav-links a{
        flex:0 0 auto;
    }
}

/* --- signed-in account control ------------------------------------------ */

.nav-account{
    position:relative;
    flex:0 0 auto;
}

.nav-account-toggle{
    display:inline-flex;
    align-items:center;
    gap:4px;
    padding:4px 8px 4px 4px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface-2);
    color:var(--lr-muted);
    cursor:pointer;
    transition:border-color .18s ease, color .18s ease;
}

.nav-account-toggle:is(:hover, :focus-visible){
    color:var(--lr-text);
    border-color:var(--lr-dim);
}

.nav-account-caret{
    transition:transform .18s ease;
}

.nav-account.is-open .nav-account-caret{
    transform:rotate(180deg);
}

.nav-account-menu{
    display:none;
    position:absolute;
    right:0;
    top:calc(100% + 10px);
    min-width:180px;
    padding:6px;
    border:1px solid var(--lr-line);
    border-radius:12px;
    background:var(--lr-surface);
    box-shadow:0 18px 50px rgba(0,0,0,.45);
    z-index:110;
}

.nav-account.is-open .nav-account-menu{
    display:block;
}

.nav-account-menu a,
.nav-account-menu button{
    display:block;
    width:100%;
    box-sizing:border-box;
    text-align:left;
    padding:9px 12px !important;
    border:0;
    border-radius:8px;
    background:transparent;
    color:var(--lr-muted);
    font:inherit;
    font-size:13.5px;
    font-weight:600;
    white-space:nowrap;
    cursor:pointer;
}

.nav-account-menu a:hover,
.nav-account-menu a:focus-visible,
.nav-account-menu button:hover,
.nav-account-menu button:focus-visible{
    background:var(--lr-surface-2);
    color:var(--lr-text);
}

.nav-account-menu form{
    margin:0;
    border-top:1px solid var(--lr-line-soft);
    margin-top:4px;
    padding-top:4px;
}

/* Below the mobile breakpoint the toggle disappears and the same three links
   render flat inside the slide-down panel, matching the other rows there. */
@media (max-width: 900px){
    .nav-account-toggle{
        display:none;
    }

    .nav-account-menu{
        display:block;
        position:static;
        min-width:0;
        padding:0;
        border:0;
        border-radius:0;
        background:transparent;
        box-shadow:none;
    }

    .nav-account-menu a,
    .nav-account-menu button{
        border-top:1px solid var(--lr-line-soft);
        border-radius:0;
        padding:13px 2px !important;
        font-size:15px;
    }

    .nav-account-menu form{
        border-top:0;
        margin-top:0;
        padding-top:0;
    }
}

/* =========================================================================
   Nav breakpoint band: 901px – 1179px.

   Measured, not guessed: with the fluid sizing above, the one-row desktop
   header stops fitting at 1180px — below that the search field would be
   shrunk past its 190px floor and the row would overflow. Rather than squeeze
   it to a stub (45px at 1024, placeholder gone) or reinstate the two-row wrap
   this file just removed, the existing slide-down panel takes over there.

   This block restates the ≤900px nav treatment for the band above it. It is
   duplication, and the alternative was worse: the ≤900px nav rules are
   interleaved with hero/footer/admin rules across two large legacy blocks, so
   widening those queries in place would have moved unrelated layout at
   1024–1179 as a side effect. Only the nav is intended to change here.

   Keep in sync with the "§20 mobile header" block and the JS breakpoint in
   templates/_nav.html (matchMedia "(min-width: 1180px)").
   ========================================================================= */

@media (min-width: 901px) and (max-width: 1179px){
    nav.site-nav{
        position:relative;
        display:flex;
        flex-wrap:nowrap;
        align-items:center;
        justify-content:space-between;
        gap:var(--sp-2);
        min-height:64px;
        padding:8px 20px;
    }

    .logo-section{
        position:absolute;
        left:50%;
        top:50%;
        transform:translate(-50%, -50%);
        margin:0;
        max-width:calc(100% - 128px);
        display:flex;
        justify-content:center;
    }
    .logo-section a{ display:block; line-height:0; }

    .logo{
        height:52px;
        max-width:100%;
        object-fit:contain;
    }

    .mobile-menu-button,
    .nav-profile{
        position:relative;
        z-index:1;
        flex:0 0 auto;
        margin:0;
        width:44px;
        height:44px;
        display:inline-flex;
        align-items:center;
        justify-content:center;
    }

    .nav-profile{
        color:var(--lr-muted);
        border-radius:50%;
        text-decoration:none;
    }
    .nav-profile:hover,
    .nav-profile:focus-visible{
        color:var(--lr-text);
        background:var(--lr-surface-2);
    }
    .nav-profile svg{ display:block; }

    .nav-links{
        position:absolute;
        left:auto;
        right:20px;
        top:calc(100% + 8px);
        transform:translateY(-8px);
        display:grid;
        gap:0;
        width:min(330px, calc(100vw - 40px));
        margin-left:0;
        padding:18px;
        border:1px solid var(--lr-line);
        border-radius:12px;
        background:var(--lr-surface);
        box-shadow:0 18px 50px rgba(0,0,0,.45);
        opacity:0;
        visibility:hidden;
        pointer-events:none;
        transition:.18s ease;
    }

    .nav-links.is-open{
        opacity:1;
        visibility:visible;
        pointer-events:auto;
        transform:translateY(0);
    }

    .nav-menu-title{
        display:block;
        font-family:var(--lr-display);
        font-weight:400;
        font-size:10px;
        letter-spacing:.2em;
        color:var(--lr-dim);
        padding:14px 2px 6px;
    }

    .nav-search-form{
        flex:none;
        width:100%;
        min-width:0;
    }

    .nav-search-form input{
        height:44px;
        font-size:14px;
    }

    .search-suggestions{
        position:static;
        margin-top:8px;
        box-shadow:none;
    }

    .nav-links a{
        border-top:1px solid var(--lr-line-soft);
        border-radius:0;
        padding:13px 2px;
        font-size:15px;
        width:100%;
        letter-spacing:0;
    }

    .nav-links a:first-of-type{
        border-top:0;
    }

    .nav-account-toggle{
        display:none;
    }

    .nav-account-menu{
        display:block;
        position:static;
        min-width:0;
        padding:0;
        border:0;
        border-radius:0;
        background:transparent;
        box-shadow:none;
    }

    .nav-account-menu a,
    .nav-account-menu button{
        border-top:1px solid var(--lr-line-soft);
        border-radius:0;
        padding:13px 2px !important;
        font-size:15px;
    }

    .nav-account-menu form{
        border-top:0;
        margin-top:0;
        padding-top:0;
    }
}

/* =========================================================================
   Grouped navigation — Shop / Discover / Your Account   (Phase 1 §2, §16)

   The header used to be one flat run of links in which "Creatine", "Account"
   and "Sign Out" were the same font, size, colour and weight, sitting in the
   same visual group. Two consequences: shopping navigation and account
   management read as one undifferentiated list, and adding Brands, Retailers,
   Compare and Guides would have wrapped the desktop header onto a second row.

   The fix reuses the machinery that already worked for .nav-account rather
   than inventing a second pattern: a toggle plus a panel on desktop, the same
   DOM flattened into a labelled section on mobile. One set of links, one set
   of keyboard behaviours, no breakpoint branching in JS.
   ========================================================================= */

.nav-group{
    position:relative;
    flex:0 0 auto;
}

.nav-group-toggle{
    display:inline-flex;
    align-items:center;
    gap:5px;
    padding:6px 10px;
    border:1px solid transparent;
    border-radius:999px;
    background:transparent;
    color:var(--lr-muted);
    font:inherit;
    font-size:13.5px;
    font-weight:600;
    letter-spacing:.01em;
    white-space:nowrap;
    cursor:pointer;
    transition:color .18s ease, border-color .18s ease, background-color .18s ease;
}

.nav-group-toggle:is(:hover, :focus-visible){
    color:var(--lr-text);
    border-color:var(--lr-line);
    background:var(--lr-surface-2);
}

.nav-group-caret{
    transition:transform .18s ease;
}

.nav-group.is-open .nav-group-caret{
    transform:rotate(180deg);
}

.nav-group.is-open .nav-group-toggle{
    color:var(--lr-text);
    border-color:var(--lr-line);
}

/* Deliberately modest: min-width 190px and at most six rows, so neither panel
   becomes the oversized mega-menu the brief warns against. */
.nav-group-menu{
    display:none;
    position:absolute;
    left:0;
    top:calc(100% + 10px);
    min-width:190px;
    padding:6px;
    border:1px solid var(--lr-line);
    border-radius:12px;
    background:var(--lr-surface);
    box-shadow:0 18px 50px rgba(0,0,0,.45);
    z-index:110;
}

.nav-group.is-open .nav-group-menu{
    display:block;
}

.nav-group-menu a{
    display:block;
    width:100%;
    box-sizing:border-box;
    padding:9px 12px !important;
    border-radius:8px;
    color:var(--lr-muted);
    font-size:13.5px;
    font-weight:600;
    white-space:nowrap;
}

.nav-group-menu a:is(:hover, :focus-visible){
    background:var(--lr-surface-2);
    color:var(--lr-text);
}

/* The active-page underline is a header-row affordance; inside a panel the
   row highlight already carries it and the rule just looks like an artefact. */
.nav-group-menu a.is-active{
    color:var(--lr-text);
    background:var(--lr-surface-2);
}

.nav-group-menu a.is-active::after{
    display:none;
}

/* Section labels exist only in the flattened mobile panel. On desktop the
   toggle already says "Shop", so a second label would be noise. */
.nav-section-label{
    display:none;
}

/* Sign out is the one item in these menus that ends the session. It used to
   be visually identical to a category link. */
.nav-account-menu .nav-signout{
    color:var(--lr-accent-deal-hover);
}

.nav-account-menu .nav-signout:is(:hover, :focus-visible){
    background:color-mix(in srgb, var(--lr-accent-deal-hover) 14%, transparent);
    color:var(--lr-accent-deal-hover);
}

/* --- flattened into the mobile panel ------------------------------------ */

@media (max-width: 1179px){
    .nav-group-toggle{
        display:none;
    }

    .nav-group{
        display:block;
        width:100%;
    }

    .nav-group-menu{
        display:block;
        position:static;
        min-width:0;
        padding:0;
        border:0;
        border-radius:0;
        background:transparent;
        box-shadow:none;
    }

    .nav-group-menu a{
        border-top:1px solid var(--lr-line-soft);
        border-radius:0;
        padding:13px 2px !important;
        font-size:15px;
        /* 44px minimum touch target, met by padding plus line-height. */
        min-height:44px;
        display:flex;
        align-items:center;
    }

    .nav-section-label{
        display:block;
        margin:22px 0 2px;
        font-size:11px;
        font-weight:700;
        letter-spacing:.14em;
        text-transform:uppercase;
        color:var(--lr-dim, #6f6f6f);
    }

    /* The divider above each label is what actually separates shopping from
       account management; the label alone reads as decoration. */
    .nav-group + .nav-group .nav-section-label,
    .nav-account .nav-section-label{
        padding-top:14px;
        border-top:1px solid var(--lr-line);
    }

    .nav-account{
        display:block;
        width:100%;
    }

    .nav-account-menu .nav-signout{
        border-top:1px solid var(--lr-line-soft);
        width:100%;
        text-align:left;
        min-height:44px;
    }
}

/* Reduced motion: the caret rotation is decorative. */
@media (prefers-reduced-motion: reduce){
    .nav-group-caret,
    .nav-group-toggle{
        transition:none;
    }
}

/* =========================================================================
   Price-alert control          (Phase 1 §7)
   Offer cards                  (Phase 1 §8)
   Product-page disclosures     (Phase 1 §6)
   ========================================================================= */

/* --- the bell ----------------------------------------------------------- */

.svg-defs{ position:absolute; width:0; height:0; overflow:hidden; }

.watch-bell{
    width:17px;
    height:17px;
    flex:0 0 auto;
}

/* Filled once the alert is live, so the state is legible without reading the
   label — the previous control signalled it with a 6px dot. */
.watch-bell--on{
    fill:currentColor;
    fill-opacity:.22;
}

.watch-label-text{
    font-weight:var(--fw-semi, 600);
}

.watch-hero-btn{
    display:inline-flex;
    align-items:center;
    gap:9px;
    min-height:44px;
}

/* Loading. The spinner replaces nothing — it appears beside the label, so the
   button does not change width mid-submit and shift the layout under a
   thumb that is still on the screen. */
.watch-spin{ display:none; }
.watch-hero-btn.is-loading .watch-spin{ display:inline-block; }
.watch-hero-btn.is-loading{ cursor:progress; }
.watch-hero-btn[disabled]{ opacity:.55; cursor:not-allowed; }

/* Saved. A single 900ms confirmation, then it stops for good. */
.watch-hero-btn.is-saved .watch-bell{
    animation:watch-saved .9s var(--ease, ease) 1;
}

@keyframes watch-saved{
    0%   { transform:scale(1); }
    35%  { transform:scale(1.18); }
    100% { transform:scale(1); }
}

/* One short pulse when the control first scrolls into view, added by
   polish.js. It runs twice and stops: a control that keeps flashing reads as
   an advertisement, which is the one thing this button must not look like. */
.watch-hero-btn.is-attention{
    animation:watch-attention 1.1s var(--ease, ease) 2;
}

@keyframes watch-attention{
    0%, 100% { box-shadow:0 0 0 0 rgba(196,65,75,0); }
    50%      { box-shadow:0 0 0 4px rgba(196,65,75,.20); }
}

/* Non-negotiable: no pulse, no scale, nothing that moves. The state changes
   are still legible because they are carried by fill and by text. */
@media (prefers-reduced-motion: reduce){
    .watch-hero-btn.is-attention,
    .watch-hero-btn.is-saved .watch-bell{
        animation:none;
    }
}

/* --- offer cards -------------------------------------------------------- */

.offer-card{
    border:1px solid var(--lr-line);
    border-radius:var(--r-md, 12px);
    background:var(--lr-surface);
    padding:var(--sp-3, 12px);
    display:grid;
    gap:8px;
}

.offer-card + .offer-card{ margin-top:8px; }

.offer-card.is-best{ border-color:var(--lr-accent-deal, #c4414b); }
.offer-card.is-stale{ opacity:.72; }

/* Primary row: who, what it costs, and the way out. Three columns on a
   comfortable width; on a phone the action drops to its own full-width row
   rather than being squeezed to an icon. */
.offer-card-primary{
    display:grid;
    grid-template-columns:minmax(0, 1fr) auto;
    grid-template-areas:
        "who     figures"
        "action  action";
    gap:10px;
    align-items:center;
}

.offer-card-who{
    grid-area:who;
    display:flex;
    align-items:center;
    gap:9px;
    min-width:0;
}

.offer-card-store{ display:grid; min-width:0; }
.offer-card-store .po-store{ font-size:var(--fs-sm, 14px); }
.offer-card-store .po-variant{
    color:var(--lr-dim);
    font-size:var(--fs-xs, 12px);
    overflow:hidden;
    text-overflow:ellipsis;
    white-space:nowrap;
}

.offer-card-figures{
    grid-area:figures;
    display:grid;
    justify-items:end;
    text-align:right;
    gap:1px;
}

.offer-card-figures .po-price{
    font-size:var(--fs-lg, 20px);
    font-weight:var(--fw-bold, 700);
    font-variant-numeric:tabular-nums;
}

.offer-card-figures .offer-metric{
    font-size:var(--fs-xs, 12px);
    font-weight:var(--fw-semi, 600);
    color:var(--lr-soft);
    font-variant-numeric:tabular-nums;
}

.offer-card .offer-go{
    grid-area:action;
    justify-self:stretch;
    text-align:center;
    min-height:44px;
    display:inline-flex;
    align-items:center;
    justify-content:center;
}

@media (min-width: 560px){
    .offer-card-primary{
        grid-template-columns:minmax(0, 1fr) auto auto;
        grid-template-areas:"who figures action";
    }
    .offer-card .offer-go{ justify-self:end; min-width:132px; }
}

/* Secondary. Closed by default; <details> gives keyboard operability and the
   expanded state to assistive tech with no aria bookkeeping of ours. */
.offer-card-more > summary{
    list-style:none;
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:8px;
    min-height:32px;
    cursor:pointer;
    color:var(--lr-dim);
    font-size:var(--fs-xs, 12px);
}

.offer-card-more > summary::-webkit-details-marker,
.offer-card-more > summary::marker{ display:none; content:""; }

.offer-card-more > summary:focus-visible{
    outline:2px solid var(--lr-text);
    outline-offset:2px;
    border-radius:6px;
}

.offer-card-summary-line{
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    gap:6px;
    min-width:0;
}

.offer-card-more-hint{ white-space:nowrap; }
.offer-card-more[open] .offer-card-more-hint::after{ content:" ▴"; }
.offer-card-more:not([open]) .offer-card-more-hint::after{ content:" ▾"; }

/* A condition the shopper must not miss stays visible while the panel is
   closed: "this price needs a code" is not a detail, it is the price. */
.offer-flag{
    display:inline-flex;
    align-items:center;
    padding:1px 7px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    font-size:11px;
    font-weight:var(--fw-semi, 600);
    color:var(--lr-soft);
    white-space:nowrap;
}

.offer-flag--coupon{ border-color:var(--lr-accent-deal, #c4414b); color:var(--lr-accent-deal, #c4414b); }

.offer-card-detail{
    display:grid;
    gap:6px;
    margin:8px 0 0;
    padding-top:8px;
    border-top:1px solid var(--lr-line-soft);
    font-size:var(--fs-xs, 12px);
}

.offer-card-detail > div{
    display:flex;
    justify-content:space-between;
    gap:12px;
}

.offer-card-detail dt{ color:var(--lr-dim); margin:0; }
.offer-card-detail dd{ margin:0; text-align:right; color:var(--lr-soft); }
.offer-card-affiliate dd a{ color:var(--lr-dim); text-decoration:underline; }

/* No horizontal scrolling at any supported width: every row wraps instead. */
.offer-table{ overflow-x:visible; }

/* --- product-page disclosures ------------------------------------------- */

/* Last on the page, quiet, and fully visible — no <details>, no toggle, no
   off-screen text. Being last is the demotion; hiding would not be legally
   adequate and is explicitly out of bounds. */
.product-disclosures{
    display:grid;
    gap:8px;
    color:var(--lr-dim);
    font-size:var(--fs-xs, 12px);
    line-height:1.55;
}

.product-disclosures a{ color:var(--lr-soft); text-decoration:underline; }
.product-disclosures .affiliate-note,
.product-disclosures .data-disclaimer,
.product-disclosures .methodology-note{ margin:0; }

/* =========================================================================
   Mobile leaderboard: making the price findable      (Phase 1 §15)

   MUST STAY LAST. `.lb-summary`'s children are re-declared at several
   top-level points in this file, and a media query adds no specificity, so a
   rule placed earlier loses to any later top-level rule. The stacked layout
   above documents the same hazard.

   The problem: after the stacked-row fix, every field got a full-width row and
   the price ended up mid-column, left-aligned, in a line that also carried the
   retailer name — visually identical to four other lines. On a deals site the
   price is the thing a thumb is scanning for, and it was the hardest number on
   the card to find.

   The brief is explicit that this must NOT be solved by making the type
   bigger. So the price stays at essentially its current size and is made
   findable by position, weight and space instead:

     - right-aligned, which gives every row a single predictable scan line
     - on the same line as the retailer, directly above View Deal, so price,
       store and action read as one block
     - tabular numerals, so the currency symbols and decimal points line up
       between rows and the column reads as a column
     - the normalized figure stays one line above: nearby, clearly related,
       and clearly secondary

   `display:contents` on .lb-offer-detail promotes the store and price spans
   into .lb-offer's grid, so this needs no markup change and the client-side
   re-render path (leaderboard.js) keeps working untouched.
   ========================================================================= */

@media (max-width: 768px){
    .lb-summary .lb-offer{
        grid-area:offer;
        display:grid;
        grid-template-columns:minmax(0, 1fr) auto;
        grid-template-areas:
            "store price"
            "buy   buy";
        align-items:center;
        column-gap:var(--sp-3);
        row-gap:var(--sp-2);
        width:100%;
        text-align:left;
    }

    .lb-summary .lb-offer-detail{
        display:contents;
    }

    .lb-summary .lb-offer-size{
        grid-area:store;
        min-width:0;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
    }

    .lb-summary .lb-offer-price{
        grid-area:price;
        justify-self:end;
        text-align:right;
        /* Same size as the surrounding text; the weight and the alignment do
           the work. A 24px price would make the card taller and louder, which
           is what the brief rules out. */
        font-size:17px;
        font-weight:var(--fw-bold, 700);
        font-variant-numeric:tabular-nums;
        letter-spacing:-0.01em;
        white-space:nowrap;
    }

    .lb-summary .lb-buy-inline{
        grid-area:buy;
        justify-self:stretch;
        text-align:center;
    }

    /* Breathing room on the one line that matters, without growing the card:
       the space comes out of the gaps around the quieter rows. */
    .lb-summary{
        row-gap:var(--sp-2);
    }

    .lb-summary .lb-metric-cell{
        row-gap:2px;
    }

    /* The normalized figure is the supporting number here, so it gives up a
       little weight to the price directly below it. It is still the ranking
       metric and still legible. */
    .lb-summary .lb-metric{
        font-size:15px;
        font-weight:var(--fw-semi, 600);
        color:var(--lr-soft);
        font-variant-numeric:tabular-nums;
    }

    /* Competing text removed from the price line: the sparkline and value bar
       are analysis, not the answer to "what does it cost", and on a 320px
       card they were sharing space with it. */
    .lb-summary .lb-spark{
        display:none;
    }
}

/* 320px is the narrowest supported width and the one where the two-column
   offer line is tightest. The store name truncates rather than the price
   wrapping: a wrapped price is unreadable, a shortened retailer name is not. */
@media (max-width: 360px){
    .lb-summary .lb-offer{
        column-gap:var(--sp-2);
    }

    .lb-summary .lb-offer-price{
        font-size:16px;
    }
}

/* Market-average history chart (Phase 1 §10). */
.market-chart{
    margin-top:var(--sp-6, 32px);
    padding-top:var(--sp-5, 24px);
    border-top:1px solid var(--lr-line);
}
.market-chart-head{
    display:flex;
    flex-wrap:wrap;
    align-items:flex-end;
    justify-content:space-between;
    gap:var(--sp-3, 12px);
}
.market-chart-controls{ display:flex; flex-wrap:wrap; align-items:center; gap:10px; }
.market-chart-form{ display:flex; align-items:center; gap:8px; }
.market-select{
    min-height:40px;
    padding:0 10px;
    border:1px solid var(--lr-line);
    border-radius:10px;
    background:var(--lr-surface-2);
    color:var(--lr-text);
    font:inherit;
    font-size:13px;
}
.market-chart-figure{ margin:var(--sp-4, 16px) 0 0; color:var(--lr-accent-deal, #c4414b); }
.market-chart-caption{
    display:grid;
    gap:3px;
    margin-top:10px;
    color:var(--lr-dim);
    font-size:var(--fs-xs, 12px);
    line-height:1.5;
}
/* The basis is the most important line here: three readings of "the market
   average" give different numbers, and a chart that does not say which one it
   is showing lets the reader assume whichever suits them. */
.market-chart-basis{ color:var(--lr-soft); font-weight:var(--fw-semi, 600); }
.market-chart-empty{
    margin:var(--sp-4, 16px) 0 0;
    color:var(--lr-dim);
    font-size:var(--fs-sm, 14px);
    line-height:1.6;
}

/* Brand and retailer pages (Phase 3 and 4). */

/* .entity-index (brand detail) and .compare-page had NO rule at all, so those
   pages had no content column: the heading and the body ran flush to x=0
   against the viewport edge while the footer below them sat in the normal 1180
   column. Same container the rest of the site uses.

   The drops feed and the brand/retailer INDEXES were in the same state when
   this was written; they have since been rebuilt on .disc-page, which carries
   its own column, so .deals-page/.deals-hero are deliberately not styled here. */
.entity-index,
.compare-page,
.entity-page{
    width:min(1180px, calc(100% - 40px));
    margin:0 auto;
    padding:var(--sp-6) 0 var(--sp-9);
}

.entity-index-note{ color:var(--lr-dim); font-size:var(--fs-sm, 14px); margin:0 0 var(--sp-4, 16px); }
.entity-grid{
    list-style:none; margin:0; padding:0;
    display:grid; gap:12px;
    grid-template-columns:repeat(auto-fill, minmax(220px, 1fr));
}
.entity-card a{
    display:grid; gap:3px;
    padding:14px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md, 12px);
    background:var(--lr-surface);
    min-height:44px;
}
.entity-card a:hover{ border-color:var(--lr-dim); }
.entity-card-name{ font-size:var(--fs-base, 15px); }
.entity-card-meta{ color:var(--lr-soft); font-size:var(--fs-xs, 12px); }
.entity-card-cats,
.entity-card-range{ color:var(--lr-dim); font-size:var(--fs-xs, 12px); }

.entity-overview{ margin:0 0 var(--sp-5, 24px); max-width:70ch; line-height:1.65; }
/* The source line is small but never hidden: an overview without a visible
   source and date is a claim we are asking the reader to take on trust. */
.entity-source{ color:var(--lr-dim); font-size:var(--fs-xs, 12px); }

.entity-facts{
    display:grid; gap:12px;
    grid-template-columns:repeat(auto-fit, minmax(160px, 1fr));
    margin:0 0 var(--sp-5, 24px);
}
.entity-fact{
    display:grid; gap:2px;
    padding:12px 14px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md, 12px);
}
.entity-fact span{ color:var(--lr-dim); font-size:var(--fs-xs, 12px); }
.entity-fact strong{ font-variant-numeric:tabular-nums; }

.entity-section{ margin-top:var(--sp-6, 32px); }
.entity-count{ color:var(--lr-dim); font-size:var(--fs-sm, 14px); margin-left:6px; }
.entity-links{ list-style:none; margin:10px 0 0; padding:0; display:flex; flex-wrap:wrap; gap:8px; }
.entity-links a{
    display:inline-flex; align-items:center; gap:6px;
    min-height:36px; padding:0 12px;
    border:1px solid var(--lr-line); border-radius:999px;
    font-size:var(--fs-xs, 12px);
}
.entity-links a span{ color:var(--lr-dim); }
.entity-certs{ list-style:none; margin:10px 0 0; padding:0; display:grid; gap:8px; }
.entity-policies{ display:grid; gap:12px; margin:12px 0 0; }
.entity-policies dt{ color:var(--lr-dim); font-size:var(--fs-xs, 12px); }
.entity-policies dd{ margin:2px 0 0; line-height:1.6; }

.category-related{
    display:grid; gap:var(--sp-5, 24px);
    margin-top:var(--sp-6, 32px);
    padding-top:var(--sp-5, 24px);
    border-top:1px solid var(--lr-line);
}
.category-related-title{ font-size:var(--fs-base, 15px); margin:0; }

/* Shopping-system surfaces (Phases 5 to 9).

   Entry points on the product page are secondary to Set Price Alert, and each
   is hidden entirely when its feature is off: an unbuilt system shows no
   control rather than one that 404s. */
.product-tools{ display:flex; flex-wrap:wrap; gap:8px; margin:var(--sp-4, 16px) 0 0; }
.product-tool{ position:relative; }
.product-tool > summary{ list-style:none; cursor:pointer; }
.product-tool > summary::-webkit-details-marker,
.product-tool > summary::marker{ display:none; content:""; }
.product-tool-panel{
    display:grid; gap:6px;
    margin-top:8px; padding:10px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md, 12px);
    background:var(--lr-surface);
}
.collection-toggle{
    width:100%; text-align:left;
    min-height:40px; padding:0 10px;
    border:0; border-radius:8px;
    background:transparent; color:var(--lr-soft);
    font:inherit; font-size:13.5px; cursor:pointer;
}
.collection-toggle:is(:hover, :focus-visible){ background:var(--lr-surface-2); color:var(--lr-text); }

.stack-form{ display:grid; gap:6px; margin-top:10px; max-width:34ch; }
.stack-form label{ font-size:var(--fs-xs, 12px); color:var(--lr-dim); }
.stack-list{ display:grid; gap:14px; }
.stack-item{
    border:1px solid var(--lr-line); border-radius:var(--r-md, 12px);
    padding:14px; display:grid; gap:10px;
}
.stack-item.is-paused{ opacity:.7; }
.stack-item-head{ display:flex; flex-wrap:wrap; justify-content:space-between; gap:8px; }
.stack-item-schedule{ color:var(--lr-dim); font-size:var(--fs-xs, 12px); }
.stack-estimate-main{ margin:0; font-size:var(--fs-lg, 20px); }
.stack-estimate-dates,
.stack-estimate-basis{ margin:2px 0 0; color:var(--lr-dim); font-size:var(--fs-xs, 12px); }

/* A low-confidence estimate has to LOOK different from a fresh one. The number
   is the same shape either way; what differs is whether it should be trusted,
   and that is the thing a glance needs to carry. */
.stack-estimate--low .stack-estimate-basis,
.stack-estimate--stale .stack-estimate-basis{ color:#e0c073; }

.stack-actions{ display:flex; flex-wrap:wrap; gap:8px; }
.stack-actions form{ margin:0; }

.planner-section{ margin-top:var(--sp-6, 32px); }
.planner-card{
    border:1px solid var(--lr-line); border-radius:var(--r-md, 12px);
    padding:14px; margin-bottom:10px; display:grid; gap:6px;
}
.planner-card--hit{ border-color:var(--lr-accent-deal, #c4414b); }
.planner-card-head{ display:flex; flex-wrap:wrap; justify-content:space-between; gap:8px; }
.planner-days{ font-variant-numeric:tabular-nums; }
.planner-basis,
.planner-dates{ margin:0; color:var(--lr-dim); font-size:var(--fs-xs, 12px); }
.planner-actions{ display:flex; flex-wrap:wrap; gap:8px; }
.planner-inline{ display:flex; gap:6px; align-items:center; }
.planner-waiting{ list-style:none; margin:10px 0 0; padding:0; display:grid; gap:8px; }
.planner-waiting li{ display:flex; flex-wrap:wrap; justify-content:space-between; gap:8px; }

.compare-scroll{ overflow-x:auto; }
.compare-table{ width:100%; border-collapse:collapse; font-size:var(--fs-sm, 14px); }
.compare-table th, .compare-table td{
    padding:10px; text-align:left; border-bottom:1px solid var(--lr-line-soft);
    vertical-align:top;
}
.compare-table thead th{ min-width:150px; }
.compare-group th{
    background:var(--lr-surface-2); font-size:var(--fs-xs, 12px);
    text-transform:uppercase; letter-spacing:.08em;
}

/* "Not recorded" is deliberately quiet but present, and italic so it can never
   be skim-read as a number. It must never be mistaken for a zero. */
.compare-missing{ color:var(--lr-dim); font-style:italic; }

.compare-brand, .compare-category{
    display:block; color:var(--lr-dim);
    font-size:var(--fs-xs, 12px); font-weight:400;
}
.compare-priority-grid, .pref-grid{
    display:grid; gap:8px; margin:10px 0;
    grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));
}
.compare-priority, .pref-option{ display:flex; align-items:center; gap:8px; min-height:44px; }
/* A flex item defaults to min-width:auto, so the membership <select> refused to
   shrink below the width of its longest option and pushed the whole page 20px
   (390) to 38px (768) sideways. Measured by the route sweep; it is the only
   horizontal overflow left on a customer-facing page. */
.pref-option > span{ min-width:0; overflow-wrap:anywhere; }

/* ── The preferred-stores list ─────────────────────────────────────────────
   Scoped to --stores on purpose. `.pref-option` is shared with the interest
   grid, whose rows are just a tick and a word and which lays them out in
   auto-fill columns of ~200px; giving THOSE a 150px control column left about
   30px for the label and rendered "Third-Party Tested" one letter per line.

   For the store rows the problem was the opposite: as a wrapping flex row the
   membership <select> began wherever the domain name above it happened to end,
   so fourteen rows had fourteen different left edges. One row per line, three
   fixed columns — tick, domain, membership — puts every control in the same
   place. */
.pref-grid--stores{ grid-template-columns:1fr; gap:0; }
.pref-grid--stores .pref-option{
    display:grid;
    grid-template-columns:auto minmax(0,1fr) minmax(0,150px);
    align-items:center;
    gap:var(--sp-3);
    padding:var(--sp-2) 0;
    min-height:48px;
}
.pref-grid--stores .pref-option select{ min-width:0; width:100%; font-size:var(--fs-sm); }
/* A hairline between rows: fourteen otherwise identical rows in one surface
   read as a single block of text. */
.pref-grid--stores > .pref-option + .pref-option{ border-top:1px solid var(--lr-line-soft); }

/* Three columns still fit a 390px phone (tick 20 + domain ~150 + control 132),
   so only genuinely narrow screens stack — dropping the control onto its own
   line at 420px made every row ~100px tall and added 700px to the page. */
@media (max-width: 360px){
    .pref-grid--stores .pref-option{
        grid-template-columns:auto minmax(0,1fr);
        row-gap:var(--sp-2);
    }
    .pref-grid--stores .pref-option select{ grid-column:2; }
}
/* 132px clipped the longest option to "No membersh" once the chevron's 34px of
   padding was taken out; 150px holds the label whole and still leaves a 390px
   phone ~160px for the domain, which fits "getrawnutrition.com". */
@media (max-width: 560px){
    .pref-grid--stores .pref-option{ gap:var(--sp-2); }
    .pref-grid--stores .pref-option > span{ font-size:var(--fs-sm); }
    .pref-grid--stores .pref-option select{ font-size:var(--fs-xs); padding-right:28px; }
}
.compare-verdicts ul{ list-style:none; margin:10px 0 0; padding:0; display:grid; gap:8px; }
.compare-verdicts-note, .compare-priorities-note{ color:var(--lr-dim); font-size:var(--fs-xs, 12px); }
.compare-warning{
    padding:10px 12px; border:1px solid var(--lr-line);
    border-radius:var(--r-md, 12px); color:var(--lr-soft);
    font-size:var(--fs-sm, 14px);
}
.compare-save{ display:flex; gap:8px; margin-top:var(--sp-4, 16px); }

.collection{ margin-top:var(--sp-6, 32px); }
.collection-create{ display:flex; gap:8px; margin:0 0 var(--sp-5, 24px); }
.collection-note, .collection-empty{ color:var(--lr-dim); font-size:var(--fs-sm, 14px); }
.collection-share{ display:grid; gap:8px; margin:12px 0; }

.pref-group-label{
    font-size:var(--fs-xs, 12px); color:var(--lr-dim);
    text-transform:uppercase; letter-spacing:.08em; margin:0;
}
.settings-group{ margin-top:var(--sp-6, 32px); }
.settings-group-note{ color:var(--lr-dim); font-size:var(--fs-xs, 12px); margin:2px 0 12px; }
.settings-links{ display:grid; gap:8px; }
.settings-link{
    display:grid; gap:3px;
    /* Was 12px 14px: the title, its description and the box edge were all
       within 12px of each other, so the card read as one dense clump. */
    padding:var(--sp-3) var(--sp-4);
    border:1px solid var(--lr-line); border-radius:var(--r-md, 12px);
    min-height:44px;
    /* A bordered card, not an inline link — the underline made the title and
       its description read as two separate links stuck together. */
    text-decoration:none;
    color:var(--lr-text);
    transition:border-color var(--dur-fast) var(--ease);
}
.settings-link:hover{ border-color:var(--lr-dim); }
.settings-link:hover strong{ text-decoration:underline; }
.settings-link small{ color:var(--lr-dim); font-size:var(--fs-xs, 12px); line-height:var(--lh-ui); }
.field-pair{ display:grid; gap:10px; }
@media (min-width: 30em){
    .field-pair{ grid-template-columns:1fr 1fr; }
}

/* Price history statistics (Phase 10).

   The label and the statistics are styled apart because they are different
   kinds of thing: the statistics are measurements a reader can act on, the
   label is our interpretation and appears only once there is enough evidence
   to support one. */
.intel-section{ margin-top:var(--sp-6, 32px); }
.intel-label{
    margin:0 0 var(--sp-4, 16px);
    padding:12px 14px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md, 12px);
    display:grid; gap:4px;
}
.intel-label--near_low{ border-color:var(--lr-accent-deal, #c4414b); }
.intel-evidence{ color:var(--lr-dim); font-size:var(--fs-xs, 12px); line-height:1.5; }
/* The "no verdict yet" state is a normal outcome, not an error, so it reads as
   a note rather than a warning. */
.intel-pending,
.intel-pattern{
    margin:0 0 var(--sp-4, 16px);
    color:var(--lr-soft);
    font-size:var(--fs-sm, 14px);
    line-height:1.6;
}
.intel-grid{
    display:grid; gap:14px;
    grid-template-columns:repeat(auto-fit, minmax(220px, 1fr));
}
.intel-period{
    border:1px solid var(--lr-line);
    border-radius:var(--r-md, 12px);
    padding:12px 14px;
}
.intel-period-title{ margin:0 0 8px; font-size:var(--fs-sm, 14px); }
.intel-stats{ display:grid; gap:4px; margin:0; }
.intel-stats > div{ display:flex; justify-content:space-between; gap:10px; }
.intel-stats dt{ color:var(--lr-dim); font-size:var(--fs-xs, 12px); margin:0; }
.intel-stats dd{ margin:0; font-variant-numeric:tabular-nums; font-size:var(--fs-xs, 12px); }
.intel-basis{ margin:8px 0 0; color:var(--lr-dim); font-size:11px; line-height:1.5; }
.intel-retailers{ margin-top:var(--sp-5, 24px); }
.intel-table{ width:100%; border-collapse:collapse; font-size:var(--fs-xs, 12px); }
.intel-table th, .intel-table td{
    padding:8px; text-align:left; border-bottom:1px solid var(--lr-line-soft);
    font-variant-numeric:tabular-nums;
}
.intel-scope{
    margin:var(--sp-4, 16px) 0 0;
    color:var(--lr-dim); font-size:var(--fs-xs, 12px); line-height:1.55;
}


/* ═════════════════════════════════════════════════════════════════════════
   PUBLIC UX POLISH — 2026-08-01
   ═════════════════════════════════════════════════════════════════════════

   Appended last, deliberately, for the reason the "Desktop header" block
   above states: this file carries eight nav eras and several duplicated
   media-query bands, and a media query adds no specificity. Anything here
   that re-points an earlier rule needs to be the last declaration, not the
   most specific one.

   Sections, in order:
     1. Header — categories as direct links
     2. Mobile panel — section headings that read as headings
     3. Discover pages — the shared page frame they never had
     4. Homepage — Top Deals board and the category shelf
     5. Leaderboard — compact filters, sort menu, mobile row
     6. Retailer pages
     7. Product page — alternatives
   ═════════════════════════════════════════════════════════════════════════ */


/* ── 1. Header: categories as direct links ───────────────────────────────
   The Shop group keeps the DOM the mobile panel flattens, but on desktop it
   lays out as a plain inline row. No toggle, no panel, no JS: the links are
   the navigation.

   Every horizontal cost in the header is fluid (see "Desktop header: one
   row, always"), and the two numbers that buy the room for five category
   links are the search field's floor and this row's internal gap. Both are
   tightened between 1180 and 1440 and released above it, which is measured
   to keep the row on one line at 1180/1280/1366/1440/1536/1920. */

@media (min-width: 1180px){
    .nav-group--shop{
        display:flex;
        align-items:center;
        min-width:0;
    }

    .nav-shop-menu{
        display:flex;
        position:static;
        align-items:center;
        gap:clamp(1px, .35vw, 6px);
        min-width:0;
        padding:0;
        border:0;
        border-radius:0;
        background:transparent;
        box-shadow:none;
    }

    .nav-shop-menu a{
        display:inline-flex;
        align-items:center;
        width:auto;
        padding:8px clamp(6px, .7vw, 11px) !important;
        border-radius:999px;
        color:var(--lr-muted);
        font-size:clamp(12.5px, .98vw, 13.5px);
        font-weight:600;
        letter-spacing:.01em;
        white-space:nowrap;
    }

    .nav-shop-menu a:is(:hover, :focus-visible){
        color:var(--lr-text);
        background:var(--lr-surface-2);
    }

    /* The active board is the one page the header can state outright, so it
       gets the crimson underline rather than another grey fill. */
    .nav-shop-menu a.is-active{
        color:var(--lr-text);
        background:transparent;
    }

    .nav-shop-menu a.is-active::after{
        content:"";
        display:block;
        position:absolute;
        left:clamp(6px, .7vw, 11px);
        right:clamp(6px, .7vw, 11px);
        bottom:1px;
        height:2px;
        margin:0;
        border-radius:2px;
        background:var(--lr-accent-deal);
    }

    .nav-shop-menu a{
        position:relative;
    }

    /* Search gives ground before the category links do, and keeps giving it
       further than the old 190px floor allowed — a 158px field is still a
       usable field; a category link that has fallen to a second row is not. */
    .nav-search-form{
        flex:0 1 clamp(158px, 20vw, 340px);
        min-width:158px;
    }
}

@media (min-width: 1500px){
    .nav-search-form{ min-width:190px; }
}


/* ── 2. Mobile panel: section headings that read as headings ─────────────
   In the hamburger the group labels ("Shop", "Discover", "Your Account") and
   their child links were near enough the same thing: 11px grey caps sitting
   directly on top of 15px grey rows, with a hairline that read as one more
   row separator. Scanning the panel you saw one long list, not three groups.

   Four cues now separate them, none of them loud:
     · a crimson tick to the left of the label, which nothing else in the
       panel has, so the eye finds group starts without reading
     · real space above the group and a tighter space below the label, so the
       label belongs to what follows rather than floating between groups
     · a heavier rule above the group instead of the hairline used between rows
     · child links set brighter than the label, reversing the old order —
       the destinations are what you tap, the label is a signpost

   The label is a <span aria-hidden>, never a link and never focusable, so it
   cannot be mistaken for a destination by pointer or by keyboard. */

@media (max-width: 1179px){
    .nav-section-label{
        position:relative;
        display:flex;
        align-items:center;
        gap:8px;
        margin:26px 0 6px;
        padding:0 0 0 0;
        border-top:0;
        font-size:11px;
        font-weight:700;
        letter-spacing:.16em;
        text-transform:uppercase;
        color:var(--lr-dim);
    }

    .nav-section-label::before{
        content:"";
        flex:0 0 auto;
        width:3px;
        height:11px;
        border-radius:2px;
        background:var(--lr-accent-deal);
    }

    /* The rule belongs to the GROUP, not to the label, so it sits above the
       whole block and does not get mistaken for a row divider. The first
       group needs no rule — the search field above it already separates it. */
    .nav-group + .nav-group,
    .nav-account{
        margin-top:18px;
        padding-top:4px;
        border-top:1px solid var(--lr-line);
    }

    .nav-group + .nav-group .nav-section-label,
    .nav-account .nav-section-label{
        padding-top:0;
        border-top:0;
    }

    /* Child rows brighter than the label they sit under. */
    .nav-group-menu a,
    .nav-account-menu a{
        color:var(--lr-soft);
    }

    .nav-group-menu a.is-active{
        color:var(--lr-text);
        background:transparent;
        font-weight:700;
    }

    /* The active row gets a crimson marker in the gutter rather than a fill,
       which at 15px on a dark panel was easy to miss. */
    .nav-group-menu a.is-active::before{
        content:"";
        width:3px;
        height:16px;
        margin-right:9px;
        border-radius:2px;
        background:var(--lr-accent-deal);
    }

    .nav-group-menu a.is-active::after{
        display:none;
    }
}


/* ── 3. Discover pages: the page frame they never had ────────────────────
   /brands, /retailers, /guides, /compare and /drops rendered their content
   as a direct child of <main> with no width constraint and no gutter, so on
   a 1440 desktop the retailer grid ran from x=0 to past the right edge and
   the last column was clipped. Every other page on the site sits inside
   .lb-page's `width:min(1180px, calc(100% - 40px))`; these simply never got
   one. .disc-page is that frame, stated once.

   Above the frame, each page gets a hero built from the SAME parts — kicker,
   title, one honest sentence, and a strip of figures that are all derived
   from live data — but the body below it is different per page, because a
   retailer index, an A–Z of brands and an explainer are not the same problem
   and a shared card grid for all three is what made them look unfinished. */

.disc-page{
    width:min(1180px, calc(100% - 40px));
    margin:0 auto;
    padding:40px 0 88px;
}

.disc-hero{
    display:grid;
    gap:26px;
    padding-bottom:26px;
    margin-bottom:32px;
    border-bottom:1px solid var(--lr-line);
}

@media (min-width: 900px){
    .disc-hero{
        grid-template-columns:minmax(0, 1fr) auto;
        align-items:end;
        gap:48px;
    }
}

.disc-hero-copy{ min-width:0; }

.disc-kicker{
    display:flex;
    align-items:center;
    gap:9px;
    margin:0 0 12px;
    font-size:11px;
    font-weight:700;
    letter-spacing:.18em;
    text-transform:uppercase;
    color:var(--lr-dim);
}

.disc-kicker::before{
    content:"";
    width:16px;
    height:2px;
    border-radius:2px;
    background:var(--lr-accent-deal);
}

.disc-title{
    margin:0;
    font-size:clamp(30px, 4.4vw, 46px);
    line-height:1.05;
    letter-spacing:-.02em;
    font-weight:800;
}

.disc-title em{
    font-style:normal;
    color:var(--lr-accent-deal-text);
}

.disc-lead{
    margin:14px 0 0;
    max-width:60ch;
    color:var(--lr-muted);
    font-size:var(--fs-md, 16px);
    line-height:1.6;
}

/* Figures, not "stat cards": no boxes, just a hairline-separated row of
   numbers set in the display face. Every one is derived from the same query
   the page below it renders, so it cannot drift from the content. */
.disc-figures{
    display:flex;
    flex-wrap:wrap;
    gap:0;
    margin:0;
    padding:0;
    list-style:none;
}

.disc-figure{
    padding:0 22px;
    border-left:1px solid var(--lr-line);
}

.disc-figure:first-child{
    padding-left:0;
    border-left:0;
}

.disc-figure:last-child{ padding-right:0; }

.disc-figure b{
    display:block;
    font-family:var(--lr-display);
    font-size:clamp(19px, 2.4vw, 26px);
    font-weight:400;
    line-height:1.1;
    font-variant-numeric:tabular-nums;
    color:var(--lr-text);
}

.disc-figure span{
    display:block;
    margin-top:6px;
    font-size:11px;
    font-weight:600;
    letter-spacing:.1em;
    text-transform:uppercase;
    color:var(--lr-dim);
}

@media (max-width: 560px){
    .disc-figures{ gap:14px 0; }
    .disc-figure{ padding:0 16px; }
}

/* A section inside a Discover page. */
.disc-section{ margin-top:44px; }

.disc-section-head{
    display:flex;
    flex-wrap:wrap;
    align-items:baseline;
    justify-content:space-between;
    gap:10px;
    margin-bottom:16px;
    padding-bottom:10px;
    border-bottom:1px solid var(--lr-line-soft);
}

.disc-section-title{
    margin:0;
    font-size:var(--fs-lg, 20px);
    font-weight:700;
    letter-spacing:-.01em;
}

.disc-section-note{
    margin:0;
    color:var(--lr-dim);
    font-size:var(--fs-sm, 13px);
}

/* ── Empty states that answer "so what do I do now?" ─────────────────────
   The old ones were a single grey sentence inside a dashed box — true, but
   a dead end. These keep the sentence and add the two or three places worth
   going instead, which is the only thing an empty page can usefully offer.
   Nothing here invents data to fill the space. */
.disc-empty{
    display:grid;
    gap:14px;
    justify-items:start;
    padding:34px 30px;
    border:1px solid var(--lr-line);
    border-left:3px solid var(--lr-accent-deal);
    border-radius:var(--r-lg, 14px);
    background:
        radial-gradient(120% 140% at 0% 0%, rgba(196,65,75,.07), transparent 62%),
        var(--lr-surface);
}

.disc-empty h2{
    margin:0;
    font-size:var(--fs-xl, 24px);
    font-weight:700;
    letter-spacing:-.01em;
}

.disc-empty p{
    margin:0;
    max-width:62ch;
    color:var(--lr-muted);
    line-height:1.65;
}

.disc-empty-links{
    display:flex;
    flex-wrap:wrap;
    gap:8px;
    margin-top:4px;
}

.disc-empty-links a{
    display:inline-flex;
    align-items:center;
    min-height:40px;
    padding:0 14px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface-2);
    color:var(--lr-soft);
    font-size:var(--fs-sm, 13px);
    font-weight:600;
    transition:border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.disc-empty-links a:is(:hover, :focus-visible){
    border-color:var(--lr-accent-deal-line);
    color:var(--lr-text);
}


/* ── Retailer index: identity first ──────────────────────────────────────
   The old cards were four stacked text lines with the domain as a link, so
   forty retailers read as forty identical paragraphs. A retailer's mark (or
   its monogram) now leads, the catalogue figures are set as figures, and the
   categories are chips rather than a dot-separated run-on. */

.ret-grid{
    list-style:none;
    margin:0;
    padding:0;
    display:grid;
    gap:12px;
    grid-template-columns:repeat(auto-fill, minmax(268px, 1fr));
}

.ret-card{ min-width:0; }

.ret-card > a{
    display:grid;
    grid-template-columns:38px minmax(0, 1fr);
    grid-template-areas:
        "mark name"
        "figs figs"
        "cats cats";
    gap:4px 12px;
    height:100%;
    padding:16px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-lg, 14px);
    background:var(--lr-surface);
    transition:border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

.ret-card > a:is(:hover, :focus-visible){
    border-color:var(--lr-accent-deal-line);
    background:var(--lr-surface-2);
}

.ret-mark{
    grid-area:mark;
    display:grid;
    place-items:center;
    width:38px;
    height:38px;
    border:1px solid var(--lr-line);
    border-radius:10px;
    background:var(--lr-bg);
    font-family:var(--lr-display);
    font-size:15px;
    color:var(--lr-soft);
    overflow:hidden;
}

.ret-mark img{ width:100%; height:100%; object-fit:contain; }

.ret-name{
    grid-area:name;
    align-self:center;
    min-width:0;
    font-size:var(--fs-md, 16px);
    font-weight:700;
    letter-spacing:-.01em;
    overflow-wrap:anywhere;
}

.ret-figs{
    grid-area:figs;
    display:flex;
    flex-wrap:wrap;
    gap:4px 16px;
    margin-top:12px;
    padding-top:12px;
    border-top:1px solid var(--lr-line-soft);
    font-size:var(--fs-xs, 12px);
    color:var(--lr-dim);
}

.ret-figs b{
    color:var(--lr-text);
    font-variant-numeric:tabular-nums;
    font-weight:700;
}

.ret-cats{
    grid-area:cats;
    display:flex;
    flex-wrap:wrap;
    gap:5px;
    margin-top:10px;
}
/* The overflow count is a fact about the list, not another category. */
.ret-cat--more{
    border-style:dashed;
    color:var(--lr-dim);
}

.ret-cat{
    display:inline-flex;
    align-items:center;
    padding:3px 8px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    font-size:11px;
    font-weight:600;
    letter-spacing:.02em;
    color:var(--lr-muted);
    text-transform:capitalize;
    white-space:nowrap;
}


/* ── Brand index: an A–Z, not another card wall ───────────────────────────
   Brands are looked up by name far more often than they are browsed, so the
   index is grouped by initial with a jump bar, and each row carries the two
   facts that decide whether to click: how many products, and what they cost. */

.brand-jump{
    display:flex;
    flex-wrap:wrap;
    gap:var(--sp-2);
    margin-bottom:var(--sp-3);
}

/* 32x32 with a 4px gap. This is the only way to move around a page of two
   dozen brands on a phone, and it was the smallest target on the site — under
   the 44px minimum on both axes, with neighbours 4px away. 40px square with an
   8px gap is a comfortable thumb target and costs one row of height. */
.brand-jump a{
    display:inline-grid;
    place-items:center;
    min-width:40px;
    height:40px;
    padding:0 var(--sp-2);
    border:1px solid var(--lr-line);
    border-radius:var(--r-sm);
    background:var(--lr-surface);
    font-family:var(--lr-display);
    font-size:13px;
    color:var(--lr-muted);
}

.brand-jump a:is(:hover, :focus-visible){
    border-color:var(--lr-accent-deal-line);
    color:var(--lr-text);
}

.brand-group{ margin-top:34px; scroll-margin-top:96px; }

.brand-group-letter{
    display:flex;
    align-items:center;
    gap:12px;
    margin:0 0 10px;
    font-family:var(--lr-display);
    font-size:15px;
    font-weight:400;
    color:var(--lr-accent-deal-text);
}

.brand-group-letter::after{
    content:"";
    flex:1 1 auto;
    height:1px;
    background:var(--lr-line);
}

.brand-rows{
    list-style:none;
    margin:0;
    padding:0;
    display:grid;
    gap:0;
}

.brand-row a{
    display:grid;
    grid-template-columns:auto minmax(0, 1fr) auto;
    grid-template-areas:
        "mark name  range"
        "mark cats  cats";
    align-items:center;
    gap:4px 14px;
    padding:12px 10px;
    border-bottom:1px solid var(--lr-line-soft);
    border-radius:8px;
    min-height:44px;
}

.brand-row a:is(:hover, :focus-visible){
    background:var(--lr-surface);
}

/* ── The brand mark ───────────────────────────────────────────────────────
   /brands listed two dozen names as plain text, which made the page a
   directory rather than a shelf — nothing to recognise at a glance, and every
   row identical until you read it.

   The tile is the same 40px chip on both this page and /retailers so the two
   indexes read as siblings. Two states, both designed:

     logo      an off-white plate. Brand logos are drawn for light backgrounds
               and a fair number are dark-on-transparent; dropping those onto
               the page's near-black tile renders them invisible. A small
               rounded plate is the convention for exactly this reason.
     monogram  the initial in the display face on a tinted tile, with a soft
               wash so it has some depth instead of reading as an empty box. */
.brand-mark,
.ret-mark{
    display:grid;
    place-items:center;
    width:40px;
    height:40px;
    flex:none;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md);
    background:
        radial-gradient(120% 120% at 25% 15%, rgba(196,65,75,.14), transparent 62%),
        var(--lr-surface-2);
    font-family:var(--lr-display);
    font-size:15px;
    line-height:1;
    color:var(--lr-soft);
    overflow:hidden;
    transition:border-color var(--dur-fast) var(--ease);
}
.brand-mark{ grid-area:mark; }

.brand-mark--logo,
.ret-mark--logo{
    background:#F4F4F5;
    padding:5px;
}
.brand-mark img,
.ret-mark img{ width:100%; height:100%; object-fit:contain; }

.brand-row a:is(:hover, :focus-visible) .brand-mark,
.ret-card > a:is(:hover, :focus-visible) .ret-mark{
    border-color:var(--lr-accent-deal-line);
}

.brand-row-name{
    grid-area:name;
    font-size:var(--fs-base, 15px);
    font-weight:600;
    overflow-wrap:anywhere;
}

.brand-row-count{
    color:var(--lr-dim);
    font-size:var(--fs-xs, 12px);
    font-variant-numeric:tabular-nums;
    white-space:nowrap;
}

.brand-row-cats{
    grid-area:cats;
    display:flex;
    flex-wrap:wrap;
    gap:5px;
    margin-top:2px;
}

.brand-row-range{
    grid-area:range;
    align-self:center;
    color:var(--lr-soft);
    font-size:var(--fs-xs, 12px);
    font-variant-numeric:tabular-nums;
    white-space:nowrap;
}

/* On a phone the price drops under the name rather than competing with it for
   the same line; the mark keeps its own column so the rows stay scannable. */
@media (max-width: 560px){
    .brand-row a{
        grid-template-columns:auto minmax(0,1fr);
        grid-template-areas:
            "mark name"
            "mark range"
            "mark cats";
        align-items:start;
        row-gap:2px;
    }
    .brand-row-range{ align-self:start; }
}


/* ── Guides: editorial, so it should read editorially ────────────────────
   A guide is a piece of writing. The index sets it as one: a wide lead card
   for the newest, then a reading list — kind, title, summary — rather than
   equal-weight tiles that make an eight-hundred-word explainer look like a
   product. */

.guide-list{
    list-style:none;
    margin:0;
    padding:0;
    display:grid;
    gap:0;
}

.guide-item a{
    display:grid;
    gap:6px;
    padding:22px 4px;
    border-bottom:1px solid var(--lr-line-soft);
}

.guide-item a:is(:hover, :focus-visible) .guide-item-title{
    color:var(--lr-accent-deal-text);
}

.guide-item-kind{
    font-size:11px;
    font-weight:700;
    letter-spacing:.14em;
    text-transform:uppercase;
    color:var(--lr-dim);
}

.guide-item-title{
    font-size:clamp(19px, 2.2vw, 24px);
    font-weight:700;
    letter-spacing:-.015em;
    line-height:1.25;
    transition:color var(--dur-fast) var(--ease);
}

.guide-item-summary{
    max-width:70ch;
    color:var(--lr-muted);
    font-size:var(--fs-base, 15px);
    line-height:1.6;
}


/* ── Methodology: an explainer, not a contract ───────────────────────────
   The content was already the right content — it was set as eleven <h2>s and
   a run of <p>s at legal-page width, which is what made a page about arithmetic
   read like terms of service. The prose is unchanged; what changes is that
   the formula, the per-category units and the four discount rules are now
   things you can see at a glance instead of sentences you have to hold. */

.method-lede{
    max-width:62ch;
    margin:0;
    font-size:var(--fs-lg, 20px);
    line-height:1.55;
    color:var(--lr-soft);
}

.method-formula{
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    gap:10px 14px;
    margin:26px 0 0;
    padding:22px 24px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-lg, 14px);
    background:var(--lr-surface);
    font-family:var(--lr-display);
    font-size:clamp(13px, 1.6vw, 17px);
    line-height:1.3;
}

.method-formula i{
    font-style:normal;
    color:var(--lr-accent-deal-text);
}

.method-formula span{
    color:var(--lr-dim);
    font-size:.8em;
}

.method-units{
    list-style:none;
    margin:0;
    padding:0;
    display:grid;
    gap:10px;
    grid-template-columns:repeat(auto-fit, minmax(190px, 1fr));
}

.method-unit{
    padding:14px 16px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md, 12px);
    background:var(--lr-surface);
}

.method-unit b{
    display:block;
    font-size:var(--fs-sm, 13px);
    font-weight:700;
}

.method-unit span{
    display:block;
    margin-top:5px;
    font-family:var(--lr-display);
    font-size:13px;
    color:var(--lr-accent-deal-text);
}

.method-rules{
    list-style:none;
    margin:0;
    padding:0;
    display:grid;
    gap:2px;
}

.method-rule{
    display:grid;
    grid-template-columns:auto minmax(0, 1fr);
    align-items:baseline;
    gap:16px;
    padding:15px 0;
    border-bottom:1px solid var(--lr-line-soft);
}

.method-rule b{
    font-family:var(--lr-display);
    font-size:clamp(15px, 2vw, 19px);
    font-weight:400;
    font-variant-numeric:tabular-nums;
    color:var(--lr-text);
    white-space:nowrap;
}

.method-rule span{
    color:var(--lr-muted);
    line-height:1.6;
    font-size:var(--fs-base, 15px);
}

/* "We do not" is a list of refusals and should look like one — the crimson
   tick is the same signal the empty states use, turned to a boundary. */
.method-nevers{
    list-style:none;
    margin:0;
    padding:0;
    display:grid;
    gap:12px;
}

.method-nevers li{
    position:relative;
    padding-left:26px;
    color:var(--lr-muted);
    line-height:1.6;
}

.method-nevers li::before{
    content:"";
    position:absolute;
    left:0;
    top:.55em;
    width:12px;
    height:2px;
    border-radius:2px;
    background:var(--lr-accent-deal);
}

.method-prose{
    max-width:66ch;
    color:var(--lr-muted);
    line-height:1.7;
}

.method-prose + .method-prose{ margin-top:14px; }

.method-prose b{ color:var(--lr-text); }


/* ── Compare: make the point of the page visible before you have picked ── */

.cmp-steps{
    list-style:none;
    counter-reset:cmp;
    margin:0;
    padding:0;
    display:grid;
    gap:12px;
    grid-template-columns:repeat(auto-fit, minmax(230px, 1fr));
}

.cmp-step{
    counter-increment:cmp;
    position:relative;
    padding:18px 18px 18px 52px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md, 12px);
    background:var(--lr-surface);
    color:var(--lr-muted);
    font-size:var(--fs-sm, 13px);
    line-height:1.6;
}

.cmp-step::before{
    content:counter(cmp);
    position:absolute;
    left:18px;
    top:17px;
    display:grid;
    place-items:center;
    width:22px;
    height:22px;
    border-radius:50%;
    background:var(--lr-accent-deal);
    color:#fff;
    font-family:var(--lr-display);
    font-size:11px;
}

.cmp-step b{
    display:block;
    margin-bottom:4px;
    color:var(--lr-text);
    font-size:var(--fs-base, 15px);
}


/* Whole-card links: the card is the target, so the browser's default
   underline and the site's link colour both have to be switched off at the
   anchor and re-stated by the parts inside it. Without this every figure and
   every category chip inside a Discover card renders underlined and tinted,
   which reads as a page full of broken links. */
.ret-card > a,
.brand-row > a,
.guide-item > a,
.disc-empty-links a,
.brand-jump a{
    color:inherit;
    text-decoration:none;
}

.ret-card > a:is(:hover, :focus-visible) .ret-name{
    color:var(--lr-accent-deal-text);
}

.brand-row > a:is(:hover, :focus-visible) .brand-row-name{
    color:var(--lr-accent-deal-text);
}

.cmp-starts{ margin-top:18px; }

/* Michroma is a subsetted caps+digits face (~9 KB) — it has no "÷", so the
   operator and the trailing note render from Inter instead of falling back to
   whatever the system offers. */
.method-formula span{
    font-family:'Inter', sans-serif;
    font-weight:var(--fw-semi, 600);
}
.method-lede{ margin-top:16px; }


/* ── 4. Homepage: the price-drop rail and the category shelf ─────────────

   REBUILT 2026-08-01.

   What was here: `.home-top-deals`, a grid of eight cards that on production
   showed no price drops at all, because the board topped itself up with
   best-value rows whenever nothing had dropped. Each card carried a product
   name, a "Best <category> value" claim, a per-unit metric, a retailer domain
   and a freshness stamp — five text rows, three of which were repeated
   verbatim on the next card along.

   What is here now: `.home-drops`, a horizontal rail whose cards can only
   describe a price that fell. Six elements, in the order they are read:
   photo, the percentage, the category, the name, the two prices, the saving.

   `.deal-rail` / `.deal-card` (defined much earlier in this file) are NOT
   touched — brand.html and retailer.html still render them. Only the
   homepage-specific overrides that dressed a non-deal as a deal are gone:
   .deal-card--value, .deal-savings--quiet and .deal-store had no other
   caller and would have outlived the markup that justified them. */

.home-drops{
    width:min(1180px, calc(100% - 40px));
    margin:0 auto;
    padding-top:8px;
}

/* A board, not a swipe rail: on desktop these are the merchandise, and a
   horizontal scroller hides two thirds of them behind a gesture nobody makes
   with a mouse. It stays a scroller on phones, where it is the right control.
   Still used by retailer.html. */
@media (min-width: 700px){
    .deal-rail--board{
        display:grid;
        grid-template-columns:repeat(auto-fill, minmax(214px, 1fr));
        gap:12px;
        overflow:visible;
    }
    .deal-rail--board .deal-card{
        width:auto;
        min-width:0;
        flex:none;
        scroll-snap-align:none;
    }
}

/* ── the rail itself ────────────────────────────────────────────────────
   A real overflow container, not a transform carousel: native scrolling gives
   momentum on touch, a trackpad gesture on macOS, arrow keys when focused and
   a scroll position the browser restores on back-navigation — none of which a
   translated track gets without re-implementing it badly. Nothing autoplays. */
.drop-rail{
    display:flex;
    gap:12px;
    overflow-x:auto;
    overscroll-behavior-x:contain;
    scroll-snap-type:x proximity;
    scroll-padding-left:2px;
    /* Room for the cards' lift-on-hover and focus ring, which a clipping
       overflow container would otherwise shave off. */
    padding:4px 2px 10px;
    margin:0 -2px;
    scrollbar-width:none;
    -webkit-overflow-scrolling:touch;
}
.drop-rail::-webkit-scrollbar{ display:none; }
/* The track is focusable so arrow keys work; it must therefore show a focus
   ring, and it must be a ring the crimson palette does not swallow. */
.drop-rail:focus-visible{
    outline:2px solid var(--lr-accent-deal-text);
    outline-offset:3px;
    border-radius:var(--r-lg, 14px);
}

.drop-rail-nav{
    display:flex;
    align-items:center;
    gap:8px;
}

.drop-nav-btn{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    width:34px; height:34px;
    padding:0;
    border:1px solid var(--lr-line);
    border-radius:50%;
    background:var(--lr-surface);
    color:var(--lr-soft);
    font-size:19px;
    line-height:1;
    cursor:pointer;
    transition:border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.drop-nav-btn:hover{
    border-color:var(--lr-accent-deal-line);
    background:var(--lr-surface-2);
    color:var(--lr-text);
}
.drop-nav-btn:focus-visible{
    outline:2px solid var(--lr-accent-deal-text);
    outline-offset:2px;
}
/* Disabled = "the track is already at this end". Kept visible rather than
   removed so the pair does not jump around as the reader scrolls. */
.drop-nav-btn[disabled]{
    opacity:.35;
    cursor:default;
    pointer-events:none;
}

.drop-card{
    display:flex;
    flex-direction:column;
    gap:10px;
    flex:0 0 216px;
    width:216px;
    padding:14px 14px 12px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-lg, 14px);
    background:var(--lr-surface);
    scroll-snap-align:start;
    transition:border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.drop-card:hover{ border-color:var(--lr-accent-deal-line); transform:translateY(-2px); }
.drop-card:focus-within{ border-color:var(--lr-accent-deal-line); }

.drop-card-face{
    position:relative;
    display:grid;
    gap:6px;
    color:inherit;
    text-decoration:none;
}

.drop-card-thumb{
    display:flex;
    align-items:center;
    justify-content:center;
    height:112px;
    border-radius:var(--r-md, 10px);
    overflow:hidden;
}
/* Product shots are transparent PNGs on a dark plate; `contain` keeps the
   whole bottle in frame and never crops a label. */
.drop-card-thumb img{
    max-width:100%;
    max-height:100%;
    width:auto; height:auto;
    object-fit:contain;
}

/* The percentage is the headline of the card and sits ON the photo, top-left,
   so the eye lands on the magnitude before the name. */
.drop-card-pct{
    position:absolute;
    top:6px; left:6px;
    padding:3px 8px;
    border-radius:var(--r-pill, 999px);
    background:var(--lr-accent-deal);
    color:#fff;
    font-family:var(--lr-display);
    font-size:12px;
    line-height:1.5;
    font-variant-numeric:tabular-nums;
}

.drop-card-cat{
    font-size:10.5px;
    font-weight:600;
    letter-spacing:.11em;
    text-transform:uppercase;
    color:var(--lr-dim);
}

.drop-card-name{
    display:-webkit-box;
    -webkit-line-clamp:2;
    -webkit-box-orient:vertical;
    overflow:hidden;
    min-height:2.7em;
    font-size:var(--fs-sm, 13px);
    font-weight:var(--fw-semi, 600);
    line-height:1.35;
    color:var(--lr-text);
}

.drop-card-prices{
    display:flex;
    align-items:baseline;
    gap:8px;
    flex-wrap:wrap;
}
.drop-card-now{
    font-family:var(--lr-display);
    font-size:19px;
    font-weight:400;
    font-variant-numeric:tabular-nums;
    color:var(--lr-text);
}
.drop-card-was{
    color:var(--lr-dim);
    font-size:var(--fs-xs, 12px);
    font-variant-numeric:tabular-nums;
}

.drop-card-save{
    color:var(--lr-accent-deal-text);
    font-size:var(--fs-xs, 12px);
    font-weight:var(--fw-semi, 600);
    font-variant-numeric:tabular-nums;
}

.drop-card-cta{ margin-top:auto; }

/* ── the empty state ────────────────────────────────────────────────────
   This is what production actually renders today, so it is designed rather
   than defaulted: it has to look like a decision, not like a section that
   failed to load. */
.drop-empty{
    padding:26px 24px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-lg, 14px);
    background:
        radial-gradient(120% 140% at 12% 0%, var(--lr-accent-deal-wash), transparent 62%),
        var(--lr-surface);
}
.drop-empty-lead{
    margin:0;
    font-family:var(--lr-display);
    font-size:clamp(17px, 2.2vw, 21px);
    font-weight:400;
    line-height:1.35;
    color:var(--lr-text);
}
.drop-empty-note{
    margin:10px 0 0;
    max-width:62ch;
    color:var(--lr-muted);
    font-size:var(--fs-sm, 13px);
    line-height:1.6;
}
.drop-empty-actions{
    display:flex;
    align-items:center;
    flex-wrap:wrap;
    gap:14px;
    margin-top:18px;
}

@media (max-width: 560px){
    .drop-card{ flex-basis:168px; width:168px; }
    .drop-card-thumb{ height:92px; }
    .drop-empty{ padding:20px 18px; }
    .drop-empty-actions .btn{ flex:1 1 100%; }
}

@media (prefers-reduced-motion: reduce){
    .drop-card{ transition:none; }
    .drop-card:hover{ transform:none; }
    .drop-rail{ scroll-behavior:auto; }
}


/* ── Category shelf ─────────────────────────────────────────────────────── */

.home-cats{
    width:min(1180px, calc(100% - 40px));
    margin:0 auto;
    padding:56px 0 8px;
    scroll-margin-top:88px;
}

.cat-shelf{
    display:grid;
    gap:12px;
    grid-template-columns:repeat(auto-fit, minmax(216px, 1fr));
}

/* REDESIGNED 2026-08-01. The card used to be four stacked text rows: name,
   two-line blurb, best cost-per-unit under the words BEST ON THE BOARD, and
   the counts. It read as a paragraph with a number in it.

   It is now led by the category NAME in display type and a photograph of
   whatever is currently number one on that board, with the counts as the only
   figures. The per-unit metric is deliberately gone: a reader choosing between
   "Creatine" and "Energy Drinks" is not choosing on ¢/g vs $/fl oz, and
   putting one figure per card forced them to re-anchor on a different unit
   five times across one row. */
.cat-card{
    position:relative;
    display:grid;
    grid-template-rows:auto auto auto auto;
    gap:6px;
    padding:18px 18px 16px;
    overflow:hidden;
    border:1px solid var(--lr-line);
    border-radius:var(--r-lg, 14px);
    background:var(--lr-surface);
    color:inherit;
    text-decoration:none;
    isolation:isolate;
    transition:border-color var(--dur) var(--ease), background var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.cat-card:is(:hover, :focus-visible){
    border-color:var(--lr-accent-deal-line);
    background:var(--lr-surface-2);
    transform:translateY(-2px);
}

/* ── the decorative shapes ──────────────────────────────────────────────
   Every orb is crimson now. They used to be crimson, gold, green and grey —
   one hue per category — which put four accent colours on one row of a site
   whose whole palette is near-black plus one red, and made the shelf read as
   a chart legend for a chart that did not exist.

   Depth comes from SIZE and OPACITY instead: three circles per card at
   different scales and alphas, all of them the same crimson, all of them
   behind the content (z-index:-1 inside the card's own isolate). The highest
   alpha here is .16 over #100F0F, which leaves the label and counts at their
   full contrast — decoration must never cost readability. */
.cat-card-motif{
    position:absolute;
    inset:0;
    z-index:-1;
    pointer-events:none;
    opacity:.9;
    transition:opacity var(--dur) var(--ease);
}

.cat-card:is(:hover, :focus-visible) .cat-card-motif{ opacity:1; }

.cat-orb{
    position:absolute;
    display:block;
    border-radius:50%;
}
.cat-orb--1{
    right:-46px; bottom:-56px;
    width:168px; height:168px;
    background:radial-gradient(circle at 32% 30%, rgba(196,65,75,.16), transparent 70%);
}
.cat-orb--2{
    right:34px; top:-38px;
    width:96px; height:96px;
    background:radial-gradient(circle at 40% 38%, rgba(196,65,75,.10), transparent 72%);
}
.cat-orb--3{
    left:-30px; bottom:26px;
    width:64px; height:64px;
    background:radial-gradient(circle at 42% 40%, rgba(196,65,75,.07), transparent 74%);
}

/* Each board still gets its own corner — same crimson, different geometry, so
   the five cards are distinguishable without a second hue. */
.cat-card--creatine .cat-orb--1{ right:auto; left:-52px; width:150px; height:150px; }
.cat-card--creatine .cat-orb--2{ right:18px; top:-30px; width:78px; height:78px; }
.cat-card--pre-workout .cat-orb--1{ width:190px; height:190px; bottom:-72px; }
.cat-card--pre-workout .cat-orb--3{ left:auto; right:56px; bottom:auto; top:44px; }
.cat-card--energy-drinks .cat-orb--1{ right:auto; left:-40px; bottom:-46px; width:158px; height:158px; }
.cat-card--energy-drinks .cat-orb--2{ right:-16px; top:22px; width:110px; height:110px; }
.cat-card--nutrition-bars .cat-orb--1{ width:146px; height:146px; right:-38px; bottom:-40px; }
.cat-card--nutrition-bars .cat-orb--3{ left:24px; bottom:-24px; width:82px; height:82px; }

/* ── the top product's photograph ───────────────────────────────────────
   Fixed height so all five cards align and the row does not reflow as the
   images arrive — the <img> carries width/height too, so the box is reserved
   before the bytes land. `contain` on a transparent PNG over the dark plate
   keeps the product whole; nothing is cropped and no background is invented.
   aria-hidden on the wrapper: the card's own label already names the board,
   and "photo of the top creatine product" is not information a screen-reader
   user needs read out before the link text. */
.cat-card-shot{
    display:flex;
    align-items:center;
    justify-content:center;
    height:104px;
    margin-bottom:2px;
    border-radius:var(--r-md, 10px);
    overflow:hidden;
}
.cat-card-shot img{
    max-width:86%;
    max-height:92%;
    width:auto; height:auto;
    object-fit:contain;
}
/* The monogram fallback, for a top product with no image yet. It must occupy
   the same box, or a card without a photo collapses and breaks the row. */
.cat-card-shot .thumb-empty{
    width:auto !important;
    height:auto !important;
    min-width:64px;
    font-size:30px;
}

.cat-card-label{
    font-family:var(--lr-display);
    font-size:clamp(17px, 1.5vw, 21px);
    font-weight:400;
    letter-spacing:.01em;
    line-height:1.2;
    color:var(--lr-text);
}

.cat-card-count{
    color:var(--lr-soft);
    font-size:var(--fs-xs, 12px);
    font-variant-numeric:tabular-nums;
}
.cat-card-count b{
    color:var(--lr-text);
    font-weight:var(--fw-semi, 600);
}

.cat-card-go{
    margin-top:10px;
    padding-top:11px;
    border-top:1px solid var(--lr-line-soft);
    color:var(--lr-accent-deal-text);
    font-size:var(--fs-xs, 12px);
    font-weight:var(--fw-semi, 600);
}
.cat-card-go span{
    display:inline-block;
    transition:transform var(--dur-fast) var(--ease);
}
.cat-card:is(:hover, :focus-visible) .cat-card-go span{ transform:translateX(3px); }

@media (max-width: 560px){
    .cat-card{ padding:16px 14px 14px; }
    .cat-card-shot{ height:88px; }
}

@media (prefers-reduced-motion: reduce){
    .cat-card{ transition:none; }
    .cat-card:is(:hover, :focus-visible){ transform:none; }
    .cat-card-go span{ transition:none; }
    .cat-card:is(:hover, :focus-visible) .cat-card-go span{ transform:none; }
}


/* ── 5. Leaderboard controls: filters, sort, active filters ──────────────

   The measured defects, taken on the live site at 1440/1280/820 before any of
   this changed:

     Sort panel      opened 33px / 113px / 143px off the LEFT edge
     Filters panel   opened 94–103px BELOW the fold, so Clear all and Done
                     were off screen at every desktop width
     Filters panel   every group expanded at once, which is what made it that
                     tall in the first place

   The two positional faults are fixed by leaderboard.js's place() — a panel's
   own width and its button's distance to the viewport edge are not expressible
   in static CSS. What CSS owns is everything below: the two custom properties
   place() writes into, sane defaults for a no-JS render, and the accordion. */

.lb-dd-panel{
    /* place() writes these; the fallbacks are what a no-JS visitor gets, and
       they are chosen to be safe rather than generous. */
    transform:translateX(var(--lb-dd-shift, 0px));
    max-height:var(--lb-dd-max-h, min(62vh, 520px));
    overscroll-behavior:contain;
}

/* The panel is a column: a scrolling body with the action bar pinned under it,
   so "Clear all" and "Done" cannot scroll away however long the groups are. */
.lb-dd-panel-filters{
    display:flex;
    flex-direction:column;
    padding:0;
    width:min(560px, calc(100vw - 24px));
}

.lb-dd-panel-filters .lb-filters{
    display:flex;
    flex-direction:column;
    min-height:0;
    gap:0;
}

.lb-dd-panel-filters .quick-filter-group{
    padding:14px 16px;
    border-bottom:1px solid var(--lr-line);
}

.filter-groups{
    flex:1 1 auto;
    min-height:0;
    overflow-y:auto;
    overscroll-behavior:contain;
    padding:4px 16px;
}

.lb-dd-panel-filters .filter-actions{
    flex:0 0 auto;
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:12px;
    margin:0;
    padding:12px 16px;
    border-top:1px solid var(--lr-line);
    background:var(--lr-surface);
    border-radius:0 0 13px 13px;
}


/* --- accordion group ---------------------------------------------------- */

.filter-acc{
    border-bottom:1px solid var(--lr-line-soft);
}

.filter-acc:last-child{ border-bottom:0; }

.filter-acc-head{
    list-style:none;
    display:flex;
    align-items:center;
    gap:10px;
    min-height:44px;
    padding:6px 2px;
    cursor:pointer;
    user-select:none;
}

.filter-acc-head::-webkit-details-marker,
.filter-acc-head::marker{ display:none; content:""; }

.filter-acc-head:focus-visible{
    outline:2px solid var(--lr-text);
    outline-offset:-2px;
    border-radius:6px;
}

.filter-acc-name{
    flex:1 1 auto;
    font-size:var(--fs-sm, 13px);
    font-weight:700;
    color:var(--lr-text);
}

/* The applied value is shown ON the closed row, so a collapsed group never
   hides the fact that it is filtering the board. */
.filter-acc-value{
    max-width:46%;
    overflow:hidden;
    text-overflow:ellipsis;
    white-space:nowrap;
    padding:2px 9px;
    border-radius:999px;
    background:var(--lr-accent-deal-wash);
    color:var(--lr-accent-deal-text);
    font-size:11.5px;
    font-weight:700;
}

.filter-acc-count{
    color:var(--lr-dim);
    font-size:11.5px;
    font-variant-numeric:tabular-nums;
}

.filter-acc-caret{
    flex:0 0 auto;
    color:var(--lr-dim);
    transition:transform var(--dur-fast) var(--ease);
}

.filter-acc[open] .filter-acc-caret{ transform:rotate(180deg); }

.filter-acc .chip-group{ padding:0 0 12px; }

.filter-acc .chip-row{
    display:flex;
    flex-wrap:wrap;
    gap:6px;
    overflow:visible;
}


/* --- sort: always a stacked menu --------------------------------------- */

.lb-dd-panel-sort{
    padding:8px;
    width:max-content;
    min-width:210px;
}

.sort-control--menu{
    display:flex !important;
    flex-direction:column;
    width:100%;
    border-radius:var(--r-md, 10px);
    background:transparent;
    border:0;
    padding:0;
}

.sort-control--menu .sort-segment{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:12px;
    width:100%;
    min-height:44px;
    padding:0 12px;
    border-radius:8px;
    border:0;
    background:transparent;
    color:var(--lr-muted);
    font-size:var(--fs-sm, 13px);
    font-weight:600;
    white-space:nowrap;
}

.sort-control--menu .sort-segment:is(:hover, :focus-visible){
    background:var(--lr-surface-2);
    color:var(--lr-text);
}

.sort-control--menu .sort-segment.is-active{
    background:transparent;
    color:var(--lr-text);
    font-weight:700;
}

/* `display:none` on .sort-segment-check is set by the pill-row era higher up
   in this file; opacity alone cannot bring a display:none element back, which
   left the stacked menu with no selection tick at all — and in the stacked
   layout the tick IS the selection cue, because the row no longer carries the
   filled pill that used to signal it. */
.sort-control--menu .sort-segment-check{
    display:block;
    opacity:0;
    flex:0 0 auto;
    margin-left:10px;
    color:var(--lr-accent-deal-text);
}

.sort-control--menu .sort-segment.is-active .sort-segment-check{ opacity:1; }


/* --- active filters, outside the panel ---------------------------------- */

.lb-controls{
    align-items:flex-start;
    justify-content:flex-start;
    gap:8px;
    row-gap:10px;
}

.lb-dd-icon{ color:var(--lr-soft); }

.lb-active-filters{
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    gap:6px;
    /* Takes the rest of the row on desktop and wraps under the two buttons on
       a phone, rather than squeezing them. */
    flex:1 1 100%;
    order:3;
}

@media (min-width: 900px){
    .lb-active-filters{ flex:1 1 auto; order:0; }
}

.lb-active-filters[hidden]{ display:none; }

.lb-active-chip{
    display:inline-flex;
    align-items:center;
    gap:7px;
    min-height:32px;
    padding:0 10px;
    border:1px solid var(--lr-accent-deal-line);
    border-radius:999px;
    background:var(--lr-accent-deal-wash);
    color:var(--lr-text);
    font-size:12px;
    font-weight:600;
    text-decoration:none;
    white-space:nowrap;
}

.lb-active-chip:is(:hover, :focus-visible){
    border-color:var(--lr-accent-deal);
    background:color-mix(in srgb, var(--lr-accent-deal) 22%, transparent);
}

.lb-active-chip svg{ color:var(--lr-accent-deal-text); flex:0 0 auto; }

.lb-active-clear{
    color:var(--lr-dim);
    font-size:12px;
    font-weight:600;
    text-decoration:underline;
    text-underline-offset:3px;
    padding:0 4px;
    min-height:32px;
    display:inline-flex;
    align-items:center;
}

.lb-active-clear:is(:hover, :focus-visible){ color:var(--lr-text); }


/* --- phones: the panel is a sheet, not a popover ------------------------ */

@media (max-width: 768px){
    .lb-dd-panel-filters{
        left:0;
        right:auto;
        width:min(calc(100vw - 24px), 440px);
    }

    /* A phone sheet gets more of the screen than a desktop popover, because
       there is nothing behind it worth preserving. */
    .lb-dd-panel{
        max-height:var(--lb-dd-max-h, 70vh);
    }

    .filter-acc-head{ min-height:48px; }
}

@media (prefers-reduced-motion: reduce){
    .filter-acc-caret{ transition:none; }
}


/* ── 6. Mobile leaderboard row: size in the compact slot ─────────────────

   Until now the collapsed phone card put the RETAILER in the slot beside the
   price and hid the pack size entirely ("the retailer answers where, the size
   does not change the ranking"). That reasoning holds for the ranking and not
   for the shopping: two rows reading "$34.99 · nutricost.com" and
   "$61.31 · nutricost.com" are not comparable until you know one is 1 lb and
   the other is 5 lb, and the size was the one field a shopper had to expand
   the row to find.

   So the compact slot is now the SIZE, and the retailer moves down one line to
   sit beside the button that goes there — which is where it is most useful
   anyway, because it says where "View deal" is about to take you. Retailer
   identity is not removed; it is repositioned.

   The size string is offer.display_size, the same normalized value the offer
   table and the product page print. Nothing is parsed in the template.

   MUST STAY AFTER the "Mobile leaderboard: making the price findable" block —
   that block sets these same grid areas at the same specificity, and a media
   query adds none, so whichever comes last wins. */

@media (max-width: 768px){
    .lb-summary .lb-offer{
        grid-template-areas:
            "size  price"
            "store buy";
    }

    /* Promote the two spans into .lb-offer's grid so they can be placed
       independently; the wrapper existed only to keep them adjacent. */
    .lb-summary .lb-offer-size{
        display:contents;
    }

    .lb-summary .lb-offer-pack{
        display:block;
        grid-area:size;
        min-width:0;
        font-size:var(--fs-xs, 12px);
        color:var(--lr-soft);
        font-weight:var(--fw-semi, 600);
        font-variant-numeric:tabular-nums;
        white-space:nowrap;
        overflow:hidden;
        text-overflow:ellipsis;
    }

    /* The dot was a separator between store and size on one line. They are on
       two lines now, so it would be a stray character. */
    .lb-summary .lb-offer-pack::before{ content:none; }

    /* A product with no recorded size leaves no empty cell behind. */
    .lb-summary .lb-offer-pack:empty{ display:none; }

    .lb-summary .lb-offer-store{
        display:block;
        grid-area:store;
        align-self:center;
        min-width:0;
        font-size:var(--fs-xs, 12px);
        color:var(--lr-dim);
        white-space:nowrap;
        overflow:hidden;
        text-overflow:ellipsis;
    }

    .lb-summary .lb-buy-inline{
        grid-area:buy;
        justify-self:end;
    }
}


/* ── 7. Retailer pages ──────────────────────────────────────────────────

   The hero carries the retailer's identity: mark, name, domain, one sentence.
   The accent is the ONLY thing an admin can change about it and it is a class,
   never a value — retailer_display.ACCENTS is the whole vocabulary, so the
   worst an admin can do is pick a different one of these five. */

.ret-hero{
    position:relative;
    overflow:hidden;
    margin:18px 0 30px;
    padding:26px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-lg, 14px);
    background:var(--lr-surface);
    isolation:isolate;
}

/* The accent tints one corner and the left edge. Nothing here changes text
   colour, so contrast cannot be broken by an admin's choice. */
.ret-hero::before{
    content:"";
    position:absolute;
    inset:0 auto 0 0;
    width:3px;
    background:var(--ret-accent, var(--lr-accent-deal));
}

.ret-hero::after{
    content:"";
    position:absolute;
    z-index:-1;
    inset:-40% -10% auto auto;
    /* Relative, not a fixed 340px. test_accessibility_phase17 rejects any
       fixed width over 320px on sight, and it is right to: this blob is
       decorative and sits inside overflow:hidden, so it could not actually
       scroll the page — but "it happens to be clipped" is not a property worth
       depending on, and a percentage costs nothing. */
    width:min(340px, 60%);
    aspect-ratio:1;
    border-radius:50%;
    background:radial-gradient(circle at 40% 40%,
               color-mix(in srgb, var(--ret-accent, var(--lr-accent-deal)) 16%, transparent),
               transparent 66%);
}

.ret-hero--crimson{ --ret-accent:#C4414B; }
.ret-hero--slate{   --ret-accent:#6E7B8B; }
.ret-hero--amber{   --ret-accent:#E0A93E; }
.ret-hero--green{   --ret-accent:#57C271; }
.ret-hero--violet{  --ret-accent:#8E7BD1; }

/* An admin hero image sits behind the copy at low opacity and is never the
   only thing carrying meaning — the page reads identically without it. */
.ret-hero-bg{
    position:absolute;
    inset:0;
    z-index:-2;
    opacity:.16;
}

.ret-hero-bg img{
    width:100%;
    height:100%;
    object-fit:cover;
    filter:grayscale(.4);
}

.ret-hero-body{
    display:grid;
    grid-template-columns:auto minmax(0, 1fr);
    align-items:start;
    gap:18px;
}

@media (min-width: 900px){
    .ret-hero-body{ grid-template-columns:auto minmax(0, 1fr) auto; align-items:center; }
}

.ret-hero-mark{
    display:grid;
    place-items:center;
    width:64px;
    height:64px;
    border:1px solid var(--lr-line);
    border-radius:14px;
    background:var(--lr-bg);
    overflow:hidden;
    font-family:var(--lr-display);
    font-size:24px;
    color:var(--lr-soft);
}

.ret-hero-mark img{ width:100%; height:100%; object-fit:contain; }

.ret-hero-copy{ min-width:0; }

.ret-hero-name{
    margin:2px 0 0;
    font-size:clamp(26px, 3.6vw, 38px);
    font-weight:800;
    letter-spacing:-.02em;
    line-height:1.1;
    overflow-wrap:anywhere;
}

.ret-hero-domain{ margin:6px 0 0; }

.ret-hero-domain a{
    color:var(--lr-dim);
    font-size:var(--fs-sm, 13px);
    text-decoration:underline;
    text-underline-offset:3px;
}

.ret-hero-domain a:is(:hover, :focus-visible){ color:var(--lr-soft); }

.ret-hero-desc{
    margin:12px 0 0;
    max-width:62ch;
    color:var(--lr-muted);
    line-height:1.6;
}

.ret-hero-message{
    display:inline-block;
    margin:12px 0 0;
    padding:6px 12px;
    border:1px solid color-mix(in srgb, var(--ret-accent, var(--lr-accent-deal)) 45%, transparent);
    border-radius:999px;
    background:color-mix(in srgb, var(--ret-accent, var(--lr-accent-deal)) 12%, transparent);
    color:var(--lr-text);
    font-size:var(--fs-sm, 13px);
    font-weight:600;
}

@media (max-width: 899.98px){
    .ret-hero-figures{ grid-column:1 / -1; margin-top:18px; }
}

.ret-cat-links{
    list-style:none; margin:0; padding:0;
    display:flex; flex-wrap:wrap; gap:8px;
}

.ret-cat-links a{
    display:inline-flex; align-items:center;
    min-height:40px; padding:0 15px;
    border:1px solid var(--lr-line); border-radius:999px;
    background:var(--lr-surface);
    color:var(--lr-soft);
    font-size:var(--fs-sm, 13px); font-weight:600;
    text-transform:capitalize; text-decoration:none;
}

.ret-cat-links a:is(:hover, :focus-visible){
    border-color:var(--lr-accent-deal-line);
    color:var(--lr-text);
}


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

.ret-offers{
    list-style:none;
    margin:0;
    padding:0;
    display:grid;
    gap:8px;
}

.ret-offer{
    display:grid;
    grid-template-columns:34px minmax(0, 1fr) auto auto;
    align-items:center;
    gap:14px;
    padding:13px 16px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md, 12px);
    background:var(--lr-surface);
}

/* Rank one is emphasised, and ranks 2+ are NOT diminished to achieve it: they
   keep the same card, the same price size and the same crimson CTA. What #1
   adds is a trophy, a crimson border, a lifted background and a label. */
.ret-offer--best{
    border-color:var(--lr-accent-deal-line);
    background:
        linear-gradient(105deg, var(--lr-accent-deal-wash), transparent 46%),
        var(--lr-surface-2);
}

.ret-offer-rank{
    display:grid;
    place-items:center;
    width:34px;
    height:34px;
    border-radius:9px;
    background:var(--lr-bg);
    color:var(--lr-dim);
    font-family:var(--lr-display);
    font-size:13px;
    font-variant-numeric:tabular-nums;
}

.ret-offer--best .ret-offer-rank{
    background:var(--lr-accent-deal);
    color:#fff;
}

.ret-offer-body{ min-width:0; display:grid; gap:3px; }

.ret-offer-flag{
    font-size:10.5px;
    font-weight:700;
    letter-spacing:.12em;
    text-transform:uppercase;
    color:var(--lr-accent-deal-text);
}

.ret-offer-name{
    font-size:var(--fs-base, 15px);
    font-weight:600;
    color:var(--lr-text);
    text-decoration:none;
    overflow-wrap:anywhere;
}

.ret-offer-name:is(:hover, :focus-visible){ color:var(--lr-accent-deal-text); }

.ret-offer-meta{
    display:flex;
    flex-wrap:wrap;
    gap:4px 12px;
    color:var(--lr-dim);
    font-size:var(--fs-xs, 12px);
}

.ret-offer-figures{
    display:grid;
    justify-items:end;
    gap:2px;
    text-align:right;
}

.ret-offer-price{
    font-size:17px;
    font-weight:700;
    font-variant-numeric:tabular-nums;
    letter-spacing:-.01em;
}

.ret-offer-metric{
    color:var(--lr-soft);
    font-size:var(--fs-xs, 12px);
    font-variant-numeric:tabular-nums;
    white-space:nowrap;
}

.ret-offer-cta{ flex:0 0 auto; }

@media (max-width: 700px){
    .ret-offer{
        grid-template-columns:30px minmax(0, 1fr) auto;
        gap:10px 12px;
        padding:12px 13px;
    }
    .ret-offer-rank{ width:30px; height:30px; }
    .ret-offer-figures{ grid-column:3; }
    .ret-offer-cta{
        grid-column:1 / -1;
        justify-self:stretch;
    }
}


/* Standalone links need a 24px target on touch (WCAG 2.5.8 AA). The exception
   in that criterion is for links INLINE IN A SENTENCE, where the line-height
   of the surrounding prose constrains them — "official policy", "How this
   works" and the contact address are all that case and are left alone. These
   two are not: the retailer's domain sits on its own line in the hero, and the
   product name is the primary link of an offer row. Measured at 375 and 390
   they were 16px and 19px tall. */
@media (max-width: 700px){
    .ret-hero-domain a,
    .ret-offer-name{
        display:inline-flex;
        align-items:center;
        min-height:24px;
    }
}


/* ══════════════════════════════════════════════════════════════════════════
   RANK ONE — the premium treatment for the row that actually won
   (2026-08-01 launch polish, §6)
   ══════════════════════════════════════════════════════════════════════════

   Before this, the difference between #1 and #2 on a board was: the numeral
   "1" instead of "2", in a slightly brighter grey, plus a small crimson chip.
   The site's entire proposition is "this one is the best value" and the row
   carrying that answer looked like every other row.

   THE HONESTY RULE, and it is the important part of this block.

   Every selector here hangs off `.lb-row.is-rank-one`, and that class is
   applied — server-side in _lb_row.html, client-side in leaderboard.js — from
   exactly one condition:

       row.rank == 1  AND  not stale  AND  pricing.primary_badge(rank=1, sort)

   The third clause is what makes the spotlight honest. `primary_badge` returns
   None for `price_desc`, where #1 is the most EXPENSIVE product, and for any
   sort mode not mapped in pricing.RANK_BADGE_BY_SORT. So on a high-to-low
   board NOTHING here applies: no crimson edge, no wash, no "#1", no laurel —
   the board just lists products in the order it was asked to. There is no
   `:first-child` or `:first-of-type` selector anywhere in this block, because
   "first in the DOM" and "won the ranking" are different facts and only one of
   them deserves the accent colour.

   Restraint: the emphasis is a left edge, a low-alpha wash and one small
   marker. No gold, no gradient sweep, no scale transform, no emoji. */

.lb-row.is-rank-one{
    position:relative;
    border-color:var(--lr-accent-deal-line);
    background:
        linear-gradient(90deg, var(--lr-accent-deal-wash), transparent 42%),
        rgba(22,21,20,.92);
}

/* The crimson edge. A 3px inset bar rather than a border, so the row keeps its
   alignment with the rows above and below — a thicker border on one row of a
   list makes every column in it sit a pixel off. */
.lb-row.is-rank-one::before{
    content:"";
    position:absolute;
    left:0; top:0; bottom:0;
    width:3px;
    background:var(--lr-accent-deal);
    pointer-events:none;
}

.lb-row.is-rank-one:hover,
.lb-row.is-rank-one[open]{
    border-color:var(--lr-accent-deal-text);
    background:
        linear-gradient(90deg, var(--lr-accent-deal-wash), transparent 46%),
        rgba(26,24,24,.96);
}

/* The rank numeral gets the display face at a larger size — the podium
   position is the headline of this row. */
.lb-row.is-rank-one .lb-rank{
    font-size:26px;
    color:var(--lr-accent-deal-text);
}

/* ── the "#1" marker ────────────────────────────────────────────────────
   Text plus a laurel stroke. The text is the accessible part: it survives a
   failed stylesheet, a monochrome display and a screen reader, none of which
   can perceive "the row is faintly redder". */
.lb-crown{
    display:inline-flex;
    align-items:center;
    gap:5px;
    margin-bottom:3px;
    padding:2px 8px 2px 6px;
    border:1px solid var(--lr-accent-deal-line);
    border-radius:var(--r-pill, 999px);
    background:var(--lr-accent-deal-wash);
    color:var(--lr-accent-deal-text);
    font-size:10.5px;
    font-weight:800;
    letter-spacing:.06em;
    line-height:1.5;
    white-space:nowrap;
}
.lb-crown b{ font-weight:800; }
.lb-crown[hidden]{ display:none; }

/* The winner's CTA. Keyed on the ROW class rather than passed into the CTA
   macro, so the server render and leaderboard.js's cloned render cannot
   disagree: the script toggles one class on the row and the button follows.
   Size and halo only — the colour is the same crimson every other View deal
   uses (see .cta-deal). */
.lb-row.is-rank-one .cta-deal{
    min-height:40px;
    font-size:12.5px;
    box-shadow:0 0 0 1px var(--lr-accent-deal-line), 0 6px 16px -8px rgba(196,65,75,.7);
}

/* Phones: the row is already a stack, so the spotlight must not add height.
   The edge and the wash carry it; the marker sits inline above the name and
   costs one short line. */
@media (max-width: 700px){
    .lb-row.is-rank-one .lb-rank{ font-size:21px; }
    .lb-crown{ font-size:10px; padding:1px 7px 1px 5px; }
}

@media (prefers-reduced-motion: reduce){
    .lb-row.is-rank-one{ transition:none; }
}


/* ── Product page: the primary offer, same rule ─────────────────────────
   `.offer-card.is-best` is the product page's equivalent of rank one, and it
   is set from the offer serializer's `best` flag (cheapest eligible offer for
   that product), which is a value ranking — so the emphasis is honest there
   for the same reason. Detail styling lives with the Other Offers block. */


/* ══════════════════════════════════════════════════════════════════════════
   PRODUCT PAGE — "Where to buy": the offer stack
   (2026-08-01 launch polish, §7)
   ══════════════════════════════════════════════════════════════════════════

   Replaces .offer-card / .offer-card-primary / .offer-card-more. Measured on
   the live page before the rewrite (1440px, /product/47, 38 offers):

     card                 1180 x 110px, x38 = 4,180px of scroll
     .offer-card-who      920px wide, holding a 98px store name and a 98px
                          flavour: ~700px of nothing between retailer and price
     size / servings      y=74, BELOW the price at y=15, inside a closed
                          <details> — so comparing two container sizes meant
                          opening two disclosures
     availability         same disclosure
     coupon/subscription  same disclosure, i.e. a conditional price read as an
                          ordinary one unless you went looking
     column headers       none

   The grid below is declared ONCE, on the container, and every row is a direct
   grid subject of it — that is what makes the price column line up down the
   page, which is the entire mechanism by which a comparison table is easier to
   read than a stack of cards. Row height is ~64px instead of 110px, and the
   38-offer page loses about 1,700px of scroll while showing strictly more
   information. */

.offer-stack{
    display:grid;
    /* retailer | variant+size | price | per-unit | status | CTA */
    /* The CTA column is a FIXED 150px, not `auto`.
       `auto` sizes to content, and the "Currently unavailable" stack is a
       second, separate grid container whose CTA cell is empty (there is
       nothing to send anyone to). So its sixth track collapsed to 0, the freed
       space was redistributed across the five fr tracks, and every column in
       the unavailable block sat ~6px off the live block directly above it —
       which is exactly the alignment this layout exists to provide, and which
       the template comment claims. Fixed width, both grids agree. */
    grid-template-columns:minmax(150px, 1.15fr) minmax(150px, 1.3fr) minmax(104px, .8fr) minmax(96px, .7fr) minmax(104px, .8fr) 150px;
    align-items:center;
    column-gap:18px;
}

.offer-stack-head{
    display:none;
}

@media (min-width: 900px){
    .offer-stack-head{
        display:grid;
        /* The CTA column is a FIXED 150px, not `auto`.
       `auto` sizes to content, and the "Currently unavailable" stack is a
       second, separate grid container whose CTA cell is empty (there is
       nothing to send anyone to). So its sixth track collapsed to 0, the freed
       space was redistributed across the five fr tracks, and every column in
       the unavailable block sat ~6px off the live block directly above it —
       which is exactly the alignment this layout exists to provide, and which
       the template comment claims. Fixed width, both grids agree. */
    grid-template-columns:minmax(150px, 1.15fr) minmax(150px, 1.3fr) minmax(104px, .8fr) minmax(96px, .7fr) minmax(104px, .8fr) 150px;
        column-gap:18px;
        padding:0 14px 8px;
        border-bottom:1px solid var(--lr-line);
        color:var(--lr-dim);
        font-size:10.5px;
        font-weight:700;
        letter-spacing:.11em;
        text-transform:uppercase;
    }
    /* The header's own sixth track matches the rows' fixed CTA column, so the
       five labels sit over the five columns they name. */
    .offer-stack-head span:last-child{ min-width:0; }
}

/* Each row spans the container's columns. `display:contents` is what lets a
   semantic row element exist while its children participate in the parent
   grid — without it every row would be its own grid and nothing would align
   between rows, which is the defect this whole block fixes. */
.offer-row-v2{
    display:grid;
    grid-template-columns:subgrid;
    grid-column:1 / -1;
    align-items:center;
    gap:4px 18px;
    padding:11px 14px;
    border-bottom:1px solid var(--lr-line-soft);
    position:relative;
}

/* subgrid is well supported now, but a browser without it must still produce
   an aligned table rather than six stacked blobs. This fallback re-declares
   the same track list on the row itself. */
@supports not (grid-template-columns: subgrid){
    .offer-row-v2{
        /* The CTA column is a FIXED 150px, not `auto`.
       `auto` sizes to content, and the "Currently unavailable" stack is a
       second, separate grid container whose CTA cell is empty (there is
       nothing to send anyone to). So its sixth track collapsed to 0, the freed
       space was redistributed across the five fr tracks, and every column in
       the unavailable block sat ~6px off the live block directly above it —
       which is exactly the alignment this layout exists to provide, and which
       the template comment claims. Fixed width, both grids agree. */
    grid-template-columns:minmax(150px, 1.15fr) minmax(150px, 1.3fr) minmax(104px, .8fr) minmax(96px, .7fr) minmax(104px, .8fr) 150px;
    }
}

.offer-row-v2:hover{ background:rgba(22,21,20,.6); }

/* ── the primary offer ───────────────────────────────────────────────────
   Same emphasis language as the leaderboard's rank one: a crimson left edge, a
   low-alpha wash, and a marker made of words. Its CTA is the same crimson as
   every other CTA on the page — see .cta-deal. */
.offer-row-v2.is-best{
    background:linear-gradient(90deg, var(--lr-accent-deal-wash), transparent 38%);
    border-bottom-color:var(--lr-accent-deal-line);
}
.offer-row-v2.is-best::before{
    content:"";
    position:absolute;
    left:0; top:0; bottom:0;
    width:3px;
    background:var(--lr-accent-deal);
}

.offer-row-v2.is-stale{ opacity:.72; }

/* Unavailable rows are not a dimmer version of a buyable one — they say so in
   words (.orv-stock) and they carry no CTA at all. Opacity alone would be a
   colour-only distinction, which fails exactly the readers who most need the
   difference to be obvious. */
.offer-row-v2.is-unavailable{ opacity:.7; }
.offer-row-v2.is-unavailable .orv-stock{ color:var(--lr-stale, #C9A227); }

/* ── LEFT: retailer ──────────────────────────────────────────────────── */
.orv-who{
    display:flex;
    align-items:center;
    gap:9px;
    min-width:0;
}
.orv-who-text{ display:grid; min-width:0; }
.po-store{
    font-size:var(--fs-sm, 13px);
    font-weight:var(--fw-semi, 600);
    color:var(--lr-text);
    overflow:hidden;
    text-overflow:ellipsis;
    white-space:nowrap;
}
.orv-brand{
    color:var(--lr-dim);
    font-size:var(--fs-xs, 12px);
    overflow:hidden;
    text-overflow:ellipsis;
    white-space:nowrap;
}
.orv-brand[hidden]{ display:none; }

.offer-row-v2 .best-label{
    padding:2px 7px;
    border:1px solid var(--lr-accent-deal-line);
    border-radius:var(--r-pill, 999px);
    background:var(--lr-accent-deal-wash);
    color:var(--lr-accent-deal-text);
    font-size:10px;
    font-weight:800;
    letter-spacing:.05em;
    white-space:nowrap;
}
.offer-row-v2 .best-label[hidden]{ display:none; }

/* ── LEFT: what is actually in the box ───────────────────────────────── */
.orv-what{ display:grid; gap:2px; min-width:0; }
.orv-variant{
    color:var(--lr-soft);
    font-size:var(--fs-sm, 13px);
    overflow:hidden;
    text-overflow:ellipsis;
    white-space:nowrap;
}
.orv-size{
    display:flex;
    flex-wrap:wrap;
    gap:3px 8px;
    color:var(--lr-dim);
    font-size:var(--fs-xs, 12px);
    font-variant-numeric:tabular-nums;
}
.orv-size .po-size{ color:var(--lr-text); font-weight:var(--fw-semi, 600); }
.orv-total:empty, .orv-servings:empty{ display:none; }

/* ── CENTRE: the money ───────────────────────────────────────────────── */
.orv-price{ display:grid; gap:2px; }
.orv-price .price-now{
    font-family:var(--lr-display);
    font-size:17px;
    font-weight:400;
    font-variant-numeric:tabular-nums;
    color:var(--lr-text);
}
.orv-delta{
    color:var(--lr-dim);
    font-size:11px;
    font-variant-numeric:tabular-nums;
}
.orv-delta[hidden]{ display:none; }
.orv-lastseen{
    color:var(--lr-dim);
    font-size:var(--fs-xs, 12px);
    font-variant-numeric:tabular-nums;
}

.orv-metric .offer-metric{
    font-family:'Inter', sans-serif;
    font-size:var(--fs-sm, 13px);
    font-weight:var(--fw-semi, 600);
    font-variant-numeric:tabular-nums;
    color:var(--lr-accent-deal-text);
}

/* ── CENTRE: status ──────────────────────────────────────────────────── */
.orv-status{ display:grid; gap:2px; }
.orv-stock{ color:var(--lr-soft); font-size:var(--fs-xs, 12px); }
.orv-checked{ color:var(--lr-dim); font-size:11px; }

/* ── RIGHT: the action ───────────────────────────────────────────────── */
.orv-go{
    display:flex;
    flex-direction:column;
    align-items:stretch;
    justify-content:center;
    gap:5px;
    min-width:132px;
}

/* The point-of-click affiliate marker. Small, quiet, and NEVER behind a
   control: it used to sit inside the card's <details>, and a disclosure the
   reader has to open is not disclosure at the point of click. */
.orv-aff{
    display:inline-flex;
    align-items:center;
    min-height:24px;
    color:var(--lr-dim);
    font-size:10px;
    line-height:1.3;
    text-align:center;
    text-decoration:none;
}
.orv-aff:hover{ color:var(--lr-soft); text-decoration:underline; }

/* ── conditions on the price ─────────────────────────────────────────
   Full-width under the row, so a row with none costs nothing. */
.orv-flags{
    grid-column:1 / -1;
    display:flex;
    flex-wrap:wrap;
    gap:6px;
    margin-top:2px;
}
.orv-flags[hidden]{ display:none; }

/* ── the "Other offers" divider ──────────────────────────────────────
   Rendered between row 1 and row 2, and only when a row 2 exists. */
.offer-stack-divider{
    grid-column:1 / -1;
    display:flex;
    align-items:center;
    gap:10px;
    margin:14px 0 2px;
    padding:0 14px;
    color:var(--lr-dim);
    font-size:10.5px;
    font-weight:700;
    letter-spacing:.11em;
    text-transform:uppercase;
}
.offer-stack-divider span{
    padding:1px 7px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-pill, 999px);
    color:var(--lr-soft);
    font-variant-numeric:tabular-nums;
    letter-spacing:0;
}
.offer-stack-divider::after{
    content:"";
    flex:1;
    height:1px;
    background:var(--lr-line);
}

.offer-stack-note{
    margin:14px 0 0;
    padding:12px 14px;
    border:1px solid var(--lr-line);
    border-radius:var(--r-md, 10px);
    background:var(--lr-surface);
    color:var(--lr-muted);
    font-size:var(--fs-sm, 13px);
    line-height:1.6;
}

.offer-stack--unavailable{ margin-top:10px; }

/* ══ PHONES ══════════════════════════════════════════════════════════════
   The grid collapses to a two-column stack. Price and CTA stay prominent;
   size and variant stay visible; nothing is clipped and nothing scrolls
   sideways. Each value keeps an inline label where the desktop column header
   was carrying the meaning. */
@media (max-width: 899px){
    .offer-stack{
        display:grid;
        grid-template-columns:1fr;
        gap:10px;
        column-gap:0;
    }
    .offer-row-v2{
        grid-template-columns:minmax(0,1fr) auto;
        gap:8px 12px;
        padding:14px;
        border:1px solid var(--lr-line);
        border-radius:var(--r-md, 10px);
    }
    @supports not (grid-template-columns: subgrid){
        .offer-row-v2{ grid-template-columns:minmax(0,1fr) auto; }
    }
    .offer-row-v2.is-best{ border-color:var(--lr-accent-deal-line); }
    .orv-who{ grid-column:1 / -1; }
    .orv-what{ grid-column:1 / -1; }
    .orv-price{ grid-column:1; }
    .orv-metric{ grid-column:2; justify-self:end; text-align:right; }
    .orv-status{ grid-column:1 / -1; }
    .orv-status{ display:flex; gap:10px; align-items:baseline; flex-wrap:wrap; }
    .orv-go{ grid-column:1 / -1; min-width:0; }
    .orv-go .cta-deal{ width:100%; }
    .orv-variant{ white-space:normal; }
    .offer-stack-divider{ padding:0 2px; margin:6px 0 0; }
}

/* =========================================================================
   UX polish sprint — 2026-08-02
   Footer (four columns + copyright) and the FAQ page.

   Appended rather than edited in place, matching how every earlier era of
   this stylesheet layered itself: the v4 block at ~6013 already re-points
   .footer-grid, so a rule here is the last word without having to unpick
   which of three earlier definitions is currently winning.
   ========================================================================= */

/* --- Footer -------------------------------------------------------------
   Was three stacked full-width sections; on a phone that meant scrolling the
   whole explainer to reach the legal bar. Four columns from 1000px, two from
   640px, one below — and the link lists are dense on purpose, because a list
   of destinations is scanned, not read. */
.footer-grid{
    grid-template-columns:1.6fr 1fr 1fr 1.4fr;
    gap:var(--sp-6) var(--sp-5);
    align-items:start;
}

/* The footer's two link columns are <nav> landmarks, and they briefly needed a
   reset here: the header was styled with a bare `nav{}` element selector —
   flex, centred, sticky, padded, border-bottom — which every <nav> in the
   document inherited, so these columns rendered as centred flex ROWS with the
   heading beside its own list and a stray hairline under it.

   That selector is now scoped to `nav.site-nav` at the source (and guarded by
   tests/test_nav_scope.py), so the reset is deleted rather than left sitting
   here inert, describing a bug that no longer exists. */
.footer-col > .eyebrow{
    margin:0 0 var(--sp-3);
}

/* The two prose columns keep a readable measure; the link columns must not
   inherit it or their items wrap at odd places. */
.site-footer .footer-col--about p,
.site-footer .footer-col--news p{
    max-width:42ch;
}

.footer-links{
    list-style:none;
    margin:0;
    padding:0;
    display:grid;
    gap:var(--sp-2);
}

.footer-links a{
    color:var(--lr-muted);
    font-weight:var(--fw-med);
    font-size:var(--fs-sm);
    text-decoration:none;
    transition:color var(--dur-fast) var(--ease);
}

.footer-links a:hover,
.footer-links a:focus-visible{
    color:var(--lr-text);
}

.footer-bar-links{
    display:flex;
    align-items:center;
    gap:var(--sp-5);
    flex-wrap:wrap;
}

/* Pushed to its own line on wide screens so the legal links and the notice
   never compete for the same optical row. */
.footer-copy{
    flex-basis:100%;
    margin:var(--sp-3) 0 0;
    font-size:var(--fs-xs);
    color:var(--lr-dim);
}

@media (max-width: 1000px){
    .footer-grid{
        grid-template-columns:1fr 1fr;
        gap:var(--sp-6) var(--sp-5);
    }
    /* The explainer and the newsletter each want the full width at this size;
       the two link columns sit side by side between them. */
    .footer-col--about,
    .footer-col--news{
        grid-column:1 / -1;
    }
}

@media (max-width: 640px){
    .footer-grid{
        grid-template-columns:1fr;
        gap:var(--sp-6);
    }

    .site-footer{
        padding:var(--sp-7) var(--sp-4) var(--sp-5);
    }

    /* The dock was 82px tall with 44px icons and 14px gaps — with seven
       platforms configured that is wider than a phone, so it scrolled the
       page sideways. Wrapping is the fix; the magnify is a pointer affordance
       and there is no pointer here anyway.

       38px icons with a 10px gap fit all seven across a 390px phone
       (7×38 + 6×10 = 326 inside a ~358px column). At 42/12 the seventh
       wrapped onto a row of its own, which reads as a mistake rather than a
       second row. wrap stays on as the backstop for an eighth platform, and
       38px is still above the 32px comfortable-target floor for a
       decorative, duplicated link. */
    .magnetic-dock{
        height:auto;
        flex-wrap:wrap;
        padding:var(--sp-3);
        gap:10px;
        width:100%;
        justify-content:flex-start;
    }

    .magnetic-dock .dock-icon{
        width:38px;
    }

    .footer-bar{
        gap:var(--sp-3);
    }

    .footer-bar-links{
        gap:var(--sp-4);
    }
}

/* --- FAQ ----------------------------------------------------------------
   <details>/<summary>, so the answers stay findable by the browser's own
   in-page search and work with JS off. */
.faq-item{
    border-top:1px solid var(--lr-line-soft);
}

.faq-item:last-of-type{
    border-bottom:1px solid var(--lr-line-soft);
}

.faq-item > summary{
    display:flex;
    align-items:center;
    gap:var(--sp-4);
    padding:var(--sp-4) 0;
    cursor:pointer;
    font-size:var(--fs-md);
    font-weight:var(--fw-semi);
    color:var(--lr-text);
    line-height:var(--lh-ui);
    list-style:none;          /* Firefox */
}

.faq-item > summary::-webkit-details-marker{ display:none; }

/* One marker, drawn by us, so it is identical across browsers and can rotate.
   ::after (not ::before) keeps it at the end of the row without a grid. */
.faq-item > summary::after{
    content:"";
    margin-left:auto;
    flex:none;
    width:9px;
    height:9px;
    border-right:2px solid var(--lr-dim);
    border-bottom:2px solid var(--lr-dim);
    transform:rotate(45deg);
    transform-origin:center;
    transition:transform var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.faq-item[open] > summary::after{
    transform:rotate(-135deg);
    border-color:var(--lr-accent-deal-text);
}

.faq-item > summary:hover,
.faq-item > summary:focus-visible{
    color:var(--lr-accent-deal-text);
    outline:none;
}

.faq-answer{
    padding:0 0 var(--sp-5);
    max-width:70ch;
}

.faq-answer p{
    color:var(--lr-soft);
    line-height:var(--lh-prose);
    margin:0 0 var(--sp-3);
}

.faq-answer p:last-child{ margin-bottom:0; }

.faq-units{
    list-style:none;
    margin:var(--sp-4) 0;
    padding:0;
    display:grid;
    grid-template-columns:repeat(auto-fit, minmax(190px, 1fr));
    gap:var(--sp-2);
}

.faq-units li{
    display:flex;
    justify-content:space-between;
    align-items:baseline;
    gap:var(--sp-3);
    padding:var(--sp-3) var(--sp-4);
    border:1px solid var(--lr-line-soft);
    border-radius:var(--r-md);
    background:var(--lr-surface);
    font-size:var(--fs-sm);
}

.faq-units b{ color:var(--lr-text); }
.faq-units span{ color:var(--lr-muted); white-space:nowrap; }

.faq-cta{
    margin-top:var(--sp-6);
}

@media (max-width: 640px){
    .faq-item > summary{
        font-size:var(--fs-base);
        gap:var(--sp-3);
    }

    .faq-units{
        grid-template-columns:1fr;
    }
}

/* ---------------------------------------------------------------------------
   Product families (2026-08-03)

   The board shows one row per product line and groups its offers by flavour.
   Everything here is layout for that second level; the row itself is unchanged
   markup, so a family row and a plain row still line up in the same grid.
   --------------------------------------------------------------------------- */

.lb-group-toggle{
    display:inline-flex;
    gap:2px;
    margin:0 0 10px;
    padding:2px;
    border:1px solid var(--lr-line);
    border-radius:999px;
    background:var(--lr-surface);
}

.lb-group-btn{
    padding:6px 14px;
    border-radius:999px;
    color:var(--lr-muted);
    font-size:12px;
    font-weight:700;
    text-decoration:none;
    white-space:nowrap;
}

.lb-group-btn:hover{
    color:var(--lr-text);
}

.lb-group-btn.is-active{
    background:var(--lr-surface-2);
    color:var(--lr-text);
    box-shadow:inset 0 0 0 1px var(--lr-line);
}

.lb-flavour-note{
    margin:0 0 6px;
    padding:0 10px;
    color:var(--lr-muted);
    font-size:11px;
    font-weight:700;
    letter-spacing:.6px;
    text-transform:uppercase;
}

.lb-flavour{
    margin:0 0 10px;
    border-top:1px solid var(--lr-line);
}

.lb-flavour:first-of-type{
    border-top:0;
}

.lb-flavour-head{
    display:flex;
    align-items:baseline;
    gap:10px;
    margin:0;
    padding:10px 10px 4px;
    font-size:13px;
    font-weight:800;
}

.lb-flavour-head a{
    color:var(--lr-text);
    text-decoration:none;
}

.lb-flavour-head a:hover{
    text-decoration:underline;
}

/* Deliberately NOT crimson. The v3 brand accent is reserved for deals,
   savings and CTAs; a flavour's cost per unit is neither, and tinting it
   would make every flavour read as a discount. */
.lb-flavour-value{
    color:var(--lr-text);
    font-variant-numeric:tabular-nums;
}

.lb-flavour-count{
    margin-left:auto;
    color:var(--lr-muted);
    font-size:11px;
    font-weight:600;
}

@media (max-width:640px){
    .lb-group-toggle{
        width:100%;
    }

    .lb-group-btn{
        flex:1;
        text-align:center;
    }

    .lb-flavour-count{
        display:none;
    }
}

/* Public shopping navigation: one Browse menu plus direct high-value links. */
.nav-sheet-head{display:none}

@media (min-width:1180px){
    .nav-group--browse{position:relative}
    .nav-primary-link{white-space:nowrap}
}

@media (max-width:1179px){
    .nav-sheet-head{
        display:flex;
        align-items:center;
        justify-content:space-between;
        min-height:48px;
        margin-bottom:10px;
        padding-bottom:12px;
        border-bottom:1px solid var(--lr-line);
        color:var(--lr-text);
        font-weight:800;
        letter-spacing:.08em;
        text-transform:uppercase;
    }

    .nav-sheet-close{
        display:inline-flex;
        align-items:center;
        justify-content:center;
        width:44px;
        height:44px;
        margin:-8px -8px -8px 0;
        padding:0;
        border:1px solid transparent;
        border-radius:8px;
        background:transparent;
        color:var(--lr-text);
        cursor:pointer;
    }

    .nav-sheet-close:is(:hover, :focus-visible){
        border-color:var(--lr-dim);
        background:var(--lr-surface-2);
        outline:none;
    }

    .nav-primary-link{
        display:flex !important;
        align-items:center;
        min-height:44px;
        border-bottom:1px solid var(--lr-line);
    }

    .nav-group--browse + .nav-primary-link{
        margin-top:18px;
        padding-top:4px !important;
        border-top:1px solid var(--lr-line);
    }
}
/* Brand discovery and editorial storefronts (2026-08-12). */
.home-brands{padding-top:var(--sp-7)}
.brand-rail{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(180px,220px);gap:12px;overflow-x:auto;scroll-snap-type:x mandatory;padding:3px 2px 12px;scrollbar-width:thin;overscroll-behavior-inline:contain}
.brand-card{scroll-snap-align:start;min-width:0;border:1px solid var(--lr-line);border-radius:14px;background:var(--lr-surface);overflow:hidden}
.brand-card-face{display:grid;grid-template-rows:68px auto 1fr auto;gap:7px;height:100%;padding:13px;color:inherit;text-decoration:none}
.brand-card-logo{display:flex;align-items:center;justify-content:center;border-radius:9px;background:#fff;color:#161616;padding:9px;text-align:center}
.brand-card-logo img{max-width:100%;max-height:48px;object-fit:contain}.brand-card-logo strong{font-size:1rem}
.brand-card-name{font-weight:800;font-size:.94rem}.brand-card-copy{color:var(--lr-dim);font-size:.78rem;line-height:1.35}.brand-card .btn{justify-self:start;padding:7px 10px;font-size:.74rem}
.entity-search-results{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin:0 0 28px}
.entity-search-results a{display:grid;gap:3px;padding:16px;border:1px solid var(--lr-line);border-radius:14px;text-decoration:none;color:inherit;background:var(--lr-surface)}
.entity-search-results span{font-size:.7rem;text-transform:uppercase;letter-spacing:.1em;color:var(--lr-dim)}.entity-search-results b{font-size:.8rem;color:var(--lr-accent-deal-text)}
.search-suggestion-action{margin-left:auto!important;font-size:.72rem!important;color:var(--lr-accent-deal-text)!important}
.brand-hero{position:relative;min-height:220px;border-radius:18px;overflow:hidden;background:linear-gradient(130deg,#161719,#3b3d42);color:#fff;margin-bottom:8px}
.brand-storefront--amber .brand-hero{background:linear-gradient(130deg,#18130b,#8f5b05)}.brand-storefront--green .brand-hero{background:linear-gradient(130deg,#0c1812,#17603b)}.brand-storefront--violet .brand-hero{background:linear-gradient(130deg,#140d1c,#5d2b81)}
.brand-hero-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:.32}.brand-hero-content{position:relative;z-index:1;display:flex;align-items:center;gap:22px;min-height:220px;padding:clamp(20px,3.5vw,40px);background:linear-gradient(90deg,rgba(0,0,0,.78),rgba(0,0,0,.15))}
.brand-hero-logo{width:clamp(92px,13vw,145px);max-height:92px;object-fit:contain;background:#fff;border-radius:12px;padding:12px}.brand-hero h1{font-size:clamp(1.8rem,3.5vw,3.25rem);margin:.15rem 0 .45rem}.brand-hero-description{max-width:650px;line-height:1.48;font-size:.94rem}.brand-official{color:#fff}.brand-source{text-align:right}
.brand-facts{margin:18px 0;display:grid;grid-template-columns:repeat(4,1fr)}.brand-facts li{list-style:none;display:grid;text-align:center;padding:12px;border-right:1px solid var(--lr-line);font-size:.85rem}
.brand-product-rail{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(160px,200px);gap:10px;overflow-x:auto;padding-bottom:8px}.brand-product-card{display:grid;gap:6px;color:inherit;text-decoration:none;border:1px solid var(--lr-line);border-radius:11px;padding:11px;font-size:.86rem}.brand-product-card img{width:64px;height:64px;object-fit:contain;margin:auto}.brand-product-card span,.brand-catalog small{color:var(--lr-dim)}
.brand-catalog{display:grid;gap:8px}.brand-catalog>a{display:grid;grid-template-columns:72px 1fr auto;align-items:center;gap:14px;padding:12px;border:1px solid var(--lr-line);border-radius:12px;color:inherit;text-decoration:none}.brand-catalog img{width:72px;height:72px;object-fit:contain}.brand-catalog span{display:grid;gap:4px}.entity-link-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}.entity-link-grid a{display:grid;padding:16px;border:1px solid var(--lr-line);border-radius:12px;color:inherit;text-decoration:none}.entity-link-grid span{color:var(--lr-dim);font-size:.84rem}
.admin-form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}.admin-form-grid label{display:grid;gap:6px}.admin-form-grid .span-2{grid-column:1/-1}.brand-admin-form fieldset{display:flex;flex-wrap:wrap;gap:14px;margin:20px 0;padding:16px;border:1px solid var(--lr-line)}
.ret-hero-full-name,.ret-mark-name{font-size:.72rem;font-weight:800;line-height:1.1;text-align:center;overflow-wrap:anywhere}
.brand-mark-name{font-size:.68rem;font-weight:800;line-height:1.05;text-align:center;overflow-wrap:anywhere}.disc-hero{padding-block:clamp(22px,4vw,44px)}
.ret-hero{min-height:220px}.ret-hero-body{padding-block:28px}.ret-hero-mark{width:82px;height:82px}.ret-hero-name{font-size:clamp(1.65rem,3.5vw,3rem)}
@media(max-width:760px){.brand-rail{grid-auto-columns:minmax(170px,72%)}.brand-hero-content{align-items:flex-start;flex-direction:column;gap:14px}.brand-hero-logo{width:110px}.brand-facts{grid-template-columns:repeat(2,1fr);padding:0}.brand-product-rail{grid-auto-columns:68%}.admin-form-grid{grid-template-columns:1fr}.admin-form-grid .span-2{grid-column:auto}}

/* Family detail pages reuse the product hero above, then present the second
   level of the catalogue as quiet nested surfaces: price group, flavour,
   exact retailer offer. */
.family-offers{
    width:min(1180px, calc(100% - 40px));
    margin:48px auto 80px;
}

.family-offers > h2{
    margin:0 0 18px;
    font-size:clamp(24px, 3vw, 34px);
}

.family-price-group{
    display:grid;
    gap:12px;
    margin-top:16px;
    padding:20px;
    border:1px solid var(--lr-line);
    border-radius:12px;
    background:var(--lr-surface);
}

.family-price-group.is-selected{
    border-color:var(--lr-accent-deal-text);
    box-shadow:inset 3px 0 0 var(--lr-accent-deal);
}

.family-price-group > h3{
    margin:0 0 2px;
    font-size:18px;
    font-variant-numeric:tabular-nums;
}

.family-flavor{
    padding:16px;
    border:1px solid var(--lr-line);
    border-radius:10px;
    background:var(--lr-surface-2);
}

.family-flavor h4{
    margin:0;
    font-size:16px;
}

.family-flavor > p{
    margin:4px 0 12px;
    color:var(--lr-muted);
    font-size:12px;
}

.family-offers .offer-list{
    display:grid;
    gap:7px;
    margin:0;
    padding:0;
    list-style:none;
}

.family-offers .offer-list li{
    display:grid;
    grid-template-columns:minmax(0, 1fr) auto auto;
    align-items:center;
    gap:14px;
    min-height:48px;
    padding:8px 10px 8px 14px;
    border:1px solid var(--lr-line);
    border-radius:8px;
    background:var(--lr-bg);
}

.family-offers .offer-list li > span{
    min-width:0;
    overflow-wrap:anywhere;
    color:var(--lr-soft);
}

.family-offers .offer-list strong{
    font-variant-numeric:tabular-nums;
}

.family-offers .offer-list a{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    min-height:34px;
    padding:7px 12px;
    border-radius:7px;
    background:var(--lr-accent-deal);
    color:white;
    font-size:12px;
    font-weight:800;
    text-decoration:none;
}

.family-offers .offer-list a:hover{
    background:var(--lr-accent-deal-hover);
}

/* Family counts are longer than the legacy "N offers" label. Give the final
   desktop track enough room for the complete summary instead of clipping it. */
@media (min-width:769px){
    .lb-head,
    .lb-summary{
        grid-template-columns:44px 64px minmax(0,1fr) 132px 168px 104px 152px;
    }

    .lb-row.no-rank .lb-summary{
        grid-template-columns:64px minmax(0,1fr) 132px 168px 104px 152px;
    }
}

@media (max-width:640px){
    .family-offers{
        width:calc(100% - 32px);
        margin:32px auto 56px;
    }

    .family-price-group{
        padding:12px;
    }

    .family-flavor{
        padding:12px;
    }

    .family-offers .offer-list li{
        grid-template-columns:minmax(0, 1fr) auto;
        gap:8px 12px;
        padding:10px;
    }

    .family-offers .offer-list a{
        grid-column:1 / -1;
        width:100%;
        min-height:40px;
    }
}

/* Public shopping system — shared image-led cards and homepage discovery. */
.home-products,
.home-faq{
    width:min(1180px, calc(100% - 40px));
    margin:72px auto 0;
}

.product-card-grid{
    display:grid;
    grid-template-columns:repeat(auto-fit, minmax(min(100%, 190px), 1fr));
    gap:16px;
}

.product-card-grid--featured{
    grid-template-columns:repeat(auto-fit, minmax(min(100%, 210px), 1fr));
}

.product-card-rail{
    display:grid;
    grid-auto-flow:column;
    grid-auto-columns:minmax(210px, 260px);
    gap:14px;
    overflow-x:auto;
    overscroll-behavior-inline:contain;
    scroll-snap-type:x mandatory;
    scroll-padding-inline:2px;
    padding:2px 2px 14px;
}

.product-card{
    min-width:0;
    height:100%;
    overflow:hidden;
    border:1px solid var(--lr-line);
    border-radius:12px;
    background:var(--lr-surface);
    box-shadow:0 12px 30px rgba(0,0,0,.16);
    scroll-snap-align:start;
}

.product-card__link{
    display:flex;
    height:100%;
    min-width:0;
    flex-direction:column;
    color:inherit;
    text-decoration:none;
}

.product-card__link:focus-visible{
    outline:3px solid var(--lr-accent-deal);
    outline-offset:-3px;
}

.product-card__media{
    position:relative;
    display:grid;
    place-items:center;
    aspect-ratio:1 / 1;
    overflow:hidden;
    border-bottom:1px solid var(--lr-line);
}

.product-card__media img{
    width:100%;
    height:100%;
    padding:16px;
    object-fit:contain;
}

.product-card__media .thumb-empty{
    display:grid;
    width:100% !important;
    height:100% !important;
    place-items:center;
    color:var(--lr-dim);
    font-family:var(--lr-display);
    font-size:clamp(42px, 8vw, 76px);
}

.product-card__badge,
.product-card__rank{
    position:absolute;
    top:10px;
    z-index:1;
}

.product-card__badge{left:10px}
.product-card__rank{
    right:10px;
    padding:5px 7px;
    border-radius:6px;
    background:rgba(0,0,0,.78);
    color:#fff;
    font-size:12px;
    font-weight:800;
}

.product-card__body{
    display:flex;
    min-width:0;
    flex:1;
    flex-direction:column;
    gap:5px;
    padding:14px;
}

.product-card__brand,
.product-card__category{
    color:var(--lr-dim);
    font-size:11px;
    font-weight:750;
    letter-spacing:.08em;
    text-transform:uppercase;
}

.product-card__name{
    display:-webkit-box;
    overflow:hidden;
    color:var(--lr-text);
    font-size:15px;
    line-height:1.3;
    overflow-wrap:anywhere;
    -webkit-box-orient:vertical;
    -webkit-line-clamp:2;
}

.product-card__variant{
    color:var(--lr-muted);
    font-size:12px;
}

.product-card__facts{
    display:flex;
    align-items:baseline;
    justify-content:space-between;
    gap:8px;
    margin-top:auto;
    padding-top:9px;
}

.product-card__price{color:var(--lr-text);font-size:20px;font-variant-numeric:tabular-nums}
.product-card__metric{color:var(--lr-soft);font-size:12px;font-weight:700;text-align:right}
.product-card__meta{display:flex;gap:5px;color:var(--lr-dim);font-size:11px}
.product-card__unavailable{color:var(--lr-muted);font-size:12px;font-weight:700}
.product-card.is-unavailable{opacity:.72}
.product-card-kicker{display:block;margin:0 0 7px;color:var(--lr-soft);font-size:11px;font-weight:800;letter-spacing:.1em;text-transform:uppercase}

.home-faq-grid{
    display:grid;
    grid-template-columns:repeat(3, minmax(0, 1fr));
    gap:14px;
}

.home-faq-grid article{
    padding:20px;
    border:1px solid var(--lr-line);
    border-radius:10px;
    background:var(--lr-surface);
}

.home-faq-grid h3{margin:0 0 8px;color:var(--lr-text);font-size:16px}
.home-faq-grid p{margin:0;color:var(--lr-muted);line-height:1.6}

@media (max-width:768px){
    .home-products,
    .home-faq{width:min(100% - 32px, 1180px);margin-top:52px}
    .home-faq-grid{grid-template-columns:1fr}
}

@media (max-width:480px){
    .product-card-grid{grid-template-columns:repeat(2, minmax(0, 1fr));gap:10px}
    .product-card__body{padding:11px}
    .product-card__price{font-size:17px}
    .product-card__facts{align-items:flex-start;flex-direction:column;gap:3px}
    .product-card__metric{text-align:left}
    .product-card-rail{grid-auto-columns:minmax(190px, 74vw)}
}

/* ========================================================================
   Public visual system — compact, consistent shopping surfaces (2026-08-16)
   ======================================================================== */
:root{
    --public-header-height:64px;
    --public-gutter:clamp(16px, 3vw, 40px);
    --public-section:clamp(40px, 6vw, 68px);
    --public-card-pad:clamp(12px, 1.4vw, 16px);
    --public-card-gap:clamp(10px, 1.4vw, 16px);
    --public-control:42px;
    --public-control-lg:46px;
    --public-radius-sm:8px;
    --public-radius:12px;
    --public-radius-lg:16px;
    --public-image-surface:linear-gradient(145deg,#181719,#0d0d0e);
}

html{max-width:100%;overflow-x:clip;scroll-padding-top:calc(var(--public-header-height) + 12px)}
[id]{scroll-margin-top:calc(var(--public-header-height) + 12px)}
.site-nav{min-height:var(--public-header-height)}
.site-nav .nav-logo img{width:auto;max-height:38px}

/* One restrained public control language. The modifier classes are reusable
   even where legacy markup still carries .btn--ghost or .btn--sm. */
.btn{min-height:var(--public-control);padding:0 16px;border-radius:var(--public-radius-sm);font-size:13px;font-weight:750;line-height:1.15}
.btn--lg{min-height:var(--public-control-lg);padding-inline:20px}
.btn--secondary,.btn--ghost{border:1px solid var(--lr-dim);background:transparent;color:var(--lr-text)}
.btn--tertiary{min-height:36px;padding-inline:8px;border:0;background:transparent;color:var(--lr-soft)}
.btn--icon{width:40px;min-height:40px;padding:0;border-radius:50%}
.btn--chip{min-height:36px;padding-inline:13px;border:1px solid var(--lr-line);border-radius:var(--r-pill);background:var(--lr-surface);font-size:12px}
.btn:is(:hover,:focus-visible),.watch-btn:is(:hover,:focus-visible){transform:none;filter:brightness(1.08)}
.btn[disabled],.btn[aria-disabled="true"],.btn.is-loading{cursor:not-allowed;opacity:.55}

/* Header clearance is flow-based. ResizeObserver only publishes the real
   sticky height for anchors and any browser restoring a scrolled position. */
.home-hero{height:auto;min-height:0;padding:clamp(28px,5vw,52px) var(--public-gutter) clamp(38px,6vw,64px)}
.home-hero-copy{position:relative;inset:auto;width:min(1180px,100%);max-width:none;margin:0 auto}
.home-hero-copy .sec-kick{margin:0 0 8px;font-size:12px;letter-spacing:.08em}
.home-hero-copy h1{max-width:670px;margin:0;font-size:clamp(36px,5.2vw,60px);line-height:1.02}
.home-hero-text{max-width:56ch;margin:12px 0 0;font-size:clamp(14px,1.3vw,16px);line-height:1.5}
.home-trust{width:min(1180px,calc(100% - 2*var(--public-gutter)));margin:0 auto;padding:12px 0;font-size:11px}
.home-products,.home-cats,.home-brands,.home-faq{width:min(1180px,calc(100% - 2*var(--public-gutter)));margin:var(--public-section) auto 0;padding:0}
.home-today-deals{margin-top:clamp(24px,3vw,36px)}
.home-sec-head,.section-heading{gap:12px;margin-bottom:18px}
.eyebrow,.sec-kick{font-size:11px;letter-spacing:.08em;line-height:1.25}
.eyebrow-detail{color:var(--lr-dim);font-family:var(--lr-display);font-weight:500;letter-spacing:0;text-transform:none;white-space:nowrap}
.section-title{font-size:clamp(24px,3vw,34px);line-height:1.1}
.link-action{font-size:12px}

/* Product cards: five stable regions, equal tracks, no content-sized widths. */
.product-card-grid,.product-card-grid--featured{grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:var(--public-card-gap)}
.product-card-grid--featured:has(>:only-child){grid-template-columns:minmax(240px,380px)}
.product-card-grid--featured:has(>:nth-child(2):last-child){grid-template-columns:repeat(2,minmax(240px,380px))}
.product-card-rail{grid-template-columns:none;grid-auto-columns:224px;gap:var(--public-card-gap);padding:2px var(--public-gutter) 12px;margin-inline:calc(-1*var(--public-gutter));scroll-padding-inline:var(--public-gutter)}
.product-card{min-height:360px;border-radius:var(--public-radius);box-shadow:none}
.product-card__link{display:grid;grid-template-rows:176px minmax(0,1fr)}
/* .product-card__media always also carries .image-bg-black (see
   _product_card.html) whose !important always wins, so this background is
   belt-and-suspenders there — dead, but harmless. It's the real background
   for .product-image-frame's other use (the category-shelf thumb, which
   does NOT carry image-bg-black) and for .brand-logo-frame. */
.product-card__media,.product-image-frame,.brand-logo-frame{background:var(--public-image-surface);border:1px solid rgba(255,255,255,.055)}
.product-card__media{aspect-ratio:auto;border-width:0 0 1px}
.product-card__media img{width:100%;height:100%;padding:18px;object-fit:contain;filter:drop-shadow(0 8px 12px rgba(0,0,0,.25))}
/* The JOCKO source photo cannot be edge-cut without erasing package details.
   Keep it reversible and make its light studio field an intentional plate. */
img[src*="/324-1133e32d.jpg"]{border-radius:8px;background:#c9c6c0;filter:brightness(.72) contrast(1.06) saturate(.92)}
.product-card__body{display:grid;grid-template-rows:minmax(57px,auto) minmax(34px,auto) 36px 18px;gap:7px;padding:var(--public-card-pad)}
.product-card__identity,.product-card__details{display:grid;min-width:0;align-content:start;gap:3px}
.product-card__name{min-height:2.6em;font-size:14px;line-height:1.3;-webkit-line-clamp:2}
.product-card__brand{min-height:1.25em;overflow-wrap:anywhere}
.product-card__category,.product-card__variant{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.product-card__price-row{display:grid;grid-template-columns:minmax(72px,auto) minmax(76px,1fr);align-items:end;gap:10px;margin:0;padding:0}
.product-card__price{font-size:19px;line-height:1;white-space:nowrap}
.product-card__metric{min-width:0;font-size:11px;line-height:1.2;text-align:right;overflow-wrap:normal}
.product-card__footer{align-self:end;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.product-card:is(:hover,:focus-within){border-color:var(--lr-dim);background:var(--lr-surface-2)}

/* Category cards: image, identity, one independent metadata line. */
.cat-shelf{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:var(--public-card-gap)}
.cat-card{grid-template-rows:88px minmax(62px,auto);gap:12px;min-height:176px;padding:12px;border-radius:var(--public-radius)}
.cat-card-shot{height:88px;margin:0;background:var(--public-image-surface)}
.cat-card-shot img{max-width:78%;max-height:78%;object-fit:contain}
.cat-card__identity{display:grid;align-content:start;gap:6px;min-width:0}
.cat-card-label{font-size:clamp(16px,1.5vw,20px);line-height:1.15}
.cat-card__meta{display:block;color:var(--lr-muted);font-family:var(--lr-display);font-size:11px;line-height:1.35;white-space:normal}

/* Brand cards: quiet adaptive logo plate, identity, pinned footer. */
.brand-rail{box-sizing:border-box;grid-auto-columns:190px;gap:var(--public-card-gap);padding:2px var(--public-gutter) 12px;margin-inline:calc(-1*var(--public-gutter));scroll-padding-inline:var(--public-gutter)}
.brand-card{display:grid;grid-template-rows:68px minmax(62px,1fr) 34px;gap:10px;min-height:190px;padding:12px;border-radius:var(--public-radius);color:inherit;text-decoration:none}
.brand-card-logo{min-height:68px;padding:10px;border-radius:var(--public-radius-sm);background:linear-gradient(145deg,#2a292b,#171719);color:var(--lr-text)}
.brand-card-logo img{width:100%;height:48px;max-height:48px;padding:2px;object-fit:contain;filter:drop-shadow(0 1px 1px rgba(0,0,0,.5))}
.brand-card__identity{display:grid;align-content:start;gap:5px;min-width:0}
.brand-card-name{display:-webkit-box;overflow:hidden;min-height:1.25em;font-size:14px;line-height:1.25;-webkit-box-orient:vertical;-webkit-line-clamp:2}
.brand-card-copy{font-size:11px;line-height:1.35}
.brand-card__footer{align-self:end;padding-top:9px;border-top:1px solid var(--lr-line);white-space:nowrap}
.brand-facts{padding:0}

/* Compact non-modal nudge; safe-area aware and never a phone-screen blocker. */
.signup-nudge{right:max(12px,env(safe-area-inset-right));bottom:max(12px,env(safe-area-inset-bottom));display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 12px;width:min(430px,calc(100vw - 24px));padding:12px 44px 12px 14px;border-radius:var(--public-radius)}
.nudge-title{grid-column:1;margin:0;font-size:14px}
.nudge-text{grid-column:1;margin:0;font-size:12px;line-height:1.35}
.nudge-cta{grid-column:2;grid-row:1/3;align-self:center;white-space:nowrap}
.nudge-dismiss{top:50%;right:8px;width:32px;height:32px;padding:8px;transform:translateY(-50%)}

/* Product hero: contained media and one coordinated action cluster. */
.product-page{gap:clamp(24px,4vw,48px);padding-top:clamp(24px,4vw,48px)}
.product-thumb{align-self:start;max-height:460px;padding:16px;border-radius:var(--public-radius-lg);background:var(--public-image-surface)}
.product-thumb img{width:100%;height:100%;max-height:420px;padding:clamp(12px,3vw,28px);object-fit:contain;filter:drop-shadow(0 12px 18px rgba(0,0,0,.28))}
.product-info h1{font-size:clamp(30px,4vw,48px);line-height:1.06}
.category-tag{padding:5px 9px;font-size:11px}
.metric-hero{margin-top:14px}
.metric-hero-figure{font-size:clamp(34px,4vw,50px);line-height:1.05}
.product-hero-actions{display:grid;justify-items:start;gap:10px;margin-top:16px}
.product-hero-actions>.deal-cta,.product-hero-actions>.product-buy-cta{width:auto;min-width:190px;margin:0}
.product-buy-cta{width:auto;margin:0}
.product-hero-actions .watch-hero{width:auto;margin:0}
.product-hero-actions .watch-hero-btn{width:auto;min-height:42px;padding-inline:16px}
.product-tools{display:flex;gap:8px;margin-top:12px}
.product-description{max-width:62ch;margin-top:16px}
.product-info,.chip-group,.chip-row{min-width:0;max-width:100%}

/* Editorial blocks use explicit desktop tracks. The legacy spotlight markup
   emits sibling articles, while the trust principles are paragraphs (not the
   older list items), so neither can rely on historical selectors. */
@media (min-width:901px){
    .spotlight-section{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 24px}
    .spotlight-section>.home-sec-head{grid-column:1/-1}
    .spotlight-section>.spotlight-entry{width:100%;min-width:0;margin-top:16px}
    .home-why{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(340px,.8fr);gap:clamp(40px,6vw,96px);align-items:end}
    .home-why>.why-lead{width:100%;margin:0}
    .home-why>.why-points{width:100%;max-width:none;margin:0}
}
.why-points p{display:grid;grid-template-columns:minmax(190px,.85fr) minmax(0,1fr);gap:16px;margin:0;color:var(--lr-muted);font-size:14px;line-height:1.4}
.why-points p strong{color:var(--lr-text)}
.why-points p span{min-width:0}

/* Leaderboards: denser thumbnails and quiet repeated actions. */
.lb-thumb,.lb-thumb img,.lb-thumb .thumb-empty{width:52px!important;height:52px!important}
.lb-thumb{display:grid;place-items:center;padding:4px;border-radius:var(--public-radius-sm);background:var(--public-image-surface)}
.lb-thumb img{padding:3px;object-fit:contain}
.lb-summary{min-height:82px}
.lb-buy-inline{min-height:36px;padding-inline:12px;background:transparent;border:1px solid var(--lr-accent-deal-line);color:var(--lr-accent-deal-text)}
.lb-name>a{display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:2}
.lb-offer-store{max-width:16ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

@media (max-width:900px){
    :root{--public-header-height:62px;--public-section:44px}
    .cat-shelf{grid-template-columns:repeat(2,minmax(0,1fr))}
    .cat-card:last-child:nth-child(odd){grid-column:auto}
    .product-page{grid-template-columns:minmax(0,38%) minmax(0,1fr)}
}

@media (max-width:640px){
    :root{--public-gutter:14px;--public-section:38px;--public-card-gap:10px}
    .site-nav .nav-logo img{max-height:32px}
    .home-hero{padding:22px var(--public-gutter) 30px}
    .home-hero-copy h1{font-size:clamp(34px,11vw,46px)}
    .home-trust{display:flex;flex-wrap:wrap;gap:3px 8px;padding:10px 0}
    .home-trust-dot{display:none}
    .home-sec-head{align-items:flex-end;margin-bottom:14px}
    .eyebrow-detail{font-size:10px}
    .product-card-grid,.product-card-grid--featured{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
    .product-card{min-height:318px}
    .product-card__link{grid-template-rows:142px minmax(0,1fr)}
    .product-card__body{grid-template-rows:minmax(55px,auto) minmax(31px,auto) 32px 16px;padding:10px}
    .product-card__price-row{grid-template-columns:1fr;gap:3px}
    .product-card__metric{text-align:left;white-space:nowrap}
    .product-card-rail{grid-auto-columns:196px}
    .cat-shelf{grid-template-columns:1fr 1fr}
    .cat-card{grid-template-rows:72px minmax(58px,auto);min-height:150px;padding:10px}
    .cat-card-shot{height:72px}
    .brand-rail{grid-auto-columns:176px}
    .brand-rail,.product-card-rail{width:calc(100% + 2*var(--public-gutter));max-width:100vw}
    .signup-nudge{grid-template-columns:1fr auto;bottom:max(8px,env(safe-area-inset-bottom));width:calc(100vw - 16px);padding:10px 40px 10px 12px}
    .nudge-text{display:none}
    .nudge-cta{grid-row:1;min-height:38px;padding-inline:12px}
    .product-page{display:grid;grid-template-columns:96px minmax(0,1fr);gap:14px;padding-top:18px}
    .product-thumb{grid-column:1;padding:6px;max-height:126px}
    .product-thumb img{max-height:112px;padding:6px}
    .product-info{grid-column:2;min-width:0}
    .product-info h1{font-size:clamp(24px,7vw,32px)}
    .product-hero-actions{display:grid;grid-template-columns:1fr;gap:8px}
    .product-hero-actions>.deal-cta,.product-hero-actions>.product-buy-cta,.product-hero-actions .watch-hero,.product-hero-actions .watch-hero-btn{width:100%;min-width:0}
    .lb-summary{min-height:0;padding-block:12px}
    .lb-offer-store{max-width:20ch}
    .lb-buy-inline{min-height:34px;padding-inline:10px}
    .why-points p{grid-template-columns:1fr;gap:2px}
}

@media (max-width:360px){
    .home-today-deals .drop-nav-btn{display:none!important}
    .home-today-deals .drop-rail-nav,.home-today-deals .link-action{white-space:nowrap}
    .home-popular .eyebrow{white-space:nowrap;font-size:10px}
    .home-popular .home-sec-head{display:grid;grid-template-columns:1fr;justify-items:start;gap:7px}
    .home-popular .link-action{justify-self:start}
    .product-card-grid,.product-card-grid--featured{grid-template-columns:1fr 1fr}
    .product-card__name{font-size:13px}
    .product-page{grid-template-columns:78px minmax(0,1fr);gap:10px}
}
