/* ===== Design tokens =====

   Navy carries the page; green only ever means money.

   Two palettes have been wrong here. The first ran five saturated hues at once — indigo,
   orange, rose, amber, sky — so nothing read as important. The second fixed that by cutting
   to one green, then handed that green the brand, the links, the CTA fill, the sale badge,
   the savings line and the section markers all at once. One hue is the right idea; making it
   the page's mass is not. A grid of cards came out as three solid green blocks per row.

   The distribution here follows the SHRA landlord portal, which answers the same question:
   a dark, desaturated navy does the structural work — header, hero, footer, primary action,
   active states — and colour appears in small amounts, as thin rules and tinted chips. Green
   is reserved for the two places money is actually discussed: the savings figure and the
   price verdict. What is left is mostly ink and paper, with the accent where the meaning is.

   Every pair below is measured against WCAG 2.1 AA. The first palette shipped a price at
   2.80:1, which is why this comment keeps saying so. */
:root, .admin-shell {
    color-scheme: light;

    /* Neutrals: a cool blue-grey ramp, so navy sits in the same family rather than on top
       of a warm paper that fights it. */
    --c-bg: #f6f7fa;
    --c-surface: #ffffff;
    --c-surface-alt: #eef0f6;
    --c-border: #dde1ea;
    --c-border-strong: #c6ccda;
    --c-text: #2c3550;
    --c-text-strong: #131a2e;   /* prices and headings — the figures that must land first */
    --c-text-muted: #5a6480;
    --c-text-subtle: #6b7390;

    /* Structure. Header, hero, footer, primary action, active states. */
    --c-navy: #233066;
    --c-navy-600: #33437f;
    --c-navy-800: #1b2450;

    /* The solid action fill, and the only text colour legible on it. */
    --c-cta: #233066;
    --c-cta-hover: #1b2450;
    --c-on-cta: #ffffff;

    /*  Money. Links, the savings line, price verdicts — text and tint, never a large fill.
        Links hover to navy rather than to a lighter green, which is how the portal does it
        and stops green spreading on interaction. */
    --c-brand: #00542d;
    --c-brand-hover: #233066;
    --c-brand-soft: #e7f3ec;
    --c-brand-dark: #0a5b33;
    --c-brand-ring: rgba(35, 48, 102, .22);

    /*  The few places that still paint a filled accent surface take the structural colour
        now, not the green. */
    --c-brand-fill: #233066;
    --c-on-brand: #ffffff;

    /* Time pressure only. */
    --c-brass: #8a4b06;
    --c-brass-soft: #fef3e5;

    /* Errors, out of stock, destructive. Never "sale". */
    --c-accent: #92300f;
    --c-accent-soft: #fdece6;

    /* The deep ground: hero, footer, and the page itself in dark mode. */
    --c-ink: #233066;
    --c-on-ink: #ffffff;
    --c-on-ink-muted: #c3cadf;
    --c-on-ink-subtle: #9aa4c4;
    --c-on-ink-accent: #8bc440;   /* the one bright note, used once per surface */

    /*  The brand mark, inlined in MainLayout so it can inherit these. Navy tile, lime arrow;
        in dark mode they swap, because a navy tile on a navy-black page is invisible. */
    --mark-ground: #233066;
    --mark-line: #8bc440;
    --mark-floor: rgba(255, 255, 255, .45);

    /*  One gradient, on the hero only. Everything else is flat — a gradient on a button is
        part of what made both earlier palettes read as a template. */
    --g-hero: linear-gradient(135deg, #1b2450 0%, #233066 55%, #2c3d80 100%);

    /* Type */
    --font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
                  "Helvetica Neue", Arial, "Noto Sans", sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

    /* Geometry — tighter than before, matching the portal's 4 / 8 / 12. */
    --radius-sm: 4px;
    --radius: 8px;
    --radius-lg: 12px;
    --radius-pill: 999px;
    --shadow-sm: 0 1px 2px rgba(19, 26, 46, .06);
    --shadow: 0 6px 20px -6px rgba(19, 26, 46, .18);
    --shadow-lg: 0 12px 34px -10px rgba(19, 26, 46, .28);

    /* Translucent header fill — a token, since it inverts with the theme. */
    --c-header-bg: rgba(255, 255, 255, .90);

    --container: 1200px;
}

/* ===== Dark theme =====

   Two selectors, one palette. The media query covers the reader who has never touched the
   toggle and whose OS is dark; the attribute covers the reader who chose.
   `:not([data-theme="light"])` is what lets an explicit light choice win on a dark OS.

   The values are duplicated because CSS cannot apply one block from two places. If you
   change one, change both; they are kept adjacent so the diff shows it.

   The portal is light-only, so this half is ours. Navy has to lighten to #536abe for the
   action fill: the brand navy on a navy-black page measures 2.71:1 against the background,
   so the button dissolves into it. #536abe is the window where white text still clears
   4.5:1 and the fill still clears 3:1 against the page — the two pull against each other,
   so there is not much room either side. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        color-scheme: dark;
        --c-bg: #0e1220;
        --c-surface: #161c2e;
        --c-surface-alt: #1e2740;
        --c-border: #2a3450;
        --c-border-strong: #3c4869;
        --c-text: #e6e9f2;
        --c-text-strong: #ffffff;
        --c-text-muted: #a2abc4;
        --c-text-subtle: #7b849e;
        --c-navy: #33437f;
        --c-navy-600: #41569f;
        --c-navy-800: #1b2450;
        --c-cta: #536abe;
        --c-cta-hover: #4a60b0;
        --c-on-cta: #ffffff;
        --c-brand: #4ec98a;
        --c-brand-hover: #7fdcab;
        --c-brand-soft: #0f2e22;
        --c-brand-dark: #4ec98a;
        --c-brand-ring: rgba(83, 106, 190, .35);
        --c-brand-fill: #536abe;
        --c-on-brand: #ffffff;
        --c-brass: #f0a84a;
        --c-brass-soft: #2d2211;
        --c-accent: #f3897a;
        --c-accent-soft: #2f1a17;
        --c-ink: #1b2450;
        --c-on-ink-muted: #c3cadf;
        --c-on-ink-subtle: #9aa4c4;
        --mark-ground: #8bc440;
        --mark-line: #141b3d;
        --mark-floor: rgba(20, 27, 61, .45);
        --g-hero: linear-gradient(135deg, #141b3d 0%, #1b2450 55%, #263566 100%);
        --shadow-sm: 0 1px 2px rgba(0, 0, 0, .40);
        --shadow: 0 6px 20px -6px rgba(0, 0, 0, .50);
        --shadow-lg: 0 12px 34px -10px rgba(0, 0, 0, .60);
        --c-header-bg: rgba(14, 18, 32, .88);
    }
}

:root[data-theme="dark"] {
    color-scheme: dark;
    --c-bg: #0e1220;
    --c-surface: #161c2e;
    --c-surface-alt: #1e2740;
    --c-border: #2a3450;
    --c-border-strong: #3c4869;
    --c-text: #e6e9f2;
    --c-text-strong: #ffffff;
    --c-text-muted: #a2abc4;
    --c-text-subtle: #7b849e;
    --c-navy: #33437f;
    --c-navy-600: #41569f;
    --c-navy-800: #1b2450;
    --c-cta: #536abe;
    --c-cta-hover: #4a60b0;
    --c-on-cta: #ffffff;
    --c-brand: #4ec98a;
    --c-brand-hover: #7fdcab;
    --c-brand-soft: #0f2e22;
    --c-brand-dark: #4ec98a;
    --c-brand-ring: rgba(83, 106, 190, .35);
    --c-brand-fill: #536abe;
    --c-on-brand: #ffffff;
    --c-brass: #f0a84a;
    --c-brass-soft: #2d2211;
    --c-accent: #f3897a;
    --c-accent-soft: #2f1a17;
    --c-ink: #1b2450;
    --c-on-ink-muted: #c3cadf;
    --c-on-ink-subtle: #9aa4c4;
    --mark-ground: #8bc440;
    --mark-line: #141b3d;
    --mark-floor: rgba(20, 27, 61, .45);
    --g-hero: linear-gradient(135deg, #141b3d 0%, #1b2450 55%, #263566 100%);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .40);
    --shadow: 0 6px 20px -6px rgba(0, 0, 0, .50);
    --shadow-lg: 0 12px 34px -10px rgba(0, 0, 0, .60);
    --c-header-bg: rgba(14, 18, 32, .88);
}


/* ===== Reset / base ===== */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
    font-family: var(--font-sans);
    color: var(--c-text);
    background: var(--c-bg);
    line-height: 1.55;
    font-size: 16px;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}
