/* ══════════════════════════════════════════════════════════════════════════
   FRAGRANCE BOX — theme system (light / dark)
   ══════════════════════════════════════════════════════════════════════════
   Added 2026-09-28. Light mode is protected by scoping, not by a claim of
   pure additivity: of 289 style rules, 277 sit under `html[data-theme="dark"]`.
   The other 12 are audited and intentional — the `:root` token definitions
   (which is the token layer, not a component), the `.fb-theme*` theme-control
   component this work introduced, `.nav-actions` (layout only, no colour),
   and `::selection`. The one rule that touches pre-existing light rendering
   is `html { scrollbar-color }` at the scrollbar block, which gives Firefox a
   dark thumb instead of the browser default; Chromium ignores scrollbar-color
   and uses the ::-webkit rules, which are dark-scoped. Nothing here restyles
   a pre-existing light-mode component. The light token values mirror what the
   existing stylesheets already compute, so consuming a token in light is a
   no-op.

   Revert: delete this file plus theme.js and remove one `ob_start` line from
   includes/security.php. Nothing else in the site is touched.

   ── Why a token layer rather than a full rewrite ──────────────────────────
   797 of the site's 1,486 hardcoded colour declarations are already
   "black at some alpha" (rgba(0,0,0,0.08) and friends). That single pattern
   covers page surfaces, text and hairlines, so overriding it per-component is
   tractable where converting every literal would not be.

   ── Palette reasoning ────────────────────────────────────────────────────
   Dark is a WARM near-black, not #000. The brand's light canvas is a warm
   off-white (#fbfbfa) and the accent is gold; a neutral black would read cold
   beside it. Surfaces step up in lightness rather than sitting on one flat
   grey, which is what gives depth without shadows. Accents keep their hue and
   gain lightness so they clear WCAG AA against the dark canvas rather than
   being naively inverted (which would turn the muted greens and reds muddy).
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Semantic tokens ───────────────────────────────────────────────────────
   One architecture for both themes. Components reference these names, never a
   literal colour, so light and dark are the same code with different values —
   which is what stops dark mode decaying into "a light theme with some
   things inverted".

   Surfaces step up in luminance rather than relying on borders alone, so the
   hierarchy survives even with hue stripped out. Dark is LITERAL BLACK as a
   base per the brand direction, with four steps above it; text is an off-white
   scale, never #fff, to avoid glare. */