img { max-width: 100%; height: auto; display: block; }
a { color: var(--c-brand); text-decoration: none; }
a:hover { color: var(--c-brand-hover); text-decoration: underline; }

h1, h2, h3, h4 {
    margin: 0 0 .5rem;
    line-height: 1.2;
    color: var(--c-text);
    font-weight: 700;
    letter-spacing: -0.01em;
}
h1 { font-size: clamp(1.75rem, 1.2rem + 1.6vw, 2.5rem); }
h2 { font-size: 1.5rem; margin-top: 2rem; }
h3 { font-size: 1.125rem; }
p  { margin: 0 0 1rem; }
small, .small { font-size: 0.85rem; color: var(--c-text-muted); }

h1:focus { outline: none; }

/* ===== Layout helpers ===== */
.container { max-width: var(--container); margin: 0 auto; padding: 0 1.25rem; }
.stack-sm > * + * { margin-top: 0.5rem; }
.stack > * + * { margin-top: 1rem; }
.stack-lg > * + * { margin-top: 1.75rem; }
.muted { color: var(--c-text-muted); }
.flex { display: flex; }
.flex-between { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.gap-sm { gap: .5rem; } .gap { gap: 1rem; } .gap-lg { gap: 1.5rem; }
.text-center { text-align: center; }

/* ===== Buttons ===== */
.btn {
    display: inline-flex; align-items: center; justify-content: center;
    gap: .5rem;
    font-family: inherit; font-weight: 600; font-size: 0.95rem;
    padding: .625rem 1.125rem;
    border-radius: var(--radius);
    border: 1px solid transparent;
    cursor: pointer;
    transition: background-color .15s, color .15s, border-color .15s, box-shadow .15s, transform .05s;
    text-decoration: none;
    line-height: 1.2;
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--c-brand); outline-offset: 2px; }

.btn-primary {
    /*  Flat and structural. This was a green gradient with a coloured glow under it, which
        put a large saturated block on every card in the grid — the single biggest reason
        the page read as "too green". The action is the navy now; green stayed behind on
        the savings line where it means something. */
    background: var(--c-cta); color: var(--c-on-cta);
}
.btn-primary:hover {
    background: var(--c-cta-hover);
    color: var(--c-on-cta); text-decoration: none;
}

.btn-ghost { background: transparent; color: var(--c-text); border-color: var(--c-border); }
.btn-ghost:hover { background: var(--c-surface-alt); color: var(--c-text); text-decoration: none; }

.btn-link { background: transparent; color: var(--c-brand); padding: 0; border: 0; }
.btn-link:hover { color: var(--c-brand-hover); text-decoration: underline; }

.btn-danger-link { background: transparent; color: var(--c-accent); border: 0; padding: 0; cursor: pointer; font: inherit; }
.btn-danger-link:hover { text-decoration: underline; }

.btn-sm { padding: .375rem .75rem; font-size: .85rem; }
.btn-lg { padding: .875rem 1.5rem; font-size: 1rem; }

/* ===== Cards ===== */
.card {
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}
.card-body { padding: 1.25rem; }
.card-hover { transition: transform .2s cubic-bezier(.2,.8,.3,1.2), box-shadow .2s, border-color .2s; }
.card-hover:hover {
    transform: translateY(-5px);
    box-shadow: var(--shadow-lg);
    border-color: var(--c-navy);
}