:root {
    color-scheme: light;

    /* Light values are the site's CURRENT rendered values, measured rather
       than chosen. That is deliberate: tokenising a working light theme must
       be a visual no-op, and the brief is that light mode is already correct.
       Anything a spec suggests that differs from these (a warmer ink, a cooler
       canvas) is left alone for the same reason. */

    --color-bg:              #fbfbfa;
    --color-surface-1:       #ffffff;
    --color-surface-2:       #fcfcfc;
    --color-surface-3:       #fafafa;
    --color-surface-hover:   #f4f4f1;
    --color-surface-active:  #eeeeea;

    --color-border-subtle:   rgba(0, 0, 0, 0.07);
    --color-border:          rgba(0, 0, 0, 0.08);
    --color-border-strong:   rgba(0, 0, 0, 0.12);

    --color-text-primary:    #000000;
    --color-text-secondary:  rgba(0, 0, 0, 0.62);
    --color-text-tertiary:   rgba(0, 0, 0, 0.58);
    --color-text-muted:      rgba(0, 0, 0, 0.45);
    /* Placeholders were borrowing --color-text-muted, but that token also backs
       two backgrounds (admin-sell.css:701 and the disabled send button), so
       lightening it for contrast would wrongly brighten those. A dedicated
       role token lets the placeholder value move alone. Light value is
       identical to the muted token it replaces, so light mode is unchanged. */
    --color-text-placeholder: rgba(0, 0, 0, 0.45);
    --color-text-on-accent:  #ffffff;

    --color-accent:          #a8842c;
    --color-accent-hover:    #8f7024;
    --color-accent-soft:     rgba(197, 167, 95, 0.14);
    --color-accent-line:     rgba(168, 132, 44, 0.42);

    --color-success:         #1a5c2a;
    --color-success-soft:    rgba(26, 92, 42, 0.10);
    --color-warning:         #7a4a00;
    --color-warning-soft:    rgba(122, 74, 0, 0.10);
    --color-danger:          #7a1f1f;
    --color-danger-soft:     rgba(122, 31, 31, 0.10);
    --color-info:            #23416a;
    --color-info-soft:       rgba(35, 65, 106, 0.10);

    --color-focus:           rgba(168, 132, 44, 0.55);
    --color-deal:            #b08b3a;
    --color-deal-soft:       rgba(198, 166, 91, 0.10);
    --color-deal-line:       rgba(198, 166, 91, 0.34);

    /* The recurring "subtle inset panel" wash. Five stylesheets hardcode the
       same light gradient, which a background-Color scan cannot see. */
    --fb-grad-panel:         linear-gradient(180deg, #fafafa 0%, #f1f1f1 100%);
    --fb-grad-fade:          linear-gradient(to bottom, rgba(255, 255, 255, 0), #ffffff 88%);

    --color-scrim:           rgba(12, 12, 12, 0.92);
    --color-shadow-card:     0 1px 2px rgba(0, 0, 0, 0.04), 0 8px 24px rgba(0, 0, 0, 0.05);
    --color-shadow-pop:      0 2px 6px rgba(0, 0, 0, 0.06), 0 16px 40px rgba(0, 0, 0, 0.09);
}

html[data-theme="dark"] {
    color-scheme: dark;

    /* Literal black base with four steps above it. Each step is only a few
       points of luminance apart, which is deliberate: separation comes from
       the stack plus borders, not from high-contrast greys. */
    --color-bg:              #0b0b0b;
    --color-surface-1:       #101010;
    --color-surface-2:       #151515;
    --color-surface-3:       #1a1a1a;
    --color-surface-hover:   #202020;
    --color-surface-active:  #262626;
    /* Dark equivalents. The first attempt at this silently failed to insert,
       so the token kept its light value in dark mode and every gradient
       override silently inherited it. Verified: the token now resolves to the
       dark ramp on a dark-theme page. */
    --fb-grad-panel:         linear-gradient(180deg, #232323 0%, #171717 100%);
    --fb-grad-fade:          linear-gradient(to bottom, rgba(20, 18, 16, 0), #141210 88%);

    --color-border-subtle:   #1c1c1c;
    --color-border:          #2a2a2a;
    --color-border-strong:   #3a3a3a;

    --color-text-primary:    #f0f0ea;
    --color-text-secondary:  #b4b6ae;
    --color-text-tertiary:   #8b8d85;
    --color-text-muted:      #6a6c65;
    /* Measured 3.46:1 on the composer's #141414 — below AA for text. #8b8d85
       is the existing tertiary step and clears AA on every surface a
       placeholder can sit on: 6.25 on the page, 5.85 on surface-1, 5.48 on
       surface-2, 5.01 on surface-3. */
    --color-text-placeholder: #8b8d85;
    --color-text-on-accent:  #000000;

    --color-accent:          #c5a75f;
    --color-accent-hover:    #d6bb7c;
    --color-accent-soft:     rgba(197, 167, 95, 0.14);
    --color-accent-line:     rgba(197, 167, 95, 0.45);

    /* Status hues are re-tuned rather than inverted: the same hue at a
       lightness that clears AA on black. A literal inversion would land the
       greens and reds in muddy, low-contrast territory. */
    --color-success:         #69c68d;
    --color-success-soft:    rgba(105, 198, 141, 0.10);
    --color-warning:         #d2b76a;
    --color-warning-soft:    rgba(210, 183, 106, 0.10);
    --color-danger:          #d97b7b;
    --color-danger-soft:     rgba(217, 123, 123, 0.10);
    --color-info:            #83a9d4;
    --color-info-soft:       rgba(131, 169, 212, 0.10);

    --color-focus:           rgba(197, 167, 95, 0.60);

    --color-deal:            #c6a65b;
    --color-deal-soft:       rgba(198, 166, 91, 0.09);
    --color-deal-line:       rgba(198, 166, 91, 0.30);

    --color-scrim:           rgba(0, 0, 0, 0.94);
    /* On black, elevation is read from the surface step rather than a drop
       shadow, so the shadow is barely there. */
    --color-shadow-card:     0 1px 2px rgba(0, 0, 0, 0.5);
    --color-shadow-pop:      0 8px 32px rgba(0, 0, 0, 0.7);
}

/* ── Existing token families, rebound ─────────────────────────────────────
   luxury-bg.css drives the page canvas from :root; help.css scopes --fbh-*
   to .page-help; rewards.css scopes --rw-* to its wheel card. All three are
   re-pointed at the semantic values rather than edited at source, so the
   light theme is untouched and the dark theme inherits one system. */

html[data-theme="dark"] {
    --fb-bg-base:          var(--color-bg);
    --fb-bg-surface:       var(--color-surface-1);
    --fb-bg-soft:          var(--color-surface-3);
    --fb-bg-radial-strong: rgba(255, 255, 255, 0.030);
    --fb-bg-radial-soft:   rgba(255, 255, 255, 0.014);
}

html[data-theme="dark"] .page-help {
    --fbh-ink:      var(--color-text-primary);
    --fbh-ink-soft: var(--color-text-secondary);
    --fbh-muted:    var(--color-text-tertiary);
    --fbh-line:     var(--color-border);
    --fbh-line-2:   var(--color-border-subtle);
    --fbh-surface:  var(--color-surface-1);
    --fbh-soft:     var(--color-surface-3);
    --fbh-gold:     var(--color-accent);
    --fbh-gold-ink: var(--color-warning);
    --fbh-shadow:   var(--color-shadow-card);
    --fbh-shadow-h: var(--color-shadow-pop);
}

html[data-theme="dark"] .fb-rewards-wheel-card {
    --rw-surface:   var(--color-surface-1);
    --rw-ink:       var(--color-text-primary);
    --rw-muted:     var(--color-text-secondary);
    --rw-faint:     var(--color-text-tertiary);
    --rw-line:      var(--color-border);
    --rw-line-soft: var(--color-border-subtle);
    --rw-gold:      var(--color-accent);
    --rw-gold-2:    var(--color-warning);
}

/* The grain overlay is a black dot texture; on a black canvas it would
   vanish, so it flips to a light speckle. */
html[data-theme="dark"] .fb-lux-layer { opacity: 0.016; }

/* ── App shell ───────────────────────────────────────────────────────────
   These were the light surfaces that survived the first pass — the admin
   sub-navigation in particular rendered as a full-width white card, which
   read as an alert rather than as chrome. */

html[data-theme="dark"] .admin-subnav,
html[data-theme="dark"] .side,
html[data-theme="dark"] .shell--admin .side {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
    color: var(--color-text-primary);
}

html[data-theme="dark"] .admin-subnav a,
html[data-theme="dark"] .side-nav a {
    color: var(--color-text-secondary);
}

html[data-theme="dark"] .admin-subnav a:hover,
html[data-theme="dark"] .side-nav a:hover {
    color: var(--color-text-primary);
    background-color: var(--color-surface-hover);
}

/* Active state. The light theme signals selection with a light fill plus an
   underline; both are reproduced rather than inventing a third language. */
html[data-theme="dark"] .admin-subnav a.active,
html[data-theme="dark"] .admin-subnav a[aria-current="page"],
html[data-theme="dark"] .side-nav a.active {
    color: var(--color-text-primary);
    background-color: var(--color-surface-3);
    border-bottom-color: var(--color-accent);
}

html[data-theme="dark"] .side-nav,
html[data-theme="dark"] .side-group,
html[data-theme="dark"] .greeting {
    border-color: var(--color-border-subtle);
}

/* The count chip gets a surface; the micro-label does not. These were grouped
   together, which put a --color-surface-2 chip behind "SIGNED IN AS" in dark
   that light mode never draws. */
html[data-theme="dark"] .side-nav-count {
    color: var(--color-text-tertiary);
    background-color: var(--color-surface-2);
}
html[data-theme="dark"] .greeting-label {
    color: var(--color-text-tertiary);
}

html[data-theme="dark"] .greeting-name { color: var(--color-text-primary); }

html[data-theme="dark"] .admin-notify,
html[data-theme="dark"] .admin-notify-item,
html[data-theme="dark"] .order-item-link {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
    color: var(--color-text-primary);
}

html[data-theme="dark"] .order-item-link:hover { background-color: var(--color-surface-hover); }
html[data-theme="dark"] .order-item-link + .order-item-link { border-top-color: var(--color-border-subtle); }
html[data-theme="dark"] .admin-notify .admin-muted,
html[data-theme="dark"] .admin-notify small { color: var(--color-text-tertiary); }

/* Accepted / deal rows keep a warm identity instead of collapsing to black. */
html[data-theme="dark"] .order-item-link.is-deal,
html[data-theme="dark"] .admin-notify-item.is-deal {
    background-color: var(--color-deal-soft);
    border-left: 2px solid var(--color-deal);
    color: var(--color-text-primary);
}

html[data-theme="dark"] .nav-account-menu,
html[data-theme="dark"] .nav-search-panel,
html[data-theme="dark"] .fb-theme-menu {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
    color: var(--color-text-primary);
    box-shadow: var(--color-shadow-pop);
}

html[data-theme="dark"] .nav-account-item { color: var(--color-text-secondary); }
html[data-theme="dark"] .nav-account-item:hover { background-color: var(--color-surface-hover); color: var(--color-text-primary); }
html[data-theme="dark"] .nav-account-item.danger { color: var(--color-danger); }
html[data-theme="dark"] .nav-account-divider { background-color: var(--color-border); }
html[data-theme="dark"] .nav-account-header { border-bottom-color: var(--color-border-subtle); }

html[data-theme="dark"] .nav {
    background-color: rgba(0, 0, 0, 0.82);
    border-color: var(--color-border-subtle);
}

html[data-theme="dark"] .nav a,
html[data-theme="dark"] .nav-account-trigger,
html[data-theme="dark"] .nav-search-trigger { color: var(--color-text-secondary); }
html[data-theme="dark"] .nav a:hover { color: var(--color-text-primary); }
html[data-theme="dark"] .nav-links a.active { color: var(--color-text-primary); }

html[data-theme="dark"] .nav-search-chip,
html[data-theme="dark"] .fai-chip,
html[data-theme="dark"] .fbal-toast {
    background-color: var(--color-surface-2);
    border-color: var(--color-border);
    color: var(--color-text-secondary);
}

html[data-theme="dark"] .fbal-toast { box-shadow: var(--color-shadow-pop); }

/* ── Fragrance AI widget ─────────────────────────────────────────────────
   The panel, pill and offline note were all still light surfaces. */

html[data-theme="dark"] .fai-panel,
html[data-theme="dark"] .fai-pill,
html[data-theme="dark"] .fai-offline,
html[data-theme="dark"] .fai-header {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
    color: var(--color-text-primary);
}

html[data-theme="dark"] .fai-offline { background-color: var(--color-surface-3); color: var(--color-text-secondary); }
html[data-theme="dark"] .fai-chip { background-color: var(--color-surface-2); }
html[data-theme="dark"] .fai-close { color: var(--color-text-tertiary); background: transparent; border-color: transparent; }
html[data-theme="dark"] .fai-close:hover { background-color: var(--color-surface-hover); color: var(--color-text-primary); }
html[data-theme="dark"] .fai-send { background-color: var(--color-accent); color: var(--color-text-on-accent); }
html[data-theme="dark"] .fai-send:disabled { background-color: var(--color-surface-3); color: var(--color-text-muted); }
html[data-theme="dark"] .fai-msg { color: var(--color-text-secondary); }
html[data-theme="dark"] .fai-msg-user { color: var(--color-text-primary); }

/* ── Surfaces ────────────────────────────────────────────────────────────
   Enumerated from every rule in the site that sets a literal white
   background. The SPA views are full-screen overlays, so leaving them light
   put a white sheet over the whole page behind near-white text. */

html[data-theme="dark"] .view-explore,
html[data-theme="dark"] .view-about,
html[data-theme="dark"] .view-order,
html[data-theme="dark"] .related-card,
html[data-theme="dark"] .ac-panel,
html[data-theme="dark"] .ac-cell,
html[data-theme="dark"] .ac-result,
html[data-theme="dark"] .ac-not-found,
html[data-theme="dark"] .fbsub-card,
html[data-theme="dark"] .fbsub-panel,
html[data-theme="dark"] .fb-admin-rewards-card,
html[data-theme="dark"] .po-card,
html[data-theme="dark"] .oc-slot,
html[data-theme="dark"] .oc-pane,
html[data-theme="dark"] .oc-btn,
html[data-theme="dark"] .oc-obj-btn,
html[data-theme="dark"] .oc-bc,
html[data-theme="dark"] .oc-swap-cell,
html[data-theme="dark"] .mp-photo-add,
html[data-theme="dark"] .mp-pick-change,
html[data-theme="dark"] .product-notes-filter,
html[data-theme="dark"] .bt-tip,
html[data-theme="dark"] .bt-static,
html[data-theme="dark"] .bt-final,
html[data-theme="dark"] .bt-final-curate,
html[data-theme="dark"] .mp-verify-card,
html[data-theme="dark"] .mp-verify-c,
html[data-theme="dark"] .fb-rewards-wheel-card,
html[data-theme="dark"] .fb-rewards-hero,
html[data-theme="dark"] .fb-rewards-state {
    background-color: var(--color-surface-1);
    color: var(--color-text-primary);
}

html[data-theme="dark"] input::placeholder,
html[data-theme="dark"] textarea::placeholder { color: var(--color-text-placeholder); }

html[data-theme="dark"] .po-copy,
html[data-theme="dark"] .po-admin-stat,
html[data-theme="dark"] .admin-pill-draft { background-color: var(--color-surface-2); color: var(--color-text-secondary); }
html[data-theme="dark"] .po-tl-dot { background-color: var(--color-text-tertiary); }

/* Translucent white panels would composite to a light box over a black
   canvas, so they become dark translucencies. */
html[data-theme="dark"] .fl-feature,
html[data-theme="dark"] .fl-step,
html[data-theme="dark"] .fl-card {
    background: rgba(255, 255, 255, 0.045);
    border-color: var(--color-border-subtle);
    color: var(--color-text-primary);
}

/* The loading splash is a full-screen white screen — left alone it is exactly
   the flash the pre-paint bootstrap exists to prevent. */
html[data-theme="dark"] #fb-splash { background-color: var(--color-bg); }
html[data-theme="dark"] #fb-splash .fb-splash-brand { color: var(--color-text-primary); }
html[data-theme="dark"] #fb-splash .fb-splash-tag { color: var(--color-text-tertiary); }

/* ── Type ─────────────────────────────────────────────────────────────────
   Every role maps to a level of the text scale rather than an ad-hoc grey,
   so hierarchy survives the swap. */

/* Small uppercase micro-labels need MORE contrast, not less — at 9–10px the
   eye gets no help from size, so these sit at secondary/tertiary rather than
   being pushed further into the muted end of the scale. */

html[data-theme="dark"] a { color: var(--color-text-primary); }
html[data-theme="dark"] .admin-back-link:hover { color: var(--color-accent); }

/* ── Type ─────────────────────────────────────────────────────────────────
   Every role maps to a level of the text scale rather than an ad-hoc grey,
   so hierarchy survives the swap. */

html[data-theme="dark"] h1, html[data-theme="dark"] h2, html[data-theme="dark"] h3,
html[data-theme="dark"] h4, html[data-theme="dark"] h5,
html[data-theme="dark"] .mp-pick-selected-name, html[data-theme="dark"] .product-card-name,
html[data-theme="dark"] .adx-title, html[data-theme="dark"] .adx-match-name,
html[data-theme="dark"] .fb-promo-modal-title, html[data-theme="dark"] .adx-fact dd,
html[data-theme="dark"] .adx-money-value, html[data-theme="dark"] .btn,
html[data-theme="dark"] .product-name {
    color: var(--color-text-primary);
}

/* .adx-btn--primary is deliberately NOT in the group above. It paints itself
   with --color-text-primary, so giving it primary TEXT as well produced cream
   on cream — measured 1:1, an invisible "Send counter-offer" CTA. Its text is
   the on-accent colour, which inverts per theme: black on the light button in
   both. Restated here only so the dark value is explicit at the token layer. */
html[data-theme="dark"] .adx-btn--primary { color: var(--color-text-on-accent); }

html[data-theme="dark"] p, html[data-theme="dark"] li, html[data-theme="dark"] dd,
html[data-theme="dark"] .mp-pick-selected-brand, html[data-theme="dark"] .adx-brand,
html[data-theme="dark"] .adx-pitch, html[data-theme="dark"] .adx-done-row dd,
html[data-theme="dark"] .product-brand, html[data-theme="dark"] .mp-verify-title {
    color: var(--color-text-secondary);
}

/* Small uppercase micro-labels need MORE contrast, not less — at 9–10px the
   eye gets no help from size, so these sit at secondary/tertiary rather than
   being pushed further into the muted end of the scale. */
html[data-theme="dark"] .mp-fact-list dt,
html[data-theme="dark"] .card-label,
html[data-theme="dark"] .admin-muted,
html[data-theme="dark"] .adx-eyebrow,
html[data-theme="dark"] .adx-section-label,
html[data-theme="dark"] .adx-money-label,
html[data-theme="dark"] .adx-fact dt,
html[data-theme="dark"] .adx-actions-head,
html[data-theme="dark"] .adx-field-label,
html[data-theme="dark"] .adx-section-aside,
html[data-theme="dark"] .adx-meta,
html[data-theme="dark"] .mp-muted,
html[data-theme="dark"] .fbh-muted,
html[data-theme="dark"] .mp-pricing-label,
html[data-theme="dark"] .product-card-meta,
html[data-theme="dark"] .fb-promo-modal-mark-text,
html[data-theme="dark"] .adx-match-sub {
    color: var(--color-text-tertiary);
}

/* ── Dashboard data rows (Profile / subscription) ─────────────────────────
   dashboard.php:2130-2134 authors .card-row-val as color:#000000 and
   .card-row-label as rgba(0,0,0,.4) — both correct for the light canvas.
   The catch is structural, not accidental: the generic dark rule at :414
   targets `p, li, dd`, and these are SPANs, so nothing in this file ever
   matched them and they kept painting black on the #0b0b0b card. Measured
   1.07:1 (value) and 1.03:1 (label) — effectively invisible.

   .card-value, the sibling that DOES pass, is a <p> and so is rescued by
   :414 at 9.6:1. Same card, same surface, opposite outcome — which is why
   this looked selective in review but is one shared cause.

   The light stylesheet also inverts the hierarchy: the value is pure black
   and the label is faded, so the label should read quieter than the data.
   Mapping the value to primary and the label to tertiary restores that,
   with roughly a 3x luminance step between them. */
html[data-theme="dark"] .card-row-val { color: var(--color-text-primary); }
html[data-theme="dark"] .card-row-label { color: var(--color-text-tertiary); }

/* Explanatory copy inside a card, extracted from inline styles in
   dashboard.php so it can be themed at all — an inline color outranks every
   stylesheet rule, so these two paragraphs measured 1.03:1 however the
   cascade was arranged. The originals differ by 0.45 vs 0.5 alpha, a delta
   below the perceptual threshold; both map to tertiary (5.85:1) rather than
   splitting them across two tiers and implying a hierarchy that isn't
   visible in light mode either. */
html[data-theme="dark"] .card-note,
html[data-theme="dark"] .card-note-lead { color: var(--color-text-tertiary); }

html[data-theme="dark"] a { color: var(--color-text-primary); }
html[data-theme="dark"] .admin-back-link,
html[data-theme="dark"] .mp-back-link a,
html[data-theme="dark"] .adx-match-go { color: var(--color-text-secondary); }
html[data-theme="dark"] .admin-back-link:hover { color: var(--color-accent); }

html[data-theme="dark"] .fb-promo-modal-desc,
html[data-theme="dark"] .adx-brand { color: var(--color-text-secondary); }

/* ── Status + badges ─────────────────────────────────────────────────────
   Tinted surfaces with a hairline, per-status. Flat #222-on-#777 for every
   badge would collapse the meaning the colours carry. */

html[data-theme="dark"] .pill { background-color: var(--color-surface-3); color: var(--color-text-secondary); }

html[data-theme="dark"] .pill.pending,
html[data-theme="dark"] .pill.draft {
    background-color: var(--color-warning-soft);
    color: var(--color-warning);
    border: 1px solid var(--color-warning-soft);
}

html[data-theme="dark"] .pill.accepted,
html[data-theme="dark"] .pill.quoted {
    background-color: var(--color-success-soft);
    color: var(--color-success);
    border: 1px solid var(--color-success-soft);
}

html[data-theme="dark"] .pill.declined,
html[data-theme="dark"] .pill.withdrawn {
    background-color: var(--color-danger-soft);
    color: var(--color-danger);
    border: 1px solid var(--color-danger-soft);
}

/* ── Buttons ─────────────────────────────────────────────────────────────
   Full state set. Opacity is never used to express a state, because it also
   dims borders, icons and children. */

html[data-theme="dark"] .btn {
    background-color: var(--color-text-primary);
    color: var(--color-bg);
    border: 1px solid var(--color-text-primary);
}
html[data-theme="dark"] .btn:hover { background-color: #ffffff; border-color: #ffffff; }

html[data-theme="dark"] .btn-text { background: transparent; color: var(--color-text-tertiary); }
html[data-theme="dark"] .btn-text:hover { background: transparent; color: var(--color-text-primary); }

/* An action with real consequences stays visibly actionable. */
html[data-theme="dark"] .mark-shipped,
html[data-theme="dark"] .admin-notify .btn-ghost {
    background-color: transparent;
    color: var(--color-text-secondary);
    border: 1px solid var(--color-border-strong);
}
html[data-theme="dark"] .mark-shipped:hover { background-color: var(--color-surface-hover); color: var(--color-text-primary); }

/* ── Money panel ─────────────────────────────────────────────────────────
   Transactional values were too recessed. The card steps up a surface level
   and the figure is promoted to primary ink. */

/* "Not quoted" / "Not set" are data states, not disabled controls, so they
   must not read as unavailable. */

/* ── Product thumbnail ───────────────────────────────────────────────────
   A defined card boundary so the bottle is not floating on the page. */

html[data-theme="dark"] .admin-table th,
html[data-theme="dark"] .admin-table td { border-bottom-color: var(--color-border-subtle); }
html[data-theme="dark"] .admin-table th { color: var(--color-text-tertiary); }
html[data-theme="dark"] .admin-table tbody tr:hover { background-color: var(--color-surface-hover); }
html[data-theme="dark"] .admin-table-wrap { border-color: var(--color-border); }

html[data-theme="dark"] .card { border-color: var(--color-border); background-color: var(--color-surface-1); }
html[data-theme="dark"] .mp-card { border-color: var(--color-border); background-color: var(--color-surface-1); }
html[data-theme="dark"] .mp-notes { background-color: var(--color-surface-2); border-color: var(--color-border-subtle); color: var(--color-text-secondary); }
html[data-theme="dark"] .mp-fact-list > div,
html[data-theme="dark"] .mp-pricing-row { border-bottom-color: var(--color-border-subtle); }

html[data-theme="dark"] .fb-promo-modal-card { background-color: var(--color-surface-1); border-color: var(--color-border); color: var(--color-text-primary); }
html[data-theme="dark"] .fb-promo-modal-close { background-color: transparent; border-color: transparent; color: var(--color-text-tertiary); }
html[data-theme="dark"] .fb-promo-modal-close:hover { background-color: var(--color-surface-hover); color: var(--color-text-primary); }
html[data-theme="dark"] .fb-promo-modal-dismiss { color: var(--color-accent); }
html[data-theme="dark"] .fb-promo-modal-cta { background-color: var(--color-text-primary); color: var(--color-bg); }
html[data-theme="dark"] .fb-promo-modal-fineprint { color: var(--color-text-tertiary); }

/* ── Lightbox (its backdrop is already dark in both themes) ────────────── */

html[data-theme="dark"] .adxl-backdrop { background-color: var(--color-scrim); }
html[data-theme="dark"] .adxl-stage { background-color: rgba(255, 255, 255, 0.04); }
html[data-theme="dark"] .adxl-counter { color: rgba(255, 255, 255, 0.72); }
html[data-theme="dark"] .adxl-hint { color: rgba(255, 255, 255, 0.58); }
html[data-theme="dark"] .adxl-tool { background-color: rgba(255, 255, 255, 0.10); border-color: rgba(255, 255, 255, 0.20); color: #fff; }
html[data-theme="dark"] .adxl-tool:hover { background-color: rgba(255, 255, 255, 0.20); border-color: rgba(255, 255, 255, 0.40); }
html[data-theme="dark"] .adxl-error { color: rgba(255, 255, 255, 0.85); }

/* ── Focus ─────────────────────────────────────────────────────────────
   One treatment everywhere. Never removed, because dark mode makes keyboard
   navigation materially harder. */

html[data-theme="dark"] :focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}

/* ── Top-right action rail ───────────────────────────────────────────────
   Generic container for controls sitting between the nav links and the
   profile selector. The theme control is the first occupant; anything added
   later inherits the gap, hit target, focus ring and both themes. */

.nav-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-shrink: 0;
}

/* ── Inline-style overrides ───────────────────────────────────────────────
   A handful of components set background/colour through a style attribute,
   which outranks any stylesheet rule regardless of specificity. These are the
   only places !important is used in this file, and only because the cascade
   offers no other way to reach them. Better fixed at the source by replacing
   the inline style with a class; this is the safety net. */

html[data-theme="dark"] .pill[style*="eef3ff"] {
    background-color: var(--color-info-soft) !important;
    color: var(--color-info) !important;
}

html[data-theme="dark"] .pill[style*="fff3e0"] {
    background-color: var(--color-warning-soft) !important;
    color: var(--color-warning) !important;
}

/* ── Notification panel controls ──────────────────────────────────────────
   "Mark all read" and "Enable alerts" carry no class, so they fell through to
   the browser default and rendered as a white button on black. */

html[data-theme="dark"] [data-notify-readall],
html[data-theme="dark"] [data-notify-enable] {
    -webkit-appearance: none;
    appearance: none;
    background-color: var(--color-surface-2);
    color: var(--color-text-secondary);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    padding: 0.375rem 0.75rem;
    font-family: inherit;
    font-size: 0.6875rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

html[data-theme="dark"] [data-notify-readall]:hover,
html[data-theme="dark"] [data-notify-enable]:hover {
    background-color: var(--color-surface-hover);
    color: var(--color-text-primary);
    border-color: var(--color-border-strong);
}

html[data-theme="dark"] [data-notify-readall]:focus-visible,
html[data-theme="dark"] [data-notify-enable]:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}

/* ── Customer-page surfaces ─────────────────────────────────────────────
   The union of every element that still rendered a light surface in dark
   mode, collected by walking all ten public pages in a real browser and
   reading computed background-color — not by reading the CSS, which misses
   surfaces composed from tokens or inline styles.

   Grouped by what they are rather than by page, so the intent survives a
   class rename. */

html[data-theme="dark"] .product-card,
html[data-theme="dark"] .wiz-pick,
html[data-theme="dark"] .wiz-quick-card,
html[data-theme="dark"] .wiz-quick-prev,
html[data-theme="dark"] .wiz-quick-next,
html[data-theme="dark"] .wiz-quick-card-ico,
html[data-theme="dark"] .wiz-ship-method,
html[data-theme="dark"] .wiz-option,
html[data-theme="dark"] .wiz-fill-switch,
html[data-theme="dark"] .wiz-plan-switch,
html[data-theme="dark"] .po-choice,
html[data-theme="dark"] .mp-method-card-frame,
html[data-theme="dark"] .mp-input,
html[data-theme="dark"] .mp-mono,
html[data-theme="dark"] .pdp-fav-btn,
html[data-theme="dark"] .fb-quickview-panel,
html[data-theme="dark"] .fb-cartdock-panel,
html[data-theme="dark"] .po-drawer-panel,
html[data-theme="dark"] .po-reserve-dock,
html[data-theme="dark"] .fbh-search-btn,
html[data-theme="dark"] .fbh-btn,
html[data-theme="dark"] .fb-img-error,
html[data-theme="dark"] .fb-img-pending,
html[data-theme="dark"] .mp-verify-icon--landing {
    background-color: var(--color-surface-1);
    color: var(--color-text-primary);
    border-color: var(--color-border);
}

/* Selected / picked states: the light theme signals with a fill plus a rule.
   Both are reproduced rather than inventing a third language. */
html[data-theme="dark"] .wiz-pick.is-selected,
html[data-theme="dark"] .wiz-pick--with-media.is-selected,
html[data-theme="dark"] .wiz-option.selected,
html[data-theme="dark"] .wiz-option.has-pop,
html[data-theme="dark"] .wiz-fill-switch.is-selected,
html[data-theme="dark"] .wiz-plan-switch.is-selected,
html[data-theme="dark"] .wiz-ship-method.is-selected,
html[data-theme="dark"] .po-choice.is-selected,
html[data-theme="dark"] .product-card.is-picked,
html[data-theme="dark"] .product-card.is-favorited,
html[data-theme="dark"] .wiz-quick-card.is-current,
html[data-theme="dark"] .po-choice--feature {
    background-color: var(--color-surface-3);
    color: var(--color-text-primary);
    border-color: var(--color-accent-line);
}

/* Unavailable states keep a recognisable shape without shouting. */
html[data-theme="dark"] .product-card.is-soldout,
html[data-theme="dark"] .wiz-pick.locked,
html[data-theme="dark"] .wiz-pick.reveal,
html[data-theme="dark"] .reveal {
    background-color: var(--color-surface-2);
    color: var(--color-text-secondary);
}

/* Primary actions invert against the dark canvas rather than staying black. */
html[data-theme="dark"] .mp-cta-btn,
html[data-theme="dark"] .fb-promo-cta,
html[data-theme="dark"] .fb-cartdock-cta,
html[data-theme="dark"] .fbh-btn-primary {
    background-color: var(--color-text-primary);
    color: var(--color-bg);
    border-color: var(--color-text-primary);
}
html[data-theme="dark"] .mp-cta-btn:hover,
html[data-theme="dark"] .fbh-btn-primary:hover { background-color: #ffffff; color: #000000; }

html[data-theme="dark"] .pdp-react-like,
html[data-theme="dark"] .pdp-react-dislike {
    background-color: var(--color-surface-2);
    border-color: var(--color-border);
    color: var(--color-text-secondary);
}
html[data-theme="dark"] .pdp-react-like:hover,
html[data-theme="dark"] .pdp-react-dislike:hover { background-color: var(--color-surface-hover); color: var(--color-text-primary); }

html[data-theme="dark"] .nav-search-backdrop { background-color: rgba(0, 0, 0, 0.7); }
html[data-theme="dark"] .fb-img-error,
html[data-theme="dark"] .fb-img-pending { background-color: var(--color-surface-3); color: var(--color-text-tertiary); }

/* ── Remaining light fills found by the browser sweep ─────────────────────
   A light fill carrying light text — those are genuine defects, not the
   deliberate inverted primary buttons. */

html[data-theme="dark"] .product-card-actions {
    background-color: rgba(0, 0, 0, 0.72);
    color: var(--color-text-secondary);
    border-color: var(--color-border);
}

html[data-theme="dark"] .wiz-step.active,
html[data-theme="dark"] .wiz-step.locked,
html[data-theme="dark"] .wiz-ship {
    background-color: var(--color-surface-1);
    color: var(--color-text-primary);
    border-color: var(--color-border);
}

html[data-theme="dark"] .wiz-step.active { border-color: var(--color-accent-line); }
html[data-theme="dark"] .wiz-step.locked { background-color: var(--color-surface-2); color: var(--color-text-tertiary); }

/* ── Preorder page ───────────────────────────────────────────────────────
   Four rules in preorder.css set a literal #fff; they were the last light
   fills in the sweep. */

html[data-theme="dark"] .po-steps,
html[data-theme="dark"] .po-choice-row,
html[data-theme="dark"] .po-drawer,
html[data-theme="dark"] .po-drawer-panel,
html[data-theme="dark"] .po-pill,
html[data-theme="dark"] .po-section-alt {
    background-color: var(--color-surface-1);
    color: var(--color-text-primary);
    border-color: var(--color-border);
}

html[data-theme="dark"] .po-pill { color: var(--color-text-secondary); }

/* Step-number badges inside .po-steps — a literal #fff pill in preorder.css. */
html[data-theme="dark"] .po-steps .reveal,
html[data-theme="dark"] .po-steps .reveal.is-current {
    background-color: var(--color-surface-1);
    color: var(--color-text-primary);
    border-color: var(--color-border);
}
html[data-theme="dark"] .po-steps .reveal.is-current {
    background-color: var(--color-accent-soft);
    color: var(--color-accent);
    border-color: var(--color-accent-line);
}

/* The step numerals: preorder.css styles `.po-steps` descendants as a
   bordered white pill, which the class-level rule above does not reach
   because the pill is the bare <span> inside each <li>. */
html[data-theme="dark"] .po-steps span {
    background-color: var(--color-surface-1);
    color: var(--color-text-primary);
    border-color: var(--color-border);
}

/* ── Dark-only text failures found by the contrast sweep ──────────────────
   Each of these sets its colour to a literal (usually #000) and sits on a
   surface this file has already darkened, so the text disappeared. Grouped
   by the level of the text scale each one should carry. */

html[data-theme="dark"] .nav-wordmark,
html[data-theme="dark"] .nav-account-name,
html[data-theme="dark"] .about-signature-name,
html[data-theme="dark"] .mp-verify-title,
html[data-theme="dark"] .wiz-quick-title,
html[data-theme="dark"] .content-subtitle,
html[data-theme="dark"] .admin-notify-title,
html[data-theme="dark"] .related-card-brand,
html[data-theme="dark"] .product-card-brand,
html[data-theme="dark"] .mp-step,
html[data-theme="dark"] .mp-step-title,
html[data-theme="dark"] .mp-step.is-active,
html[data-theme="dark"] .wiz-plan-opt.is-active,
html[data-theme="dark"] .oob-cta,
html[data-theme="dark"] .greeting-name {
    color: var(--color-text-primary);
}

html[data-theme="dark"] .mp-step-sub,
html[data-theme="dark"] .mp-verify-foot,
html[data-theme="dark"] .mp-field-label,
html[data-theme="dark"] .mp-hero-eyebrow,
html[data-theme="dark"] .mp-hero-step-num,
html[data-theme="dark"] .mp-footer-sub,
html[data-theme="dark"] .mp-footer-eye,
html[data-theme="dark"] .po-card-num,
html[data-theme="dark"] .po-hero-from,
html[data-theme="dark"] .pc-qv-label,
html[data-theme="dark"] .pdp-info-eyebrow,
html[data-theme="dark"] .pdp-spec-label,
html[data-theme="dark"] .collection-stat-label,
html[data-theme="dark"] .wiz-quick-sub,
html[data-theme="dark"] .wiz-plan-note,
html[data-theme="dark"] .wiz-plan-opt-onetime,
html[data-theme="dark"] .wiz-plan-opt-monthly,
html[data-theme="dark"] .fb-rewards-eyebrow,
html[data-theme="dark"] .fb-rewards-lede,
html[data-theme="dark"] .fb-rewards-hub-sub,
html[data-theme="dark"] .fb-rewards-legend-title,
html[data-theme="dark"] .fb-rewards-next,
html[data-theme="dark"] .fbh-fcard-sum,
html[data-theme="dark"] .fbh-hero-sub,
html[data-theme="dark"] .mp-verify-eyebrow,
html[data-theme="dark"] .product-card-surcharge,
html[data-theme="dark"] .admin-notify-meta {
    color: var(--color-text-tertiary);
}

html[data-theme="dark"] .fb-splash-tag { color: var(--color-text-tertiary); }

/* Second pass — the remaining dark-only failures. */

html[data-theme="dark"] .content-title,
html[data-theme="dark"] .content-subtitle,
html[data-theme="dark"] .admin-notify-amt,
html[data-theme="dark"] .wiz-quick-main,
html[data-theme="dark"] .wiz-quick-spec,
html[data-theme="dark"] .wiz-quick-tag,
html[data-theme="dark"] .mp-method-card-title {
    color: var(--color-text-primary);
}

html[data-theme="dark"] .admin-notify-cust,
html[data-theme="dark"] .mp-method-card-text,
html[data-theme="dark"] .mp-method-card-eye,
html[data-theme="dark"] .product-card-surcharge,
html[data-theme="dark"] .wiz-plan-opt-onetime {
    color: var(--color-text-secondary);
}

/* Bare text leaves whose colour is set on a parent that is itself unthemed. */
html[data-theme="dark"] .mp-method-card p,
html[data-theme="dark"] .mp-method-card span,
html[data-theme="dark"] .wiz-quick-card span,
html[data-theme="dark"] .wiz-plan-opt span {
    color: var(--color-text-secondary);
}

/* ── Admin workspace body + nav ───────────────────────────────────────────
   dashboard.php hardcodes the work surface and the nav to light values, and
   `body.is-admin-workspace #nav-el` carries an ID, so it outranks a class-only
   dark rule. These need matching-or-greater specificity to win. */

html[data-theme="dark"] body.is-admin-workspace {
    background: var(--color-bg);
    color: var(--color-text-primary);
}

/* Only the ink is restated. The background is left to nav.css so the admin
   workspace gets the same translucent bar and backdrop blur as the storefront;
   forcing --color-surface-1 here made it the one opaque nav in the product. */
html[data-theme="dark"] body.is-admin-workspace #nav-el {
    color: var(--color-text-primary);
}

/* The selected/unread notification rows carry a translucent gold tint from
   the light theme; the amounts inside them are a dark gold that then has
   almost nothing to sit on. */
html[data-theme="dark"] .admin-notify-amt {
    color: var(--color-text-primary);
}

html[data-theme="dark"] .admin-notify-item.is-unread,
html[data-theme="dark"] .admin-notify-item.is-sel {
    background-color: var(--color-deal-soft);
    border-left-color: var(--color-deal);
}

html[data-theme="dark"] .admin-notify-stage-txt,
html[data-theme="dark"] .wiz-num,
html[data-theme="dark"] .wiz-fill-opt-half {
    color: var(--color-text-tertiary);
}

/* ── Higher-specificity light rules ────────────────────────────────────────
   A handful of light-theme rules carry two or more classes and therefore
   outrank a two-class dark override. These need to match or beat them. */

html[data-theme="dark"] .admin-notify-item.is-sell .admin-notify-amt,
html[data-theme="dark"] .admin-notify-item.is-sell .admin-notify-link,
html[data-theme="dark"] .wiz-step.complete .wiz-num,
html[data-theme="dark"] .wiz-step.active .wiz-num {
    color: var(--color-text-primary);
}

html[data-theme="dark"] .admin-notify-item.is-sell .admin-notify-amt::before {
    background: var(--color-deal);
}

html[data-theme="dark"] .admin-notify-item.is-sell {
    box-shadow: inset 3px 0 0 var(--color-deal);
    background: var(--color-deal-soft);
}

html[data-theme="dark"] .wiz-step.complete .wiz-num,
html[data-theme="dark"] .wiz-step.active .wiz-num {
    color: var(--color-accent);
}

/* The same multi-class pattern found by scanning the light stylesheets for
   two-class selectors that hardcode #000. Matched at equal-or-higher
   specificity so the dark value actually lands. */
html[data-theme="dark"] .product-card.bt-hover,
html[data-theme="dark"] .product-notes-filter.is-active,
html[data-theme="dark"] .wiz-fill-switch.is-full,
html[data-theme="dark"] .wiz-plan-switch.is-monthly,
html[data-theme="dark"] .wiz-ship-method.is-selected,
html[data-theme="dark"] .wiz-step.complete {
    color: var(--color-text-primary);
    border-color: var(--color-accent-line);
}

html[data-theme="dark"] .wiz-fill-opt-half,
html[data-theme="dark"] .wiz-num {
    color: var(--color-text-tertiary);
}

html[data-theme="dark"] .admin-notify-advance {
    color: var(--color-text-secondary);
    border-color: var(--color-border);
}
html[data-theme="dark"] .admin-notify-advance:hover {
    color: var(--color-text-primary);
    background: var(--color-surface-hover);
}

/* ── Checkout wizard: the last dark-only text failures ───────────────────
   All fourteen come from the /order wizard and the promo-code chip. They are
   listed in the same state-class form the light stylesheets use, so a
   multi-class light rule cannot outrank them. */

html[data-theme="dark"] .admin-btn-sm.ghost {
    color: var(--color-text-secondary);
    border-color: var(--color-border);
}
html[data-theme="dark"] .admin-btn-sm.ghost:hover {
    color: var(--color-text-primary);
    background: var(--color-surface-hover);
}

html[data-theme="dark"] .fb-promo-code-val,
html[data-theme="dark"] .fb-promo-modal-mark-text {
    color: var(--color-text-primary);
}

html[data-theme="dark"] .wiz-title,
html[data-theme="dark"] .wiz-ship-method-name,
html[data-theme="dark"] .wiz-ship-method-price,
html[data-theme="dark"] .wiz-review-val,
html[data-theme="dark"] .wiz-total-label,
html[data-theme="dark"] .wiz-total-line-label,
html[data-theme="dark"] .wiz-soldout-badge,
html[data-theme="dark"] .wiz-ship-title,
html[data-theme="dark"] .wiz-ship-method-free {
    color: var(--color-text-primary);
}

html[data-theme="dark"] .wiz-plan-opt.wiz-plan-opt-onetime,
html[data-theme="dark"] .wiz-fill-opt.wiz-fill-opt-half {
    color: var(--color-text-secondary);
}

/* The promo modal's own header, and the preview panel the admin Promotions
   section renders it inside. Both were still light behind light text. */
html[data-theme="dark"] .fb-promo-modal-head,
html[data-theme="dark"] .fb-promo-modal-body,
html[data-theme="dark"] .fb-promo-modal-foot {
    background-color: var(--color-surface-1);
    color: var(--color-text-primary);
}
html[data-theme="dark"] .admin-promo-preview {
    background-color: var(--color-surface-2);
    color: var(--color-text-primary);
}
html[data-theme="dark"] .card.admin-promo-preview-card {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
}

/* ── Nav selected state ───────────────────────────────────────────────────
   nav.css marks the current link with `color: #000 !important`, which no
   plain dark rule can displace — the active link stayed pure black on a
   black bar. The mirror of the light treatment is a light ink on a light
   translucency, with the accent carrying the selection so it reads without
   relying on weight alone. */

html[data-theme="dark"] .nav-links a.nav-link-active,
html[data-theme="dark"] .nav-links a.is-active,
html[data-theme="dark"] a.nav-link-active {
    color: var(--color-text-primary) !important;
    background: rgba(255, 255, 255, 0.07);
    border-color: var(--color-accent-line);
}

html[data-theme="dark"] .nav-links a.nav-link-active:hover,
html[data-theme="dark"] .nav-links a.is-active:hover {
    background: rgba(255, 255, 255, 0.11);
}

html[data-theme="dark"] .nav-links a:not(.nav-link-active):not(.is-active) {
    color: var(--color-text-tertiary);
}

html[data-theme="dark"] .nav-links a:not(.nav-link-active):hover {
    color: var(--color-text-primary);
    background: var(--color-surface-hover);
    border-color: var(--color-border);
}

/* ── Root canvas ─────────────────────────────────────────────────────────
   nav.css sets `html, body { background: #fff; color: #000 }`. The body has
   the luxury gradient painting over it, but <html> itself kept pure white, so
   it showed through on overscroll and before the gradient resolved. The root
   element needs its own rule, not just a body descendant one. */

html[data-theme="dark"] {
    background: var(--color-bg);
}

html[data-theme="dark"] body {
    background-color: var(--color-bg);
    color: var(--color-text-primary);
}

/* ── Late additions ───────────────────────────────────────────────────────
   These only became measurable once <body> carried a real background: the
   contrast walk had been falling through to the white <html> and passing them
   by accident. */

html[data-theme="dark"] .collection-hero-from,
html[data-theme="dark"] .collection-faq-q,
html[data-theme="dark"] .crumbs-current,
html[data-theme="dark"] .pdp-pick-counter {
    color: var(--color-text-secondary);
}

/* FAQ questions. A bare `summary` selector ties with nav.css's
   `.mp-faq-item summary` on specificity, so source order would decide it and
   nav.css wins. Qualifying by the item class puts this rule clearly ahead. */
html[data-theme="dark"] .mp-faq-item summary,
html[data-theme="dark"] .collection-faq-item summary,
html[data-theme="dark"] .faq-item summary,
html[data-theme="dark"] details > summary {
    color: var(--color-text-secondary);
}

html[data-theme="dark"] .stock-badge.ok {
    color: var(--color-success);
    background-color: var(--color-success-soft);
}

/* ── Browser-native surfaces ─────────────────────────────────────────────
   These sit outside the cascade's reach: the UA paints them itself, so a
   light-mode literal stays light in dark mode unless it is themed here. */

::selection {
    background: rgba(168, 132, 44, 0.28);
    color: var(--color-text-primary);
}

html[data-theme="dark"] ::selection {
    background: rgba(217, 184, 102, 0.34);
    color: #ffffff;
}

/* Standard scrollbar properties cover Firefox; the ::-webkit rules below cover
   Chromium, which ignores scrollbar-color. */
html {
    scrollbar-color: rgba(0, 0, 0, 0.28) transparent;
}

html[data-theme="dark"] {
    scrollbar-color: rgba(255, 255, 255, 0.26) transparent;
}

html[data-theme="dark"] ::-webkit-scrollbar { width: 10px; height: 10px; }
html[data-theme="dark"] ::-webkit-scrollbar-track { background: transparent; }
html[data-theme="dark"] ::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.22);
    border-radius: 999px;
}
html[data-theme="dark"] ::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.34); }