/* ===== Badges / pills ===== */
.badge {
    display: inline-flex; align-items: center;
    font-size: .75rem; font-weight: 700; letter-spacing: .02em;
    padding: .2rem .55rem;
    border-radius: var(--radius-pill);
    background: var(--c-surface-alt);
    color: var(--c-text-muted);
    text-transform: uppercase;
}
/* A saving is good news, so the sale badge is the accent rather than the old hot-rose
   gradient — which competed with the orange price directly beneath it. */
.badge-sale {
    /*  Tint and ink rather than a solid fill. A discount badge sits on top of a product
        photo on every card; as a saturated pill it was competing with the image and with
        the button below it. As a tint it still reads as good news and stops shouting. */
    background: var(--c-brand-soft); color: var(--c-brand-dark);
    box-shadow: none;
}
.badge-low  { background: var(--c-brand-soft); color: var(--c-brand-dark); }
.badge-stock { background: var(--c-brand-soft); color: var(--c-brand-dark); }
.badge-out  { background: var(--c-accent-soft); color: var(--c-accent); }
.badge-ends { background: var(--c-brass-soft); color: var(--c-brass); }

/* ===== Forms ===== */
.input, input[type=text], input[type=email], input[type=password], input[type=number], input[type=search],
select, textarea {
    font-family: inherit; font-size: 1rem;
    width: 100%;
    padding: .625rem .85rem;
    background: var(--c-surface);
    color: var(--c-text);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--radius);
    transition: border-color .15s, box-shadow .15s;
}
.input:focus, input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--c-brand);
    box-shadow: 0 0 0 3px var(--c-brand-ring);
}
label.field { display: block; margin-bottom: 1rem; }
label.field > span { display: block; font-weight: 600; font-size: .9rem; margin-bottom: .35rem; color: var(--c-text); }

.valid.modified:not([type=checkbox]) { outline: none; border-color: var(--c-brand); }
.invalid { border-color: var(--c-accent); }
.validation-message { color: var(--c-accent); font-size: .85rem; margin-top: .25rem; }

/* ===== Tables ===== */
.table {
    width: 100%; border-collapse: collapse;
    background: var(--c-surface);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    border: 1px solid var(--c-border);
}
.table th, .table td { padding: .85rem 1rem; text-align: left; border-bottom: 1px solid var(--c-border); }
.table th { background: var(--c-surface-alt); font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; color: var(--c-text-muted); font-weight: 600; }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr.is-best { background: var(--c-brand-soft); }
.table tbody tr.is-best td { font-weight: 600; }

/* ===== Alerts ===== */
.alert { padding: .85rem 1rem; border-radius: var(--radius); border: 1px solid; }
.alert-success { background: var(--c-brand-soft); border-color: var(--c-brand); color: var(--c-brand-dark); }
.alert-error { background: var(--c-accent-soft); border-color: var(--c-accent); color: var(--c-accent); }

/* ===== Hero ===== */
/*  The hero used to be a purple gradient that animated its background position forever,
    with four fake sparkle dots and a floating lavender blob. Three separate tells of a
    template, two of them running animation frames on every visit for no information.

    What it is now: a ledger. Ruled lines on deep ink, one soft green glow where the
    headline sits, and nothing moving. The ruling is drawn with repeating-linear-gradient
    rather than an image, so it costs no request and scales with the box. */
.hero {
    position: relative;
    background: var(--g-hero);
    color: var(--c-on-ink);
    border-radius: 20px;
    padding: clamp(2.5rem, 5vw, 4.5rem) clamp(1.5rem, 4vw, 3.5rem);
    margin: 1.5rem 0 2.25rem;
    overflow: hidden;
    box-shadow: 0 24px 48px -16px rgba(6, 22, 18, .55);
}
.hero::before {
    content: ""; position: absolute; inset: 0;
    background-image:
        /* the glow, behind the headline */
        radial-gradient(720px circle at 18% 8%, rgba(139, 196, 64, .20) 0, transparent 55%),
        /* ruled lines, horizontal then vertical */
        repeating-linear-gradient(to bottom, rgba(255,255,255,.05) 0 1px, transparent 1px 34px),
        repeating-linear-gradient(to right, rgba(255,255,255,.035) 0 1px, transparent 1px 34px);
    pointer-events: none;
}
/*  A single hairline rule down the left, the way a ledger column is ruled off. Sits inside
    the padding so it reads as part of the page, not as a border on the box. */
.hero::after {
    content: "";
    position: absolute; left: clamp(.75rem, 2vw, 1.75rem); top: 0; bottom: 0;
    width: 2px;
    background: linear-gradient(to bottom, transparent, rgba(139, 196, 64, .60) 20%,
                                rgba(139, 196, 64, .60) 80%, transparent);
    pointer-events: none;
}
.hero h1 {
    color: var(--c-on-ink); max-width: 22ch;
    font-size: clamp(2.4rem, 1.2rem + 4vw, 4rem);
    font-weight: 800; letter-spacing: -0.03em; line-height: 1.05;
    margin-bottom: .75rem;
}
/*  Was a three-stop gradient clipped to the text. Flat lime instead: the gradient added
    nothing a solid colour does not, and clipped-gradient headlines are one of the more
    recognisable template tells. */
.hero h1 .grad { color: var(--c-on-ink-accent); }
.hero p { color: var(--c-on-ink-muted); max-width: 56ch; font-size: 1.15rem; }
.hero .hero-form { display: flex; gap: .6rem; max-width: 520px; margin-top: 1.75rem; position: relative; }
.hero .hero-form input {
    background: #fff; color: #2c3550; border-color: transparent;
    padding: .8rem 1.1rem; border-radius: var(--radius); font-size: 1rem;
}
/*  On the navy hero the primary button would be navy on navy. The portal answers this with
    a light button on its dark band, which is also the highest-contrast option available —
    so here the fill and the text swap. */