/* The AI message list carries its own scrollbar rule; it is a black thumb on
   a dark panel otherwise. */
html[data-theme="dark"] .fai-msgs::-webkit-scrollbar { width: 8px; }
html[data-theme="dark"] .fai-msgs::-webkit-scrollbar-track { background: transparent; }
html[data-theme="dark"] .fai-msgs::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.22);
    border-radius: 999px;
}

/* Chrome autofill paints its own background, which would punch a light block
   into a dark form. The box-shadow sits on the inside so it covers it without
   shifting layout, and it is transitioned so it fades rather than snaps. */
html[data-theme="dark"] input:-webkit-autofill,
html[data-theme="dark"] input:-webkit-autofill:hover,
html[data-theme="dark"] input:-webkit-autofill:focus,
html[data-theme="dark"] textarea:-webkit-autofill,
html[data-theme="dark"] select:-webkit-autofill {
    -webkit-text-fill-color: var(--color-text-primary);
    caret-color: var(--color-text-primary);
    -webkit-box-shadow: 0 0 0 1000px var(--color-surface-2) inset;
    box-shadow: 0 0 0 1000px var(--color-surface-2) inset;
    transition: background-color 9999s ease-out 0s;
}

/* Firefox autofill uses :autofill directly. */
html[data-theme="dark"] input:autofill,
html[data-theme="dark"] textarea:autofill,
html[data-theme="dark"] select:autofill {
    background: var(--color-surface-2);
    color: var(--color-text-primary);
}

/* ── Floating UI: states and surfaces not covered above ──────────────────
   The widget and cart dock are hidden until opened, so a route-level scan
   never sees them. These are the pieces that were still light-only. */

html[data-theme="dark"] .fb-cartdock-item-media {
    background: linear-gradient(180deg, var(--color-surface-2) 0%, var(--color-surface-3) 100%);
}

html[data-theme="dark"] .fai-empty-sub,
html[data-theme="dark"] .fai-foot,
html[data-theme="dark"] .fai-head-text { color: var(--color-text-tertiary); }
html[data-theme="dark"] .fai-empty-title { color: var(--color-text-secondary); }
html[data-theme="dark"] .fai-input { background: var(--color-surface-2); color: var(--color-text-primary); border-color: var(--color-border); }
html[data-theme="dark"] .fai-input::placeholder { color: var(--color-text-placeholder); }

html[data-theme="dark"] .fai-chip:hover {
    background: var(--color-surface-3);
    border-color: var(--color-border-strong);
    color: var(--color-text-primary);
}

html[data-theme="dark"] .fai-chip:focus-visible,
html[data-theme="dark"] .fb-cartdock-close:focus-visible,
html[data-theme="dark"] .fb-cartdock-item:focus-visible { outline-color: var(--color-accent); }

html[data-theme="dark"] .fb-cartdock-item { border-bottom-color: var(--color-border-subtle); color: var(--color-text-primary); }
html[data-theme="dark"] .fb-cartdock-item-meta,
html[data-theme="dark"] .fb-cartdock-empty p { color: var(--color-text-tertiary); }
html[data-theme="dark"] .fb-cartdock-trigger,
html[data-theme="dark"] .fb-cartdock-badge { color: var(--color-text-primary); }
html[data-theme="dark"] .fb-cartdock-trigger { background: var(--color-surface-1); border-color: var(--color-border); box-shadow: var(--color-shadow-pop); }

/* ── Mobile nav + account dropdown panels ─────────────────────────────────
   Both are near-opaque white sheets (rgba(255,255,255,.98)) with a backdrop
   blur, so on a dark page they read as a flash of daylight. Found by the
   responsive sweep: 11 routes leaked at 320/390/430 and were clean at 768+.

   .nav-account-menu is a floating dropdown at EVERY viewport, so it is themed
   globally. .nav-links is an inline list on desktop and only becomes a sheet
   under nav.css's @media (max-width: 720px) — so its panel rules are scoped to
   that same breakpoint.

   The scoping is not cosmetic. These rules were previously unscoped, which
   put a 1px border-bottom on all eight desktop nav links (in light the base
   border is transparent) and painted the ACTIVE link with --color-surface-hover,
   the hover token, overriding the intended rgba(255,255,255,.07). That is what
   made the desktop links read as bordered buttons rather than links. */

html[data-theme="dark"] .nav-account-menu {
    background: rgba(20, 18, 16, 0.97);
    border-color: var(--color-border);
    box-shadow: 0 16px 40px -16px rgba(0, 0, 0, 0.7), 0 4px 12px -4px rgba(0, 0, 0, 0.5);
}

@media (max-width: 720px) {
    html[data-theme="dark"] .nav-links {
        background: rgba(20, 18, 16, 0.97);
        border-color: var(--color-border);
        box-shadow: 0 16px 40px -16px rgba(0, 0, 0, 0.7), 0 4px 12px -4px rgba(0, 0, 0, 0.5);
    }

    html[data-theme="dark"] .nav-links > li > a,
    html[data-theme="dark"] .nav-links > li > a.nav-link-active {
        color: var(--color-text-secondary);
        background: transparent;
        border-bottom-color: var(--color-border-subtle);
    }

    html[data-theme="dark"] .nav-links > li > a:hover,
    html[data-theme="dark"] .nav-links > li > a.nav-link-active {
        color: var(--color-text-primary);
        background: var(--color-surface-hover);
    }

    html[data-theme="dark"] .nav-links > li > a.nav-link-active {
        box-shadow: inset 2px 0 0 var(--color-accent);
    }
}

/* ── Light gradients ─────────────────────────────────────────────────────
   A `background: linear-gradient(...)` sets background-IMAGE, which paints
   over any background-color a dark rule sets. So every one of these looked
   light in dark mode no matter what colour the surface underneath was — and a
   scanner reading only computed backgroundColor reports them as transparent
   and clean. Found by reading the rendered backgroundImage, not the colour. */

html[data-theme="dark"] .product-card-media,
html[data-theme="dark"] .fb-cartdock-item-media,
html[data-theme="dark"] .wiz-cart-media,
html[data-theme="dark"] .fb-quickview-media {
    background: var(--fb-grad-panel);
    background-image: var(--fb-grad-panel);
}

/* .mp-pick-grid.is-collapsed::after — a white fade laid over the collapsed
   grid to soften its lower edge. */
html[data-theme="dark"] .mp-pick-grid.is-collapsed::after {
    background: var(--fb-grad-fade);
}

/* .fbh-cat-ico and .fbh-cta — help-centre tiles and its call-to-action, both
   warm light washes in the light theme. */
html[data-theme="dark"] .fbh-cat-ico {
    background: linear-gradient(160deg, rgba(224, 169, 79, 0.12), rgba(224, 169, 79, 0.05));
    color: var(--color-warning);
}

html[data-theme="dark"] .fbh-cta {
    background: linear-gradient(180deg, var(--color-surface-2), var(--color-surface-1));
    color: var(--color-text-primary);
    border-color: var(--color-border);
}

/* .nav-search-thumb — a pale placeholder in the search overlay. */
html[data-theme="dark"] .nav-search-thumb {
    background: var(--fb-grad-panel);
    background-image: var(--fb-grad-panel);
}

html[data-theme="dark"] .po-section-alt {
    background: var(--fb-grad-panel);
    background-image: var(--fb-grad-panel);
}

/* .fb-promo-modal-cta::before — a specular sweep across the primary button. */
html[data-theme="dark"] .fb-promo-modal-cta::before {
    background: linear-gradient(120deg, transparent 30%, rgba(255, 255, 255, 0.07) 50%, transparent 70%);
}

/* ── Product image fit: deliberately NOT changed ──────────────────────────
   `object-fit: cover` scales the 500x500 source to fill the 4:5 card and
   crops 50px per side (20% of the width). Measured across all 75 products:
   ZERO have any opaque content inside that cropped band, so no bottle is
   ever clipped. Changing it to `contain` would alter the card layout in both
   themes for no visible gain, so the existing cover behaviour stands. */

/* ── Selected-state glyphs ────────────────────────────────────────────────
   The wizard's selected option paints itself with a black 2px bar, a black
   border and a black checkmark. The check is a data-URI background image with
   `stroke='black'` baked in, so no colour declaration can reach it — a
   mask is the way to make a baked-glyph icon themeable: the SVG's alpha
   becomes the mask shape and background-color paints it.

   Light mode is untouched: it keeps the original background-image. */
html[data-theme="dark"] .wiz-option.selected {
    border-color: var(--color-accent-line);
    color: var(--color-text-primary);
}

html[data-theme="dark"] .wiz-option.selected::before {
    background: var(--color-accent);
}

html[data-theme="dark"] .wiz-option.selected::after {
    background-image: none;
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round'><path d='M3 8.5l3.2 3.2L13 5'/></svg>");
    mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round'><path d='M3 8.5l3.2 3.2L13 5'/></svg>");
    background-color: var(--color-text-primary);
}

/* Feature icons that were hardcoded to stroke="#000". A presentation
   attribute cannot be overridden by CSS, which is exactly why they stayed
   black in dark mode; the markup now uses currentColor like the other 113
   inline icons, and this rule makes sure the colour actually arrives. */
html[data-theme="dark"] .po-feature-ico,
html[data-theme="dark"] .fl-feature-ico {
    color: var(--color-text-primary);
}
html[data-theme="dark"] .po-feature-ico:hover,
html[data-theme="dark"] .fl-feature-ico:hover { color: var(--color-accent); }
html[data-theme="dark"] .po-feature-ico:focus-visible,
html[data-theme="dark"] .fl-feature-ico:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* ── 404 search box ──────────────────────────────────────────────────────
   A genuinely white input on the not-found page, with a dark icon stroke on
   it. Found by the icon audit. */
html[data-theme="dark"] .nf-search,
html[data-theme="dark"] .nf-search input,
html[data-theme="dark"] .ac-not-found,
html[data-theme="dark"] .nf-card {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
    color: var(--color-text-primary);
}

html[data-theme="dark"] .nf-search input::placeholder { color: var(--color-text-placeholder); }
html[data-theme="dark"] .nf-search-ico { color: var(--color-text-tertiary); }
html[data-theme="dark"] .nf-search-ico svg,
html[data-theme="dark"] .nf-search-ico { stroke: currentColor; }

/* ── Product detail page ─────────────────────────────────────────────────
   Three more light-only surfaces on /products/<slug>, found by the gradient
   and icon sweep:
     .pdp-hero-media       linear-gradient(#fafafa -> #efefef)  (a different
                           ramp from the shared #fafafa -> #f1f1f1 one, so
                           matching on the old value would have missed it)
     .related-card-media   the same wash inside the "you may also like" rail
     .pdp-react-btn /
     .pdp-fav-btn          background: #fff, with a black glyph inside */