.hero .hero-form .btn-primary {
    background: #fff; color: var(--c-navy-800);
    border-radius: var(--radius);
    padding: .8rem 1.4rem; font-weight: 700; white-space: nowrap;
    box-shadow: none;
}
.hero .hero-form .btn-primary:hover { background: var(--c-surface-alt); color: var(--c-navy-800); }
.hero .hero-msg { margin-top: .85rem; color: #b6dd7a; font-size: .95rem; font-weight: 600; }
.hero .hero-msg.err { color: #ffc9bd; }
.hero-stats {
    display: flex; gap: 2.5rem; margin-top: 2.25rem; position: relative; flex-wrap: wrap;
}
.hero-stats .stat .num {
    display: block; font-size: 1.9rem; font-weight: 800; letter-spacing: -.02em;
    color: var(--c-on-ink-accent);
}
.hero-stats .stat .lbl { color: var(--c-on-ink-subtle); font-size: .85rem; text-transform: uppercase; letter-spacing: .08em; }

/* ===== Product grid ===== */
.product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 1.25rem;
}

.product-card { display: flex; flex-direction: column; }
.product-card .thumb {
    aspect-ratio: 1;
    background: radial-gradient(120% 100% at 50% 0%, var(--c-surface) 0%, var(--c-surface-alt) 100%);
    display: flex;
    align-items: center; justify-content: center; padding: 1rem;
    position: relative; overflow: hidden;
}
/* max-width/height:auto keep the image fluid despite its width/height attributes,
   which are there to reserve layout space and avoid CLS. */
.product-card .thumb img { max-width: 100%; height: auto; max-height: 100%; object-fit: contain; transition: transform .3s ease; }
.product-card:hover .thumb img { transform: scale(1.06); }
.product-card .thumb .badge { position: absolute; top: .75rem; left: .75rem; }
.product-card .body { padding: 1rem 1.25rem 1.25rem; flex: 1; display: flex; flex-direction: column; }
.product-card .brand { font-size: .75rem; text-transform: uppercase; letter-spacing: .05em; color: var(--c-text-muted); }
/* Amazon titles are SEO strings, not names — "Amazon Fire TV Stick 4K Plus (newest model)
   with AI-powered Fire TV Search, Wi-Fi 6, stream hundreds of thousands of movies and
   shows, free & live TV, find shows faster with Alexa+". Left unclamped, one card runs to
   six lines while its neighbour runs to two and the grid loses its baseline. Clamped for
   display only: the product page still shows the full title, which is what a shopper
   checking they have the right item actually needs. */
.product-card .title { font-size: 1rem; font-weight: 600; margin: .25rem 0 .75rem; line-height: 1.35;
    display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden; }
.product-card .title a { color: inherit; }
/* Amazon gives every colour and size its own ASIN, so one product legitimately occupies
   several catalogue rows. The listing pages show one card and say how many it stands for —
   without this the grid reads as duplicated rather than as one product in five colours. */
.product-card .variants { display: inline-block; margin: -.5rem 0 .6rem; font-size: .72rem;
    font-weight: 600; color: var(--c-text-muted); background: var(--c-surface-alt);
    border: 1px solid var(--c-border); border-radius: var(--radius-pill); padding: .1rem .5rem; }
.product-card .title a:hover { color: var(--c-brand); text-decoration: none; }
.product-card .price-row { display: flex; align-items: baseline; gap: .5rem; margin-top: auto; padding-top: .5rem; }
/*  The price carries its weight through type, not hue. It was orange (#f97316), which is
    2.80:1 on white — the least legible thing on the card was the number the card exists
    to show, and it failed AA even at large-text size. Ink puts it at 16.5:1 and leaves
    green free for the saving, which is the part a shopper is actually pleased about. */
.product-card .price { font-size: 1.45rem; font-weight: 800; color: var(--c-text-strong); letter-spacing: -.025em; }
.product-card .was-price { font-size: .95rem; color: var(--c-text-subtle); text-decoration: line-through; }
.product-card .retailer { font-size: .85rem; color: var(--c-text-muted); }
.product-card .savings { margin: .3rem 0 0; font-size: .82rem; font-weight: 700; color: var(--c-brand); }
.product-card .cta { margin-top: 1rem; width: 100%; }

/* ===== Footer ===== */
/*  The footer keeps the ink ground in both themes — it is the same surface as the hero,
    which is what ties the top and bottom of the page together. In dark mode it sits a
    shade above the page rather than below it. */
.site-footer {
    margin-top: 4rem;
    padding: 2.5rem 0 2rem;
    background: var(--c-ink);
    color: var(--c-on-ink-muted);
}
.site-footer a { color: var(--c-on-ink-muted); }
.site-footer a:hover { color: var(--c-on-ink); }
.site-footer h4 { color: var(--c-on-ink); font-size: .9rem; text-transform: uppercase; letter-spacing: .05em; margin-bottom: .75rem; }
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 2rem; margin-bottom: 2rem; }
.footer-brand { font-size: 1.25rem; font-weight: 700; color: var(--c-on-ink); margin-bottom: .5rem; }
.footer-tagline { color: var(--c-on-ink-subtle); font-size: .9rem; max-width: 32ch; }
.footer-bottom { border-top: 1px solid rgba(255,255,255,.1); padding-top: 1.25rem; font-size: .85rem; color: var(--c-on-ink-subtle); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; }
@media (max-width: 720px) {
    .footer-grid { grid-template-columns: 1fr 1fr; }
    .footer-grid > :first-child { grid-column: 1 / -1; }
}

/* ===== Section header ===== */
.section-head { display: flex; align-items: end; justify-content: space-between; gap: 1rem; margin: 2.5rem 0 1.15rem; }
.section-head h2 {
    margin: 0; font-size: 1.65rem; letter-spacing: -.02em;
    position: relative; padding-left: .9rem;
}
.section-head h2::before {
    content: ""; position: absolute; left: 0; top: .18em; bottom: .18em;
    width: 4px; border-radius: 2px; background: var(--c-navy);
}
.section-head .link { font-weight: 700; }

/* ===== Infinite deal feed =====
   The sentinel is both the trigger for the next batch and the no-JS way forward, so it
   stays in the flow and only swaps what it shows. */