html[data-theme="dark"] .pdp-hero-media,
html[data-theme="dark"] .related-card-media {
    background: linear-gradient(180deg, #232323 0%, #161616 100%);
    background-image: linear-gradient(180deg, #232323 0%, #161616 100%);
    border-color: var(--color-border);
}

html[data-theme="dark"] .pdp-react-btn,
html[data-theme="dark"] .pdp-fav-btn {
    background-color: var(--color-surface-2);
    border-color: var(--color-border);
    color: var(--color-text-secondary);
}

html[data-theme="dark"] .pdp-react-btn svg,
html[data-theme="dark"] .pdp-fav-btn svg { stroke: currentColor; }

html[data-theme="dark"] .pdp-react-btn:hover:not(.is-readonly):not(.is-active),
html[data-theme="dark"] .pdp-fav-btn:hover:not(.is-active) {
    background-color: var(--color-text-primary);
    border-color: var(--color-text-primary);
    color: var(--color-bg);
}

html[data-theme="dark"] .pdp-react-btn.is-active.pdp-react-like,
html[data-theme="dark"] .pdp-react-btn.is-active.pdp-react-dislike {
    border-color: var(--color-border);
}

/* ══════════════════════════════════════════════════════════════════════════
   FRAGRANCE AI WIDGET — dark theme
   ══════════════════════════════════════════════════════════════════════════
   The assistant's replies are rendered by renderMarkdown() in fragrance-ai.js,
   which emits plain <a> / <strong> / <br> into .fai-msg-ai. There is no
   utility-class markup to theme, so every colour comes from fragrance-ai.css —
   and twelve of those rules had no dark counterpart. The visible symptom was
   the worst of the twelve: .fai-msg-ai kept `background: #f6f5f3` while a
   separate rule painted .fai-msg text with the dark theme's muted ink, giving
   light-grey text on a near-white card.

   Light mode is untouched: every rule here is inside the dark scope.
   ══════════════════════════════════════════════════════════════════════════ */

html[data-theme="dark"] .fai-msg-ai,
html[data-theme="dark"] .fai-typing {
    background: var(--color-surface-2);
    border-color: var(--color-border-subtle);
    color: var(--color-text-primary);
}

/* Inline links inside the reply. The light rule pins #111 with no dark
   counterpart, so links were near-invisible against the pale card. */
html[data-theme="dark"] .fai-msg-ai a {
    color: var(--color-accent);
    text-decoration-color: var(--color-accent-line);
}
html[data-theme="dark"] .fai-msg-ai a:hover { color: var(--color-accent-hover); }
html[data-theme="dark"] .fai-msg-ai a:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
    border-radius: 2px;
}
html[data-theme="dark"] .fai-msg-ai strong { color: var(--color-text-primary); }
html[data-theme="dark"] .fai-msg-ai br { color: inherit; }

/* The outlined action buttons under a reply ("Build your box", "Browse the
   collection"). */
html[data-theme="dark"] .fai-msg-link {
    background: var(--color-surface-3);
    border-color: var(--color-border-strong);
    color: var(--color-text-primary);
}
html[data-theme="dark"] .fai-msg-link:hover {
    background: var(--color-surface-hover);
    border-color: var(--color-accent);
    color: var(--color-text-primary);
}
html[data-theme="dark"] .fai-msg-link:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}
html[data-theme="dark"] .fai-msg-links { color: inherit; }

/* Composer: the .fai-text field, its placeholder, and the send affordance. */
html[data-theme="dark"] .fai-text {
    background: var(--color-surface-2);
    border-color: var(--color-border-strong);
    color: var(--color-text-primary);
}
html[data-theme="dark"] .fai-text::placeholder { color: var(--color-text-placeholder); opacity: 1; }
html[data-theme="dark"] .fai-text:focus {
    border-color: var(--color-accent);
    box-shadow: 0 0 0 2px var(--color-accent-soft);
    outline: none;
}
html[data-theme="dark"] .fai-text:disabled {
    background: var(--color-surface-1);
    color: var(--color-text-muted);
    border-color: var(--color-border);
}

html[data-theme="dark"] .fai-send { color: var(--color-text-primary); }
html[data-theme="dark"] .fai-send:hover:not(:disabled) {
    background: var(--color-accent-soft);
    color: var(--color-accent);
}
html[data-theme="dark"] .fai-send:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}
html[data-theme="dark"] .fai-send svg { stroke: currentColor; }

/* Status dot, header text, typing indicator, pill chrome. */
html[data-theme="dark"] .fai-dot { background: var(--color-text-tertiary); }
html[data-theme="dark"] .fai-status { color: var(--color-text-tertiary); }
html[data-theme="dark"] .fai-title { color: var(--color-text-primary); }
html[data-theme="dark"] .fai-pill-ico { color: var(--color-text-tertiary); }
html[data-theme="dark"] .fai-pill-label { color: inherit; }
html[data-theme="dark"] .fai-head-id { color: inherit; }
html[data-theme="dark"] .fai-suggest { color: inherit; }
html[data-theme="dark"] .fai-offline {
    background: var(--color-warning-soft);
    color: var(--color-warning);
}
html[data-theme="dark"] .fai-foot { color: var(--color-text-tertiary); }

/* ══════════════════════════════════════════════════════════════════════════
   WIDELY-SHARED SURFACES
   ══════════════════════════════════════════════════════════════════════════
   139 rules across the site set `background: #fff` literally. Most are inputs,
   panels, cards and chips. They are grouped here by component family rather
   than fixed one at a time, because fixing them individually is how the last
   pass missed fourteen gradients.

   Deliberate inversions are deliberately NOT in this list: active pills,
   slider knobs and primary CTAs are meant to be light fills with dark content.
   Those were verified individually in a browser before being excluded. */

/* ── Wizard / order / preorder ────────────────────────────────────────── */
html[data-theme="dark"] .wiz-pick,
html[data-theme="dark"] .wiz-option,
html[data-theme="dark"] .wiz-step,
html[data-theme="dark"] .wiz-quick-card,
html[data-theme="dark"] .wiz-ship-method,
html[data-theme="dark"] .wiz-review-edit,
html[data-theme="dark"] .wiz-ship-zip,
html[data-theme="dark"] .po-pref,
html[data-theme="dark"] .po-pill,
html[data-theme="dark"] .po-success,
html[data-theme="dark"] .view-order,
html[data-theme="dark"] .view-order,
html[data-theme="dark"] .blacklist-card {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
    color: var(--color-text-primary);
}

html[data-theme="dark"] .wiz-ship-zip::placeholder,
html[data-theme="dark"] .wiz-quick-arrow,
html[data-theme="dark"] .wiz-confirm-ico { color: var(--color-text-tertiary); }

html[data-theme="dark"] .wiz-ship-zip:focus,
html[data-theme="dark"] .wiz-review-edit:focus,
html[data-theme="dark"] .product-search input:focus {
    border-color: var(--color-accent);
    box-shadow: 0 0 0 2px var(--color-accent-soft);
    outline: none;
}

html[data-theme="dark"] .wiz-step.is-active,
html[data-theme="dark"] .wiz-quick-card.is-current {
    background-color: var(--color-surface-3);
    border-color: var(--color-accent-line);
}

/* Slider knobs sit on a track; both need to flip or the control disappears. */
html[data-theme="dark"] .wiz-fill-switch,
html[data-theme="dark"] .wiz-plan-switch {
    background: var(--color-surface-3);
    border-color: var(--color-border-strong);
}
html[data-theme="dark"] .wiz-fill-knob,
html[data-theme="dark"] .wiz-plan-knob,
html[data-theme="dark"] .wiz-fill-switch.is-selected .wiz-fill-knob,
html[data-theme="dark"] .wiz-plan-switch.is-selected .wiz-plan-knob {
    background: var(--color-text-primary);
    border-color: var(--color-text-primary);
}

html[data-theme="dark"] .po-step.is-done,
html[data-theme="dark"] .po-step.is-current { background-color: var(--color-surface-3); }
html[data-theme="dark"] .po-tl-dot,
html[data-theme="dark"] .po-ostat-dot { background-color: var(--color-text-tertiary); }
html[data-theme="dark"] .wiz-promo-chip-text strong { color: var(--color-text-primary); }

/* ── Product + collection ─────────────────────────────────────────────── */
html[data-theme="dark"] .product-card,
html[data-theme="dark"] .related-card,
html[data-theme="dark"] .fb-quickview-panel,
html[data-theme="dark"] .fb-cartdock-panel {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
    color: var(--color-text-primary);
}

html[data-theme="dark"] .product-search input,
html[data-theme="dark"] .notes-chip-host,
html[data-theme="dark"] .product-notes-filter select {
    background-color: var(--color-surface-2);
    border-color: var(--color-border-strong);
    color: var(--color-text-primary);
}
html[data-theme="dark"] .product-search input::placeholder { color: var(--color-text-placeholder); }

html[data-theme="dark"] .fb-qv-react {
    background-color: var(--color-surface-2);
    border-color: var(--color-border-strong);
    color: var(--color-text-secondary);
}
html[data-theme="dark"] .fb-qv-react:hover { background-color: var(--color-surface-hover); color: var(--color-text-primary); }
html[data-theme="dark"] .fb-qv-react svg { stroke: currentColor; }

/* ── Admin ────────────────────────────────────────────────────────────── */
html[data-theme="dark"] .ac-panel,
html[data-theme="dark"] .ac-cell,
html[data-theme="dark"] .ac-cell.is-filled,
html[data-theme="dark"] .ac-result,
html[data-theme="dark"] .ac-not-found,
html[data-theme="dark"] .ac-input,
html[data-theme="dark"] .ac-select,
html[data-theme="dark"] .admin-select,
html[data-theme="dark"] .admin-textarea,
html[data-theme="dark"] .admin-notify,
html[data-theme="dark"] .admin-filter-bar select,
html[data-theme="dark"] .intel-card,
html[data-theme="dark"] .intel-kpi,
html[data-theme="dark"] .intel-live-pill,
html[data-theme="dark"] .intel-ai-form input[type="number"],
html[data-theme="dark"] .popularity-input,
html[data-theme="dark"] .stock-adjust-btn,
html[data-theme="dark"] .fb-admin-rewards-card {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
    color: var(--color-text-primary);
}

html[data-theme="dark"] .ac-input::placeholder,
html[data-theme="dark"] .admin-textarea::placeholder,
html[data-theme="dark"] .popularity-input::placeholder { color: var(--color-text-placeholder); }

html[data-theme="dark"] .ac-input:focus,
html[data-theme="dark"] .ac-select:focus,
html[data-theme="dark"] .admin-select:focus,
html[data-theme="dark"] .admin-textarea:focus,
html[data-theme="dark"] .intel-ai-form input[type="number"]:focus,
html[data-theme="dark"] .popularity-input:focus {
    border-color: var(--color-accent);
    box-shadow: 0 0 0 2px var(--color-accent-soft);
    outline: none;
}

html[data-theme="dark"] .admin-notify-actions button,
html[data-theme="dark"] .stock-pill,
html[data-theme="dark"] .toggle-chip,
html[data-theme="dark"] .admin-filter-chips .chip,
html[data-theme="dark"] .dash-density-btn.is-active,
html[data-theme="dark"] .density-btn.is-active,
html[data-theme="dark"] .product-notes-filter.is-active,
html[data-theme="dark"] .product-notes-filter.is-active.notes-chip-host {
    background-color: var(--color-surface-2);
    border-color: var(--color-border);
    color: var(--color-text-secondary);
}
html[data-theme="dark"] .density-btn:hover,
html[data-theme="dark"] .dash-density-btn:hover { color: var(--color-text-primary); background-color: var(--color-surface-hover); }

html[data-theme="dark"] .admin-switch-knob { background-color: var(--color-text-primary); }
html[data-theme="dark"] .po-success .po-review,
html[data-theme="dark"] .bt-tip { background-color: var(--color-surface-2); color: var(--color-text-secondary); border-color: var(--color-border); }

/* ══════════════════════════════════════════════════════════════════════════
   ADMIN / DASHBOARD SHELL
   ══════════════════════════════════════════════════════════════════════════
   The admin surface is the one place a white card reads worst, because it sits
   directly on a black page with no surrounding chrome to soften the step. */

/* The page canvas stays the flat background. The admin SIDEBAR no longer
   does: it used to be pulled to --color-bg here so it would recede on a black
   page, but that made the dashboard's own navigation a different object from
   the card sections beside it, and it is the one panel a user works from all
   day. It now takes the same surface as .card so the workspace reads as one
   system, matching the customer dashboard. */
html[data-theme="dark"] body.is-admin-workspace {
    background-color: var(--color-bg);
    color: var(--color-text-primary);
}
html[data-theme="dark"] .shell--admin .side {
    background-color: var(--color-surface-1);
}

html[data-theme="dark"] .shell--admin .admin-subnav {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
    box-shadow: var(--color-shadow-sm);
}

html[data-theme="dark"] .admin-subnav a {
    color: var(--color-text-secondary);
    border-radius: 999px;
}
html[data-theme="dark"] .admin-subnav a:hover {
    color: var(--color-text-primary);
    background-color: var(--color-surface-hover);
}
html[data-theme="dark"] .admin-subnav a.active,
html[data-theme="dark"] .admin-subnav a[aria-current="page"] {
    color: var(--color-text-primary);
    background-color: var(--color-surface-3);
    box-shadow: inset 0 -2px 0 var(--color-accent);
}

html[data-theme="dark"] .side {
    background-color: var(--color-surface-1);
    /* The rule previously set only border-COLOR. .side declares no border in
       the light sheet (dashboard.php:1043), so with no width or style to act
       on, this was a no-op: dark got a filled, unoutlined slab while .card got
       surface-1 plus a 1px edge, and the two read as different objects. Stated
       in full so it matches .card exactly. box-sizing is border-box, so this
       insets the content by 1px without changing the panel's size. */
    border: 1px solid var(--color-border);
}
html[data-theme="dark"] .side-nav a {
    color: var(--color-text-secondary);
    background-color: transparent;
}
html[data-theme="dark"] .side-nav a:hover {
    color: var(--color-text-primary);
    background-color: var(--color-surface-hover);
}
html[data-theme="dark"] .side-nav a.active,
html[data-theme="dark"] .side-nav a[aria-current="page"] {
    color: var(--color-text-primary);
    background-color: var(--color-surface-3);
    box-shadow: inset 3px 0 0 var(--color-accent);
}
html[data-theme="dark"] .side-nav a:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}
html[data-theme="dark"] .side-nav-count { background-color: var(--color-surface-3); color: var(--color-text-tertiary); }
html[data-theme="dark"] .greeting-label { color: var(--color-text-tertiary); }
html[data-theme="dark"] .greeting-name { color: var(--color-text-primary); }

/* ── Top navigation + profile dropdown ─────────────────────────────────
   The dropdown is a floating surface, so it also needs a shadow that reads
   on black rather than the light-theme drop shadow. */
html[data-theme="dark"] .nav,
html[data-theme="dark"] #nav-el,
html[data-theme="dark"] .nav-account-menu {
    background-color: rgba(11, 11, 11, 0.94);
    border-color: var(--color-border);
    box-shadow: var(--color-shadow-pop);
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
}

html[data-theme="dark"] .nav-account-item {
    color: var(--color-text-secondary);
    background-color: transparent;
}
html[data-theme="dark"] .nav-account-item:hover,
html[data-theme="dark"] .nav-account-item:focus-visible {
    color: var(--color-text-primary);
    background-color: var(--color-surface-hover);
    outline: none;
}
html[data-theme="dark"] .nav-account-item.danger { color: var(--color-danger); }
html[data-theme="dark"] .nav-account-header {
    background-color: transparent;
    border-bottom-color: var(--color-border-subtle);
}
html[data-theme="dark"] .nav-account-header-name { color: var(--color-text-primary); }
html[data-theme="dark"] .nav-account-header-email { color: var(--color-text-tertiary); }
html[data-theme="dark"] .nav-account-divider { background-color: var(--color-border-subtle); }
html[data-theme="dark"] .nav-account-trigger { color: var(--color-text-primary); }
html[data-theme="dark"] .nav-search-panel { background-color: var(--color-surface-1); border-color: var(--color-border); }

/* ── Buttons: the generic ghost/primary pair ────────────────────────────
   Only `.admin-notify .btn-ghost` had a dark rule; the bare `.btn-ghost` used
   across the dashboard and wizard had none, so it kept the light fill. */
html[data-theme="dark"] .btn-ghost {
    background-color: transparent;
    border-color: var(--color-border-strong);
    color: var(--color-text-secondary);
}
html[data-theme="dark"] .btn-ghost:hover {
    background-color: var(--color-surface-hover);
    border-color: var(--color-border-strong);
    color: var(--color-text-primary);
}
html[data-theme="dark"] .btn-ghost:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
}
html[data-theme="dark"] .btn-ghost:disabled {
    opacity: 0.5;
    color: var(--color-text-muted);
}

/* ── SPA page container ─────────────────────────────────────────────────
   `.page` and the footer carry no background of their own, but the
   white behind them did. Pin them to the canvas so the route that is
   showing can never flash a white sheet. */
html[data-theme="dark"] .page,
html[data-theme="dark"] .page-hidden,
html[data-theme="dark"] .brand,
html[data-theme="dark"] footer#footer-el {
    background-color: transparent;
    color: var(--color-text-primary);
}

/* ── Send button, both states ────────────────────────────────────────────
   The composer starts disabled because the AI endpoint is quota-gated
   (`sendable` comes from the server, not from the theme). WCAG exempts
   inactive controls from the contrast minimum, but "exempt" is not a reason to
   make the arrow invisible — at the inherited 0.35 opacity the glyph sat at
   3.2:1 and read as a smudge. The disabled state is now clearly present while
   still plainly distinguishable from the enabled one. */
html[data-theme="dark"] .fai-send:disabled {
    color: var(--color-text-tertiary);
    background-color: transparent;
    opacity: 0.85;
}

html[data-theme="dark"] .fai-send:not(:disabled) {
    color: var(--color-text-primary);
    background-color: var(--color-surface-3);
    opacity: 1;
}
html[data-theme="dark"] .fai-send:not(:disabled):hover {
    background-color: var(--color-accent-soft);
    color: var(--color-accent);
}

/* ══════════════════════════════════════════════════════════════════════════
   AUDIT SWEEP — components that carry a light-mode literal and no dark peer
   ══════════════════════════════════════════════════════════════════════════
   Added 2026-09-28. A contrast sweep that walked every text-bearing leaf
   element on 13 routes (rather than a hand-picked selector list) found 284
   below-AA nodes across 26 classes. Every one has the same shape:

       some-stylesheet.css:  .thing { color: rgba(0,0,0,0.45) }

   authored for the light canvas, with no counterpart in this file. The generic
   dark rule at :414 only covers `p, li, dd`, so anything that is a `span`,
   `strong`, `em`, `select` or `button` — or that carries a class colour at all
   — keeps painting black onto a #0b0b0b surface.

   These are grouped by the ROLE they play, not one rule per class, and every
   value is a semantic token. Measured against --color-surface-1 (#0b0b0b):
   primary 17.21:1, secondary 9.60:1, tertiary 5.85:1, success 9.43:1.

   Light mode is untouched — every selector below is dark-scoped. */

/* — Primary: emphasised data and prose emphasis ——————————————————————
   These are `strong`/`em` nested inside copy that IS already themed, so they
   escaped the :414 rule by element type rather than by class. */
html[data-theme="dark"] .fb-cartdock-note strong,
html[data-theme="dark"] .product-pickhint strong,
html[data-theme="dark"] .product-summary-count strong,
html[data-theme="dark"] .nav-search-empty-body em,
html[data-theme="dark"] .nav-search-close,
html[data-theme="dark"] .product-filter select { color: var(--color-text-primary); }

/* — Secondary: readable body copy ————————————————————————————————— */
html[data-theme="dark"] .fb-cartdock-note,
html[data-theme="dark"] .wiz-opt-name,
html[data-theme="dark"] .wiz-opt-desc,
html[data-theme="dark"] .wiz-review-availability,
html[data-theme="dark"] .order-view,
html[data-theme="dark"] .bt-replay,
html[data-theme="dark"] .faq-body p,
html[data-theme="dark"] .faq-body-inner p,
html[data-theme="dark"] .mp-faq-item p { color: var(--color-text-secondary); }

/* — Tertiary: quiet labels, meta and hairline copy ————————————————————
   Deliberately one tier down from the values they annotate, so a label never
   competes with the datum beside it. This is the same label-quiet/value-clear
   split applied to the dashboard data rows. */
html[data-theme="dark"] .fb-cartdock-item-brand,
html[data-theme="dark"] .collection-faq-hint,
html[data-theme="dark"] .wiz-pick-sub,
html[data-theme="dark"] .wiz-review-label,
html[data-theme="dark"] .wiz-ship-method-desc,
html[data-theme="dark"] .wiz-ship-hint,
html[data-theme="dark"] .wiz-edit,
html[data-theme="dark"] .wiz-opt-price-sub,
html[data-theme="dark"] .wiz-opt-price-from,
html[data-theme="dark"] .fb-frq-label,
html[data-theme="dark"] .blacklist-card-remove,
html[data-theme="dark"] .page-footer,
html[data-theme="dark"] .pc-stat { color: var(--color-text-tertiary); }

/* — Success: the green reactions and shipping line ————————————————————
   The light literals were #1c7d3f (3.80:1) and #1a6b2a (2.98:1) on the dark
   card. Hue is preserved by the token; only the lightness step changes, the
   same way the gold accent is re-stepped rather than inverted. */
html[data-theme="dark"] .fb-cartdock-ship strong,
html[data-theme="dark"] .fb-cartdock-ship svg,
html[data-theme="dark"] .pc-stat-like.is-mine { color: var(--color-success); }

/* ══════════════════════════════════════════════════════════════════════════
   AUDIT SWEEP — components carrying a light-mode literal with no dark peer
   ══════════════════════════════════════════════════════════════════════════
   Added 2026-09-28. A sweep that walked every text-bearing leaf element on 13
   routes — rather than a hand-picked selector list, which is how the account
   page defect survived earlier passes — found 284 below-AA nodes across 26
   classes. Every one has the same shape:

     cart-dock.css:316   .fb-cartdock-note strong { color: #000; }

   authored for the light canvas, with no counterpart here. The catch is
   structural: the generic dark rule at :414 covers `p, li, dd`, so anything
   that is a span, strong, em, select or button — or that carries a class
   colour of its own — keeps painting black onto a #0b0b0b surface.

   Grouped by the ROLE each plays, not one rule per class, every value a
   semantic token. Against --color-surface-1 (#0b0b0b): primary 17.21:1,
   secondary 9.60:1, tertiary 5.85:1, success 9.43:1. All dark-scoped, so
   light mode is untouched. */

/* — Primary: emphasised data and prose emphasis ——————————————————————
   These are strong/em nested inside copy that IS already themed, so they
   escaped :414 by element type rather than by class. */
html[data-theme="dark"] .fb-cartdock-note strong,
html[data-theme="dark"] .product-pickhint strong,
html[data-theme="dark"] .product-summary-count strong,
html[data-theme="dark"] .wiz-opt-count strong,
html[data-theme="dark"] .nav-search-empty-body em,
html[data-theme="dark"] .nav-search-close,
html[data-theme="dark"] .pdp-blacklist-btn,
html[data-theme="dark"] .empty-text em,
html[data-theme="dark"] .product-filter select { color: var(--color-text-primary); }

/* — Secondary: readable body copy ————————————————————————————————— */
html[data-theme="dark"] .fb-cartdock-note,
html[data-theme="dark"] .empty-text,
html[data-theme="dark"] .wiz-opt-name,
html[data-theme="dark"] .wiz-opt-desc,
html[data-theme="dark"] .wiz-review-availability,
html[data-theme="dark"] .order-view,
html[data-theme="dark"] .bt-replay,
html[data-theme="dark"] .faq-body p,
html[data-theme="dark"] .faq-body-inner p,
html[data-theme="dark"] .mp-faq-item p { color: var(--color-text-secondary); }

/* — Tertiary: quiet labels, meta and hairline copy ————————————————————
   Deliberately one tier below the value they annotate, so a label never
   competes with the datum beside it. Same label-quiet/value-clear split the
   dashboard data rows use. */
html[data-theme="dark"] .fb-cartdock-item-brand,
html[data-theme="dark"] .collection-faq-hint,
html[data-theme="dark"] .wiz-pick-sub,
html[data-theme="dark"] .wiz-review-label,
html[data-theme="dark"] .wiz-ship-method-desc,
html[data-theme="dark"] .wiz-ship-hint,
html[data-theme="dark"] .wiz-edit,
html[data-theme="dark"] .wiz-opt-price-sub,
html[data-theme="dark"] .wiz-opt-price-from,
html[data-theme="dark"] .wiz-opt-count,
html[data-theme="dark"] .fb-frq-label,
html[data-theme="dark"] .blacklist-card-remove,
html[data-theme="dark"] .page-footer,
html[data-theme="dark"] .pc-stat { color: var(--color-text-tertiary); }

/* — Sold-out state ———————————————————————————————————————————————————
   wizard.css:422-432 fade these to 0.4/0.45 black on the LIGHT canvas. Two
   classes outrank the rules above, so the state form is matched here. Sold-out
   must still be readable — de-emphasised, not hidden. */
html[data-theme="dark"] .wiz-option.is-soldout .wiz-opt-name,
html[data-theme="dark"] .wiz-option.is-soldout .wiz-opt-desc,
html[data-theme="dark"] .wiz-pick.is-soldout .wiz-pick-sub {
    color: var(--color-text-tertiary);
}

/* — Success: reaction counts and the shipping line ————————————————————
   The light literals were #1c7d3f (3.80:1) and #1a6b2a (2.98:1) on the dark
   card. Hue is preserved by the token; only the lightness step changes, the
   same way the gold accent is re-stepped rather than inverted. */
html[data-theme="dark"] .fb-cartdock-ship strong,
html[data-theme="dark"] .fb-cartdock-ship svg,
html[data-theme="dark"] .pc-stat-like.is-mine { color: var(--color-success); }

/* ── Last three from the sweep ───────────────────────────────────────────
   Two of these were inline `color:` declarations, which outrank every
   stylesheet rule — the dark value could never land until the colour moved
   into a class (see .wiz-review-availability in wizard.css and
   .card-note-body in dashboard.php; both keep their original light value). */
html[data-theme="dark"] .wiz-cart-sub { color: var(--color-text-secondary); }
html[data-theme="dark"] .wiz-cart-sub strong { color: var(--color-text-primary); }
html[data-theme="dark"] .wiz-restart { color: var(--color-text-secondary); }
html[data-theme="dark"] .wiz-restart:hover { color: var(--color-text-primary); }
html[data-theme="dark"] .card-note-body { color: var(--color-text-secondary); }

/* ── Two more light literals surfaced by the cart-dock / product states ────
   Found by opening the dock with items in it, which earlier passes never did. */
html[data-theme="dark"] .fb-cartdock-progress-fill {
    background: var(--color-accent);
}
/* .pdp-cta.pdp-cta-active is deliberately inverted (white fill, black text) —
   the same pattern as .adx-btn--primary, and 21:1 on its own. It is NOT a
   theme leak and is allowlisted in the contrast audit with this reasoning. */

/* ══════════════════════════════════════════════════════════════════════════
   ADMIN WORKSPACE + 404 — the second sweep
   ══════════════════════════════════════════════════════════════════════════
   Added 2026-09-28. The first sweep covered customer routes and the one admin
   section it knew about. The admin workspace has SIXTEEN sections, so the
   first pass measured roughly a sixteenth of it. Extending the sweep found 558
   below-AA nodes across 34 classes — the same mechanism as before (a
   light-mode literal with no dark counterpart), just never looked at.

   Every tier below is assigned from the element's LIGHT value, not by eye:
   #000 -> primary, 0.5-0.82 -> secondary, 0.4-0.45 -> tertiary, green/red ->
   the status tokens. Specificity was checked per selector; where the light
   rule carries two classes (.admin-subnav a.active, .intel-num.intel-num-strong
   and friends) the dark selector is written to outrank it. */

/* — Primary: data values, product names, the active nav item ————————————
   .admin-subnav a.active is (0,2,1) in the light sheet, so the dark selector
   is written at (0,3,2) to beat it. It is also given the gold underline the
   brand uses elsewhere, so the current section is unmistakable in dark. */
html[data-theme="dark"] .admin-subnav a.active {
    color: var(--color-text-primary);
    border-bottom-color: var(--color-accent);
}
html[data-theme="dark"] .admin-metric-row-val,
html[data-theme="dark"] .intel-prod-name,
html[data-theme="dark"] .intel-num-strong,
html[data-theme="dark"] .intel-vis-name,
html[data-theme="dark"] .admin-tier-key,
html[data-theme="dark"] .nf-card-b,
html[data-theme="dark"] .nf-card-b strong { color: var(--color-text-primary); }

/* — Secondary: body copy, values that are context rather than data ———————— */
html[data-theme="dark"] .admin-metric-row-label,
html[data-theme="dark"] .admin-field-hint,
html[data-theme="dark"] .admin-switch-label,
html[data-theme="dark"] .admin-switch-label span,
html[data-theme="dark"] .admin-user-email,
html[data-theme="dark"] .admin-metric-title,
html[data-theme="dark"] .admin-metric-highlight-label,
html[data-theme="dark"] .admin-subhead,
html[data-theme="dark"] .admin-td-muted,
html[data-theme="dark"] .intel-prod-brand,
html[data-theme="dark"] .intel-tier-label,
html[data-theme="dark"] .intel-tier-num,
html[data-theme="dark"] .intel-url,
html[data-theme="dark"] .nf-search-ph,
html[data-theme="dark"] .nf-search-ph b,
html[data-theme="dark"] .nf-card-b span { color: var(--color-text-secondary); }

/* — Tertiary: labels, meta, timestamps ————————————————————————————————
   Set on the TABLE rather than on td, so the cell default is themed without
   out-specifying .intel-num-strong, which carries its own primary rule. */
html[data-theme="dark"] .intel-table { color: var(--color-text-secondary); }
html[data-theme="dark"] .stock-qty-meta,
html[data-theme="dark"] .admin-spark-cap,
html[data-theme="dark"] .admin-field-label,
html[data-theme="dark"] .admin-hint,
html[data-theme="dark"] .intel-stream-time,
html[data-theme="dark"] .intel-stream-url,
html[data-theme="dark"] .intel-card-meta,
html[data-theme="dark"] .intel-kpi-sub,
html[data-theme="dark"] .intel-table th,
html[data-theme="dark"] .intel-vis-meta,
html[data-theme="dark"] .fb-promo-code-label,
html[data-theme="dark"] .fb-promo-code-action-text,
html[data-theme="dark"] .nf-path,
html[data-theme="dark"] .nf-path code { color: var(--color-text-tertiary); }

/* — Status: profit green and loss red ——————————————————————————————————
   The margin value was assigned with el.style.color in JS, which no rule can
   override; it now toggles a class like its sibling two lines above. */
html[data-theme="dark"] .admin-metric-row-val.is-green,
html[data-theme="dark"] .admin-metric-highlight-val.is-green { color: var(--color-success); }
html[data-theme="dark"] .admin-metric-row-val.is-red,
html[data-theme="dark"] .admin-metric-highlight-val.is-red { color: var(--color-danger); }