.feed-more { position: relative; display: flex; align-items: center; justify-content: center;
              padding: 2rem 0 .5rem; min-height: 3.5rem; }
.feed-more[data-loading] a { visibility: hidden; }
.feed-more[data-loading]::after {
    content: ""; position: absolute;
    width: 22px; height: 22px; border-radius: 50%;
    border: 2px solid var(--c-border-strong); border-top-color: var(--c-brand);
    animation: feed-spin .7s linear infinite;
}
/* Nothing left to fetch: the link would now lead to a page the reader has already seen. */
.feed-more[data-done] { display: none; }
@keyframes feed-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .feed-more[data-loading]::after { animation: none; border-top-color: var(--c-border-strong); }
}

/* ===== Theme toggle =====

   Which glyph shows is decided in CSS from the root attribute, never by script writing
   into the button. That matters here because Blazor's enhanced navigation swaps the
   header's DOM on every page change: anything JS had written into the button would be
   thrown away on the first link click, while <html data-theme> survives untouched. */
.theme-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; padding: 0;
    border-radius: var(--radius-pill);
    border: 1px solid var(--c-border-strong);
    background: var(--c-surface);
    color: var(--c-text-muted);
    cursor: pointer;
    transition: color .15s, border-color .15s, background-color .15s;
}
.theme-toggle:hover { color: var(--c-brand); border-color: var(--c-brand); }
.theme-toggle:focus-visible { outline: 2px solid var(--c-brand); outline-offset: 2px; }
.theme-toggle svg { width: 18px; height: 18px; display: block; }

/* The button offers the theme you would get by pressing it, so on a light page it shows
   a moon. Default (no attribute, light system): offer dark. */
.theme-toggle .i-dark  { display: block; }
.theme-toggle .i-light { display: none; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .theme-toggle .i-dark  { display: none; }
    :root:not([data-theme="light"]) .theme-toggle .i-light { display: block; }
}

/* After the media query on purpose: equal specificity, so source order is what lets an
   explicit choice beat the OS in both directions. */
:root[data-theme="dark"]  .theme-toggle .i-dark  { display: none; }
:root[data-theme="dark"]  .theme-toggle .i-light { display: block; }
:root[data-theme="light"] .theme-toggle .i-dark  { display: block; }
:root[data-theme="light"] .theme-toggle .i-light { display: none; }

/* ===== Motion preferences ===== */
@media (prefers-reduced-motion: reduce) {
    /* The hero's two infinite animations are gone entirely, so there is nothing left here
       to switch off — only the hover transitions, which still need it. */
    .card-hover, .product-card .thumb img { transition: none; }
    .card-hover:hover { transform: none; }
}

/* ===== Blazor error UI ===== */
#blazor-error-ui {
    background: var(--c-ink); color: var(--c-on-ink);
    bottom: 0; box-shadow: 0 -1px 2px rgba(0,0,0,0.2);
    display: none; left: 0; padding: 0.6rem 1.25rem 0.7rem 1.25rem; position: fixed;
    width: 100%; z-index: 1000;
}
#blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.5rem; }
.blazor-error-boundary { background: #92300f; padding: 1rem; color: #fff; border-radius: var(--radius); }
.blazor-error-boundary::after { content: "An error has occurred."; }

/* Price standing vs our own recorded history - see PriceVerdict. Absent until we
   hold enough observations for the claim to be true. */
.product-card .verdict { margin: .3rem 0 0; font-size: .78rem; font-weight: 700; }
.product-card .verdict::before { content: "\25CF"; margin-right: .35rem; font-size: .7em; vertical-align: .1em; }
/*  Four hardcoded hues became three tokens. "Good" used to be blue, which belonged to no
    other element on the site; both positive verdicts are the accent now and the words do
    the distinguishing, which is what they are there for. */
.verdict-best { color: var(--c-brand); }
.verdict-good { color: var(--c-brand); }
.verdict-neutral { color: var(--c-text-muted); }
.verdict-poor { color: var(--c-brass); }