/* — Placeholders that had NO dark rule at all ————————————————————————
   .adx-input, .fbh-search input and .nav-search-input each carried a
   light-only placeholder and nothing in this file ever matched them. */
html[data-theme="dark"] .adx-input::placeholder,
html[data-theme="dark"] .fbh-search input::placeholder,
html[data-theme="dark"] .nav-search-input::placeholder { color: var(--color-text-placeholder); }

/* — Event-kind badges ————————————————————————————————————————————————
   White ink on the light fills fails AA, and the four failures are NOT the
   four that happened to render in today's data — checkout, marketplace, promo
   and meta all fail. Measured black-on-fill: 8.28, 5.90, 7.15, 8.19.
   The other five are dark enough that black would break them, so they are
   restated as white to stop a blanket rule flipping them later.

   The four are written as COMPOUND selectors (.intel-stream-kind.X) rather
   than single classes. As single classes they tied with the white rule below
   at (0,2,1) and lost on source order, which is how the meta badge survived
   the first attempt still white. Compound is (0,3,1) and wins outright. */
html[data-theme="dark"] .intel-stream-kind.intel-stream-kind--checkout,
html[data-theme="dark"] .intel-stream-kind.intel-stream-kind--marketplace,
html[data-theme="dark"] .intel-stream-kind.intel-stream-kind--promo,
html[data-theme="dark"] .intel-stream-kind.intel-stream-kind--meta { color: var(--color-bg); }
html[data-theme="dark"] .intel-stream-kind,
html[data-theme="dark"] .intel-stream-kind--page,
html[data-theme="dark"] .intel-stream-kind--product,
html[data-theme="dark"] .intel-stream-kind--cart,
html[data-theme="dark"] .intel-stream-kind--search,
html[data-theme="dark"] .intel-stream-kind--auth { color: #ffffff; }

/* — Last two from the admin sweep ————————————————————————————————————————
   .admin-self-tag is the "You" marker beside the signed-in user's row; it was
   an inline colour rendering 101 times across the users table and the security
   audit log, and no rule could reach it.

   .intel-tabs a.is-active is (0,2,1) in the light sheet, so the dark selector
   is written at (0,3,1). The sibling .intel-filterbar a.is-active is white on
   black in BOTH themes and is a deliberate inversion, so it is left alone. */
html[data-theme="dark"] .admin-self-tag { color: var(--color-text-tertiary); }
html[data-theme="dark"] .intel-tabs a.is-active {
    color: var(--color-text-primary);
    border-bottom-color: var(--color-accent);
}

/* — Security-log severity ————————————————————————————————————————————
   The amber measured 4.31:1 on the dark canvas, just under AA. Hue is carried
   by the status tokens, which clear it comfortably: danger 6.63:1, warning
   10.05:1. low/info were plain greys and become the tertiary text step. */
html[data-theme="dark"] .admin-sev { color: var(--color-text-tertiary); }
html[data-theme="dark"] .admin-sev--critical,
html[data-theme="dark"] .admin-sev--high { color: var(--color-danger); }
html[data-theme="dark"] .admin-sev--medium { color: var(--color-warning); }

/* ══════════════════════════════════════════════════════════════════════════
   NAV + PROMO BAR — consistency pass
   ══════════════════════════════════════════════════════════════════════════
   The storefront nav is already global: every page includes includes/nav.php
   and only sets $navActive. Two real defects, both the same shape as the rest
   of this work — a light-only literal with no dark counterpart. */

/* .nav-search-input carries color:#000 in nav.css. Its ::placeholder was
   themed, which made it LOOK covered, but the input's own text stayed black
   and measured 1.07:1 on the #0b0b0b search panel. The search field was the
   one part of the top bar you could not read. */
html[data-theme="dark"] .nav-search-input { color: var(--color-text-primary); }

/* The promo bar is #0a0a0a in BOTH themes. On white that reads as a strong
   black band; on #000 it is a 1.02:1 difference and the bar stops reading as
   an element at all — the text passed contrast while the bar itself vanished.
   Lifting it to surface-2 and giving the hairline real weight restores the
   band in dark without touching the light design, which is already correct. */
html[data-theme="dark"] .fb-promo {
    background-color: var(--color-surface-2);
    border-bottom-color: var(--color-border-strong);
}

/* ══════════════════════════════════════════════════════════════════════════
   PROFILE DROPDOWN ICONS — the black-outline bug
   ══════════════════════════════════════════════════════════════════════════
   The markup is correct: every icon is <svg stroke="currentColor"> with no
   hardcoded paint. But nav.css:243 sets `.nav-account-item svg { color:
   rgba(0,0,0,.5) }`, and an explicit CSS `color` outranks the presentation
   attribute — so currentColor never re-resolves and the glyphs stayed black at
   50% on the #0b0b0b dropdown. Measured, not inferred: stroke computed to
   rgba(0,0,0,0.5) in dark and the correct rgba(0,0,0,0.5) in light.

   The same defect exists in the four hover/danger variants and in the
   dashboard's forked copy, so the whole family is restated here. Icons sit one
   step below their label's ink, which is why they are tertiary rather than
   secondary. */
html[data-theme="dark"] .nav-account-item svg { color: var(--color-text-tertiary); }
html[data-theme="dark"] .nav-account-item:hover svg { color: var(--color-text-primary); }
html[data-theme="dark"] .nav-account-item.danger svg { color: var(--color-danger); }
html[data-theme="dark"] .nav-account-item.danger:hover svg { color: var(--color-danger); }

/* The cart dock's shipping tick is the same shape of bug: #1c7d3f measures
   3.80:1 on the dark card. The rule beside it (.fb-cartdock-note) is brand
   gold at #b89a4e and is intentional, so it is left alone. */
html[data-theme="dark"] .fb-cartdock-ship svg { color: var(--color-success); }

/* The side-panel divider is rgba(0,0,0,.08) in both themes. On white that is
   a faint grey rule; on #0b0b0b it is a black line at 8% and effectively not
   there, so the two groups ran together. The dark value has to be a LIGHT
   translucent, not a dark one — the mirror of the light treatment. */
html[data-theme="dark"] .side-nav-divider {
    background: var(--color-border-subtle);
}

/* ── Security-severity tiles (admin > security) ───────────────────────────
The tile count, its "hot" red and the tile border were inline light-only
literals. The red measured 2.87:1 on the dark canvas, and a border of
rgba(0,0,0,.08) is a black line at 8% on a near-black page, so the tiles had
no visible edge either. Both now read from the token layer. */
html[data-theme="dark"] .admin-sev-tile {
    border-color: var(--color-border);
    background-color: var(--color-surface-1);
}
html[data-theme="dark"] .admin-sev-tile-val.is-hot {
    color: var(--color-danger);
}

/* ── Product page engage lock ──────────────────────────────────────────────
product.php sets .pdp-engage-lock to a light-canvas grey literal and never
counters it, so the lock text was near-black on a dark surface. The darker
surface ladder made that measurable (1.13:1 on surface-3), but the defect was
latent before that: the same rule was unreadable at any dark surface value. */
html[data-theme="dark"] .pdp-engage-lock {
    color: var(--color-text-tertiary);
}

/* ── Admin stat tiles with a semantic value colour ─────────────────────────
The green/amber/red stat values were inline styles, so no rule could reach them
and they stayed on their light-canvas values in dark mode. The light literals
are preserved on .admin-stat-val--* in dashboard.php; these are the dark
counterparts, sourced from the status tokens rather than repeated literals. */
html[data-theme="dark"] .admin-stat-val--good   { color: var(--color-success); }
html[data-theme="dark"] .admin-stat-val--warn   { color: var(--color-warning); }
html[data-theme="dark"] .admin-stat-val--danger { color: var(--color-danger); }

/* ════════════════════════════════════════════════════════════════════════════
   THEME CONTROL — collapsed by default, unfolds on demand
   ════════════════════════════════════════════════════════════════════════════
   Replaces a permanently-visible three-button segmented control. The previous
   build was `display:inline-flex` with three 2.75rem buttons, so it held
   ~140px of the header at all times and the active mode was one of three equal
   options rather than the headline state.

   Structure: .fb-theme > .fb-theme-trigger (always visible, shows the ACTIVE
   PREFERENCE) + .fb-theme-opts (the three options, revealed on demand).

   Sizing is driven by custom properties so the control can be retuned without
   touching selectors. The container is fixed-height and the options cross-fade
   in place, so the header does not reflow when the control opens. Below 821px
   the option row is overlaid instead of expanding inline, because at that
   width an inline expansion would push the account control off-screen.

   Colour comes entirely from the existing token layer, so this block is theme
   agnostic and adds no dark-only overrides.
   ─────────────────────────────────────────────────────────────────────────── */

.fb-theme {
  /* 46px outer with a 1px border and no vertical padding gives a 44px
     content box, which is the tap target the brief asks for. The neighbours in
     this header are 40px, so 46 still sits comfortably inside the 60px bar. */
  --fb-theme-h: 2.875rem;
  --fb-theme-collapsed: 2.75rem;
  --fb-theme-icon: 1.125rem;
  --fb-theme-pad: 0.1875rem;
  --fb-theme-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --fb-theme-dur: 190ms;

  position: relative;
  display: inline-flex;
  align-items: center;
  height: var(--fb-theme-h);
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-surface-1);
}

.fb-theme-ico {
  width: var(--fb-theme-icon);
  height: var(--fb-theme-icon);
  display: block;
  flex: none;
  pointer-events: none;
}

.fb-theme-trigger {
  -webkit-appearance: none;
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: calc(var(--fb-theme-h) - 2px);
  height: 100%;
  padding: 0 0.625rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--color-text-primary);
  cursor: pointer;
  transition: background-color var(--fb-theme-dur) var(--fb-theme-ease);
}
.fb-theme-trigger:hover { background: var(--color-surface-hover); }
.fb-theme-trigger:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* The option row is an anchored popover at EVERY width, not an inline
   expansion. Inline growth was measured pushing the account control 196px to
   the right at 1024px and 1440px — a visible header jump, which the brief
   explicitly forbids. An overlay cannot reflow the header at any viewport, and
   it is the conventional pattern for a fixed bar with a backdrop blur.

   Reveal is transform + opacity so the browser animates on the compositor and
   no layout is recalculated per frame. */
.fb-theme-opts {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 2px;
  height: var(--fb-theme-h);
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-surface-1);
  box-shadow: var(--color-shadow-pop);
  opacity: 0;
  transform: translateY(-4px) scale(0.97);
  transform-origin: top right;
  pointer-events: none;
  transition: opacity calc(var(--fb-theme-dur) * 0.8) var(--fb-theme-ease),
              transform var(--fb-theme-dur) var(--fb-theme-ease);
}
.fb-theme[data-open="true"] .fb-theme-opts {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

.fb-theme-btn {
  -webkit-appearance: none;
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  flex: none;
  height: 100%;
  padding: 0 0.625rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--color-text-secondary);
  font: inherit;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--fb-theme-dur) var(--fb-theme-ease),
              color var(--fb-theme-dur) var(--fb-theme-ease);
}
.fb-theme-btn:hover { color: var(--color-text-primary); background: var(--color-surface-hover); }
.fb-theme-btn:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }

/* Selected state carries colour AND a border, so it is not signalled by hue
   alone. */
.fb-theme-btn[aria-pressed="true"] {
  color: var(--color-text-primary);
  background: var(--color-surface-active);
  border: 1px solid var(--color-border-strong);
  padding: 0 calc(0.625rem - 1px);
}

/* Labels are a progressive enhancement: hidden on touch-width controls where
   three labelled options would crowd the header. */
.fb-theme-txt { display: inline; }

/* ── Narrow viewports: icons only, three labelled options would crowd the bar ── */
@media (max-width: 820px) {
  .fb-theme-txt { display: none; }
  .fb-theme-btn { padding: 0 0.8125rem; }
  .fb-theme-btn[aria-pressed="true"] { padding: 0 calc(0.8125rem - 1px); }
}

/* ── Reduced motion: state changes still work, they just do not animate ───── */
@media (prefers-reduced-motion: reduce) {
  .fb-theme-opts,
  .fb-theme-btn,
  .fb-theme-trigger { transition: none; }
}

/* ── Admin overview stat cards + sparklines ────────────────────────────────
Three light-canvas literals reached this block with no dark counterpart, so in
dark mode the whole row failed:

  .admin-spark-wrap            color rgba(0,0,0,.45)  -> measured 1.07:1 on the
                               #0b0b0b canvas. A black line chart on a black
                               card: the Users, Orders and Products sparklines
                               were effectively invisible.
  .admin-spark-wrap[gold]      color #8a6a00          -> 3.88:1. Visible, but
                               a hardcoded dark gold picked for the light
                               canvas rather than the theme's accent.
  .admin-stat                  border rgba(0,0,0,.08) -> a black line at 8% on a
                               near-black card, so the cards had no edge at
                               all; their only separator is the border.

The values below are the faithful dark mirror of the light treatment, read from
the token layer. The cards stay transparent exactly as in light — the border is
the separator in both themes, it just has to be a light translucent in dark. */
html[data-theme="dark"] .admin-spark-wrap {
  color: var(--color-text-tertiary);
}
html[data-theme="dark"] .admin-spark-wrap[data-tone="gold"] {
  color: var(--color-accent);
}
html[data-theme="dark"] .admin-stat {
  border-color: var(--color-border);
}

/* ── Admin queue sublines + clickable rows ─────────────────────────────────
Nine inline `color:rgba(0,0,0,…)` declarations sat directly on the spans in
the sell and auth queue tables. An inline style outranks every stylesheet rule
short of `!important`, so none of them could be themed and all of them rendered
black-on-black in dark mode — the seller email under the name measured 1.1:1
against the #101010 cell. The colours now live in `.admin-cell-sub`,
`.admin-subtitle` and `.admin-cell-note`, declared with their original light
values in dashboard.php and given dark values here.

The queue row is also a target now (`tr[data-href]`), so the hover tint has to
exist in both themes. */
html[data-theme="dark"] .admin-cell-sub,
html[data-theme="dark"] .admin-subtitle {
  color: var(--color-text-secondary);
}
html[data-theme="dark"] .admin-cell-note {
  color: var(--color-text-primary);
}
html[data-theme="dark"] .admin-table tbody tr[data-href]:hover > td,
html[data-theme="dark"] .admin-table tbody tr[data-href]:focus-within > td {
  background: var(--color-surface-hover);
}

/* ── Native selects carrying .admin-input ───────────────────────────────────
The queue filters are `<select class="admin-input">`, but the dark surface
group above lists `.admin-select` and not `.admin-input`, so they kept the
light `background:#fff` and the "All open" box rendered as a white rectangle
in dark mode. Both classes name the same control family; both are listed now
so the next one added is already covered. */
html[data-theme="dark"] select.admin-input,
html[data-theme="dark"] .admin-filter-bar select {
    background-color: var(--color-surface-1);
    border-color: var(--color-border);
    color: var(--color-text-primary);
}
html[data-theme="dark"] select.admin-input:focus {
    border-color: var(--color-accent);
    box-shadow: 0 0 0 2px var(--color-accent-soft);
    outline: none;
}

/* ── Order review page (/dashboard/orders/<ref>) ────────────────────────────
order-detail.php and preorder.css carried light-canvas literals for the status
summary, the "Paid" pill, the information card and the definition-list term
labels, with no dark counterpart. Measured on the order review page in dark:
the five status labels and eight term labels sat at 1.08:1 (black on near-black)
and the information card was still painted rgba(255,255,255,.72).

The curation panel that dominates this page is themed by the --oc-* palette in
includes/order_curation_panel.php, which now reads the same tokens used here,
so the page matches the main product page rather than carrying its own dark
palette. */
html[data-theme="dark"] .po-ostat-label,
html[data-theme="dark"] .po-info-card b,
html[data-theme="dark"] .po-info-card span {
  color: var(--color-text-primary);
}
html[data-theme="dark"] .po-info-card {
  border-color: var(--color-border);
  background: var(--color-surface-1);
}
html[data-theme="dark"] .po-status-paid {
  color: var(--color-success);
  background: var(--color-success-soft);
}
html[data-theme="dark"] .po-review-row .k {
  color: var(--color-text-tertiary);
}
/* The fulfilment-step rail sets its own colours on state classes, which carry
   MORE specificity than the plain dark rules above, so each state is restated
   here. `.po-ostat-num` has no rule of its own and was inheriting the light
   ink from its container. */
html[data-theme="dark"] .po-ostat-item.is-todo .po-ostat-label {
  color: var(--color-text-tertiary);
}
html[data-theme="dark"] .po-ostat-num {
  color: var(--color-text-primary);
}
html[data-theme="dark"] .po-ostat-item.is-done .po-ostat-dot {
  background: var(--color-text-primary);
  border-color: var(--color-text-primary);
  color: var(--color-bg);
}
html[data-theme="dark"] .po-ostat-item.is-done::before,
html[data-theme="dark"] .po-ostat-item.is-current::before {
  background: var(--color-text-secondary);
}
html[data-theme="dark"] .po-ostat-item.is-current {
  background: var(--color-surface-hover);
}

/* ── Cart dock count badge ─────────────────────────────────────────────────
Found by sweeping with a session that had items in the cart, which is why it
never appeared before: the badge is only rendered when the count is non-zero.
It paints white on the brand gold #b89a4e, which measures 2.7:1, and carries a
hardcoded `outline: 2px solid #000` — a black ring that also disappears against
the dark header. Fourteen routes were affected.

The gold is kept, because it is the brand accent, but the ink inverts: a solid
gold pill with a near-black numeral is both readable and a stronger badge than
the washed-out white-on-gold it replaces. */
html[data-theme="dark"] .fb-cartdock-count {
  background: var(--color-accent);
  color: var(--color-bg);
  outline-color: var(--color-bg);
}

/* ── Two more gold count chips, and two unthemed strongs ────────────────────
`.collection-cta-count` is the same badge as the cart dock count — brand gold
with white ink, 2.7:1 — on the collection page, and had no dark rule at all.
The two <strong> elements below inherit their ink from a light-canvas parent,
so a container-level dark rule never reached them. */
html[data-theme="dark"] .collection-cta-count {
  background: var(--color-accent);
  color: var(--color-bg);
}
html[data-theme="dark"] .pdp-pick-counter strong,
html[data-theme="dark"] .fb-cartdock-empty strong {
  color: inherit;
}
html[data-theme="dark"] .pdp-pick-counter {
  border-color: var(--color-border);
  background: var(--color-surface-2);
  color: var(--color-text-secondary);
}
html[data-theme="dark"] .fb-cartdock-empty {
  color: var(--color-text-secondary);
}

/* ── Order review page: the admin-only banner ───────────────────────────────
Only rendered for staff, so it is invisible to a customer sweep and only shows
up when the audit runs with an admin session. The banner kept a white fill and
a dashed black border, then the admin label and the customer's own email link
were painted in near-black and near-white on top of it: 1.14:1 and 2.05:1. */
html[data-theme="dark"] .po-admin-banner {
  border-color: var(--color-border);
  background: var(--color-surface-2);
  color: var(--color-text-secondary);
}
html[data-theme="dark"] .po-admin-label {
  color: var(--color-text-tertiary);
}
html[data-theme="dark"] .po-ship-link {
  color: var(--color-text-primary);
}

/* ── Order review page: the admin fulfilment cards ─────────────────────────
`.po-ship-card` and `.po-admin-card` are the two staff-only panels on this
page. Both kept a near-white fill (rgba(255,255,255,.86)) and a black border,
so the customer's email link and the admin labels sat on a white sheet inside
a black page: 1.14:1 and 3.36:1 measured. They are invisible to a customer
session, so the audit only reaches them as an admin. */
html[data-theme="dark"] .po-ship-card,
html[data-theme="dark"] .po-admin-card {
  background: var(--color-surface-1);
  border-color: var(--color-border);
  box-shadow: var(--color-shadow-card);
  color: var(--color-text-primary);
}
html[data-theme="dark"] .po-ship-k {
  color: var(--color-text-tertiary);
}
html[data-theme="dark"] .po-admin-banner-dot {
  background: var(--color-text-tertiary);
}

/* ── Order review page: the values inside the fulfilment cards ──────────────
These were masked by the white card they sat on: fixing the card turned the
page dark and revealed them. `.po-ship-v` carried a hardcoded #0a0a0a, which
on the now-dark surface measured 1.04:1 for the customer's name, address and
email. */
html[data-theme="dark"] .po-ship-v {
  color: var(--color-text-primary);
}
html[data-theme="dark"] .po-ship-v.is-missing {
  color: var(--color-text-tertiary);
}
html[data-theme="dark"] .po-ship-src {
  color: var(--color-text-tertiary);
}

/* ── Order review page: the admin edit controls ─────────────────────────────
The staff-only fulfilment form kept native white inputs and a native white
select on the dark card. Same pattern as the queue filter: a light-canvas
background with no dark counterpart. */
html[data-theme="dark"] .po-admin-sel,
html[data-theme="dark"] .po-admin-inp {
  background: var(--color-surface-2);
  border-color: var(--color-border);
  color: var(--color-text-primary);
}
html[data-theme="dark"] .po-admin-inp::placeholder {
  color: var(--color-text-placeholder);
}

/* ── Order review page: dark surfaces that were not on the ladder ───────────
The contrast audit passes on this page but it only checks TEXT and bright
leftovers. It never asked whether a DARK surface is the right shade, and five
were not:

  input.oc-search       #3b3b3b  — lighter than every step of the ladder; the
                          rule sets no background, so the UA default under
                          color-scheme:dark was showing through
  div.oc-subst          a 1295px-tall gold wash over a near-black panel, which
                          reads as a mismatched sheet rather than a section
  div.oc-admin          rgba(10,10,10,.02) — an outer wash that is invisible on
                          #0b0b0b, so the section had no real boundary
  .po-btn / .po-success-check / .po-admin-btn   #000 / #0a0a0a filled shapes

The filled controls use the system's own answer for ink on the brand accent:
--color-accent with --color-text-on-accent, which is already white in light
and #000 in dark. Measured 8.4:1. */
html[data-theme="dark"] .oc-search {
  background: var(--color-surface-2);
  border-color: var(--color-border);
  color: var(--color-text-primary);
}
html[data-theme="dark"] .oc-search::placeholder {
  color: var(--color-text-placeholder);
}
html[data-theme="dark"] .oc-subst {
  background: var(--color-surface-2);
  border-color: var(--color-accent-line);
}
html[data-theme="dark"] .oc-admin {
  background: var(--color-surface-1);
  border-color: var(--color-border);
}
html[data-theme="dark"] .po-btn,
html[data-theme="dark"] .po-btn-primary,
html[data-theme="dark"] .po-success-check,
html[data-theme="dark"] .po-admin-btn {
  background: var(--color-accent);
  color: var(--color-text-on-accent);
}
/* Ghost is the OUTLINED variant, so it must clear the fill the filled rule
   above gives it. Without this it kept the gold background and its light ink
   measured 2.02:1. */
html[data-theme="dark"] .po-btn-ghost {
  background: transparent;
  color: var(--color-text-primary);
  border-color: var(--color-border-strong);
}
html[data-theme="dark"] .po-btn-ghost:hover {
  border-color: var(--color-text-primary);
}

/* ── Two last off-token darks on surfaces that carry them ───────────────────
Found by the same surface audit. Both are filled or inset shapes carrying a
light-canvas literal; the rewards pill is #0a0a0a, which on the #0b0b0b canvas
is a near-black lozenge with no separation, and the admin note is a wash of
rgba(10,10,10,.02) on the panel it sits in. */
html[data-theme="dark"] .fb-rewards-indicator {
  background: var(--color-accent);
  color: var(--color-text-on-accent);
}
html[data-theme="dark"] .oc-obj-note {
  background: var(--color-surface-2);
  border-color: var(--color-border);
  color: var(--color-text-primary);
}
/* The pill's own text is a child span, so it kept the light ink and measured
   2.02:1 against the new gold fill. It takes the on-accent ink with it. */
html[data-theme="dark"] .fb-rewards-indicator-text,
html[data-theme="dark"] .fb-rewards-indicator-time {
  color: var(--color-text-on-accent);
}
/* Moved off an inline style, which no rule could reach. */
html[data-theme="dark"] .oc-obj-note--flag {
  background: var(--color-surface-2);
  border-color: var(--color-border);
}

/* ── Sell page: the verification-method card icons ──────────────────────────
nav.css hardcodes these two to light-canvas values with no dark counterpart:
the card-head icon is #000 and the bullet ticks are #1a5c2a. On the dark
card that measured 1.10:1 and 2.36:1 — a black package glyph and an almost
black tick, both invisible. Light keeps its originals, which measured 21:1
and 8.05:1. */
html[data-theme="dark"] .mp-method-card-head iconify-icon {
  color: var(--color-text-primary);
}
html[data-theme="dark"] .mp-method-card-bullets iconify-icon {
  color: var(--color-success);
}
html[data-theme="dark"] .mp-method-card-title {
  color: var(--color-text-primary);
}
html[data-theme="dark"] .mp-method-card-frame {
  background-color: var(--color-surface-1);
  border-color: var(--color-border);
}

/* ── Sell page: the remaining icons and the active selection ───────────────
nav.css sets each of these to a light-canvas literal with no dark
counterpart, so on the dark card they all measured 1.10:1 — effectively
invisible. Measured before, after, in both themes:

  .mp-method-card-head iconify-icon    #000          1.10 -> 16.63
  .mp-method-card-bullets iconify-icon #1a5c2a       2.36 ->  9.12
  .mp-pick-search iconify-icon         rgba(0,0,0,.45) 1.10 -> 9.28
  .mp-pick-search-clear iconify-icon   rgba(0,0,0,.60) 1.10 -> 9.28
  .mp-photo-add iconify-icon           rgba(0,0,0,.55) 1.10 -> 9.28
  .mp-fineprint iconify-icon           rgba(0,0,0,.50) 1.10 -> 5.85
  .fb-rewards-indicator-link dot       rgba(255,255,255,.45) on gold 2.02 -> 8.43
  .fb-rewards-indicator-x              rgba(255,255,255,.55) on gold 2.32 -> 8.43

The selected method card carried `border: 2px solid #000` from nav.css with no
dark rule at all, so the active choice was outlined in pure black on a black
card. Same for the active step chip's bottom rule and its filled span. */
html[data-theme="dark"] .mp-pick-search iconify-icon,
html[data-theme="dark"] .mp-pick-search-clear iconify-icon,
html[data-theme="dark"] .mp-photo-add iconify-icon {
  color: var(--color-text-secondary);
}
html[data-theme="dark"] .mp-fineprint iconify-icon {
  color: var(--color-text-tertiary);
}
html[data-theme="dark"] .fb-rewards-indicator-link iconify-icon,
html[data-theme="dark"] .fb-rewards-indicator-x {
  color: var(--color-text-on-accent);
}

/* Active selection. The 2px black outline is replaced with the accent so the
   selected card reads as chosen without becoming a black hole, and the
   padding compensation that nav.css pairs with the 2px border is kept. */
html[data-theme="dark"] .mp-method-card input:checked ~ .mp-method-card-frame {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 1px var(--color-accent-soft);
}
html[data-theme="dark"] .mp-step.is-active {
  border-bottom-color: var(--color-accent);
}
html[data-theme="dark"] .mp-step.is-active span {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-text-on-accent);
}

/* ── Mobile drawer theme control ────────────────────────────────────────────
The drawer row is a new surface, so it needs the token layer in both themes
rather than the light-canvas literals the rest of nav.css uses. */
html[data-theme="dark"] .nav-drawer-controls {
  border-top-color: var(--color-border);
}
html[data-theme="dark"] .nav-drawer-controls-label {
  color: var(--color-text-tertiary);
}
html[data-theme="dark"] .nav-drawer-controls .fb-theme {
  background: var(--color-surface-2);
  border-color: var(--color-border);
}

/* ── Mobile drawer "Recommended" tag ─────────────────────────────────────────
The tag is painted in nav.css with a light-canvas rgba(0,0,0,…) literal, which
on the dark canvas is not a tint at all — rgba(0,0,0,0.58) over #0b0b0b is a
darker black, so the marker that was meant to stand out from the row labels
would have been the least visible thing in the list. It takes the token layer
here instead. The selector is html[data-theme="dark"]-scoped so the light theme,
which is correct, is untouched. */
html[data-theme="dark"] .nav-rec-tag {
  color: var(--color-text-tertiary);
}

/* ── Promo dock in dark mode ────────────────────────────────────────────────
The dock was light-only: a solid #000 circle on a #0b0b0b page, so it had no
edge at all, and the white glyph on black was the only thing distinguishing it
from the background. The trigger now takes a real surface and a border from the
token layer, which is what makes the gold icon read as a glyph. */
html[data-theme="dark"] .fb-promodock-trigger {
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  color: var(--color-text-primary);
}
html[data-theme="dark"] .fb-promodock-icon { color: var(--color-accent); }
html[data-theme="dark"] .fb-promodock[data-state="applied"] .fb-promodock-trigger {
  background: var(--color-success-soft);
  border-color: var(--color-success);
}
html[data-theme="dark"] .fb-promodock[data-state="applied"] .fb-promodock-icon {
  color: var(--color-success);
}
html[data-theme="dark"] .fb-promodock-min {
  background: var(--color-surface-1);
  border-color: var(--color-border);
  color: var(--color-text-secondary);
}
html[data-theme="dark"] .fb-promodock-min:hover {
  background: var(--color-surface-3);
  color: var(--color-text-primary);
}
html[data-theme="dark"] .fb-promodock-min:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}
html[data-theme="dark"] .fb-promodock-tip {
  background: var(--color-surface-1);
  border: 1px solid var(--color-border);
  color: var(--color-text-primary);
}
html[data-theme="dark"] .fb-promodock-tip-sub { color: var(--color-text-secondary); }
html[data-theme="dark"] .fb-promodock-badge { background: var(--color-accent); color: var(--color-text-on-accent); }

/* ── Collection card: a dislike the visitor cast must not read as a like ─────
The success rule for these cards listed `.pc-stat.is-mine`, which also matches
`.pc-stat-dislike.is-mine` — so on the collection cards a dislike the visitor
had cast showed in green (measured rgb(105,198,141), 9.43:1) instead of the
danger tone, making the like/dislike pair ambiguous exactly where it matters
most. That selector has been narrowed to `.pc-stat-like.is-mine` in both
copies of the rule; likes lose nothing because the next line already named
them. `.pc-stat-dislike.is-mine` had no dark rule at all — light mode targets
the two separately and was always correct. */
html[data-theme="dark"] .pc-stat-dislike.is-mine {
  color: var(--color-danger);
}
