/* ============================================================================
   gilded.css -- the redesign's palette and typography, and nothing else yet.

   HOW THIS FILE IS SAFE TO SHIP TODAY. Every rule below is scoped to
   :root.gilded, and nothing sets that class. Linking this stylesheet therefore
   changes not one pixel of the live homepage; it only makes the gilded palette
   available to anything that opts in. That is deliberate -- the build target
   for the redesign is site/preview/, unlinked and noindex, never the live
   homepage, and this file has to be able to exist before that does.

   HOW IT WORKS. index.html defines the whole palette as custom properties in
   its own <style> block, and every colour on the page -- CSS, inline styles,
   and the markup the JS builds -- resolves through one of them. This file
   REDEFINES those same properties. So restyling the page is a redefinition,
   not a rewrite, and if this stylesheet ever fails to arrive the page falls
   back to its own definitions and renders exactly as it does now. That is the
   reason the definitions live in index.html rather than here: a var() with no
   definition is not a wrong colour, it is no colour at all.

   ⚠️ THE VERSION IN THE LINK MUST BE BUMPED BY HAND ON EVERY EDIT.
   Cloudflare does not edge-cache this site's HTML but does cache stylesheets,
   so an HTML change and a CSS change pushed in one commit do NOT go live
   together. A stale guide.css shipped a visibly broken page on 2026-08-25 for
   exactly this reason. Every page linking this file must carry
   `gilded.css?v=<date>` and that date must change whenever this file does.
   Forgetting reproduces that bug.

   Palette values are from drafts/scentsearch-gilded-build-spec.md.
   ========================================================================= */

/* --- the faces, served from our own origin ---------------------------------
   ⚠️ SELF-HOSTED SO THAT TYPOGRAPHY ADDS NO THIRD PARTY. Linking Google's
   stylesheet would send every visitor's IP address to Google on every page
   load, which privacy.html §3 undertakes to name and §5 would have to be
   updated for. These files were fetched once at build time instead; a
   visitor's browser never contacts Google or gstatic. SIL Open Font License
   1.1 both -- the licence text is in site/fonts/, and site/fonts/manifest.json
   records each file's source URL, size and sha256.

   ⚠️ ONE FILE PER STYLE, NOT PER WEIGHT, BECAUSE BOTH ARE VARIABLE FONTS.
   Asking Google for weights 500, 600 and 700 returns three byte-identical
   copies of one variable file (verified by sha256). Declaring them separately
   would make a browser fetch the same bytes three times; a font-weight RANGE
   is the correct declaration and the browser instantiates the axis.

   Declaring a face costs nothing until something uses it: a browser fetches a
   webfont only when a rendered element asks for that family. So while nothing
   sets class="gilded", these rules download no bytes at all.

   font-display: swap -- text is readable in the fallback immediately and is
   never hidden waiting on a download.
   ------------------------------------------------------------------------ */

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 500 700;
  font-display: swap;
  src: url('/fonts/cormorant-garamond-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 500 700;
  font-display: swap;
  src: url('/fonts/cormorant-garamond-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 500 700;
  font-display: swap;
  src: url('/fonts/cormorant-garamond-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 500 700;
  font-display: swap;
  src: url('/fonts/cormorant-garamond-italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/fonts/jost-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/fonts/jost-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root.gilded {
  /* --- the ground ------------------------------------------------------- */
  /* warm near-black, sitting behind every photograph. The current palette is
     green-black (18, 24, 20); the redesign's is brown-black. */
  --ink-rgb:          12, 10, 7;
  --ink-deep-rgb:      8, 7, 4;
  --ink-deepest-rgb:   5, 4, 2;

  /* --- the two that carry the brand ------------------------------------- */
  --gold-rgb:        217, 179, 92;
  --cream-rgb:       239, 230, 204;

  --white-rgb:       255, 255, 255;
  --black-rgb:         0, 0, 0;

  /* --- accents ---------------------------------------------------------- */
  /* ⚠️ The spec names no replacement for these three, so they are carried over
     unchanged rather than invented. They are the rose used on a few hover
     states, the bronze, and the alert ramp. If the redesign wants them warmed
     to match the new ground, that is a decision to take deliberately and
     record here -- not a gap to fill by guessing. */
  --rose-rgb:        232, 180, 184;
  --bronze-rgb:      201, 138, 75;
  --alert-rgb:       255, 120, 120;
  --alert-soft-rgb:  255, 180, 180;
  --alert-pale-rgb:  255, 200, 200;

  /* --- solids ----------------------------------------------------------- */
  --shadow-ink: #1a1206;   /* text on a gold fill */
  --panel:      #15110b;   /* solid card fill */
  --ink-800:    #17120b;   /* chip fill */
  --ink-700:    #211a10;
  --ink-600:    #2b2318;
  --alert-text: #e8a0a0;
  --amber:      #d9a468;

  /* --- tokens the current page has no equivalent for --------------------- */
  /* Nothing reads these yet. They are here so the gilded palette lives in one
     file rather than arriving piecemeal with the phases that use it. */
  --muted:         #b3a88c;   /* secondary text */
  --muted-rgb:     179, 168, 140;   /* the same colour as channels, for the
                                      A-Z bar's dead letters at 0.35 */
  --gold-lt:       #f0d58a;   /* link text on dark pills */
  --gold-icon:     #e8c874;   /* icon strokes — §4.3's compass; a shade under --gold-lt */
  --surface-glass: rgba(14, 11, 7, 0.72);   /* translucent bands over a photo */
  --panel-glass:   rgba(12, 10, 7, 0.74);   /* sidebars, blend bar */
  --line:          rgba(217, 179, 92, 0.22);
  --line-soft:     rgba(255, 255, 255, 0.14);

  --radius-card: 6px;
  --radius-pill: 999px;

  /* The scrim for text that genuinely MUST sit on a photograph — the blend
     bar, a note-page hero title. Kept from the note-tile retune that Design
     then refused for the tiles (an opaque caption strip beats a dimmed photo
     there), because the stops themselves are sound where the trade is right.

     ⚠️ NOTHING READS THIS YET, AND ITS ONE MEASUREMENT DOES NOT TRANSFER.
     The 5.37:1 that these stops produced was measured with --black-rgb, for
     20px text, on the white-photo bound. This token is written with
     --ink-rgb as Design specified, which is a DIFFERENT colour, and every
     surface that adopts it carries different type sizes. So measure each use;
     do not cite the 5.37. */
  --photo-scrim: linear-gradient(180deg,
    rgba(var(--ink-rgb), 0) 0%,
    rgba(var(--ink-rgb), 0.50) 45%,
    rgba(var(--ink-rgb), 0.92) 100%);

  /* Chips (§4.1). The border is a GRADIENT, so it cannot be a border-color --
     it is painted as a border-box background layer under a transparent border.
     --chip-text is the spec's #e9dcb8; the selected chip takes --shadow-ink,
     the same dark used for text on any gold fill. */
  --chip-text:       #e9dcb8;
  --chip-wire:       linear-gradient(135deg, rgba(247,231,168,0.55), rgba(138,100,32,0.55));
  --chip-wire-hover: linear-gradient(135deg, rgba(247,231,168,0.85), rgba(138,100,32,0.85));
  --chip-fill-on:    linear-gradient(180deg, #fbe9ad 0%, #c99a3e 55%, #e9cf85 100%);

  /* The "wire" edge, and the gold the page is named for. Gradients rather than
     flat colour is the whole identity, so they are palette, not decoration. */
  --wire: linear-gradient(135deg,
          #5e4313 0%, #f7e7a8 16%, #b8892f 33%, #fff3c6 50%,
          #8a6420 67%, #f0d58a 84%, #5e4313 100%);
  --gold-text:        linear-gradient(180deg, #f2d27e 0%, #dcae4c 48%, #a8761f 100%);
  --gold-text-bright: linear-gradient(180deg, #fdeab0 0%, #f0cd72 50%, #cf9d42 100%);
  --gold-text-fade:   linear-gradient(180deg, #e8c267 0%, #d4a444 50%, #a06e1c 100%);
  --gold-fill:        linear-gradient(180deg, #fbe9ad 0%, #d8ae55 42%, #a87a26 58%, #e9cf85 100%);

  /* --- typography -------------------------------------------------------- */
  /* Both families are SELF-HOSTED from site/fonts/ -- see the @font-face block
     at the top of this file. Linking Google Fonts would send every visitor's
     IP to Google on every page load, a new third party that privacy.html §3
     undertakes to name and §5 would have to be updated for; fetching the files
     once at build time costs neither.

     ⚠️ A TOKEN IS NOT A REQUEST. These names existed for a day while nothing
     referenced var(--font-display), so Cormorant downloaded zero bytes and the
     headline stayed in the system font. A browser fetches a webfont only when a
     RENDERED ELEMENT asks for the family -- declaring the face and naming the
     token are both necessary and neither is sufficient. homepage-
     characterisation.js asserts the download itself, in both directions.

     Both stacks end in a real system face, so a face that fails to load
     degrades to something legible rather than to a default serif. */
  --font-display: "Cormorant Garamond", "Cormorant", Garamond, "Times New Roman", serif;
  --font-body:    "Jost", "Futura", "Century Gothic", "Segoe UI", system-ui, sans-serif;
}

/* ===========================================================================
   PHASE 2 — components, CSS only, on the markup that is already there.
   Still scoped to .gilded, so all of it is inert on the live page.
   Nothing below restructures markup or touches the interactive layer.
   ========================================================================= */

/* --- §B1 "How it works" ----------------------------------------------------
   Three wire cards. The numerals are display-face gold on an OPAQUE --panel
   fill, so the eyebrow's problem — a gradient's darkest stop with only the
   scrim behind it — does not arise here: the card fill is what the text sits
   on, not the photograph. */
:root.gilded .how-it-works {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  max-width: 1128px;
  margin: 72px auto 40px;
  padding: 0 56px;
  position: relative;
  z-index: 1;
}
:root.gilded .how-step {
  background: linear-gradient(var(--panel), var(--panel)) padding-box,
              var(--wire) border-box;
  border: 1.5px solid transparent;
  border-radius: var(--radius-card);
  box-shadow: inset 0 1px 0 rgba(var(--gold-rgb), 0.18),
              0 14px 34px rgba(var(--black-rgb), 0.55);
  padding: 30px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  text-align: left;
}
:root.gilded .how-num {
  font-family: var(--font-display);
  font-size: 44px;
  line-height: 1;
  background: var(--gold-text-bright);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}
:root.gilded .how-step h3 {
  margin: 0;
  font-family: var(--font-body);
  font-size: 18px;
  font-weight: 600;
  color: rgb(var(--cream-rgb));
}
:root.gilded .how-step p {
  margin: 0;
  font-size: 15px;
  line-height: 1.55;
  color: var(--muted);
}
/* ⚠️ 900px, not the 600px used elsewhere: three 1fr columns inside 1128px hit
   ~330px each at 900 and the headings start wrapping mid-phrase well before
   the phone breakpoint. This is the width the CONTENT needs, not the width the
   rest of the page changes at. */
@media (max-width: 900px) {
  :root.gilded .how-it-works {
    grid-template-columns: 1fr;
    gap: 14px;
    margin: 40px auto 28px;
    padding: 0 16px;
  }
}

/* --- §4.2 Glass bands ------------------------------------------------------
   The two content bands that sit over the photograph: the crawlable "Learn
   before you search" block and the footer. Translucent, blurred, no gold
   border. */

:root.gilded .explore-group,
:root.gilded .site-footer {
  background: var(--surface-glass);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

/* The affiliate banner is the topmost text on the page, so it sits where the
   scrim is thinnest. ⚠️ It fails the bound on the LIVE page at 1.79:1 (phone)
   and 1.99:1 (desktop) -- rgba(white, .42) at 10px over rgba(ink, .35) over a
   light photo. The scrim alone lifts it to 2.32:1, which is better and still
   nowhere near 4.5. Making it a glass band like the other two, with --muted
   text, takes it to 5.88:1. The live banner is untouched; see the note at
   .site-footer for why that is Bryan's call and not mine. */
:root.gilded .affiliate-banner {
  background: var(--surface-glass);
  color: var(--muted);
}

:root.gilded .explore-group {
  /* The spec says no gold border. TRANSPARENT rather than removed, on purpose:
     `border: none` would take 2px out of the box and move everything below it,
     which is a layout change dressed up as a colour change. */
  border-color: transparent;
}

:root.gilded .site-footer {
  margin-top: 64px;   /* §4.2: at least 64px of open photo between the bands */
  /* ⚠️ THIS IS A FIX, NOT A RESTYLE, AND THE LIVE PAGE STILL HAS THE PROBLEM.
     The footer text is rgba(white, 0.4) at 11px. Measured against the lightest
     photograph that can load, that is 3.82:1 on the live page today and 3.78:1
     with the scrim -- under the 4.5:1 body-text threshold either way. The scrim
     did not cause it and does not meaningfully worsen it (0.04). Raising it to
     --muted inside .gilded takes it to 7.8:1, but the live footer is untouched
     because everything here is scoped. Fixing the live page is a separate
     one-line change and is Bryan's call. */
  color: var(--muted);
}

/* ⚠️ THE SIX LINKS NEEDED THEIR OWN RULE, AND UNTIL 2026-09-22 THEY GOT THE
   OLD COLOUR NO MATTER WHAT THIS FILE SAID. They carried
   style="color:rgba(var(--white-rgb), 0.55)" inline, which beats every
   stylesheet rule — so the rule above lifted the prose AROUND them to 7.76:1
   and the links themselves stayed put. The gilded footer shipped readable text
   and links that had not moved. Inline attributes stripped; the live colour is
   reproduced by `.site-footer a` in index.html, where this can override it. */
:root.gilded .site-footer a {
  color: var(--muted);
  text-decoration: none;
}
:root.gilded .site-footer a:hover,
:root.gilded .site-footer a:focus-visible {
  color: var(--gold-lt);
  text-decoration: underline;
}

/* ⚠️ THE MOBILE BLUR DROP HAS TO KEEP WINNING, AND IT WOULD NOT HAVE.
   index.html kills backdrop-filter on .site-footer, .candle-tile and four
   other surfaces under 600px — a deliberate performance decision, commented
   as such. A `:root.gilded .site-footer` rule has specificity (0,2,0) and
   silently outranks the plain `.site-footer` (0,1,0) inside that media query,
   so the blur this file adds would have come back on phones and nothing would
   have said so. Restated here at matching specificity. Any future gilded rule
   that adds backdrop-filter belongs in this block too. */
@media (max-width: 600px) {
  :root.gilded .explore-group,
  :root.gilded .site-footer,
  :root.gilded .hero-header {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

/* --- §5.4 Reduced motion ---------------------------------------------------
   The page carries three animations: slowZoom, a 20s infinite Ken Burns on the
   full-screen photograph, and shimmer on the two skeleton placeholders. There
   is no prefers-reduced-motion block anywhere in index.html today, so all
   three run for a visitor who has asked their system for less motion.

   ⚠️ THIS IS THE ONE RULE IN PHASE 2 THAT IS ARGUABLY IN THE WRONG PLACE, AND
   IT IS HERE ON PURPOSE. Everything else in this file is a restyle, which
   should be inert until the redesign is reviewed. This is an accessibility
   fix, and gating it means the LIVE page keeps zooming a full-screen photo at
   a visitor who switched motion off. Scoped anyway, because the instruction
   for this phase was that nothing reaches the live page unreviewed -- but the
   same four declarations, unscoped, would fix it for everyone today and that
   is worth deciding separately rather than leaving implied.

   The CROSSFADE is the transition on background-image, so killing the
   transition stops it. The ROTATION itself is JS (a timer swapping the image)
   and is untouched here -- §5.4 also asks for that to stop, which is not
   something CSS can do, and this phase adds no JS. */
@media (prefers-reduced-motion: reduce) {
  :root.gilded .bg-slideshow {
    animation: none;
    transition: none;
  }
  :root.gilded .skeleton-img,
  :root.gilded .skeleton-line {
    animation: none;
  }
}

/* --- §6.2 Header bar ------------------------------------------------------
   Brand (mark + wordmark) · nav · actions (Saved, For brands).

   The four product-type entries are deliberately absent: they run a
   pre-filtered search rather than navigating, so they belong with the type tabs
   and filterState.type, not here. Shipping them as <a href> would be a link
   that lies about what it does; making them real ?type= URLs is costed in
   drafts/scentsearch-type-urls-costing.md and is Bryan's decision. */
/* ⚠️ THE BANNER IS position:fixed, AND THE OLD HEADER ONLY CLEARED IT BY
   ACCIDENT. .hero-header used to carry 2.4rem of top padding, which happened
   to be taller than the disclosure; the 78px bar has none, so at 390px -- where
   the banner wraps to two lines -- it printed straight through the wordmark and
   the Saved control.

   sticky rather than a margin on the header: it puts the banner back in FLOW,
   so the header is pushed down by whatever height the text actually takes at
   that width, and it still pins to the top on scroll, which is the point of an
   affiliate disclosure. A hardcoded margin would be a magic number per
   breakpoint that a longer sentence silently invalidates. */
:root.gilded .affiliate-banner {
  position: sticky;
}

/* ⚠️ H2: flex-start, NOT space-between. With three children and
   space-between the nav floated in the middle of the bar; once H1 removes the
   mark the left side is one short wordmark, which left a visible hole between
   brand and nav. The nav now starts one 36px gap after the brand and the
   actions are pinned right by their own auto margin -- which also keeps the
   phone rows correct, where the header wraps and space-between no longer
   applies. */
:root.gilded .hero-header {
  box-sizing: border-box;
  height: 78px;
  justify-content: flex-start;
  align-items: center;
  gap: 36px;
  padding: 0 56px;
  border-bottom: 1px solid var(--line);
  /* ⚠️ THE GLASS IS LOAD-BEARING, NOT DECORATION. Spec §6.2 asks for it, and
     the measurement says the same thing: the nav sits high in the viewport
     where the scrim is thinnest (alpha 0.60), with nothing behind it, and the
     links measured 3.86:1 on desktop against the 4.5 floor. The band gives
     them a backdrop and takes them to 8.19:1. Border alone did not. */
  background: rgba(var(--ink-rgb), 0.55);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
/* The brand lock-up — the WORDMARK ALONE under .gilded.
   ⚠️ H1 (round two) moves the mark out of the header and into the hero. The
   <img> stays in the header markup because it is the LIVE page's logo and
   hiding it is a gilded-only decision; .hero-mark below is the same file shown
   in its new place. Exactly one of the two is visible in either state. */
:root.gilded .site-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
}
:root.gilded .site-logo { display: none; }
:root.gilded .site-wordmark {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 27px;
  line-height: 1;
  white-space: nowrap;
  /* Gradient text: the fill is painted behind the glyphs and clipped to them,
     so `color` must be transparent for any of it to show.

     ⚠️ --gold-text-bright, NOT the spec's --gold-text, AND THE REASON IS A
     MEASUREMENT. A gradient's worst pixel is its darkest stop, and
     --gold-text bottoms out at #a8761f: over the header band and the scrim,
     against a white photograph, the lower third of every glyph measured
     2.71:1 desktop and 2.69:1 phone -- under the 3.0 large-text floor and
     well under the 4.5 the 22px phone size actually needs. --gold-text-bright
     stops at #cf9d42 and clears both. Same family, same identity, legible.

     ⚠️ AND THE HARNESS READ IT AS 1.00:1 FIRST. color is transparent here by
     construction, so a probe that reads cs.color measures nothing at all and
     reports a perfect failure on perfectly legible text. Measure the darkest
     STOP. */
  background: var(--gold-text-bright);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
  filter: drop-shadow(0 1.5px 0 rgba(var(--black-rgb), 0.6));   /* engraved edge */
}

:root.gilded .site-nav {
  display: flex;
  flex-grow: 1;             /* H2: takes the slack, so its items stay left */
  flex-wrap: wrap;          /* ⚠️ wraps rather than overflowing at 390px */
  align-items: center;
  gap: 30px;
}
:root.gilded .site-nav a {
  /* Cream, not --chip-text: this is text on a glass band, not a chip. */
  color: rgb(var(--cream-rgb));
  font-family: var(--font-body);
  font-size: 15px;
  letter-spacing: 0.3px;
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
}
:root.gilded .site-nav a:hover,
:root.gilded .site-nav a:focus-visible {
  color: var(--gold-lt);
  border-bottom-color: var(--gold-lt);
}

/* --- §6.2 Header actions --------------------------------------------------
   Saved, then "For brands" as the wire pill at the right edge. */
:root.gilded .site-actions {
  display: flex;
  align-items: center;
  margin-left: auto;        /* H2: pinned right in BOTH the row and wrap cases */
  gap: 18px;
}

/* ⚠️ THE PAGE SHIPPED TWO "Saved" BUTTONS THE MOMENT THE HEADER ONE APPEARED.
   #savedBtn in .secondary-actions predates it and does the same thing. Hidden
   rather than deleted: it is the LIVE page's only Saved control, and the live
   page has no header one (.site-actions is display:none there). Measured
   before and after -- live has exactly one visible Saved, gilded has exactly
   one. Delete #savedBtn only when the button wall itself goes (round one §9d). */
:root.gilded #savedBtn { display: none; }
/* .site-saved is a <button>, so every inherited control style has to be
   unwound before it can read as a nav item. */
:root.gilded .site-saved {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 15px;
  letter-spacing: 0.3px;
  color: rgb(var(--cream-rgb));
  white-space: nowrap;
}
:root.gilded .site-saved:hover,
:root.gilded .site-saved:focus-visible { color: var(--gold-lt); }
/* ⚠️ The count is written by syncSavedCount() and is EMPTY at zero, so this
   rule paints nothing rather than a gold "(0)". */
:root.gilded .site-saved .saved-count { color: var(--gold-lt); }

/* The wire pill. The border is a GRADIENT, so it is painted as a border-box
   background layer under a transparent border -- the same construction the
   chips use, and the reason a border-color cannot do this. */
:root.gilded a.nav-cta {
  background: linear-gradient(rgb(var(--ink-rgb)), rgb(var(--ink-rgb))) padding-box,
              var(--wire) border-box;
  border: 1.5px solid transparent;
  border-radius: var(--radius-pill);
  padding: 10px 18px;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1;
  color: var(--gold-lt);
  text-decoration: none;
  white-space: nowrap;
}
:root.gilded a.nav-cta:hover,
:root.gilded a.nav-cta:focus-visible { color: rgb(var(--cream-rgb)); }

/* ⚠️ MEASURED AT 390px: with the logo and the nav on one row, space-between
   left the nav about 180px wide and each link took its own line -- 113px of
   header, pushing the hero down by that much on the smallest screen where it
   matters most. Stacking the header and giving the nav its own full-width row
   puts all three on one line. Wrapping is kept as the backstop, so a longer
   label degrades to two lines rather than to a sideways scroll. */
@media (max-width: 600px) {
  :root.gilded .hero-header {
    height: auto;          /* ⚠️ releases the 78px; three groups cannot fit it */
    flex-wrap: wrap;
    gap: 10px 12px;
    padding: 12px 16px;
  }
  /* Two rows, not three: brand and actions share the first, the nav takes the
     second on its own. Stacking all three cost 113px of header on the screen
     where the hero has least room. */
  :root.gilded .site-brand   { order: 1; }
  :root.gilded .site-actions { order: 2; gap: 14px; }
  :root.gilded .site-nav     { order: 3; width: 100%; justify-content: center; gap: 18px; }
  /* ⚠️ 24px IS A MEASURED FLOOR, NOT A TASTE CALL. At 22px/600 the wordmark is
     not "large text" by WCAG (that needs >=24px, or >=18.66px at weight 700),
     so it faced the 4.5 bar and measured 4.36:1 against a white photograph --
     short by 3%. 24px clears the large-text definition, which drops the
     requirement to 3.0, and the same 4.36:1 then has real headroom. Do not
     shrink it back without re-running the contrast bound. */
  :root.gilded .site-wordmark { font-size: 24px; }
  :root.gilded .site-logo     { height: 28px; }
  :root.gilded a.nav-cta      { padding: 8px 14px; font-size: 13px; }
}

/* --- §6.3 Product-type tabs ------------------------------------------------
   Styled entirely by the existing .chip / .chip.active rules below, which is
   the payoff for restyling all seven chip families as a set in Phase 2: these
   needed no new chip CSS at all, only a row to sit in. */
/* ⚠️ THE ROW NOW SITS ABOVE THE FIELD (§3a), so its margin moved from top to
   bottom. It was `14px auto 0` while the nav rendered under the bar; with the
   node moved above .ai-search-container the 14px has to be on the other side
   or the tabs sit flush against it. */
:root.gilded .type-tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 0 auto 14px;
  max-width: 880px;
  padding: 0 16px;
}

/* ⚠️ ONE SCROLLING ROW ON PHONE — THE BIGGEST SINGLE SAVING IN THE HERO.
   Six labels do not fit 390px, so wrapping made this THREE rows and 118px of
   the first screen, for a control that is six words long. Nothing is removed:
   every tab is still reachable, by swiping instead of by wrapping.

   ⚠️ `flex: 0 0 auto` on the tab is what makes it scroll rather than squash —
   without it the flex items shrink to fit and the row silently becomes
   unreadable instead of scrollable.

   scrollbar-width/::-webkit-scrollbar hide the bar, which on a touch device
   is not a control anyone uses; the overflowing pill at the right edge is the
   affordance. Keyboard users reach the tabs by tabbing, which scrolls the
   container natively. */
@media (max-width: 600px) {
  :root.gilded .type-tabs {
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    scroll-padding-inline: 16px;
    /* R1: snap, so a swipe lands on a tab rather than mid-label. `proximity`
       rather than `mandatory` — mandatory fights a short flick near the end of
       a six-item row. */
    scroll-snap-type: x proximity;
    /* R1: the right-edge fade is what says "there is more". Without it the row
       reads as six items that happen to be clipped.
       ⚠️ mask-image needs the -webkit- prefix to reach Safari, where this row
       matters most, and the unprefixed property alone silently does nothing
       there — the fade would be missing on exactly the browsers this scroller
       was built for.
       ⚠️ The stops go through --black-rgb rather than #000. Only the ALPHA of
       a mask matters, so the colour is arbitrary — but this file has no colour
       literals by construction, and an exception here is an exception someone
       later copies. */
    -webkit-mask-image: linear-gradient(90deg, rgba(var(--black-rgb), 1) 0,
                        rgba(var(--black-rgb), 1) calc(100% - 28px), transparent 100%);
            mask-image: linear-gradient(90deg, rgba(var(--black-rgb), 1) 0,
                        rgba(var(--black-rgb), 1) calc(100% - 28px), transparent 100%);
  }
  :root.gilded .type-tabs::-webkit-scrollbar { display: none; }
  :root.gilded .type-tab { flex: 0 0 auto; scroll-snap-align: start; }
}

/* The pill shape itself is §3b, and it lives with the chip block further down
   rather than here, because that block is where the no-radius rule it
   supersedes is written. */

/* --- §3 Typography — the faces actually get asked for -----------------------
   ❌ UNTIL 2026-09-21 NOTHING IN THIS FILE REFERENCED --font-display, so
   Cormorant downloaded zero bytes and the headline stayed the 22px system
   font. Phase 1c had "verified" the faces by measuring synthetic spans the
   probe created — which proved the FILES work, not that the page asks for
   them. A browser fetches a webfont only when a rendered element wants that
   family, and nothing did.

   The characterisation harness now checks it in both directions: the display
   face must load if and only if the page is gilded. */

:root.gilded body {
  font-family: var(--font-body);
}

:root.gilded h1,
:root.gilded h2,
:root.gilded h3,
:root.gilded .candle-title,
:root.gilded .explore-heading {
  font-family: var(--font-display);
  font-weight: 600;
}

/* The hero headline. ⚠️ THE TYPE TREATMENT, NOT THE COPY — the wording change
   is still Bryan's call (spec §12.2) and this lands whatever the words end up
   being. clamp() so it never needs a breakpoint of its own. */
/* §9a: the two sentences take a line each. `display: block` rather than a
   <br>, so the live page keeps them flowing as one sentence. */
:root.gilded .hero-value h1 .h1-line { display: block; }

/* ⚠️ THE SPANS BREAK THE SENTENCE IN TWO; THEY DO NOT STOP LINE 2 WRAPPING
   AGAIN — and it did, so the headline rendered in THREE lines. Measured with a
   non-wrapping clone of each span:

       desktop   line 2 wants 758px   .hero-value box was 640px   wrapped
       phone     line 2 wants 370px   box was 358px               wrapped

   .hero-value's base max-width is 640px, sized for the old one-sentence
   headline. Widened to 880 under .gilded — the same width as the search field
   below it, so the hero reads as one column rather than two — which clears
   758px at the 68.3px size and 821px at the 74px cap.

   ⚠️ PHONE MISSED BY 12px, WHICH IS WHY THE FLOOR MOVES AND NOT THE WHOLE
   CLAMP. 370 against 358 at 34px: dropping the clamp FLOOR to 32px takes the
   line to ~348px and fits, with no effect on any width above the floor. A
   smaller upper bound would have shrunk the headline everywhere to fix 12px at
   one end. */
:root.gilded .hero-value {
  max-width: 880px;
}

/* ❌ A1: THE GOLD HEADLINE WAS MEASURED AND DOES NOT CLEAR. Applied exactly as
   §A1 specifies it — --gold-text-bright on line 1, --gold-text on line 2 — and
   read with the contrast harness against the white-photo bound. The headline is
   large text, so the floor is 3.0, not 4.5:

       line 1 (bright)       4.20 desktop / 3.56 phone   clears
       line 2 (--gold-text)  2.87 desktop / 2.57 phone   FAILS both ends

   Line 2 sits lower, where the scrim is denser, and still fails: its darkest
   stop is #a8761f against #cf9d42. So the PAIR does not clear, which is A1's
   second branch — the headline stays cream and Design strikes the gold from
   the spec. Cream measures 8.28 / 7.02.

   ⚠️ FOR THE RECORD, BRIGHT ON BOTH LINES WOULD PASS (4.20 / 3.56). It is not
   applied because it loses the one-step-darker relationship between the lines,
   which is the whole point of the two-tone treatment — that is Design's call,
   not a number's.

   ⚠️ AND MY FIRST MEASUREMENT OF THIS WAS WRONG. An ad-hoc probe mapped the
   element's DOCUMENT position onto the scrim gradient and reported every
   gradient failing by a mile (best 2.29:1). The scrim is `position: fixed;
   inset: 0` — VIEWPORT-relative — so document position is the wrong axis
   entirely. The numbers above come from the harness, which gets this right. */
:root.gilded .hero-value h1 {
  font-size: clamp(32px, 5vw, 74px);
  line-height: 1.05;
  letter-spacing: -0.5px;
  text-wrap: balance;
  margin-top: 0;            /* the 20px gap belongs to .hero-mark, once */
  margin-bottom: 14px;
}

/* --- §4b: the mark, in the hero --------------------------------------------
   The drafts copy of the correction list predated §4b, so this was first built
   from the numbers round two states inline. §4b has since been read and the
   numbers match it exactly -- 248x165, 190x126 on phone, 20px gaps,
   object-fit: contain, and `.site-logo { display: none }` in the header.

   ⚠️ TWO DELIBERATE DEPARTURES FROM §4b's CODE BLOCK, both about how rather
   than what:

   1. §4b writes the shadow as rgba(0,0,0,0.65), a literal. This page has NO
      colour literals by construction -- 414 were converted to tokens, and the
      round-trip proof is what makes that trustworthy. Written through
      --black-rgb, which expands to the same colour.
   2. §4b wraps the mark in .hero-brand with flex-direction: column and
      gap: 20px. The margin here produces the identical 20px today with no new
      element. ⚠️ WHEN THE EYEBROW LANDS, TAKE THE WRAPPER: one `gap` spacing
      three children is right, and three margins are not.

   ⚠️ THE EYEBROW IS NOT HERE. §4b's block is mark / 20px / eyebrow / 20px /
   headline, but the eyebrow is round one §9b, which round two's own H5 lists
   as outstanding. It gets built as its own step.

   Sized by BOTH axes because the box is fixed: object-fit keeps the aspect
   ratio honest if Logo.png is ever replaced with a differently-proportioned
   file (248/165 = 1.503, which is the current file's ratio). */
/* R3: the eyebrow and ornament have landed, so the wrapper owns the spacing.
   One `gap` for three children, as this file's own note promised — the mark
   carries no margin, and the h1 keeps only its own margin-bottom. */
:root.gilded .hero-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;                /* mark -> eyebrow -> ornament, all §4b's 20px */
  margin-bottom: 20px;      /* ...and the same gap again before the h1 */
}
:root.gilded .hero-mark {
  display: block;
  width: 248px;
  height: 165px;
  object-fit: contain;
  margin: 0;                /* the wrapper's gap owns the spacing now */
  filter: drop-shadow(0 10px 28px rgba(var(--black-rgb), 0.65));
}

/* §9b the eyebrow.
   ❌ THE SPEC'S --gold-text-bright FAILS HERE, AND MEASUREMENT WINS. Against
   the white-photo bound it measured 3.13:1 desktop and 2.99:1 phone, under the
   4.5 floor that 15px/500 faces. The wordmark uses the same gradient and
   passes at 4.51 only because the header's glass band sits behind it; this
   line sits directly on the photograph, where the scrim is ~0.70 and nothing
   else lifts it. The solver wants a scrim of 0.81 — retuning the whole page's
   atmosphere for one caption.

   ✅ Solid --gold-lt instead: measured 5.34 desktop / 5.11 phone.
   ⚠️ AND IT COSTS NOTHING VISUALLY, which is why this is the right trade
   rather than a grudging one: a three-stop vertical gradient across ~11px of
   glyph height is invisible at this size. The gradient earns its keep on the
   27px wordmark and the 68px headline, not on a 15px tracked caption.

   The engraved edge stays a drop-shadow rather than the `.engraved-text`
   text-shadow, so the treatment still matches the wordmark's. */
:root.gilded .hero-eyebrow {
  display: block;
  margin: 0;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 3.5px;
  text-transform: uppercase;
  color: var(--gold-lt);
  filter: drop-shadow(0 1.5px 0 rgba(var(--black-rgb), 0.6));
}

/* §9b the ornament: a 260px wire rule with a centre diamond. The two halves
   are pseudo-elements so the diamond can sit in the flow between them. */
:root.gilded .hero-ornament {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 260px;
  max-width: 100%;
}
:root.gilded .hero-ornament::before,
:root.gilded .hero-ornament::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--wire);
}
:root.gilded .orn-diamond {
  width: 7px;
  height: 7px;
  margin: 0 12px;
  background: var(--gold-fill);
  transform: rotate(45deg);
}

@media (max-width: 600px) {
  /* ⚠️ 150px, NOT §4b's 190. Measured stacked rather than in isolation: the
     hero carries a mark, eyebrow, ornament, two-line h1, subhead, count line,
     type tabs and the field before the fold, and 190x126 was the largest
     single block in it. 150x100 keeps the aspect ratio (248/165 = 1.503) and
     the brand moment, and returns 26px. §4b names 200 and 300 as desktop
     alternatives and did not size the phone against everything else on the
     screen. */
  :root.gilded .hero-mark {
    width: 150px;
    height: 100px;
  }
  /* The tracking is what breaks first at 390px: 15px + 3.5px across 28
     characters overflows the gutter before the type size does. */
  :root.gilded .hero-eyebrow { font-size: 13px; letter-spacing: 2.5px; }
  :root.gilded .hero-ornament { width: 200px; }
}

/* --- §4.1 The search field ------------------------------------------------
   The most prominent control on the page. Wire border, pill radius, and the
   double ring the spec specifies — a band of page ground, then a gold
   hairline, so the field reads as inset into the photograph. */

:root.gilded .ai-search-container {
  /* ⚠️ border-box, AND THIS IS WHY THE BAR MEASURED 98px AGAINST A 78px RULE.
     The base container is content-box with 8px padding and this adds a 2px
     border, so "min-height: 78px" meant 78 of CONTENT and 98 on screen. The
     height was never being ignored -- it was being added to. Measured
     2026-09-21 on preview.html at 1366: container 98.0px, button 50px, 24px of
     air above and below it. */
  box-sizing: border-box;
  /* translucent fill, so the existing backdrop-filter still has something to
     do; the wire is a border-box layer under a transparent border */
  background: linear-gradient(var(--surface-glass), var(--surface-glass)) padding-box,
              var(--wire) border-box;
  border: 2px solid transparent;
  box-shadow:
    0 0 0 5px rgb(var(--ink-rgb)),
    0 0 0 6.5px rgba(var(--gold-rgb), 0.55),
    inset 0 2px 6px rgba(var(--black-rgb), 0.7),
    0 24px 60px rgba(var(--black-rgb), 0.7);
}

:root.gilded .search-icon {
  display: flex;
  align-items: center;
  padding-left: 16px;
  color: var(--gold-lt);
  flex: 0 0 auto;
}

/* ✅ HIDDEN UNDER 600px, AND IT BUYS BACK A WHOLE ROW. Measured 2026-09-21 at
   390x844, container outer height: live 106.5px in two rows, gilded 139.5px in
   THREE -- because at that width the icon cannot share a row with the input and
   took one of its own, 28px (icon 20 + gap 8) of empty row the live page does
   not have.

   Safe to hide rather than re-flow: the span is aria-hidden="true" in the
   markup and carries no text, so nothing is lost to a screen reader, and the
   placeholder plus the "Find My Scents" button both say what the control is.
   The alternatives were both worse -- sharing a row drops the input to ~160px,
   and forcing the button full-width moves the right inset that was measured as
   correct. */
@media (max-width: 600px) {
  :root.gilded .search-icon { display: none; }
}

:root.gilded .ai-search-container input {
  font-family: var(--font-body);
}

/* The primary button: gold fill, dark text (§4.1) */
:root.gilded .ai-search-container .search-btn {
  background: var(--gold-fill);
  color: var(--shadow-ink);
  border: none;
  border-radius: var(--radius-pill);
  font-weight: 600;
  font-family: var(--font-body);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7),
              inset 0 -1px 0 rgba(var(--black-rgb), 0.35),
              0 6px 18px rgba(var(--black-rgb), 0.55);
}
:root.gilded .ai-search-container .search-btn:hover {
  background: var(--gold-fill);
  border: none;
  filter: brightness(1.06);
}

/* ⚠️ HEIGHT AND PILL RADIUS ARE DESKTOP-ONLY, AND THAT IS NOT A DETAIL.
   Under 600px the container sets flex-wrap: wrap, so the field is MORE THAN
   ONE ROW on a phone. A fixed height there would clip it, and a 999px radius
   on a multi-row box is a stadium, not a pill. The existing mobile rules give
   it 28px and let it size to content; they are left alone.

   ❌ AND MY OWN CORRECTION OF THIS COMMENT WAS HALF WRONG, 2026-09-22. It
   originally said the input is "flex: 1 1 100%"; I replaced that with "the
   base rule is flex: 1" and called the original mistaken. Both rules exist:
   the BASE is `flex: 1`, and index.html's `@media (max-width: 600px)` block
   overrides it to `flex: 1 1 100%` — which is the one that governs at the
   width the comment is about, so the original was right and the correction
   introduced an error. The only part that was genuinely wrong was "TWO rows":
   at 390px the gilded bar rendered THREE, which is the defect below.
   ⚠️ Read the media query that applies at the width under discussion, not
   just the base rule.

   ⚠️ MEASURED 2026-09-21, phone 390x844, container outer height:

       live (icon hidden)    106.5px   input 41 + gap 8 + button 39.5  = 2 rows
       gilded (icon shown)   139.5px   icon 20 + input 46 + button 37.5 = 3 rows

   :root.gilded .search-icon sets display:flex, and at 390px the icon cannot
   share a row with the input, so it takes one of its own: 28px of empty row
   (icon 20 + gap 8) that the live page does not have. **This is a gilded-only
   regression and it is NOT fixed here** -- every way to fix it changes the
   phone bar's layout (share a row and the input drops to ~160px; force the
   button full-width and the right inset Bryan measured as correct moves), so
   it is a decision rather than a tightening. Reported with the numbers.

   ⚠️ The sticky state takes the compact height. The spec says "78px desktop,
   64px compact (results page)" — there is no results-page class to hang that
   on, and the bar compacting when it sticks is the same intent with a state
   that already exists. Recorded as an interpretation, not a reading.

   ⚠️ AND THIS CHANGES THE STICKY RESERVE. The spacer measures the grid's
   deficit at runtime rather than being told a height, which is exactly why
   that was fixed first — 78px here needs no corresponding edit there. The
   stickyShift snapshot is the check. */
@media (min-width: 601px) {
  :root.gilded .ai-search-container {
    /* §6's 880px, which had never been applied — the bar was still on the base
       720px. Desktop only: under 601px it is already full width.

       ⚠️ THE STICKY STATE IS UNAFFECTED AND THAT IS WANTED. The sticky rule
       sets `width: min(720px, calc(100% - 24px))`, and a width inside a larger
       max-width simply wins — so the bar is 880 in the hero and 720 once it
       pins, which is what pairs it with the 720px summary box below it in the
       results state.

       ⚠️ SIX ELEMENTS ARE CENTRED ON THIS BAR AT 720px — .example-chips,
       .recent-searches, .search-scene, .search-scene-wrap, .refine-row and
       .how-it-works. None is widened to match, because none of them FILLS
       720: each is centred, content-sized and narrower than either bound, so
       the cap is not what positions them. Measured at 1366/1100/930/700 before
       and after rather than assumed. The results-side three stay at 720 on
       purpose — they belong with the sticky bar, not the hero one. */
    max-width: 880px;
    border-radius: var(--radius-pill);
    /* height, not min-height: the bar is a fixed-height control, and with
       border-box above this is now the number that appears on screen. */
    height: 78px;
    align-items: center;
    /* ⚠️ VERTICAL PADDING GOES, HORIZONTAL STAYS EXACTLY AS IT WAS. Bryan
       measured the gap between the button and the right edge as correct, and
       it is the base 8px padding plus the 2px border = 10px. Touching
       padding-right would move the one dimension that was already right, so
       only the two vertical sides are zeroed here. The button's inset then
       falls out of the arithmetic rather than being set: (78 - 4 border - 60
       button) / 2 = 7, + 2 border = 9px above and below, down from 24px. */
    padding-top: 0;
    padding-bottom: 0;
  }
  :root.gilded .ai-search-container .search-btn { height: 60px; }
  /* Round one §1's 20px, also never applied — the input was on the base 16px.
     Desktop only; the ≤600px rule sets 15px and the bar there is two rows. */
  :root.gilded .ai-search-container input { font-size: 20px; }

  :root.gilded .ai-search-wrapper.is-sticky .ai-search-container { height: 64px; }
  :root.gilded .ai-search-wrapper.is-sticky .ai-search-container .search-btn { height: 48px; }
}

/* --- §6.4 / §4.3 The Matchmaker pill, and clearing the button wall ---------
   The wall is six buttons: Matchmaker, Browse Guides, Smells like a product,
   Need Inspiration?, Saved, Ambient. §6.4 gives each a destination, and only
   the ones whose destination EXISTS are cleared here.

   ⚠️ NEVER HIDE A CONTROL WHOSE DESTINATION IS NOT BUILT. The spec's own
   checklist for this phase is "the chip wall is gone; EVERY OLD ACTION IS
   STILL REACHABLE" -- so hiding a button before its replacement exists is a
   feature removal wearing a restyle's clothes, and it would be invisible in a
   screenshot. State of the six:

     Saved          -> header            ✅ hidden
     Browse Guides  -> header nav Guides ✅ hidden -- same href
     Matchmaker     -> this pill         ✅ built
     Smells like    -> the 4th Try chip  ✅ hidden 2026-09-22, once §9e landed
                                            and gave it somewhere to go
     Need Inspiration? -> KEPT           ⏸ Bryan, 2026-09-22: it is the only
                                            family/mood browse on the site, and
                                            the Try chips teach phrasing rather
                                            than replacing it. Now the SECOND
                                            entry pill rather than a wall chip.
     Ambient        -> the caption pill  ❌ §9c/§5.2 not built. Its button is
                                            still the ONLY toggleAmbientAudio()
                                            control, so it stays visible.

   The wall is 6 -> 3: two entry pills and Ambient. */

/* ⚠️ ONE RULE FOR BOTH PILLS. They are the two ways in for a visitor who does
   not know what to type — the Matchmaker asks four questions, Need Inspiration
   browses 8 families and 7 moods — and they only read as a pair if they cannot
   drift apart. Anything true of one is written here; only the icon differs,
   and that lives in the markup. */
:root.gilded .entry-pill {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  padding: 11px 20px;
  border: 1px solid rgba(var(--gold-rgb), 0.45);
  border-radius: var(--radius-pill);
  background: rgba(var(--ink-rgb), 0.55);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1;
  color: var(--gold-lt);
}
:root.gilded .entry-pill:hover {
  border-color: rgba(var(--gold-rgb), 0.8);
  color: rgb(var(--cream-rgb));
}
/* The icon takes its own stroke rather than the label's colour, so the hover
   above brightens the text without washing the icon out. */
:root.gilded .entry-pill .pill-icon {
  display: inline-flex;
  align-items: center;
  color: var(--gold-icon);
}
:root.gilded .entry-pill .pill-arrow { display: inline; }

:root.gilded .guides-launch { display: none; }
:root.gilded .smells-launch { display: none; }
/* §9c: Ambient now lives in the Now-smelling pill's speaker. */
:root.gilded #ambientAudioBtn { display: none; }

/* --- Hero row order, and §9f -----------------------------------------------
   Spec order under the search field is Try line, then the entry pills; §9f
   (Bryan, 2026-09-22, option a) puts recent searches under the Try line. The
   markup order is pills, Try, recent, so two of the three move.

   ⚠️ `order` MOVES PAINTING, NOT THE DOM, so tab order still follows the
   markup — the pills are still tabbed before the Try chips. Same caveat as the
   Try chips themselves, and the same reason: reordering the markup would
   reorder the LIVE page, which this wrapper exists to avoid. */
:root.gilded .hero-actions {
  display: flex;
  flex-direction: column;
}
:root.gilded .hero-actions .example-chips     { order: 1; }
:root.gilded .hero-actions .recent-searches   { order: 2; }
:root.gilded .hero-actions .secondary-actions { order: 3; margin-top: 4px; }

/* §9f: recent searches as a quiet line under the Try chips rather than a
   second row of loud ones. */
:root.gilded .recent-searches {
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--muted);
}
:root.gilded .recent-chip { font-size: 13px; }

/* ⚠️ ON PHONE IT MOVES INTO THE FIELD'S FOCUS STATE — §9f option (b), which
   was the option NOT chosen for desktop and is the right one here. Measured:
   as a permanent row it costs 74px of a 844px screen, and it is only ever
   useful to someone about to type, which is exactly when the field has focus.
   Desktop keeps option (a), the quiet line under the Try chips, where the
   room exists.

   The sibling combinator is what makes this CSS-only: .recent-searches is not
   inside .ai-search-container, so :focus-within has to be read from the
   container and reach across to .hero-actions.

   ⚠️ It shifts the rows below it when it opens. That is a user-initiated
   change, not load CLS, and the alternative — reserving 74px permanently for
   a row that is usually empty — is the cost this rule exists to avoid. */
@media (max-width: 600px) {
  :root.gilded .hero-actions .recent-searches { display: none; }
  :root.gilded .ai-search-container:focus-within ~ .hero-actions .recent-searches {
    display: flex;
  }
}

/* --- §9c / §5.2 The "Now you're smelling" pill -----------------------------
   ⚠️ IN THE FLOW, NOT PINNED TO THE VIEWPORT. §5.2 puts it "bottom-centre of
   the first screen", which reads as position:fixed — and a fixed element
   above the results grid is the defect [B3a] exists to catch, the one the
   footer already caused once (69px of every scrolled page covered). It sits
   at the end of the hero instead, which is the bottom of the first screen on
   load and scrolls away honestly afterwards. */
:root.gilded .now-smelling {
  /* flex + fit-content, not inline-flex: `margin: auto` does not centre an
     inline-level box, and the pill has to sit centred under the hero. */
  display: flex;
  width: fit-content;
  align-items: center;
  gap: 4px;
  margin: 18px auto 0;
  padding: 5px 5px 5px 6px;
  border: 1px solid rgba(var(--gold-rgb), 0.28);
  border-radius: var(--radius-pill);
  background: rgba(var(--ink-deep-rgb), 0.62);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
:root.gilded .ns-body {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 3px 8px 3px 3px;
  background: none;
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 13px;
  color: rgb(var(--cream-rgb));
}
:root.gilded .ns-thumb {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border-radius: 50%;
  background-size: cover;
  background-position: center;
  border: 1px solid rgba(var(--gold-rgb), 0.35);
}
/* [hidden] needs the explicit none: the flex display above would otherwise
   win, and the caption would show as an empty gap on the landing page. */
:root.gilded .ns-caption { white-space: nowrap; }
:root.gilded .ns-caption[hidden] { display: none; }
:root.gilded .ns-dots { display: inline-flex; gap: 5px; align-items: center; }
:root.gilded .ns-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(var(--cream-rgb), 0.32);
}
:root.gilded .ns-dot.on { background: var(--gold-lt); }
:root.gilded .ns-speaker {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  color: rgba(var(--cream-rgb), 0.55);
}
:root.gilded .ns-speaker:hover { color: var(--gold-lt); }
/* The wave is the ON state: off shows a bare speaker, which is what the
   emoji pair (🔈 / 🔊) said on the button this replaces. */
:root.gilded .ns-speaker .ns-wave { opacity: 0; }
:root.gilded .ns-speaker.on { color: var(--gold-lt); }
:root.gilded .ns-speaker.on .ns-wave { opacity: 1; }

/* --- §9e The Try line ------------------------------------------------------
   Eight example chips down to four, behind a "Try:" label, on one row.

   ⚠️ ORDER IS SET IN CSS, NOT BY MOVING MARKUP. The spec lists the four as
   woody / clean hotel lobby / fresh linen / smells like, and the three
   survivors sit in the DOM as fresh linen (3rd), woody (7th), clean hotel
   (8th). Reordering the markup would reorder the LIVE page's chips, which is
   a visible change to a page this redesign has kept untouched — so one
   `order` on the odd one out does it instead. Flex order only moves painting,
   not the DOM, so tab order still follows the markup: worth knowing, and not
   worth a live reorder to fix for three chips. */
:root.gilded .try-label {
  display: inline-flex;
  align-items: center;
  order: -1;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--muted);
  margin-right: 2px;
}
:root.gilded .example-chip:not(.ex-try) { display: none; }
:root.gilded .ex-try-last    { order: 1; }
/* The chip family above gives it fill, border and text; this gives it the
   geometry the base .example-chip rule would have, since it does not carry
   that class. */
:root.gilded .try-smells-like {
  display: inline-block;
  order: 2;
  padding: 7px 14px;
  border-radius: 20px;
  font-family: var(--font-body);
  font-size: 13px;
  cursor: pointer;
}

/* --- §4.1 Chips ------------------------------------------------------------
   Seven chip families already exist and all seven share one look, so they are
   restyled as a set: .chip (type tabs), .filter-chip, .example-chip,
   .recent-chip, .refine-chip, .related-chip and .size-chip.

   RADIUS IS DELIBERATELY NOT TOUCHED. The seven carry four different radii
   (16 / 18 / 20 / 999px) and the spec does not ask for one. Changing them
   would be a size change made in passing, and a size change on a wrapping row
   of chips is a reflow. Fill, border and text only. */

:root.gilded .chip,
:root.gilded .filter-chip,
:root.gilded .example-chip,
:root.gilded .recent-chip,
:root.gilded .refine-chip,
:root.gilded .related-chip,
:root.gilded .size-chip,
:root.gilded .type-tab,
/* §9e's Try chip. It is here for the LOOK only — it carries no data-q and no
   binder touches it, which is exactly why it can sit in this list safely. */
:root.gilded .try-smells-like {
  background: linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
              var(--chip-wire) border-box;
  border: 1px solid transparent;
  color: var(--chip-text);
}

/* ⚠️ EVERY ONE OF THESE HOVER RULES WOULD HAVE DESTROYED THE BORDER, and it
   would have looked like a chip that loses its edge on mouseover rather than
   like a bug. A gradient border is a border-box BACKGROUND under a transparent
   border, so the existing `border-color: rgba(gold, .7)` on hover paints a
   flat colour over it, and the two rules that also set `background` on hover
   (.example-chip, .refine-chip) replace the layer that IS the border. Both
   halves have to be restated together. */
:root.gilded .chip:hover,
:root.gilded .filter-chip:hover,
:root.gilded .example-chip:hover,
:root.gilded .recent-chip:hover,
:root.gilded .refine-chip:hover,
:root.gilded .related-chip:hover,
:root.gilded .size-chip:hover,
:root.gilded .type-tab:hover,
:root.gilded .try-smells-like:hover {
  background: linear-gradient(var(--ink-700), var(--ink-700)) padding-box,
              var(--chip-wire-hover) border-box;
  border-color: transparent;
  color: var(--gold-lt);
}

/* Selected: the gold fill with dark text. Last, so that a chip which is both
   selected and hovered reads as selected -- these and the hover rules above
   are the same specificity, so source order is what decides it. */
:root.gilded .chip.active,
:root.gilded .filter-chip.active,
:root.gilded .size-chip.active,
:root.gilded .type-tab.active {
  background: var(--chip-fill-on) padding-box,
              var(--chip-fill-on) border-box;
  border-color: transparent;
  color: var(--shadow-ink);
}

/* ⚠️ §3b — THE ONE FAMILY THAT GETS A RADIUS, AND IT SUPERSEDES THE RULE
   STATED AT THE TOP OF THIS BLOCK. That rule ("radius is deliberately not
   touched") was right while the spec named no radius: a size change made in
   passing, on a wrapping row, is a reflow. §3b now names the radius AND the
   padding for the type tabs specifically, so for these it is no longer in
   passing. The other six families keep their own radii until someone asks;
   --radius-pill is the token if they are ever unified.

   999px rather than var(--radius-pill) because that is what §3b writes, and
   the two are the same value -- worth unifying when the other six are.

   Placed AFTER the seven-family block on purpose: the base .type-tab rule in
   index.html carries its own radius and padding, and while (0,3,0) beats
   (0,1,0) regardless of order, keeping the exception next to the rule it
   supersedes is what stops someone re-adding the no-radius claim above it. */
:root.gilded .type-tab {
  border-radius: 999px;
  padding: 9px 18px;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1;
  letter-spacing: 0.2px;
}

/* --- §4.1 / §7.3 The product card as a wire card ---------------------------
   Restyle of .candle-tile. The compare checkbox, the save button, the shop
   link and every data-* attribute are untouched -- this step adds no markup
   and removes none. */

:root.gilded .candle-tile {
  background: linear-gradient(var(--panel), var(--panel)) padding-box,
              var(--wire) border-box;
  /* The spec asks for 1.5px. ⚠️ MEASURED 2026-09-20: Chrome resolves this to a
     computed 1px at DPR 1, 2 AND 3 -- the tile is 513.25px tall in all three,
     unchanged from the 1px border it replaces. So the wire renders 1px here
     and the card's box did not grow, which is why this step moved no snapshot.
     The declared value is kept because it is the spec's and because engines
     that do render fractional borders will use it; do not "fix" the gap by
     chasing it, and do not assume the 0.5px is buying anything in Chrome. */
  border: 1.5px solid transparent;
  border-radius: var(--radius-card);
  box-shadow: inset 0 1px 0 rgba(255, 240, 190, 0.18),
              0 14px 34px rgba(var(--black-rgb), 0.55);
}

/* ⚠️ SAME TRAP AS THE CHIPS, AND WORSE HERE BECAUSE IT ONLY SHOWS ON HOVER.
   .candle-tile:hover sets border-color: rgba(gold, .8), which paints a flat
   colour over the gradient that IS the wire. The card would lose its edge
   the moment a cursor touched it -- on the most-looked-at component on the
   page, in a state no screenshot of a resting page would catch. The lift is
   kept in the shadow instead, where it costs no layout. */
:root.gilded .candle-tile:hover {
  border-color: transparent;
  box-shadow: inset 0 1px 0 rgba(255, 240, 190, 0.3),
              0 18px 44px rgba(var(--black-rgb), 0.62);
}

/* --- §5.1 The scrim --------------------------------------------------------
   A vertical darkening between the photograph and the page, so text stays
   readable while the photo stays vivid.

   ⚠️ IT IS A PSEUDO-ELEMENT, NOT THE NEW DIV THE SPEC ASKS FOR. The spec calls
   for a .scrim element; body::before does the same job with no markup change,
   which keeps this whole phase CSS-only. It also sidesteps a name that is
   already spoken for in prose: index.html carries a comment about "the scrim"
   above .quiz-tile-text, describing a fixed scrim and a build step that
   normalises image luminance. ⚠️ NEITHER EXISTS IN THIS PROJECT -- there is no
   build step at all, and the comment directly contradicts the one below it,
   which says nothing filters the image. Treat that comment as foreign; it
   describes no code here.

   Fixed, so it is relative to the viewport rather than the page: the photo
   never scrolls and neither does its darkening. */

:root.gilded .bg-slideshow {
  z-index: -2;            /* the photo drops below the scrim */

  /* ⚠️ THIS ONE LINE BRIGHTENS EVERY PHOTOGRAPH ON THE SITE AT ONCE, and it
     changes the backdrop under ALL existing text, not only the text this
     redesign touches. It is safe only because the scrim above replaces what it
     was doing: the two belong together and neither should ship alone.

     MEASURED at 1366x900 and 390x844 against a WHITE photograph -- the
     lightest thing that can ever load, so these are bounds, not samples of
     whichever image happened to be on screen:

                            0.7 + scrim   none + scrim   none + retuned scrim
       hero h1 desktop         8.06:1        5.42:1            6.41:1
       hero h1 PHONE           6.85:1        4.31:1  FAIL      5.40:1
       explore heading        12.81:1       11.14:1           11.14:1
       footer link             6.21:1        6.16:1            6.16:1
       card price             15.95:1       13.87:1           13.87:1

     ⚠️ THE MIDDLE COLUMN IS WHY THIS IS ITS OWN STEP. Dropping the filter with
     the spec's own scrim stops put the phone hero at 4.31:1, under the 4.5
     floor -- a real regression, introduced by deleting one declaration, and
     invisible without measuring. The stops were retuned (0.45/0.62 -> 0.52/0.68
     at the 0% and 30% positions) because the phone hero sits at 28% of the
     viewport where the scrim is thinnest. The spec anticipates this: it says
     its values were set against full-page boards and want tuning on a real
     screen.

     The phone hero at 5.40:1 is the tightest margin on the page and the number
     to re-measure if the stops move again. */
  filter: none;
}

:root.gilded body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Stops from spec §5.1, written through --ink-rgb rather than as literals so
     a palette change carries the scrim with it. */
  background: linear-gradient(180deg,
    rgba(var(--ink-rgb), 0.52) 0%,
    rgba(var(--ink-rgb), 0.68) 30%,
    rgba(var(--ink-rgb), 0.80) 42%,
    rgba(var(--ink-rgb), 0.86) 100%);
}

/* ⚠️ TWO THINGS DELIBERATELY LEFT ALONE ON THIS CARD.

   backdrop-filter: blur(12px) is now wasted work -- the padding-box fill is
   fully opaque, so there is nothing behind the card left to blur. It is NOT
   removed here, because backdrop-filter creates a stacking context and
   .candle-popup is positioned inside the tile: dropping it could let the
   popup escape the tile's context and order against other cards. That is a
   behavioural change hiding inside a performance tidy-up, and it belongs in
   its own step with the flip exercised. (isolation: isolate is the tool if it
   is ever done.)

   The mobile rule .candle-tile { background: rgba(ink, .92) } exists because
   the blur is dropped under 600px and the card needed to stay readable
   without it. The gilded fill is fully opaque, so that intent is already met
   and no restatement is needed -- unlike the backdrop-filter drop above,
   where the gilded rule would have overridden the decision rather than
   satisfied it. */

/* === §B2 "Learn before you search" ========================================
   Design's two columns: guides left, notes right, 48px gutter. The markup is
   shared with the live page, so every rule here is an override of the base
   pills-and-panel look rather than the only styling the section has.

   ⚠️ TWO SHAPES PER COLUMN, AND THAT IS A CRAWL CONSTRAINT, NOT A LAYOUT
   CHOICE. Nine notes get tiles and seven a text row; five guides get the ruled
   list and ten a text row. tests\homepage-links.js [A5]/[A6] need all 16 note
   pages, all 15 guides and both hubs reachable from here, and a design that
   showed only the featured ones would drop 17 links off the homepage's only
   crawlable path. The text rows are what make the featured shapes affordable.
   Extended to the guides on 2026-09-22: Design specified it for the notes, and
   applying the same pattern beats inventing a second answer. */

:root.gilded .explore-cols {
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: 48px;
  align-items: start;
}

/* ⚠️ The panel chrome goes, but the GLASS BAND stays -- §4.2 gives
   .explore-group a blurred translucent background, and that is what keeps this
   text legible over the photograph. Only the box around it is removed. */
:root.gilded .explore-group {
  margin: 0;
  padding: 20px 22px 22px;
  border-radius: 10px;
}

:root.gilded .explore-subheading {
  font-family: var(--font-display);
  font-size: 15px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 14px;
}

:root.gilded .explore-subheading a {
  color: var(--gold-lt);
}

/* --- left: the ruled list ------------------------------------------------ */

:root.gilded .guide-list a {
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}

:root.gilded .guide-title {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 500;
  color: rgb(var(--cream-rgb));
}

:root.gilded .guide-cat {
  font-size: 14px;
  color: var(--muted);
}

/* --- right: the nine tiles ----------------------------------------------- */

:root.gilded .note-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

/* ✅ DESIGN TOOK A FOURTH OPTION (2026-09-22) AND IT IS BETTER THAN MY RETUNE:
   don't dim the photograph, move the text off it. The image keeps the top of
   the tile at full strength; the name and descriptor sit on an OPAQUE --panel
   strip beneath.

   The argument is the site's own: these photographs stand in for smelling the
   thing, so a half-strength photo is a worse answer to "what does vetiver look
   like" than a smaller clean one. And the measurement stops depending on the
   image — text on an opaque panel is measured ONCE, where my retuned scrim had
   to hold across 38 arbitrary photographs and every future swap. A 39th note
   cannot break this.

   My retuned gradient survives as --photo-scrim below, for the surfaces where
   text genuinely must sit on a photo. */
:root.gilded .note-tile {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  height: 150px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--panel);
}

/* ⚠️ An <img>, not a background-image div as the spec's CSS implies. A per-tile
   URL can only reach such a div as an inline style, and an inline style is
   unreachable from every stylesheet — so the live page could not switch the
   photographs off. object-fit renders the same. Measured: live loads 0 of 9. */
:root.gilded .note-tile-img {
  display: block;
  flex: 0 0 96px;
  width: 100%;
  min-height: 0;
  object-fit: cover;
}

/* ⚠️ PADDING IS 6/8, NOT THE SPEC'S 8/10, AND THE 2px MATTERS. At 8/10 the
   caption's line boxes fit the 52px strip EXACTLY — a probe measuring
   getBoundingClientRect reported "fits with 0px spare" and "0 of 9 cut off".
   It was wrong about the only thing that shows: a line box can fit while the
   DESCENDER INK paints outside it, and the tile's overflow:hidden clips ink,
   not boxes. At 6x zoom the y and the comma in "velvety, romantic" were sliced
   flat. Freeing 4px here fixes it without touching Design's 96px photo band or
   the 150px tile, so the section height it computed is unchanged. */
:root.gilded .note-tile-cap {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  padding: 6px 12px 8px;
  background: var(--panel);
}

/* ⚠️ NO GRADIENT, NO SCRIM, NO TEXT OVER THE IMAGE. The ::after overlay that
   stood here is gone with the overlay design. Two failures it produced are
   worth keeping as history, because both were invisible to a green suite:

   ❌ The spec's original stops (0 at 30%, 0.82 at 100%) failed the white-photo
      bound at 2.41:1 for the 20px name against a 4.5 floor. Retuning cleared
      it at 5.37:1 — and cost the photograph half its strength, which is the
      trade Design has now refused.
   ❌ And that 5.37 was fiction until the stacking was fixed. ::after generates
      after the content, so at `z-index: auto` the scrim painted OVER the names
      while the probe composited photo → scrim → text and reported a
      comfortable figure for a stack Chrome does not paint. The screenshot
      showed it; the number did not.

   Both are why the probe now hit-tests elementFromPoint before believing any
   ratio it computes. */

:root.gilded .note-tile-name {
  font-family: var(--font-display);
  font-size: 20px;
  line-height: 1.05;
  color: rgb(var(--cream-rgb));
}

/* ⚠️ `display: block` IS LOAD-BEARING AND WAS LOST ONCE. The live page hides
   this element (`.note-tile-desc { display: none }`), so gilded has to turn it
   back on; the override used to ride along on the scrim's z-index rule, and
   deleting that rule with the scrim silently hid every descriptor. The ratio
   below kept computing from computed style the whole time — it was the
   hit-test that caught it, on a rect of all zeros. */
:root.gilded .note-tile-desc {
  display: block;
  font-size: 12px;
  line-height: 1.3;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Brightness on the image rather than a scale transform: the photo is the
   point now, and scaling it inside a 96px band crops it further on hover. */
:root.gilded .note-tile-img {
  transition: filter 0.3s ease;
}

:root.gilded .note-tile:hover .note-tile-img,
:root.gilded .note-tile:focus-visible .note-tile-img {
  filter: brightness(1.06);
}

:root.gilded .note-tile:hover .note-tile-name,
:root.gilded .note-tile:focus-visible .note-tile-name {
  color: var(--gold-lt);
}

/* --- both text rows, and the hub links ----------------------------------- */

/* ❌ The separators were rgba(white, 0.3) so the "·" would sit back from the
   links. Measured against the white-photo bound: 2.69:1 against a 4.5 floor.
   A middot between two links is text content, not decoration, so it does not
   get the decorative exemption. The row is now one colour, which is how a
   "a · b · c" row normally reads anyway. */
:root.gilded .guide-more,
:root.gilded .note-more {
  margin-top: 16px;
  font-size: 13px;
  color: var(--muted);
}

:root.gilded .guide-more a,
:root.gilded .note-more a {
  color: var(--muted);
}

/* Now that BOTH columns end in a text row, the row needs to say what it is or
   it reads as leftovers. */
:root.gilded .more-label {
  display: block;
  margin-bottom: 4px;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

:root.gilded .explore-all a {
  border: 0;
  padding: 0;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gold-lt);
}

/* ⚠️ 900px, not the 600px used elsewhere: 5fr/7fr of 1100px leaves the notes
   column at ~390px by 900, which is under the 3 x 110px the tile grid needs
   once the gutter is counted. That is the width the CONTENT breaks at. */
@media (max-width: 900px) {
  :root.gilded .explore-cols {
    grid-template-columns: 1fr;
    gap: 22px;
  }
}

/* ❌ Stacking the COLUMNS is not enough: the tile grid stayed 3-across, so at
   390px each tile was ~97px wide and "Sandalwood" was cut off mid-word. The
   suite cannot see this -- a clipped name is still a present, correctly
   coloured, correctly linked name -- and the screenshot is what showed it.
   Two across gives ~124px of text width, which the longest name clears. */
@media (max-width: 600px) {
  :root.gilded .note-tiles {
    grid-template-columns: repeat(2, 1fr);
  }
  :root.gilded .note-tile {
    height: 132px;
  }
  :root.gilded .note-tile-img {
    flex: 0 0 82px;
  }
  :root.gilded .note-tile-name {
    font-size: 18px;
  }
}

@media (prefers-reduced-motion: reduce) {
  :root.gilded .note-tile-img {
    transition: none;
  }
  :root.gilded .note-tile:hover .note-tile-img,
  :root.gilded .note-tile:focus-visible .note-tile-img {
    transform: none;
  }
}

/* === OVERLAYS (drafts\scentsearch-overlays-build-spec.md) ==================
   ⚠️ SCOPE NOTE, AND IT IS THE WHOLE COSTING: this block restyles only the
   overlay surfaces a stylesheet can actually reach. Measured 2026-09-22 on the
   real file:

     quiz steps + results   0 inline styles in JS, 16 classes emitted  REACHABLE
     saved panel            1 inline style, 6 classes                  REACHABLE
     sort / filters sheets  0 inline styles in JS, 39 classes          REACHABLE
     every overlay's SHELL  .quiz-modal / .quiz-panel, classed         REACHABLE
     ---------------------------------------------------------------------
     compare PANEL interior openCompare(): 10,811 chars, 33 style="",
                            and ZERO class attributes                  UNREACHABLE
     smells-like results    runSmellsLikeSearch(): 11 style="", 0 class UNREACHABLE
     compare BAR contents   a count span and two buttons — the spec's
                            per-product chips do not exist yet         NOT A RESTYLE

   Nothing below targets those three. A rule aimed at an inline-styled element
   does nothing, silently, and would read as "built". */

/* --- §1 shared overlay chrome --------------------------------------------
   The existing markup already uses .quiz-modal as the scrim and .quiz-panel as
   the panel for EVERY overlay — quiz, compare, smells-like, sort and filters
   all reuse them. So the spec's .ov-scrim / .ov-panel need no markup change;
   they are these two classes. */
:root.gilded .quiz-modal {
  background: rgba(7, 6, 4, 0.80);
}

:root.gilded .quiz-panel {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1.5px solid transparent;
  border-radius: 8px;
  box-shadow:
    0 40px 100px rgba(var(--black-rgb), 0.70),
    inset 0 1px 0 rgba(255, 240, 190, 0.18);
  padding: 34px 38px 28px;
  max-height: calc(100vh - 80px);
  overflow-y: auto;
}

/* ⚠️ Transform and opacity only, never height or width — the CLS rule this
   project already paid for twice. */
@media (prefers-reduced-motion: reduce) {
  :root.gilded .quiz-modal,
  :root.gilded .quiz-panel {
    transition: none;
  }
}

/* --- §2 quiz steps -------------------------------------------------------- */

:root.gilded .quiz-q {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 40px;
  line-height: 1.1;
  color: var(--gold-lt);
}

:root.gilded .quiz-sub {
  font-size: 15.5px;
  color: var(--muted);
}

/* The dots. showQuizStep() already writes is-current / is-done, so this is a
   look change with no JS behind it. */
:root.gilded .quiz-progress span {
  height: 5px;
  width: 8px;
  border-radius: 999px;
  background: rgba(var(--gold-rgb), 0.25);
  transition: width 180ms ease, background 180ms ease;
}

:root.gilded .quiz-progress span.is-done {
  width: 8px;
  background: var(--gold-lt);
}

:root.gilded .quiz-progress span.is-current {
  width: 30px;
  background: linear-gradient(90deg, #fbe9ad, #c99a3e);
}

/* ⚠️ THE SPEC'S `display: flex; align-items: flex-end` IS WRONG FOR THIS
   MARKUP, and it renders as a plausible-looking tile. The existing DOM is
   `<img class="quiz-tile-media">` and `<span class="quiz-tile-text">` as
   SIBLINGS, so a flex row puts the photo and the label side by side — a 100px
   image beside a 105px column of text — rather than the label over the photo.

   The contrast probe could not see it: the label was rendered, unobscured and
   measured a comfortable 19.77:1. It was simply not where the design puts it.
   The screenshot is what showed it, again.

   So the photo is absolutely positioned to fill the tile (the note-tile
   pattern) and the text block is pinned to the bottom above it. CSS only — no
   markup change, which is the whole reason this overlay was costed as
   reachable. */
:root.gilded .quiz-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}

:root.gilded .quiz-tile {
  position: relative;
  display: block;
  height: 176px;
  border: 0;
  border-radius: 6px;
  overflow: hidden;
  padding: 0;
  text-align: left;
}

:root.gilded .quiz-tile-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

:root.gilded .quiz-tile-text {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  padding: 12px 14px;
}

:root.gilded .quiz-tile::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(var(--black-rgb), 0.35) 35%,
    rgba(var(--black-rgb), 0.85) 100%);
}

/* ⚠️ The text has to be lifted ABOVE that gradient explicitly. ::after
   generates after the content, so at z-index auto the scrim paints over the
   label — exactly the bug the note tiles shipped with for a day, found by a
   screenshot rather than by any ratio. */
:root.gilded .quiz-tile-label {
  font-family: var(--font-display);
  font-size: 23px;
  color: rgb(var(--white-rgb));
}

/* ⚠️ The family is set explicitly because .quiz-tile is a <button>, and a
   button does not inherit the page font — it takes the UA's. Measured: this
   caption rendered in Arial while every other overlay string was Jost, which
   no ratio would ever have shown. */
:root.gilded .quiz-tile-cap {
  font-family: var(--font-body);
  font-size: 13px;
  color: rgba(var(--white-rgb), 0.78);
}

:root.gilded .quiz-tile,
:root.gilded .filter-option {
  font-family: var(--font-body);
}

/* ⚠️ The inner ring is --panel, NOT the spec's --bg: no such token exists, and
   an undefined var() invalidates the WHOLE declaration — the selected ring
   would simply never paint, silently. --panel is the surface the tile sits on,
   which is what the gap is meant to read as. */
:root.gilded .quiz-tile[aria-pressed="true"] {
  box-shadow: 0 0 0 2px var(--panel), 0 0 0 4px var(--gold-icon);
}

:root.gilded .quiz-tile[aria-pressed="true"]::after {
  background: linear-gradient(180deg,
    rgba(var(--black-rgb), 0.10) 35%,
    rgba(var(--black-rgb), 0.85) 100%);
}

/* The avoid step is deliberately flat — no photography, because a beautiful
   photo makes the "no thanks" answer feel wrong. Do not "fix" this. */
:root.gilded .quiz-tile.is-flat {
  height: 84px;
  border-radius: 6px;
  align-items: center;
  padding: 0 16px;
  background: rgba(var(--white-rgb), 0.03);
  border: 1px solid var(--line);
}

:root.gilded .quiz-tile.is-flat::after { content: none; }

:root.gilded .quiz-tile.is-flat .quiz-tile-label { font-size: 17px; }

:root.gilded .quiz-nav {
  border-top: 1px solid var(--line);
}

:root.gilded .quiz-saved-offer {
  font-size: 14px;
  color: var(--muted);
}

:root.gilded .quiz-link {
  color: var(--gold-lt);
  text-decoration: underline;
}

/* Panel widths (spec §1). .quiz-panel is shared by EVERY overlay, so the
   widths have to be per-overlay or one number would govern all five. The ids
   already exist in the markup. */
:root.gilded #quizModal .quiz-panel        { max-width: 880px; }
:root.gilded #quizResultsModal .quiz-panel { max-width: 1040px; }
:root.gilded #smellsLikeModal .quiz-panel  { max-width: 620px; }

@media (max-width: 900px) {
  :root.gilded .quiz-tiles { grid-template-columns: repeat(2, 1fr); }
}

/* --- §3 quiz results ------------------------------------------------------ */

:root.gilded .quiz-res-name {
  font-family: var(--font-display);
  font-weight: 600;
  color: rgb(var(--cream-rgb));
}

:root.gilded .quiz-res-brand,
:root.gilded .quiz-res-why,
:root.gilded .quiz-res-text {
  color: var(--muted);
}

:root.gilded .quiz-res-price {
  color: rgb(var(--cream-rgb));
}

:root.gilded .quiz-res-thumb {
  border-radius: var(--radius-card);
}

:root.gilded .quiz-res-honesty {
  font-size: 12.5px;
  color: var(--muted);
}

:root.gilded .quiz-res-empty {
  font-size: 15px;
  color: var(--muted);
}

/* --- §6 saved panel ------------------------------------------------------- */

:root.gilded .saved-sheet {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1.5px solid transparent;
  box-shadow: 0 40px 100px rgba(var(--black-rgb), 0.70);
}

:root.gilded .saved-item {
  border-bottom: 1px solid var(--line);
}

:root.gilded .saved-item-name {
  font-family: var(--font-display);
  font-size: 18px;
  color: rgb(var(--cream-rgb));
}

:root.gilded .saved-empty {
  font-size: 15px;
  color: var(--muted);
  text-align: center;
}

:root.gilded .saved-item-remove-btn {
  color: var(--gold-lt);
}

/* --- §7 sort and filters sheets ------------------------------------------- */

:root.gilded .filter-sheet-section {
  border-top: 1px solid var(--line);
}

:root.gilded .filter-option {
  border-radius: 6px;
  font-size: 15.5px;
  border: 1px solid var(--line);
  background: transparent;
  color: rgba(var(--white-rgb), 0.85);
}

:root.gilded .filter-option.selected {
  background: rgba(var(--gold-rgb), 0.10);
  border-color: rgba(var(--gold-rgb), 0.55);
  color: rgb(var(--cream-rgb));
}

:root.gilded .filter-note {
  font-size: 12.5px;
  color: var(--muted);
}

:root.gilded .filter-error {
  color: #f0b7a4;
}

/* ⚠️ Phone: every overlay except the compare bar becomes a bottom sheet. Same
   markup, different CSS — which is only possible because these shells are
   classed rather than inline-styled. */
@media (max-width: 700px) {
  :root.gilded .quiz-panel {
    border-radius: 14px 14px 0 0;
    max-height: 92vh;
    padding: 22px 18px 20px;
  }
  :root.gilded .quiz-q { font-size: 30px; }
  :root.gilded .quiz-tile { height: 150px; }
  :root.gilded .quiz-tile-label { font-size: 20px; }
}

/* --- §5 smells-like status block -----------------------------------------
   ⚠️ THIS BLOCK WAS UNREACHABLE UNTIL 2026-09-22. runSmellsLikeSearch() built
   it with inline styles and no classes, so every rule below would have done
   nothing at all — silently. The refactor that moved those declarations into
   .sl-* base rules is what makes this possible, and render-parity.js proved
   the live page renders identically before and after it.

   This is the reachability PROOF as much as a restyle: if these rules move the
   computed style under .gilded while the live page stays byte-identical, the
   refactor did what it claimed. */
:root.gilded .sl-name {
  font-family: var(--font-display);
  font-size: 17px;
  color: rgb(var(--cream-rgb));
}

:root.gilded .sl-notes {
  color: var(--muted);
}

:root.gilded .sl-hint {
  opacity: 1;
  color: var(--muted);
}

:root.gilded .sl-note-chip {
  border-radius: var(--radius-pill);
  background: rgba(var(--gold-rgb), 0.10);
  border: 1px solid var(--line);
  color: var(--gold-lt);
}

:root.gilded .sl-conf {
  border-radius: var(--radius-pill);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* --- The match badge ------------------------------------------------------
   This is the site's argument in three words: the one element that tells a
   visitor WHY this product is in front of them. It only became restylable on
   2026-09-23 — before that all three tiers were inline-styled and no rule here
   could reach them.

   The tiers are ranked by WEIGHT, not by hue: filled and gold for the strongest
   claim, filled and pale for the middle, outlined and quiet for the weakest.
   A visitor reads the confidence before reading the words. */
:root.gilded .match-badge {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 5px 11px;
  border-radius: var(--radius-pill);
}

:root.gilded .match-badge.is-best {
  background: linear-gradient(180deg, #fbe9ad, #c99a3e 55%, #e9cf85);
  color: var(--shadow-ink);
}

:root.gilded .match-badge.is-close {
  background: rgba(var(--cream-rgb), 0.9);
  color: var(--shadow-ink);
}

/* ⚠️ The quiet one stays quiet. Giving the weakest tier the same fill as the
   others would make "possible" look like a recommendation, which is the one
   thing this badge must not do on a site whose whole argument is that it does
   not overstate what it knows.

   ❌ --surface-glass FAILED THE WHITE-PHOTO BOUND AT 3.44:1 against a 4.5
   floor. It is translucent (alpha 0.72) and this badge sits directly ON the
   product photograph, so a pale image showed through and took --muted text
   with it. ⚠️ The query the harness runs only ever produces the 'best' label,
   so this tier never rendered and the probe reported it as "not rendered" --
   unmeasured, which reads exactly like a pass. It was caught only by building
   the badge synthetically from its real class names.

   --panel is opaque, so the measurement stops depending on which photo loaded:
   the same reasoning that put the note-tile captions on a solid strip. Now
   7.97:1 (measured after the change, not predicted before it), and still the
   only outlined, unfilled tier. */
:root.gilded .match-badge.is-possible {
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--muted);
}

/* The affiliate badge is a disclosure, not a promotion: it should be legible
   and unremarkable rather than the brightest thing on the card. */
:root.gilded .affiliate-badge {
  font-family: var(--font-body);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 4px 9px;
  border-radius: var(--radius-pill);
  background: var(--surface-glass);
  border: 1px solid var(--line);
  color: var(--gold-lt);
}

:root.gilded .candle-popup .candle-title {
  font-family: var(--font-display);
  font-size: 19px;
}

/* --- §8 the grid's empty, loading and error states ------------------------
   Unreachable until 2026-09-23: all four blocks were inline-styled, so no rule
   here could touch them. They matter more than their traffic suggests — an
   empty grid is the moment a visitor decides whether the site is broken or
   whether they asked the wrong question, and this one can tell them which. */
:root.gilded .grid-state {
  font-family: var(--font-body);
  color: rgb(var(--cream-rgb));
}

:root.gilded .grid-state-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 30px;
  line-height: 1.15;
  color: var(--gold-lt);
}

:root.gilded .grid-state-title.is-lg { font-size: 34px; }
:root.gilded .grid-state-title.is-tight { font-size: 22px; }

:root.gilded .grid-state-body {
  font-size: 15px;
  line-height: 1.55;
  color: var(--muted);
}

/* ⚠️ The error keeps the alert tone; the two EMPTY states deliberately do not.
   "Nothing matched that search" is not a failure — it is the site saying it
   would rather return nothing than return noise, which is the same argument as
   refusing an unsourced figure. Colouring it like an outage would misreport
   what happened. */
:root.gilded .grid-state-body.is-error {
  font-size: 15px;
  color: #f0b7a4;
}

:root.gilded .grid-state-retry {
  font-family: var(--font-body);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 13px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: var(--surface-glass);
  color: var(--gold-lt);
}

/* =========================================================================
   §11  THREE SURFACES THAT FELL BETWEEN SPECS — 2026-09-23

   The smells-like banner and the search scene are on screen after essentially
   every search, and neither appears in any spec; "Need Inspiration?" was kept
   deliberately and had no gilded rule at all. All three became reachable in
   e36e158 and e741fda; this is the restyle those refactors were for.
   ========================================================================= */

/* --- §11.1  the smells-like banner ---------------------------------------
   This is the site's argument in one sentence — "here is what we think that
   product smells of, and how sure we are" — so it reads as a considered
   statement rather than a system notice.

   ⚠️ OPAQUE --panel, NOT --surface-glass, AND THE REASON IS MEASURED. The
   banner is inside #candleGrid, so it can sit anywhere in a scrolled page —
   its backdrop is an arbitrary photograph at whatever alpha the FIXED scrim
   happens to have at that y. Over a pure-white photo, --muted on
   --surface-glass bounds at ~3.3:1, which fails 4.5 for the 11px foot. The
   same argument as the note-tile caption strip: measured once against one
   known fill rather than per image and per scroll position. */
:root.gilded .smells-like-banner {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-card);
  font-family: var(--font-body);
  color: var(--muted);
  padding: 14px 20px 15px;
}

/* The product name is the thing the sentence is about, so it takes the
   display face. Flat --gold-lt rather than the --gold-text gradient: that
   gradient bottoms out at #a8761f and its lower third measured 2.71:1, which
   is the failure the header wordmark already paid for. */
:root.gilded .slb-name {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--gold-lt);
}

:root.gilded .slb-note-chip {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  color: var(--chip-text);
  padding: 3px 11px;
}

/* The disclaimer. It has to be readable — it is the honest half of the claim
   above it — so it is --muted on the opaque panel, not a dimmer white. */
:root.gilded .slb-foot {
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--muted);
}

/* --- §11.2  the search scene ---------------------------------------------
   The summary box under the search bar: what we understood, and which notes
   we are matching or avoiding. Same opaque fill for the same reason — it sits
   high on the page where the scrim is THINNEST, which is where the phone hero
   already measured 4.31:1 with a scrim under it. */
:root.gilded .search-scene {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-card);
  font-family: var(--font-body);
}

:root.gilded .search-scene-body {
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--chip-text);
}

/* "Matching notes" / "Avoiding" — the same label treatment as the explore
   section's text rows, so a label reads as a label everywhere on the page. */
:root.gilded .scene-label {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

:root.gilded .scene-pill {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  color: var(--chip-text);
  padding: 3px 11px;
  font-size: 11.5px;
}

/* ⚠️ An excluded note stays a DIFFERENT KIND of pill, not a dimmer one —
   the base rule's decision, kept. A visitor reading "not vanilla" needs to
   see a negation, and a lower-contrast gold pill reads as a disabled one. */
:root.gilded .scene-pill.is-avoid {
  background:
    linear-gradient(rgba(var(--alert-rgb), 0.10), rgba(var(--alert-rgb), 0.10)) padding-box,
    linear-gradient(135deg, rgba(var(--alert-soft-rgb), 0.55),
                            rgba(var(--alert-rgb), 0.35)) border-box;
  color: var(--alert-text);
}

/* --- §11.3  Need Inspiration? --------------------------------------------
   ⚠️ MOST OF THIS PANEL WAS ALREADY REACHED and needed nothing: .quiz-modal,
   .quiz-panel, .quiz-sub, .quiz-nav and .chip are all in earlier sections, so
   the panel already carries the wire border and the chips already carry the
   chip wire. What had no rule is the two things that make it a browsable menu
   rather than a dialog: its heading and its two section labels. */
:root.gilded .quiz-panel h2 {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: 0.005em;
  color: var(--gold-lt);
  margin: 0 0 8px 0;
}

:root.gilded .chip-label {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* =========================================================================
   §12  SELECTION, AND THE SCROLLBARS — 2026-09-23

   Both found by looking at the preview, and both are the same shape as the
   hover bug §2 already records: a gilded rule for a component's RESTING state,
   written at higher specificity than the base rule for its SELECTED state, so
   choosing something changed nothing.
   ========================================================================= */

/* --- §12.1  the quiz's selected state -------------------------------------
   ❌ MEASURED BEFORE AND AFTER, because "it looks subtle" is not a diagnosis.
   On the live page selecting an Avoid option moves THREE properties:
     background rgba(255,255,255,0.03) -> rgba(212,175,55,0.10)
     border     rgba(255,255,255,0.12) -> rgba(212,175,55,0.75)
     label      rgba(255,255,255,0.78) -> rgb(243,229,171)
   Gilded moved ONE — the label — and the other two were frozen at their
   unselected values.

   The cause is specificity, not a missing colour. `.quiz-tile.is-flat.selected`
   in index.html is (0,3,0); §2's `:root.gilded .quiz-tile.is-flat` is (0,4,0)
   and sets background and border, so the resting rule outranked the selected
   one. Everything here is therefore written at (0,5,0) or above, and the
   measurement is repeated afterwards rather than assumed. */
:root.gilded .quiz-tile.is-flat.selected {
  background:
    linear-gradient(rgba(var(--gold-rgb), 0.16), rgba(var(--gold-rgb), 0.16)) padding-box,
    var(--chip-wire-hover) border-box;
  border: 1px solid transparent;
  /* the ring is what carries at a glance — a border colour change on an 84px
     button reads as nothing across a 2-column grid */
  box-shadow: 0 0 0 1px rgba(var(--gold-rgb), 0.65);
}

:root.gilded .quiz-tile.is-flat.selected .quiz-tile-label {
  color: var(--gold-lt);
}

/* ⚠️ THE PHOTO TILES LOST THEIR CHECK BADGE ENTIRELY, which is worse than a
   subtle state: the badge IS the affordance. The base rule draws it with
   `.quiz-tile.selected::after`, and §2 repurposes ::after as the photo scrim
   at (0,3,1) — so the scrim won and the tick never rendered. Redrawn on
   ::before, which nothing else on this element uses, so both can coexist. */
:root.gilded .quiz-tile.selected {
  box-shadow: 0 0 0 2px rgba(var(--gold-rgb), 0.9);
}

:root.gilded .quiz-tile.selected::before {
  content: "\2713";
  position: absolute;
  top: 10px;
  right: 10px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  /* ⚠️ z-index above .quiz-tile::after, which is the scrim. Without it the
     badge is painted over by the gradient — the note-tile bug, third time. */
  z-index: 2;
  background: var(--gold-fill);
  color: var(--shadow-ink);
  font-size: 14px;
  line-height: 24px;
  text-align: center;
  font-weight: 600;
}

/* The sheets' options already had a gilded selected rule and it works, but it
   was written in the same quiet register. Brought up to match, so "selected"
   means one thing everywhere in the overlays. */
:root.gilded .filter-option.selected {
  background: rgba(var(--gold-rgb), 0.16);
  border-color: rgba(var(--gold-rgb), 0.75);
  box-shadow: 0 0 0 1px rgba(var(--gold-rgb), 0.45);
  color: var(--gold-lt);
}

/* --- §12.2  scrollbars ----------------------------------------------------
   The compare panel scrolls, and its scrollbar was the browser default: a
   white trough and a grey thumb down the side of a near-black panel. The base
   stylesheet already solved this twice — .notes-scroll and .popup-scroll — so
   this is that pattern applied to the surfaces the redesign added, rather than
   a new invention.

   Firefox takes the two standard properties; WebKit needs the pseudo-elements,
   and ignores the standard ones. Both are declared, which is why this looks
   duplicated. */
:root.gilded .compare-panel,
:root.gilded .quiz-panel,
:root.gilded .quiz-modal,
:root.gilded .filter-option-list,
:root.gilded #savedPanel {
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--gold-rgb), 0.45) transparent;
}

:root.gilded .compare-panel::-webkit-scrollbar,
:root.gilded .quiz-panel::-webkit-scrollbar,
:root.gilded .quiz-modal::-webkit-scrollbar,
:root.gilded .filter-option-list::-webkit-scrollbar,
:root.gilded #savedPanel::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

:root.gilded .compare-panel::-webkit-scrollbar-track,
:root.gilded .quiz-panel::-webkit-scrollbar-track,
:root.gilded .quiz-modal::-webkit-scrollbar-track,
:root.gilded .filter-option-list::-webkit-scrollbar-track,
:root.gilded #savedPanel::-webkit-scrollbar-track {
  background: transparent;
}

:root.gilded .compare-panel::-webkit-scrollbar-thumb,
:root.gilded .quiz-panel::-webkit-scrollbar-thumb,
:root.gilded .quiz-modal::-webkit-scrollbar-thumb,
:root.gilded .filter-option-list::-webkit-scrollbar-thumb,
:root.gilded #savedPanel::-webkit-scrollbar-thumb {
  background: rgba(var(--gold-rgb), 0.45);
  border-radius: 999px;
}

:root.gilded .compare-panel::-webkit-scrollbar-thumb:hover,
:root.gilded .quiz-panel::-webkit-scrollbar-thumb:hover,
:root.gilded .quiz-modal::-webkit-scrollbar-thumb:hover,
:root.gilded .filter-option-list::-webkit-scrollbar-thumb:hover,
:root.gilded #savedPanel::-webkit-scrollbar-thumb:hover {
  background: rgba(var(--gold-rgb), 0.7);
}

/* ⚠️ The CORNER is a separate pseudo-element and defaults to white. The
   compare panel scrolls in both axes at 3 columns on a narrow desktop, so it
   is the one surface where the corner actually renders. */
:root.gilded .compare-panel::-webkit-scrollbar-corner,
:root.gilded .quiz-panel::-webkit-scrollbar-corner {
  background: transparent;
}

/* --- §12.3  the bar has to grow where its content is two rows -------------
   ❌ MEASURED 2026-09-23 AND VISIBLY BROKEN FROM 601px TO 1100px: the search
   button sat 91px from the bar's top -- 73px BELOW its 78px bottom edge,
   hanging outside the control entirely. Same at 700, 800 and 900.

   §6's `height: 78px` is right, and its arithmetic ((78 - 4 border - 60
   button) / 2 + 2 = 9px above and below) is right -- for the state it was
   measured in, which is ONE ROW. It was applied from 601px up, and at
   <= 1100px this bar is deliberately TWO rows: CONTROLS_INLINE_QUERY is
   '(max-width: 1100px)' and placeSearchControls() moves the results toolbar
   INSIDE the container there, with index.html's own block putting the input on
   its own row. A fixed height cannot contain a layout that was designed to
   wrap.

   ⚠️ THE DESKTOP GEOMETRY IS UNTOUCHED. This block starts at 601 and stops at
   1100, so >= 1101px keeps height 78, button 159x60, right inset 10 and top
   and bottom 9 -- re-measured at 1101/1200/1366/1600/1920/2560 after the
   change, all identical to before it.

   min-height rather than height, so the one-row case at the top of this range
   still lands on exactly 78. */
@media (min-width: 601px) and (max-width: 1219px) {
  :root.gilded .ai-search-container {
    height: auto;
    min-height: 78px;
    /* §6 zeroed these because a one-row bar centres by itself; a two-row bar
       needs the gutter back or the rows sit flush against the border. */
    padding-top: 10px;
    padding-bottom: 10px;
    row-gap: 10px;
  }
  /* ⚠️ A 999px radius on a ~150px box is a blob, not a pill. The stadium shape
     only reads as one while the box is about as tall as a control. */
  :root.gilded .ai-search-container,
  :root.gilded .ai-search-wrapper.is-sticky .ai-search-container {
    border-radius: 22px;
  }
  /* The sticky variant has the same fault for the same reason: §6 pins it to
     64px, which is shorter still, and the toolbar is inside it at this width. */
  :root.gilded .ai-search-wrapper.is-sticky .ai-search-container {
    height: auto;
    min-height: 64px;
  }
}

/* =========================================================================
   §13  THE FOOTER'S TWO COLUMNS — 2026-09-23

   Spec §8: credit on the left, links on the right, keeping the Pexels credit
   and the full candle-safety line.

   ⚠️ ONE THING THE SPEC ASKS FOR IS DELIBERATELY NOT BUILT, because it would
   be an invention rather than layout:
     - the "© line on the left". No copyright line exists on this site today,
       and CLAUDE.md records that no legal entity is named anywhere and that
       privacy.html and terms.html are held pending legal review. Adding one
       here would be the first such assertion, made by a stylesheet change.
   The left column is therefore the Pexels credit, which is what is there.

   ❌ THIS BLOCK LISTED A SECOND SUCH ITEM AND THE REASON GIVEN WAS FALSE —
   CORRECTED 2026-09-23, the same day it was written. It said: "'Editorial
   standards' among the links. No such page exists; tests/homepage-links.js
   fails on a dead href, correctly."

   site/editorial-standards.html EXISTS: 8,312 bytes, ten <h2>s, in
   sitemap.xml, linked from 18 pages, serving 200. Nothing was checked before
   that sentence was written — its absence from the footer was read back as
   evidence that the page did not exist, which is the very thing it was
   offered to explain. So the spec's §8 list was right and the reason for not
   following it was invented.

   ✅ THE LINK IS NOW IN THE FOOTER, added by Bryan's decision the same day, so
   the spec's §8 list is followed in full. Seven links rather than six: the nav
   column wraps one line earlier on a phone, which the stacked-and-centred
   block below already handles.

   The markup change this rests on is two inline spans, and the LIVE page was
   measured before and after: footer box 1366x69 and 390x112 unchanged, text
   identical, all six link boxes identical. ⚠️ render-parity could NOT prove
   that -- it compares elements by PATH, and wrapper spans renumber the paths,
   so it reports "new elements" rather than a diff. Right tool, wrong job. */
:root.gilded .site-footer .footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px 24px;
}

/* The separator only made sense while these were one flowing line. */
:root.gilded .footer-sep { display: none; }

:root.gilded .footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  justify-content: flex-end;
}

/* ⚠️ The interpuncts BETWEEN the links are text nodes in the markup, not
   generated content, so they cannot be hidden without touching the live page.
   In a flex row they become their own flex items, which is why the gap above
   is the small one -- the separators still carry most of the spacing and
   removing them is a live change this does not need to make. */

/* The safety line is prose, not a column: it keeps the full width under both,
   and stays at --muted, which is the 5.29:1 measured on the live page. */
:root.gilded .site-footer .footer-safety {
  max-width: none;
  margin-top: 10px;
}

@media (max-width: 600px) {
  /* Stacked and CENTRED: two columns in 390px would give each about 170px and
     wrap the links into a ragged block against the right edge.
     ⚠️ Centred rather than left-aligned, and that was a correction: the first
     version set the nav to flex-start and left the credit alone, which
     measured credit left=134 (centred, inherited from .site-footer) against
     nav left=20. Two different alignments in a three-line footer. The live
     page centres this footer on a phone, so centred is both consistent and
     the smaller change. */
  :root.gilded .site-footer .footer-links {
    display: block;
  }
  /* ⚠️ display:block + text-align, NOT a centred flex row. The six links wrap
     to two lines at 390px, and justify-content centres each LINE of a flex
     container independently -- measured, the block sat 69px left of centre and
     a first-to-last span cannot even express where wrapped items are.
     text-align centres both lines against the same axis as the credit. */
  :root.gilded .footer-nav {
    display: block;
    text-align: center;
    margin-top: 6px;
  }
}

/* =========================================================================
   §14  THE CARD AND POPUP INTERIOR — 2026-09-23

   Spec §7.3. The card was made reachable in the refactor of renderCandleCards
   and then never restyled — the third surface to fall between specs, and the
   one a visitor looks at most. 26 classes had no rule.

   ⚠️ THE NOTE STRIP IN §7.3 IS DELIBERATELY NOT BUILT. It wants "the product's
   top 3 notes as image slices" from the note image library, and that library
   is `Scent Images\`, which was costed into this redesign and TURNED DOWN on
   2026-09-19: the site keeps real photographs. A strip of generated note
   images is exactly what that decision refused. It is also a markup build
   rather than a restyle. Everything else in §7.3 is here.
   ========================================================================= */

/* --- §14.1  the Shop button ------------------------------------------------
   ⚠️ THE REVENUE ELEMENT, AND IT HAD NO GILDED RULE AT ALL. Spec §7.3 asks for
   "a visible gold Shop button" and it was rendering in the base dark gradient
   with a thin gold border.

   It was also invisible to the first coverage audit: the class is emitted as
   class="shop-now-btn${... ' is-sold' ...}", and that extractor skipped any
   token containing a template expression. The most important button on the
   page was missing from the report that was supposed to find missing buttons.

   Same treatment as the search field's primary button, so "the gold one" means
   one thing on this page. The rel and every data-* attribute are untouched --
   this is a colour change on an <a> that characterisation asserts six
   attributes on. */
:root.gilded .shop-now-btn {
  background: var(--gold-fill);
  color: var(--shadow-ink);
  border: none;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-weight: 600;
  letter-spacing: 0.02em;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7),
              inset 0 -1px 0 rgba(var(--black-rgb), 0.35),
              0 4px 14px rgba(var(--black-rgb), 0.5);
}

:root.gilded .shop-now-btn:hover,
:root.gilded .shop-now-btn:focus-visible {
  background: var(--gold-fill);
  filter: brightness(1.06);
}

/* ⚠️ Sold Out keeps the base rule's dimming and must NOT become gold-on-gold:
   a disabled-looking control that reads as the primary action is worse than a
   plain one. Flat, unlit, no gradient. */
:root.gilded .shop-now-btn.is-sold {
  background: var(--ink-700);
  color: var(--muted);
  box-shadow: none;
  filter: none;
}

/* --- §14.2  the card's body ----------------------------------------------- */

/* "an 11px caps line" -- the type-and-brand line above the name. */
:root.gilded .brand-line {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

:root.gilded .candle-info {
  font-family: var(--font-body);
}

/* "Why it matched:" plus the match insight. It is the site's argument for the
   tile, so it reads as prose rather than as metadata. */
:root.gilded .insight-text {
  font-family: var(--font-body);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--chip-text);
}

:root.gilded .candle-foot {
  border-top: 1px solid var(--line);
  padding-top: 10px;
}

:root.gilded .candle-price {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  color: var(--gold-lt);
}

:root.gilded .size-chips {
  gap: 6px;
}

:root.gilded .size-note {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --- §14.3  the throw meter ------------------------------------------------
   Kept as a meter rather than restyled into chips: it encodes a quantity, and
   the dots are the only thing on the card that does. */
:root.gilded .throw-meter {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--muted);
}

:root.gilded .throw-dots {
  color: var(--gold-lt);
  letter-spacing: 0.12em;
}

:root.gilded .throw-tip {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  color: var(--chip-text);
  font-size: 12px;
}

/* --- §14.4  scent DNA ------------------------------------------------------
   ⚠️ The tier labels stay distinct from the notes. scentDnaHtml() deliberately
   never invents a Top/Middle/Base split for notes without documented
   structure, so the visual difference between a tiered DNA and the flat
   fallback is carrying a real distinction and must not be flattened. */
:root.gilded .scent-dna,
:root.gilded .scent-dna-simple {
  font-family: var(--font-body);
}

:root.gilded .dna-tier-label {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

:root.gilded .dna-tier-notes {
  font-size: 12px;
  color: var(--chip-text);
}

:root.gilded .dna-note {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  color: var(--chip-text);
}

/* --- §14.5  the hover popup ------------------------------------------------
   §7.3 keeps it for the full description now that the Shop button is on the
   card itself. */
:root.gilded .popup-desc {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.55;
  color: var(--chip-text);
}

:root.gilded .popup-ai-reason {
  font-family: var(--font-body);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted);
  border-left: 2px solid rgba(var(--gold-rgb), 0.55);
  padding-left: 10px;
}

/* --- §14.6  the labels above the two chip rows ---------------------------- */
:root.gilded .related-label,
:root.gilded .recent-label {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

:root.gilded .recent-clear {
  font-family: var(--font-body);
  font-size: 11px;
  color: var(--muted);
  text-decoration: underline;
}

:root.gilded .recent-clear:hover {
  color: var(--gold-lt);
}

/* --- §14.7  the loading skeleton ------------------------------------------
   It is on screen for as long as /candles takes, which on a cold cache is not
   brief, and it was still the base grey. */
:root.gilded .skeleton-card {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-card);
}

:root.gilded .skeleton-line {
  background: linear-gradient(90deg,
    rgba(var(--gold-rgb), 0.06) 0%,
    rgba(var(--gold-rgb), 0.16) 50%,
    rgba(var(--gold-rgb), 0.06) 100%);
  border-radius: 3px;
}

/* =========================================================================
   §15  THE COMPARE MODAL — 2026-09-23

   The largest single gap: 31 classes in the render path plus five on the
   static shell, none with a gilded rule. It is also the sharpest instance of
   the between-specs pattern — openCompare's 10,811 characters were rewritten
   from 33 inline styles to zero specifically to make this possible, proved
   with render-parity, and then given six characterisation invariants — and the
   restyle itself was never scheduled. The scaffolding was built and the thing
   it was for never happened.

   ⚠️ THE SHOP LINKS HERE CARRY rel="nofollow sponsored noopener" AND SIX
   data-* ATTRIBUTES, all asserted by the compare-panel invariants. Everything
   below is colour, type and spacing; no selector touches an attribute, and
   .cmp-shop keeps its is-sold modifier.
   ========================================================================= */

/* --- §15.1  the shell ------------------------------------------------------ */
:root.gilded .compare-modal {
  background: rgba(7, 6, 4, 0.80);
}

:root.gilded .compare-panel-shell {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1.5px solid transparent;
  border-radius: 8px;
  box-shadow:
    0 40px 100px rgba(var(--black-rgb), 0.70),
    inset 0 1px 0 rgba(255, 240, 190, 0.18);
}

:root.gilded .compare-panel-footer {
  border-top: 1px solid var(--line);
  background: transparent;
}

/* --- §15.2  the bar that opens it ------------------------------------------
   ⚠️ Still the only thing fixed to the bottom edge, which [B3a] asserts and
   the overlap invariants now also watch. Colour only. */
:root.gilded .compare-bar {
  background:
    linear-gradient(var(--surface-glass), var(--surface-glass)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  color: var(--chip-text);
  font-family: var(--font-body);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

:root.gilded .compare-clear {
  color: var(--muted);
  opacity: 1;      /* the base rule dims it to 0.7; --muted already carries it */
}

/* --- §15.3  the column heads ---------------------------------------------- */
:root.gilded .cmp-photo {
  border-radius: var(--radius-card);
  border: 1px solid var(--line);
}

:root.gilded .cmp-name {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 600;
  line-height: 1.15;
  color: var(--chip-text);
}

:root.gilded .cmp-brand,
:root.gilded .cmp-type {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --- §15.4  the row labels down the left ---------------------------------- */
:root.gilded .cmp-label {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

:root.gilded .cmp-row {
  border-bottom: 1px solid rgba(var(--gold-rgb), 0.10);
  padding-bottom: 8px;
}

/* --- §15.5  notes, and the shared/unique distinction -----------------------
   ⚠️ THE ONE THING IN THIS PANEL THAT IS AN ARGUMENT RATHER THAN A FIELD.
   "Shared notes" is why two products are comparable and "only in" is how they
   differ, so the two must not converge to one colour. Shared keeps gold,
   unique stays quiet — the base rule's distinction, carried over rather than
   flattened. `structuralBits` renders from these, and a compare-panel
   invariant asserts the sentence they produce. */
:root.gilded .cmp-notes {
  font-family: var(--font-body);
  font-size: 12px;
  line-height: 1.45;
  color: var(--chip-text);
}

:root.gilded .cmp-shared {
  color: var(--gold-lt);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

:root.gilded .cmp-unique {
  color: var(--muted);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* --- §15.6  sizes and price ------------------------------------------------ */
:root.gilded .cmp-size,
:root.gilded .cmp-size-line {
  font-family: var(--font-body);
  font-size: 12px;
}

:root.gilded .cmp-size-label {
  color: var(--chip-text);
  font-weight: 500;
}

:root.gilded .cmp-price {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--gold-lt);
}

:root.gilded .cmp-strong {
  color: var(--chip-text);
  font-weight: 600;
}

:root.gilded .cmp-merchant {
  color: var(--muted);
  font-size: 11px;
}

:root.gilded .cmp-sep {
  color: rgba(var(--gold-rgb), 0.35);
}

/* --- §15.7  the Shop link --------------------------------------------------
   ⚠️ NOT the card's full gold fill. On the card the Shop button is the single
   call to action; here there is one per SIZE VARIANT -- two products render
   seven of them -- and seven gold blocks in a table would read as seven
   primary actions. The wire pill keeps them recognisably the same control at
   a weight the table can carry. */
:root.gilded .cmp-shop {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire-hover) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  color: var(--gold-lt);
  font-family: var(--font-body);
  letter-spacing: 0.04em;
}

:root.gilded .cmp-shop:hover,
:root.gilded .cmp-shop:focus-visible {
  color: var(--chip-text);
  filter: brightness(1.12);
}

/* Sold out stays legible rather than merely faded -- 0.65 opacity on an
   11px label is the shape of the contrast failures this project has fixed
   twice. Flat and quiet instead. */
:root.gilded .cmp-shop.is-sold {
  background: var(--ink-700);
  border-color: var(--line);
  color: var(--muted);
  opacity: 1;
}

/* --- §15.8  throw, claims, and the empty cell ----------------------------- */
:root.gilded .cmp-throw { font-family: var(--font-body); font-size: 12px; }
:root.gilded .cmp-dots { color: var(--gold-lt); letter-spacing: 0.12em; }
:root.gilded .cmp-throw-word { color: var(--muted); font-size: 11px; }
:root.gilded .cmp-claims {
  font-family: var(--font-body);
  font-size: 11px;
  line-height: 1.5;
  color: var(--chip-text);
}
:root.gilded .cmp-empty { color: var(--muted); }

/* --- §15.9  "At a glance" and the scent picture ---------------------------- */
:root.gilded .cmp-glance {
  background: rgba(var(--gold-rgb), 0.06);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 12px 14px;
}

:root.gilded .cmp-glance-label,
:root.gilded .cmp-ai-title {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gold-lt);
}

:root.gilded .cmp-glance-item,
:root.gilded .cmp-ai-item {
  font-family: var(--font-body);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--chip-text);
}

:root.gilded .cmp-ai {
  background: rgba(var(--gold-rgb), 0.06);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 12px 14px;
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .cmp-ai-body {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--chip-text);
}

:root.gilded .cmp-foot {
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-family: var(--font-body);
  font-size: 11px;
}

/* =========================================================================
   §16  THE LAST THREE CLUSTERS — 2026-09-23
   Results toolbar, the overlay internals made reachable in the September
   refactors, and the quiz results panel.
   ========================================================================= */

/* --- §16.1  the results toolbar -------------------------------------------
   Sort and Filters, the two controls that survived removing "Clear filters".
   ⚠️ NOTHING HERE TOUCHES WIDTH, POSITION OR PADDING. The toolbar is
   position:absolute with no max-width and grows rightward under the centred
   summary box; that is the overlap this project shipped once and now guards
   with two invariants. A wider control reproduces it, so this is colour,
   border and type only -- and the chrome-overlap invariants run after it. */
:root.gilded .results-toolbar {
  font-family: var(--font-body);
}

:root.gilded .toolbar-action-btn {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  color: var(--chip-text);
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: 0.06em;
}

:root.gilded .toolbar-action-btn:hover,
:root.gilded .toolbar-action-btn:focus-visible {
  background:
    linear-gradient(var(--ink-700), var(--ink-700)) padding-box,
    var(--chip-wire-hover) border-box;
  color: var(--gold-lt);
}

/* The filters count. It is the only signal that a result set is narrowed --
   the per-filter chips were removed in 2026-08-24 and this took their job --
   so it stays high-contrast rather than quiet. */
:root.gilded .filters-badge {
  background: var(--gold-fill);
  color: var(--shadow-ink);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-weight: 600;
  box-shadow: 0 0 0 1px rgba(var(--gold-rgb), 0.35);
}

:root.gilded .toolbar-action-btn.is-active {
  border-color: transparent;
  background:
    linear-gradient(rgba(var(--gold-rgb), 0.14), rgba(var(--gold-rgb), 0.14)) padding-box,
    var(--chip-wire-hover) border-box;
  color: var(--gold-lt);
}

:root.gilded .refine-row,
:root.gilded .related-searches {
  font-family: var(--font-body);
}

/* --- §16.2  the overlay internals -----------------------------------------
   Every one of these became reachable in a September refactor and none was
   ever styled -- the smells-like modal's own controls, the banner's inner
   rows, the saved panel's header, and the price inputs in the filters sheet. */

/* the "smells like X" input, which is the only free-text field on the page
   besides search, so it answers to the same treatment */
:root.gilded .sl-input {
  background: rgba(var(--ink-rgb), 0.7);
  border: 1px solid transparent;
  background-image:
    linear-gradient(rgba(var(--ink-rgb), 0.7), rgba(var(--ink-rgb), 0.7)),
    var(--chip-wire);
  background-origin: padding-box, border-box;
  background-clip: padding-box, border-box;
  border-radius: var(--radius-card);
  color: var(--chip-text);
  font-family: var(--font-body);
  font-size: 15px;
}

:root.gilded .sl-input::placeholder { color: rgba(var(--white-rgb), 0.38); }

:root.gilded .sl-input:focus {
  background-image:
    linear-gradient(rgba(var(--ink-rgb), 0.7), rgba(var(--ink-rgb), 0.7)),
    var(--chip-wire-hover);
  outline: none;
}

:root.gilded .sl-status,
:root.gilded .sl-fineprint {
  font-family: var(--font-body);
  color: var(--muted);
}

:root.gilded .sl-resolved {
  font-family: var(--font-body);
  color: var(--chip-text);
}

/* The banner's inner rows. .slb-name, .slb-note-chip and .slb-foot are §11.1;
   these four are the lines around them. */
:root.gilded .slb-line {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.5;
}

:root.gilded .slb-notes,
:root.gilded .slb-dna {
  margin-top: 6px;
}

/* ⚠️ SHAPE ONLY. The four confidence colours come from the .sl-conf-* classes
   the smells-like status block already defines, so there is ONE source for
   them rather than a second copy here -- the base rule makes the same point.
   A colour on this selector would silently win over the tier. */
:root.gilded .slb-conf {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.04em;
  border-radius: var(--radius-pill);
}

:root.gilded .saved-head {
  border-bottom: 1px solid var(--line);
  padding-bottom: 10px;
  margin-bottom: 4px;
}

:root.gilded .saved-head h3 {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 600;
  color: var(--gold-lt);
  margin: 0;
}

:root.gilded .saved-close {
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
}

:root.gilded .saved-close:hover,
:root.gilded .saved-close:focus-visible { color: var(--gold-lt); }

/* ⚠️ The saved panel's Shop link is the same one-per-row case as the compare
   panel's, so it takes the same wire pill rather than the card's gold fill. */
:root.gilded .saved-shop {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  padding: 3px 11px;
  color: var(--gold-lt);
  font-family: var(--font-body);
}

:root.gilded .filter-row { gap: 8px; }

:root.gilded .price-range-inputs input {
  background: rgba(var(--ink-rgb), 0.7);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  color: var(--chip-text);
  font-family: var(--font-body);
}

:root.gilded .price-range-inputs input:focus {
  border-color: rgba(var(--gold-rgb), 0.55);
  outline: none;
}

:root.gilded .price-range-dash { color: var(--muted); }

/* --- §16.3  the quiz results panel ----------------------------------------
   The end of the Matchmaker, and the one screen in the flow that was never
   restyled -- the steps got §2 and the results did not. */
:root.gilded .quiz-results-panel {
  max-width: 560px;
}

:root.gilded .quiz-step,
:root.gilded .quiz-substep {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

:root.gilded .quiz-res-item {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-card);
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .quiz-dot {
  background: rgba(var(--gold-rgb), 0.25);
  border-radius: var(--radius-pill);
}

:root.gilded .quiz-dot.is-done,
:root.gilded .quiz-dot.is-current { background: var(--gold-lt); }

/* --- §16.4  the two controls inside the search field ----------------------- */
:root.gilded .clear-btn {
  color: var(--muted);
  background: transparent;
  border: none;
}

:root.gilded .clear-btn:hover,
:root.gilded .clear-btn:focus-visible { color: var(--gold-lt); }

/* The heart. ⚠️ Its ACTIVE state is set by the base rule and is not touched:
   a saved product turning gold is the feedback, and overriding the resting
   colour here without the active one would flatten the difference. */
:root.gilded .fav-btn {
  color: rgba(var(--white-rgb), 0.75);
  text-shadow: 0 1px 3px rgba(var(--black-rgb), 0.6);
}

:root.gilded .fav-btn:hover { color: var(--gold-lt); }

/* --- §16.5  the hero's trust line and the overlays' secondary buttons -------
   Both turned up in the coverage sweep rather than in any spec, and both are
   real surfaces rather than layout: the trust line is the sentence under the
   hero, and .secondary is Back / Skip / Close / Cancel in every overlay on the
   page. */
:root.gilded .hero-trust {
  font-family: var(--font-body);
  font-size: 12.5px;
  letter-spacing: 0.06em;
  color: var(--muted);
}

:root.gilded .quiz-nav button.secondary,
:root.gilded .quiz-panel button.secondary {
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  color: var(--muted);
  font-family: var(--font-body);
  letter-spacing: 0.04em;
}

:root.gilded .quiz-nav button.secondary:hover,
:root.gilded .quiz-panel button.secondary:hover,
:root.gilded .quiz-nav button.secondary:focus-visible,
:root.gilded .quiz-panel button.secondary:focus-visible {
  border-color: rgba(var(--gold-rgb), 0.55);
  color: var(--gold-lt);
}

/* ❌ .secondary-btn IS NOT STYLED HERE, AND THE FIRST VERSION OF THIS BLOCK
   WAS WRONG ABOUT WHY. It carried a rule with the comment "styled to match
   rather than hidden, because hiding it is a markup decision" -- but §6.2
   already hides it: `:root.gilded #savedBtn { display: none }`, because the
   page shipped two Saved buttons the moment the header gained one. Styling a
   display:none element is coverage theatre: it moves a class off the unreached
   list and changes nothing a visitor sees. Removed, and recorded in the
   coverage baseline as deliberate instead. */

/* =========================================================================
   §17  THE 38 NOTE PAGES — 2026-09-23

   Build spec §8. These are GENERATED from data/note-specs/*.toml, so this is
   one stylesheet section plus two head substitutions in the generator, not 38
   edits — and the pages already carry their own Pexels background, so §8's
   full-page image needs no new imagery and no note-image library.

   ⚠️ GATED BEHIND GILD_NOTE_PAGES, which is False. The nightly run executes
   the working copy of the generator at 03:00, so an ungated head change would
   ship to all 38 live pages overnight unreviewed. Render one with
   --preview-dir --gild, look at it, then flip the constant.

   ⚠️ .chip ON THESE PAGES IS NOT THE HOMEPAGE'S CHIP. The note pages' five
   descriptor chips are PLAIN TEXT by an explicit decision — "a descriptor chip
   has no honest query", because `dry` matches no note and `dry cedar` is
   silently a plain cedar search. They must not gain a border, a hover or a
   cursor that suggests they are clickable. Colour and type only.
   ========================================================================= */

/* --- the site header, defined ONCE for all 53 pages that carry it -----------
   ❌ IT USED TO BE DEFINED TWICE AND RENDERED TWO WAYS, 2026-09-23 to 09-24.
   The markup is byte-identical everywhere. The CSS was not: 51 pages link
   scent-notes.css and took their pill and wordmark from it — in PRE-GILDED gold,
   rgba(212,175,55,…) and #f3e5ab — while scent-notes.html and guides.html link
   only this file, where §20.6 had supplied the header's BOX and nothing else, so
   their search control rendered as plain text and their wordmark as Cormorant
   20px against the others' Jost 15px.

   Nothing failed, because each page was internally consistent. It is the
   `.search-cta` and `.sublist` shape a third time: an old gold beside a new gold
   reads as a slightly wrong shade rather than as a defect.

   ⚠️ THE BOX IS UNSCOPED NOW, NOT SCOPED TO THE TWO PAGES THAT LACKED IT. Scoping
   it was what created the split — it fixed the pages in front of me and left the
   other 51 depending on a stylesheet they happen to link. `.note-header` exists
   only on these pages, so an unscoped rule under `:root.gilded` reaches exactly
   them, and scent-notes.css's copy becomes redundant rather than authoritative.
   One definition is the whole point; a page that stops linking scent-notes.css
   can no longer lose its header.

   ⚠️ THE WORDMARK TAKES THE DISPLAY FACE, AND THAT WAS READ OFF THE HOMEPAGE
   RATHER THAN PREFERRED. `.site-wordmark` — the brand lock-up — is
   var(--font-display) at 27px. So the two index pages were already closer to the
   brand than the 51, whose var(--font-body) came from `.note-header a` below.
   `tests\header-parity.js` [D1] reads the homepage's own computed family and
   compares, so the two move together instead of drifting apart again.
   It stays SOLID, not the homepage's gradient: a clipped gold gradient has lost
   to a contrast measurement three times here, bottoming out at 2.71:1. */
:root.gilded .note-header {
  background: var(--surface-glass);
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
:root.gilded .note-header-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
}
/* ⚠️ HEIGHT, NOT WIDTH — Logo.png is 380px wide by default and index.html sizes
   it by width with two width-only media overrides, so a competing `width` leaves
   the mark's height at whatever the ratio produces. */
:root.gilded .note-header-logo img {
  height: 34px;
  width: auto;
  display: block;
}
:root.gilded .note-header a { color: var(--chip-text); font-family: var(--font-body); }
:root.gilded .note-header-logo span {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 20px;
  letter-spacing: 0.3px;
  color: var(--chip-text);
  /* ⚠️ EXPLICIT, TO OVERRIDE scent-notes.css's `@media (max-width: 480px) {
     .note-header-logo span { display: none } }`. That threshold was inherited,
     not measured, and it drops the brand on every common phone. Measured on the
     real header at 1px-for-1px: the gap between the logo and the pill is 53px at
     390, 38px at 375, 23px at 360 — and the band OVERFLOWS at 320. So the
     wordmark fits everywhere that matters and only fails on the very smallest
     viewports; the breakpoint below is set from those numbers. This rule is
     (0,3,1) against that file's (0,1,1), so it wins at every width. */
  display: inline;
}
/* Below 360 the pill and the wordmark stop fitting — measured, not chosen: 320
   overflows the band. 360 is the narrowest common Android width and it clears by
   23px, so the cut is just under it. */
@media (max-width: 359px) {
  :root.gilded .note-header-logo span { display: none; }
}
/* The pill. Bryan's call 2026-09-24: it reads as a control, where plain text
   beside a wordmark is ambiguous. Shape from the 51 pages, colour from the
   CURRENT palette — every channel through --gold-rgb, so it tracks a retune. */
/* ⚠️ `.note-header .note-header-search` LOOKS REDUNDANT AND IS NOT — DO NOT
   "SIMPLIFY" IT TO THE BARE CLASS. Two other rules already colour this anchor at
   (0,3,1): `:root.gilded .note-header a` just above, and §19's
   `:root.gilded .doc-page a`, which sits LATER in this file and so wins on
   404.shtml. A bare `:root.gilded .note-header-search` is (0,3,0) and loses to
   both — measured, via the matched-rule list Chrome reports rather than by
   reasoning about the cascade: the pill's text came out --chip-text on five
   families and --gold-lt on the sixth. The descendant form is (0,4,0) and wins
   everywhere. `tests\header-parity.js` compares this colour across families, so
   the regression cannot come back quietly. */
:root.gilded .note-header .note-header-search {
  flex-shrink: 0;
  font-family: var(--font-body);
  color: var(--gold-lt);
  font-size: 13px;
  padding: 8px 18px;
  border: 1px solid rgba(var(--gold-rgb), 0.45);
  border-radius: 30px;
  background: rgba(var(--gold-rgb), 0.08);
  text-decoration: none;
  white-space: nowrap;
}
:root.gilded .note-header .note-header-search:hover {
  background: rgba(var(--gold-rgb), 0.18);
  text-decoration: none;
}

:root.gilded .page h1 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0.005em;
  color: var(--gold-lt);
}

:root.gilded .page h2 {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--chip-text);
}

:root.gilded .page,
:root.gilded .page p,
:root.gilded .page li {
  font-family: var(--font-body);
  color: var(--chip-text);
}

/* The Quick Answer is the page's thesis and the DefinedTerm description, so it
   carries the weight rather than sitting in a tinted box. */
:root.gilded .quick-answer {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-card);
}

:root.gilded .qa-text {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  color: var(--chip-text);
}

:root.gilded .qa-meta {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* "By the numbers" — the real figures that are this page's differentiator. */
:root.gilded .data-panel {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-card);
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .data-panel a { color: var(--gold-lt); }

/* ⚠️ Plain text, deliberately — see the header note. No border, no cursor. */
:root.gilded .chip {
  background: transparent;
  border: none;
  color: var(--muted);
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: 0.04em;
  cursor: default;
}

:root.gilded .chip-text { color: var(--chip-text); }
:root.gilded .chip-row { gap: 10px; }

/* Citations. The label names a document, never a site — that rule is in the
   generator, and this only makes it look like a reference. */
:root.gilded .src {
  color: var(--gold-lt);
  text-decoration: underline;
  text-underline-offset: 2px;
}

:root.gilded .page-footnote {
  font-family: var(--font-body);
  font-size: 11px;
  color: var(--muted);
  border-top: 1px solid var(--line);
  padding-top: 10px;
}

:root.gilded .back {
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--muted);
}

:root.gilded .back:hover { color: var(--gold-lt); }

/* --- §17.1  the notes index, scent-notes.html -----------------------------
   The same .note-tile component as the homepage at the 210px size Design
   specified: image flex 0 0 150px, name 26px, descriptor 13.5px.

   ❌ EVERY SELECTOR HERE IS SCOPED TO body.scent-notes-index, AND IT WAS NOT
   WHEN THIS SHIPPED — it was a live homepage regression for the day it was up.
   .note-tiles and .note-tile are the SAME CLASS NAMES the homepage uses for its
   explore-section tiles; reusing the component at another size is the whole
   point. So ":root.gilded .note-tiles" was DECLARED TWICE at identical
   specificity (§B2 above, and here), the later one won, and the descendant
   rules below at (0,3,0) outranked the homepage's own (0,2,0). Measured on the
   LIVE homepage before the fix:

       1366   4 columns not 3, gap 16 not 10, tiles 124x216 not ~150 tall,
              image band 140 not 96, grid 700px against the build report's 498
              ** Sandalwood 125px of name in a 94px box, Bergamot and
                 Patchouli the same — clipped silently by overflow: hidden
       390    1 column, grid 2,073px tall

   ⚠️ THE PHONE CASE REINTRODUCED A DEFECT ANOTHER RULE'S COMMENT SAYS WAS
   FIXED. §B2's 600px rule exists because the grid stayed 3-across and
   "Sandalwood was cut off mid-word"; the 520px rule here is later in the file
   and silently beat it. A fix recorded in a comment does not survive a second
   section reusing the same class.

   ⚠️ AND THE HOOK ALREADY EXISTED AND MATCHED NOTHING. A
   ":root.gilded .scent-notes-index .note-tile" selector was written on the
   first pass while scent-notes.html's <body> never got the class — the scoping
   was authored and the markup was not, which is worse than no scoping, because
   the file reads as though it were scoped. The class is on the body now.

   ⚠️ NO GUARD IN THIS PROJECT WOULD HAVE CAUGHT THIS, AND IT IS WORTH BEING
   PRECISE ABOUT WHY. gilded-coverage.py measures classes a gilded rule never
   REACHES; this was a rule reaching too FAR, which is the opposite direction
   and invisible to it. gilded-scope.py checks every rule is :root.gilded-
   scoped, and every one of these was. The characterisation harness asserts
   homepage invariants but holds no tile geometry. What found it was reading
   the spec against the build and then measuring the live page — and what
   would catch the next one is a homepage invariant pinning the explore grid
   at 3 columns and the tile at 150px, which does not exist.

   ⚠️ NO PRODUCT COUNT ON THE TILE, and that is a decision rather than an
   omission. The spec asks for "1,222 products" per tile, and those figures
   exist only inside each note page's SS:GENERATED block, recomputed nightly.
   Hand-copying them onto a static index recreates exactly the staleness the
   homepage's note list already carries a warning about. Putting counts here
   honestly means generating this page, which is its own build.

   ⚠️ TONKA IS A TEXT PLATE, deliberately. It is the one documented no-image
   note -- there is no free-without-attribution photograph of tonka beans, and
   a lookalike is a wrong claim about the note. Its .note-tile-img is an empty
   <span>, which the fallback in the tile decision already provides for. */
:root.gilded .scent-notes-index .note-tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}

/* ⚠️ height: auto IS REQUIRED, and min-height alone could not do it. The
   homepage's :root.gilded .note-tile sets height: 150px for its compact tile;
   a min-height on top of a fixed height just clamps the used value, so every
   tile here sat at exactly 210px and clipped the second line of its sentence
   -- 38 of 38, worst overflow 26px. Cancelling the inherited height is what
   lets the tile grow.

   These tiles carry the note's editorial sentence, the only prose on the index
   and the reason to click through, so they size to their content with 210 as
   the floor to keep the grid even. Reusing a component at a different size
   means cancelling every declaration the first size set -- the nowrap on the
   descriptor was the same lesson, ten lines down. */
:root.gilded .scent-notes-index .note-tile {
  height: auto;
  min-height: 210px;
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-card);
}

/* ⚠️ 140px, NOT the spec's 150, and the 10px difference is a clipped line of
   text. At 150 the caption has 60px for a 26px name, a 13.5px descriptor and
   its padding -- about 67px of content. Flex children shrink below their
   content by default, so the descriptor collapsed to 7.4px and cut its own
   text off, while its rect stayed inside the tile: two checks measured the
   boxes and passed, and only a 2x screenshot showed it. */
:root.gilded .scent-notes-index .note-tiles .note-tile-img { flex: 0 0 140px; }

:root.gilded .scent-notes-index .note-tiles .note-tile-name,
:root.gilded .scent-notes-index .note-tiles .note-tile-desc { flex: 0 0 auto; }

/* ⚠️ flex: 0 0 auto -- as `1 1 auto` this caption shrank below its content and
   clipped the sentence's second line, which is the same shrink that squashed
   the descriptor, one level further out. Fixing the children was not enough
   while their container could still shrink. */
:root.gilded .scent-notes-index .note-tiles .note-tile-cap {
  flex: 0 0 auto;
  background: var(--panel);
  padding: 10px 14px 12px;
  gap: 4px;
  justify-content: flex-start;
}

/* ⚠️ UNDOES THE HOMEPAGE TILE'S ONE-LINE TREATMENT. The shared
   :root.gilded .note-tile-desc rule sets white-space: nowrap with an ellipsis,
   which is right for a 110px tile showing "warm, resinous" and wrong here --
   it silently truncated all 38 editorial sentences to
   "Warm, resinous, and golden - a classic base that...". Reusing a component
   at a different size means re-checking every declaration that assumed the
   first size, not just the dimensions. */
:root.gilded .scent-notes-index .note-tiles .note-tile-desc {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  font-size: 13.5px;
  line-height: 1.45;
}

:root.gilded .scent-notes-index .note-tiles .note-tile-name {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--gold-lt);
}

:root.gilded .scent-notes-index .note-tiles .note-tile-desc {
  font-family: var(--font-body);
  font-size: 13.5px;
  color: var(--muted);
}

:root.gilded .scent-notes-index .note-tiles .note-tile:hover .note-tile-name { color: var(--chip-text); }

@media (max-width: 900px) {
  :root.gilded .scent-notes-index .note-tiles { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 520px) {
  :root.gilded .scent-notes-index .note-tiles { grid-template-columns: 1fr; }
}

/* =========================================================================
   §18  THE GUIDE PAGES — 2026-09-23

   drafts/gilded-guides-costing.md, steps 1 and 2 of its own order: the tokens
   and a restyle of the classes that already exist. That is most of the visible
   change for none of the markup risk, and it is the same shape as §17 — colour
   and type over markup nobody touches.

   ⚠️ THE COSTING'S §0 PREMISE IS NOW SETTLED, AND IT RESOLVED THE OTHER WAY.
   It said "there are no gilded tokens on a guide page to reuse", and offered
   three bad options. That was true of a plan where gilded.css only REDEFINED
   index.html's tokens. It does not: the `:root.gilded` block at the top of
   this file is a COMPLETE palette, which is why the 38 note pages work while
   linking only scent-notes.css (0 custom properties) and this file. A guide
   takes the same two head lines a note page takes, and nothing is duplicated.

   ⚠️ SIX CLASSES WERE ALREADY REACHED BEFORE A LINE OF THIS SECTION EXISTED,
   because the guides share scent-notes.css with the note pages: .note-header,
   .note-header-search, .back, .data-panel (38 uses across 9 guides), .page.
   Measured, not assumed — tests/gilded-coverage.py's own extractors were run
   over site/guides/. So §17 does part of this job already, and the rules below
   deliberately do not restate it.

   WHAT IS NOT BUILT HERE, each a decision rather than an omission, and each
   costed in the draft:
     - .guide-bg, .ss-header / .ss-footer bands, .guide-layout / .guide-aside.
       New markup on 11 pages, and the rail restructures the top-level layout.
     - .guide-h2-num / .guide-h2-rule numbering wrappers. Every H2 on 11 pages.
     - .guide-callout split into --warn / --care / --note. These are ONE class
       today (.guide-safety, 95 instances). Splitting them is editorial
       judgement about what is a warning versus a note, on pages where that
       distinction is a safety claim — 41 of them on the two soap guides. Not a
       rename, and not a stylesheet's call to make.
     - .guide-progress and a generated TOC with an IntersectionObserver. The
       guides carry NO executable JavaScript at all — every <script> on them is
       application/ld+json. That is a change in kind on pages whose whole value
       is being static and traceable, and it adds a failure mode (a broken
       observer mis-highlighting) to pages that currently cannot fail. If the
       TOC is ever generated, generate it at BUILD time like the note-page
       blocks, and the pages stay static.
   ========================================================================= */

/* --- §18.1  the ground, the hero band and the reading surface --------------
   ⚠️ body:has(), NOT a bare `:root.gilded body`. That selector is now live on
   every gilded page — the homepage, 38 note pages, the notes index — and the
   homepage's ground is its own photo layer. The ARTICLES are the only pages
   whose page background is actually visible (the hero covers the top band,
   .guide-body is 820px centred, so ~270px of bare ground shows each side at
   1366px); the two hubs cover theirs with scent-notes.css's fixed .bg photo.
   So the change is scoped to the shape that needs it, with no markup.

   :has() is baseline in every current browser, and the degradation is benign:
   an engine without it renders the old green-black ground under a gilded page
   rather than breaking anything. */
:root.gilded body:has(.guide-body) {
  background: rgb(var(--ink-rgb));
}

/* The hero's background-image — gradient plus the page's own photograph — is
   set INLINE per page and is the one thing genuinely unique to each guide, so
   it is left alone. Only the colour behind it moves. */
:root.gilded .guide-hero {
  background-color: rgb(var(--ink-rgb));
}

:root.gilded .guide-hero h1 {
  font-family: var(--font-display);
  color: var(--gold-lt);
}

/* The eyebrow becomes the wire pill the rest of the site uses. A gradient
   border is a background layer under a transparent border, so the fill has to
   be painted as padding-box in the same declaration — set `border-color` on
   this and the wire disappears under it. */
:root.gilded .guide-eyebrow {
  background:
    linear-gradient(rgba(var(--ink-rgb), 0.62), rgba(var(--ink-rgb), 0.62)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .guide-hero .guide-standfirst {
  font-family: var(--font-body);
  color: rgba(var(--cream-rgb), 0.92);
}

/* The reading surface. ⚠️ KEEPS guide.css's 16px radius, not --radius-card's
   6px: this is an 820px sheet holding 6,800 words, not a tile, and the card
   radius on a panel this size reads as a mistake. The wire edge is what makes
   it gilded; the geometry is already right. */
:root.gilded .guide-body {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
}

:root.gilded .guide-body p,
:root.gilded .guide-body li {
  font-family: var(--font-body);
  color: var(--chip-text);
}

/* h1/h2/h3 already take --font-display from the global gilded rule; these set
   the colour, which that rule deliberately does not. */
:root.gilded .guide-body h2 { color: var(--gold-lt); }
:root.gilded .guide-body h3 { color: var(--chip-text); }

:root.gilded .guide-meta {
  font-family: var(--font-body);
  color: var(--muted);
  border-bottom-color: var(--line);
}

/* --- §18.2  "On this page" ------------------------------------------------ */
:root.gilded .guide-toc {
  background: rgba(var(--gold-rgb), 0.05);
  border-color: var(--line);
}

:root.gilded .guide-toc h2 {
  font-family: var(--font-body);
  color: var(--muted);
}

:root.gilded .guide-toc a { color: var(--gold-lt); }
:root.gilded .guide-toc li { font-family: var(--font-body); }

/* --- §18.3  Expanders -----------------------------------------------------
   ⚠️ NOTHING HERE TOUCHES height OR max-height, and that is load-bearing.
   guide.css's own comment records why the disclosure snaps: animating height
   emits a layout shift on every frame of the tween, which is the exact bug
   that .results-toolbar-spacer and .search-sticky-spacer shipped on the
   homepage. A restyle is the natural place to reintroduce it. Do not. */
:root.gilded .guide-expander {
  border-color: var(--line);
  background: rgba(var(--gold-rgb), 0.04);
}

:root.gilded .guide-expander > summary {
  font-family: var(--font-body);
  color: var(--gold-lt);
  background: rgba(var(--gold-rgb), 0.07);
}

:root.gilded .guide-expander > summary:hover {
  background: rgba(var(--gold-rgb), 0.14);
}

:root.gilded .guide-expander > summary::before {
  border-right-color: var(--gold-lt);
  border-bottom-color: var(--gold-lt);
}

:root.gilded .guide-expander > summary:focus-visible {
  outline-color: rgba(var(--gold-rgb), 0.7);
}

/* --- §18.4  Tables --------------------------------------------------------
   The wrapper keeps its own overflow-x — a wide table must never make the PAGE
   scroll sideways, and that is guide.css's rule, not this one's to relax. */
:root.gilded .guide-table-wrap { border-color: var(--line); }

:root.gilded .guide-table {
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .guide-table caption { color: var(--muted); }

:root.gilded .guide-table th,
:root.gilded .guide-table td {
  border-bottom-color: rgba(var(--gold-rgb), 0.14);
}

:root.gilded .guide-table thead th {
  background: rgba(var(--gold-rgb), 0.10);
  color: var(--gold-lt);
}

/* --- §18.5  The safety callout --------------------------------------------
   ⚠️ IT STAYS TERRACOTTA, AND IT MUST NOT BECOME GOLD. These pages are
   AUTHORED INSTRUCTIONS about hot wax, open flame and lye — a different
   liability position from describing a merchant's product, which is why
   guide.css gave the callout a component of its own. A restyle that folds it
   into the page's gold makes the one element that must not read as body copy
   read as body copy. The hue is carried over unchanged; only the ground it
   sits on moves, because the old fill was mixed for a green-black page. */
:root.gilded .guide-safety {
  background: rgba(224, 122, 95, 0.10);
  border-color: rgba(224, 122, 95, 0.55);
}

:root.gilded .guide-safety p {
  font-family: var(--font-body);
  color: rgba(var(--cream-rgb), 0.92);
}

:root.gilded .guide-safety strong { color: #f3bda8; }

/* --- §18.6  Footnotes and sources -----------------------------------------
   ⚠️ THE WRAPPER ONLY. NEVER ADD A CLASS TO THE FOOTNOTE ANCHOR, AND NEVER
   REORDER ITS ATTRIBUTES. tests/guide-footnotes.py CHK-001b and CHK-001c match
   a marker with  <a href="#fn-N" ...>[N]</a>  — href FIRST. Anything after it
   is tolerated, so the existing class, id and aria-label are all fine where
   they are. Put a class BEFORE the href and the regex matches nothing across
   675 markers on 11 pages: CHK-001c fails loudly while CHK-001b passes
   vacuously on an empty marker list, and one loud failure beside one silent
   pass is worse than either alone. .fn-ref is already on a wrapper <span>, so
   there is nothing to change and this rule reaches the anchor through it. */
:root.gilded .fn-ref a { color: var(--gold-lt); }
:root.gilded .fn-back { color: var(--gold-lt); }

:root.gilded .guide-sources { border-top-color: var(--line); }

:root.gilded .guide-sources li {
  font-family: var(--font-body);
  color: var(--muted);
}

/* The :target highlight is how a reader knows which source they jumped to. */
:root.gilded .guide-sources li:target {
  background: rgba(var(--gold-rgb), 0.13);
  box-shadow: 0 0 0 6px rgba(var(--gold-rgb), 0.13);
}

/* --- §18.7  Byline, next-guide nav, footer links --------------------------- */
:root.gilded .guide-author {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
}

:root.gilded .guide-author p {
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .guide-author strong { color: var(--gold-lt); }
:root.gilded .guide-author a { color: var(--gold-lt); }

:root.gilded .guide-next a {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: 12px;
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .guide-next a:hover {
  background:
    linear-gradient(var(--ink-700), var(--ink-700)) padding-box,
    var(--chip-wire-hover) border-box;
}

:root.gilded .guide-next span {
  font-family: var(--font-body);
  color: var(--muted);
}

:root.gilded .guide-footer-links {
  font-family: var(--font-body);
  color: var(--muted);
  border-top-color: var(--line);
}

:root.gilded .guide-footer-links a { color: var(--gold-lt); }
:root.gilded .guide-footer-links a:hover { color: var(--chip-text); }

/* --- §18.8  The hubs ------------------------------------------------------- */
:root.gilded .hub-header p,
:root.gilded .hub-intro {
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .hub-divider { background: rgba(var(--gold-rgb), 0.55); }

:root.gilded .hub-card {
  background:
    linear-gradient(var(--panel-glass), var(--panel-glass)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
}

:root.gilded .hub-card:hover {
  background:
    linear-gradient(rgba(var(--gold-rgb), 0.08), rgba(var(--gold-rgb), 0.08)) padding-box,
    var(--chip-wire-hover) border-box;
}

:root.gilded .hub-card h2 { color: var(--gold-lt); }

:root.gilded .hub-card p {
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .hub-cta {
  font-family: var(--font-body);
  color: var(--gold-lt);
}

/* A guide that does not exist yet — a <div>, never an <a>, so a reader never
   meets a 404 and a crawler never meets a broken internal link. It has to keep
   reading as unavailable rather than as a dimmer card, so the dashed edge is
   kept and the wire is NOT applied to it. */
:root.gilded .hub-card--soon {
  background: rgba(var(--ink-rgb), 0.45);
  border: 1px dashed rgba(var(--gold-rgb), 0.24);
}

:root.gilded .hub-card--soon h2 { color: rgba(240, 213, 138, 0.6); }
:root.gilded .hub-card--soon p { color: var(--muted); }
:root.gilded .hub-card--soon .hub-cta { color: var(--muted); }

/* --- §18.9  the two index pages, which are their own shape -----------------
   guides.html (the top-level "Scent Guides" index the homepage links) and
   guides/index.html ("Making Guides") link NO stylesheet at all — both carry
   their whole look in an inline <style> — so nothing above reaches either, and
   neither is covered by §17. guides/index.html exists so that /guides/
   resolves instead of returning the origin's 403 for a directory with no
   index; its canonical points at making.html, which is why it is not simply
   deleted. Humans get a working page; crawlers are told which one counts.

   ⚠️ EVERY RULE HERE IS SCOPED TO body.guides-index, AND THAT IS NOT TIDINESS.
   These two pages name their components .card, .cta, .cards, .group,
   .group-note, .intro and .divider — the most generic names on the site, and
   they are NOT unique to these pages. Measured across every .html in site/ and
   site/guides/: .footer-links is also on index.html, for-brands.html and
   preview.html, and .intro is on scent-notes.html, the notes index shipped
   this morning. A bare `:root.gilded .footer-links a { color: ... }` would
   therefore recolour the LIVE homepage footer — the same footer §13 was
   careful with, and whose links were left at their measured 0.55 on purpose.
   That is a live regression bought for a page nobody was styling.

   The hook is one attribute on one <body> per page. It is the only markup this
   whole section touches. */
:root.gilded .guides-index .card {
  background:
    linear-gradient(var(--panel-glass), var(--panel-glass)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
}

:root.gilded .guides-index .card:hover {
  background:
    linear-gradient(rgba(var(--gold-rgb), 0.08), rgba(var(--gold-rgb), 0.08)) padding-box,
    var(--chip-wire-hover) border-box;
}

:root.gilded .guides-index .card h2 { color: var(--gold-lt); }

:root.gilded .guides-index .card p,
:root.gilded .guides-index .intro,
:root.gilded .guides-index .group-note {
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .guides-index .card .cta,
:root.gilded .guides-index .cta {
  font-family: var(--font-body);
  color: var(--gold-lt);
}

:root.gilded .guides-index .divider { background: rgba(var(--gold-rgb), 0.55); }

/* The eleven making guides, listed under the Making Guides card. ⚠️ FOUND BY
   THE COVERAGE GUARD ON ITS FIRST RUN OVER THE GUIDES, which is what it is for:
   these eleven links carry a gold left rule at the OLD palette's
   rgba(212,175,55,0.35) and white text at 0.85, on a page that is now gilded.
   Nothing looked broken, because an old gold on a new gold reads as a slightly
   wrong shade rather than as a defect -- the same shape as .search-cta on the
   notes index, and the reason a per-page-type check was worth extending. */
:root.gilded .guides-index .sublist a {
  border-left-color: rgba(var(--gold-rgb), 0.4);
  background: rgba(var(--gold-rgb), 0.05);
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .guides-index .sublist a:hover {
  background: rgba(var(--gold-rgb), 0.11);
  border-left-color: rgba(var(--gold-rgb), 0.78);
}

:root.gilded .guides-index .sublist .lead { color: var(--gold-lt); }

:root.gilded .guides-index .footer-links a {
  font-family: var(--font-body);
  color: var(--gold-lt);
}

/* --- §18.10  two scent-notes.css surfaces the guides still carry ------------
   These are not guide classes, but the guides are where they survive, and the
   coverage measurement over site/guides/ is what surfaced them. Both are in
   scent-notes.css, which the note pages also link.

   ⚠️ .search-cta IS ALSO ON THE LIVE, ALREADY-GILDED scent-notes.html, so this
   rule is a change to a page that shipped this morning -- and a correcting one:
   that button has been rendering in the PRE-gilded gold (rgba(212,175,55,.45)
   on a 0.12 fill) on a gilded page since the flip. Nothing failed, because
   nothing was measuring a class the note-page template had cut; the notes index
   and four guides still use it. The spec pages dropped their search CTAs, which
   is why §17 never needed this.

   ❌ .data-updated GOT A RULE HERE AND IT WAS REMOVED AGAIN, same session.
   scent-notes.css sets it to rgba(255,255,255,0.45), which over --panel
   composites to roughly [126,124,121] -- about 4.4:1, under the 4.5 floor. That
   looked like a real contrast fix. It is not, because BOTH of its two uses are
   <p class="data-updated"> INSIDE .guide-body, and ":root.gilded .guide-body p"
   is (0,3,0) against this rule's (0,2,0). The text already renders at 13.78:1.
   The rule changed nothing and measured as though it had -- the probe reported
   15.5px chip-text where the rule asks for --muted, which is the tell.
   Coverage theatre, the same call as .secondary-btn in the baseline: a rule on
   an element some other rule already owns moves a class off the unreached list
   and moves nothing a visitor sees. It stays unreached, on purpose. */
:root.gilded .search-cta {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  color: var(--gold-lt);
}

:root.gilded .search-cta:hover {
  background:
    linear-gradient(var(--ink-700), var(--ink-700)) padding-box,
    var(--chip-wire-hover) border-box;
}

/* =========================================================================
   §19  THE ELEVEN REMAINING PAGES — 2026-09-23

   drafts\gilded-remaining-pages-costing.md. The five standard pages, the four
   long-form root guides, for-brands and the 404. Every one of them linked NO
   stylesheet at all and carried its whole look in an inline <style>, so the
   first change each needed was a <link> — the same markup pass guides.html took.

   ⚠️ THE MEASUREMENT THAT DECIDED THE SHAPE: 29 distinct classes across all
   eleven, and TEN of them were already reached before a line of this section
   existed — back, page, chip, chip-row, search-cta, note-header,
   note-header-search, fn-ref, fn-back, footer-links. So a page arrives with its
   palette, header, back link, chips and footnote markers already done, and the
   work is the 19 that were left. There are also only SIX inline style=
   attributes across all eleven, against the homepage's ~96, so the
   reachability problem that made the homepage unfinishable by stylesheet alone
   does not arise here at all.

   ⚠️ EVERY RULE IS SCOPED TO body.doc-page, WHICH IS THE ONLY MARKUP THIS
   SECTION ADDS BEYOND THE CLASS AND THE LINK. `subtitle` is also on
   scent-notes.html, which is live and gilded — the THIRD generic-name collision
   in two days, after .footer-links and .intro on the guides index, where a bare
   rule would have recoloured the live homepage footer. `container`, `updated`,
   `callout`, `sources`, `headline` and `network` are clean today and are
   exactly the names that collide next. The hook costs one attribute per page
   and removes the whole class of accident.

   `doc-page` is a SCOPING HOOK, not a claim about what these pages are. The
   404 and for-brands are not documents in any editorial sense; they are in the
   set because they need the same shell.
   ========================================================================= */

/* --- §19.1  the shell, all eleven pages -----------------------------------
   Two shells underneath: the six standard pages sit on a flat #121814 with no
   photo, and the four long-form pages plus the 404 carry a fixed .bg image over
   #0f1410. Only the first has a visible ground, but the token is set for both
   so a page that later drops its photo does not fall back to green-black. */
:root.gilded body.doc-page {
  background: rgb(var(--ink-rgb));
}

/* h1 and h2 already take --font-display from the global gilded rule; these set
   the colour, which that rule deliberately does not. Gold on both, matching the
   guides (§18.1) rather than the note pages, because four of these ARE guides
   in everything but directory and the rest sit beside them in the footer. */
:root.gilded .doc-page h1,
:root.gilded .doc-page h2 {
  color: var(--gold-lt);
}

:root.gilded .doc-page h3 {
  font-family: var(--font-display);
  color: var(--chip-text);
}

:root.gilded .doc-page p,
:root.gilded .doc-page li,
:root.gilded .doc-page dd,
:root.gilded .doc-page dt {
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .doc-page a { color: var(--gold-lt); }

/* The line under the h1, and the "last updated" line. Both are white at 0.55
   and 0.45 today, which over --panel composites to about 5.3:1 and 4.4:1 — the
   second under the 4.5 floor for 13px text. --muted is 7.97:1 on this ground. */
:root.gilded .doc-page .subtitle,
:root.gilded .doc-page .updated {
  font-family: var(--font-body);
  color: var(--muted);
}

/* ❌ .container GETS NO RULE, AND THAT IS DELIBERATE. It is identical on all six
   pages that use it — max-width: 720px; margin: 0 auto; padding: 40px 20px 80px
   — and carries no colour, no border and no background. The costing said three
   shell rules; measuring the declarations rather than the class list says two.
   A rule here would move a class off the unreached list and move nothing a
   visitor sees, which is the call already made for .secondary-btn and
   .data-updated. It is recorded in the baseline instead. */

/* --- §19.2  the four long-form pages --------------------------------------
   how-smell-works, how-to-choose, fragrance-oil-uses, candle-care. */

/* ⚠️ .callout IS EDITORIAL EMPHASIS AND GOLD IS RIGHT FOR IT — read before
   styling, not assumed from the name: "Culture explained 6% of the variance.
   The molecule itself explained 41%", "I am not a neuroscientist." A pulled-out
   point, not a warning. Contrast it with .disclaimer below. */
:root.gilded .doc-page .callout {
  border-left-color: rgba(var(--gold-rgb), 0.5);
  background: rgba(var(--gold-rgb), 0.05);
  font-family: var(--font-body);
  color: var(--chip-text);
}

/* ⚠️ .disclaimer GOES TERRACOTTA, BY BRYAN'S DECISION 2026-09-23, AND IT WAS
   GOLD BEFORE. It carries "Always follow the specific care and safety
   instructions from the manufacturer" on the two pages about burning candles
   and putting fragrance oil on skin — manufacturer safety instructions, which
   is the same reason .guide-safety is not gold. Styled as ordinary gold it read
   as one more panel on a page full of gold panels; the one element that must
   not be mistaken for body copy now cannot be. Same hue as .guide-safety (§18.5)
   so the two read as one thing across the site. */
:root.gilded .doc-page .disclaimer {
  background: rgba(224, 122, 95, 0.10);
  border: 1px solid rgba(224, 122, 95, 0.55);
  font-family: var(--font-body);
  color: rgba(var(--cream-rgb), 0.92);
}

:root.gilded .doc-page .disclaimer strong { color: #f3bda8; }

/* The sources block and the byline, the same components the guides carry under
   different names. ⚠️ The footnote MARKERS inside them are already styled by
   §18.6, and the rule there holds here too: never put a class before href on a
   footnote anchor. These two pages carry 40 and 6 entries and, as of
   2026-09-23, are finally read by tests\guide-footnotes.py. */
:root.gilded .doc-page .sources {
  border-top: 1px solid var(--line);
}

:root.gilded .doc-page .sources li {
  font-family: var(--font-body);
  color: var(--muted);
}

:root.gilded .doc-page .page-author {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  font-family: var(--font-body);
  color: var(--chip-text);
}

:root.gilded .doc-page .page-author strong { color: var(--gold-lt); }

:root.gilded .doc-page .table-wrap { border-color: var(--line); }

:root.gilded .doc-page .quiz-cta {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  color: var(--gold-lt);
}

:root.gilded .doc-page .quiz-cta:hover {
  background:
    linear-gradient(var(--ink-700), var(--ink-700)) padding-box,
    var(--chip-wire-hover) border-box;
}

/* --- §19.3  for-brands.html ------------------------------------------------
   The commercial page. Its own vocabulary, shared with nothing. */
:root.gilded .doc-page .headline {
  font-family: var(--font-display);
  color: var(--gold-lt);
}

:root.gilded .doc-page .standfirst {
  font-family: var(--font-body);
  color: var(--chip-text);
}

/* ⚠️ TERRACOTTA, for the same reason .disclaimer is: it is the terms panel on
   the page that invites brands to send us data, so it is the one block a reader
   must not skim as body copy. */
:root.gilded .doc-page .terms-callout {
  background: rgba(224, 122, 95, 0.10);
  border: 1px solid rgba(224, 122, 95, 0.55);
  font-family: var(--font-body);
  color: rgba(var(--cream-rgb), 0.92);
}

:root.gilded .doc-page .network,
:root.gilded .doc-page .pubid {
  font-family: var(--font-body);
  color: var(--muted);
}

/* --- §19.4  404.shtml ------------------------------------------------------
   ⚠️ NEVER PUT AN SSI DIRECTIVE (<!--#) IN THAT FILE, and note the FILENAME is
   the mechanism — cPanel serves /404.shtml, not whatever .htaccess names.
   tests\homepage-links.js [C1c] fails on a directive. Six of its eleven classes
   were already reached, the highest hit rate on the list.

   ❌ ITS STYLESHEET LINK IS ROOT-ABSOLUTE, AND THE MARKUP PASS GOT THIS WRONG
   FIRST. Every other page in §19 takes `href="gilded.css?v="` relative, the way
   the guides and note pages do. **The 404 is served AT THE MISSING URL'S OWN
   PATH** — /deep/a/b/missing/ returns this page — so a relative href resolves
   against a directory that does not exist and the page arrives unstyled,
   precisely when a visitor is already lost. [C2] caught it on the first run,
   which is what that check is for. It is the mirror of the preview.html lesson:
   root-absolute breaks file://, relative breaks the 404, and each page needs the
   form that matches how it is actually reached. Every other href on that page
   was already root-absolute for the same reason. */
:root.gilded .doc-page .nf-search {
  background:
    linear-gradient(var(--ink-800), var(--ink-800)) padding-box,
    var(--chip-wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  color: var(--gold-lt);
}

:root.gilded .doc-page .nf-list a {
  font-family: var(--font-body);
  color: var(--gold-lt);
}

/* --- §19.5  contact.html --------------------------------------------------- */
:root.gilded .doc-page .email-box {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  border-radius: var(--radius-card);
  font-family: var(--font-body);
  color: var(--chip-text);
}

/* =========================================================================
   §20  THE NOTE PAGES' READING SHEET — 2026-09-24

   Build spec §8.5, with one decision taken against it and recorded here.

   ✅ THE SHEET IS OPAQUE, NOT GLASS, BY BRYAN'S DECISION. §8.5 specifies
   "one glass reading sheet: rgba(12,10,7,0.8) … blur 8". That is the same
   trade Design refused for the note TILES, where the fourth option — moving
   the text onto an opaque --panel caption strip — was taken precisely so the
   contrast measurement stops depending on which photograph loaded. This is
   that question at page scale and ×38 photographs, and it gets the same
   answer: opaque ground, photograph at full strength around it.

   What that buys, and it is the whole point: body text composites over a
   KNOWN colour, so contrast here is one computed number rather than a bound
   per photo. --chip-text on --panel is 13.78:1, measured on the guides, which
   use this exact fill.

   ⚠️ AND THE BLUR GOES WITH IT. backdrop-filter samples what is behind a
   surface; behind an opaque one there is nothing to sample. Keeping it would
   be a GPU cost that renders identically — and CLAUDE.md already records the
   mobile blur being dropped for performance once.

   820px, matching .guide-body, so ~270px of photograph shows each side at
   1366 and the full-page background is a photograph rather than trim. §8.5's
   "40px margin from the viewport sides" is deliberately NOT followed: at 40px
   an opaque sheet hides the image everywhere except two thin strips, which
   would make the full-page background — chosen over the hero-only version —
   invisible while every page still paid for it.

   ⚠️ RADIUS 16, NOT §8.5's 6, for the reason already recorded at .guide-body:
   this is an 820px sheet holding a long page, not a tile, and a card radius on
   a panel this size reads as a mistake.

   ⚠️ SCOPED WITH :has(), BECAUSE THIS STEP IS CSS-ONLY AND .page IS SHARED.
   .page is on 46 pages — the 38 note pages plus guides.html, guides/index.html
   and the four long-form root guides — so a bare rule would drop an opaque
   sheet onto pages that have their own. .quick-answer is on exactly the 38
   note pages and nothing else (measured), and it is a direct child of .page.
   An engine without :has() simply renders no sheet, which is the benign
   direction. The generator step replaces this with a real body class.

   ❌ TWO PARTS OF THE DECISION ARE NOT IN THIS SECTION, BECAUSE CSS CANNOT
   REACH THEM, AND SAYING SO IS THE POINT:

     - REMOVING THE INLINE DARK GRADIENT (§8.1/§5.1). It lives in a style=""
       attribute on .bg, and an inline style beats every stylesheet rule. The
       pages stay darkened twice until the generator strips it.
     - THE TOP-ANCHORED SCRIM. The scrim is :root.gilded body::before, which is
       SHARED BY EVERY GILDED PAGE. Re-anchoring it here would change the
       homepage, the guides and the 11 standalone pages. It needs a note-page
       body hook first, which is markup.

   ⚠️ AND THE HERO IS ON THE SHEET FOR NOW, WHICH §8.5 DOES NOT WANT. The spec
   puts the breadcrumb, h1, quick answer and chips ON THE PHOTOGRAPH with no
   box, and only "everything below the hero" on the sheet. A note page today
   has no hero/body split at all — .page holds the h1 and the quick answer
   along with everything else — so splitting them is generator work. Until
   then the sheet starts at the h1. The intermediate state is coherent (photo,
   glass header, one sheet) and it is not the finished design.
   ========================================================================= */
/* ✅ SCOPE UPGRADED 2026-09-24 from :has(> .quick-answer) to the real body
   hook the generator now writes. The :has() version was the honest CSS-only
   scope while this step was CSS-only; it is not needed once markup exists, and
   a class cannot fail on an engine that lacks :has(). */
/* ✅ THE SHEET MOVED OFF .page AND ONTO .note-sheet — 2026-09-24, step 2.
   The note that used to stand here said "the hero is on the sheet for now,
   which §8.5 does not want" and called it an intermediate state. It is no
   longer intermediate: render_spec_body now emits the breadcrumb, the ornament
   and the h1 as children of .page, and wraps everything below them in
   .note-sheet. So .page is the 820px column and nothing else; the sheet is the
   opaque ground under the reading matter only.

   ⚠️ .page KEEPS ITS SIDE PADDING AND THE SHEET SITS INSIDE IT. box-sizing is
   border-box site-wide, so 868 - 2x24 is exactly the 820px measure §20 was
   built around, and at phone width the same padding is the 16px+ gutter that
   would otherwise have to be re-derived. A sheet stretched edge to edge on a
   390px screen is the failure this avoids. */
:root.gilded .note-page .page {
  max-width: 868px;
  margin: 0 auto;
  padding: 0 24px 64px;
  background: none;
  border: 0;
}

/* 30px under the CTAs, which is what "just below the Shop buttons" is. Before
   the .note-body change this margin was invisible at >= 1300px because the grid's
   height, not this value, decided where the sheet began. */
:root.gilded .note-body { margin-top: 30px; }

:root.gilded .note-sheet {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  border-radius: 16px;
  padding: 34px 38px 44px;
  margin-top: 30px;
}

/* The sheet is 820px where the page was 720, so the measure grows from 672 to
   744px of text. That is the guides' measure exactly, at the same body size. */

@media (max-width: 900px) {
  :root.gilded .note-page .page {
    padding: 0 16px 40px;
  }
  :root.gilded .note-sheet {
    padding: 26px 20px 34px;
    border-radius: 14px;
    margin-top: 22px;
  }
}

/* --- §20.2  the hero, on the photograph ------------------------------------
   Build spec §8.3: breadcrumb, ornament, h1, sitting directly on the picture
   with no box. This is the structural half of the note-page work — everything
   before it was palette, type and one opaque panel.

   ⚠️ THE MIDDLE CRUMB IS PLAIN TEXT AND THAT IS A DECISION, NOT AN OMISSION.
   §8.3 writes "Scent notes / [Family] / Vanilla" without saying what links
   where. The family filter chips were dropped from the index, so a family crumb
   has nowhere to point, and this project refuses a link that lies — the Saved
   control is a <button> for exactly that reason. "Scent notes" links to the
   index; the last two crumbs are text.

   ⚠️ THE ORNAMENT IS A SEPARATE CLASS FROM .hero-ornament, NOT A REUSE. The
   homepage's is 260px under a 248px mark; this one is 180px under a breadcrumb,
   on a page with no mark at all. They share a drawing and nothing else, and one
   class serving two proportions is how a shared rule ends up overridden twice.
   ========================================================================== */

:root.gilded .note-breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 0;
  padding-top: 30px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 1.6px;
  text-transform: uppercase;
}

/* ⚠️ SOLID --gold-lt, NOT A GRADIENT, ON EVERY PART OF THE BREADCRUMB. §9b
   already measured this exact case: a tracked, uppercase, sub-16px line sitting
   on the photograph took --gold-text-bright to 3.13:1 and had to become solid.
   A three-stop vertical gradient across ~10px of glyph is invisible anyway, so
   this costs nothing and does not have to be re-argued per page. */
:root.gilded .note-breadcrumb a {
  color: var(--gold-lt);
  text-decoration: none;
  border-bottom: 1px solid rgba(var(--gold-rgb), 0.35);
  padding-bottom: 1px;
}
:root.gilded .note-breadcrumb a:hover {
  border-bottom-color: var(--gold-lt);
}
:root.gilded .note-crumb { color: var(--chip-text); }
:root.gilded .note-crumb-sep { color: rgba(var(--gold-rgb), 0.45); }

/* Same drawing as §9b's ornament, at this page's proportions. */
:root.gilded .note-ornament {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 180px;
  max-width: 100%;
  margin: 22px auto 18px;
}
:root.gilded .note-ornament::before,
:root.gilded .note-ornament::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--wire);
}
/* The diamond IS reused from §9b — .orn-diamond is 7px on both pages, so the
   two rails differ and the centre does not. Reusing the rail would have been
   the wrong half to share. */

/* The h1. §8.3 asks for Cormorant 96px; clamped rather than fixed, because the
   SEO title is kept ("Vanilla Candles & Scent Notes", decided 2026-09-17 —
   those titles rank), so this is a four-word line, not the one-word "Vanilla"
   the board draws. 96px on four words overflows 820px at every desktop width.

   ⚠️ NO GRADIENT, AND THE MEASUREMENT IS THE ARGUMENT — see §20.3 below. It
   inherits solid --gold-lt from :root.gilded .page h1. */
:root.gilded .note-page .page h1 {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(34px, 6.4vw, 62px);
  font-weight: 600;
  line-height: 1.04;
  letter-spacing: -0.005em;
  text-align: center;
  text-wrap: balance;
}

@media (max-width: 600px) {
  :root.gilded .note-breadcrumb {
    font-size: 11px;
    letter-spacing: 1.1px;
    padding-top: 22px;
  }
  :root.gilded .note-ornament { width: 140px; margin: 16px auto 14px; }
}

/* --- §20.3  the hero's two columns, the CTAs and the figures card ----------
   Build spec §8.3 and §8.4. The numbers move OUT of the reading column into a
   card in the margin; the co-note chips stay in the column. Decided by Bryan
   2026-09-24, and it honours the 2026-09-16 amendment rather than reversing it
   — that amendment protected the column's progression, and figures in the
   margin do not interrupt a column at all.

   ⚠️ THE PAGE GROWS TO 1340px ONLY AT >= 1300px, AND THE SHEET STAYS 820.
   .note-sheet is pinned left rather than re-centred, so the sheet sits under
   the hero's text column and the card hangs off the same top edge. Centring an
   820px sheet under a 1340px hero would replace the header seam this session
   just removed with a new one.

   ⚠️ NOTHING ABOVE THE BUTTONS MOVES VERTICALLY, WHICH IS WHY §20.1's PIXEL
   STOPS ARE NOT RE-SOLVED. The breadcrumb, ornament and h1 keep their order and
   their padding, so they stay at 62-226px and their measured 6.63:1 / 4.43:1
   still hold. The CTAs are NEW and sit BELOW the h1 in thinner scrim, so they
   are measured on their own — see the figures below this block. */



/* Stacked below 1300px: the card sits full-width ABOVE the sheet, so the
   figures still come before the prose at every width. Chosen over moving it to
   the end of the page, which would put one page's numbers in two different
   places depending on the window. */
:root.gilded .note-cta-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 22px;
}

/* ⚠️ THE PRIMARY IS A GOLD FILL WITH --shadow-ink ON IT, WHICH IS THE ONE
   PAIRING THIS PALETTE ALREADY GUARANTEES: --shadow-ink exists precisely as
   "text on a gold fill". A cream or gold label on this fill would have to be
   measured per stop of the gradient. */
:root.gilded .note-cta-primary,
:root.gilded .note-cta-secondary {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.3px;
  text-decoration: none;
  border-radius: var(--radius-pill);
  padding: 12px 22px;
  white-space: nowrap;
}
:root.gilded .note-cta-primary {
  background: var(--gold-fill);
  color: var(--shadow-ink);
  border: 1px solid transparent;
}
:root.gilded .note-cta-secondary {
  background:
    linear-gradient(rgba(var(--ink-rgb), 0.72), rgba(var(--ink-rgb), 0.72)) padding-box,
    var(--wire) border-box;
  border: 1.5px solid transparent;
  color: var(--gold-lt);
}
:root.gilded .note-cta-primary:hover { filter: brightness(1.06); }
:root.gilded .note-cta-secondary:hover { color: rgb(var(--cream-rgb)); }

/* --- the card -------------------------------------------------------------
   ⚠️ OPAQUE --panel, NOT GLASS, FOR THE REASON §20 ALREADY GIVES. It carries
   small text over an arbitrary photograph; on glass every figure here would be
   a bound per photo instead of one number. Same ground as the reading sheet, so
   the two read as one family. */
:root.gilded .note-figures {
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
  border-radius: 16px;
  padding: 26px 28px 22px;
  margin: 30px auto 0;
  max-width: 420px;
  font-family: var(--font-body);
}
/* ⚠️ EVERY <p> RULE IN THIS CARD IS SCOPED THROUGH .note-figures, AND IT HAD TO
   BE. ":root.gilded .page p" is (0,2,1); a bare ".fig-count" rule is (0,2,0) and
   LOSES, so the whole card rendered in --chip-text and the gold count and the
   muted secondary lines simply did not apply. It looked fine in a screenshot —
   cream on near-black is perfectly legible — and the contrast probe is what
   found it, by reporting .fig-count's text as [233,220,184] where --gold-lt is
   [240,213,138]. Same collision as .data-updated in §18 from the other side:
   there a new rule changed nothing because a (0,3,0) rule already owned the
   element; here because a (0,2,1) rule did.
   ⚠️ The phone overrides below need the same scope, or they lose to the same
   rule the moment one of them sets a colour. */
:root.gilded .note-figures .fig-count {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 60px;
  line-height: 1;
  letter-spacing: -1px;
  color: var(--gold-lt);
}
:root.gilded .note-figures .fig-label {
  margin: 4px 0 0;
  font-size: 14px;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--chip-text);
}
:root.gilded .note-figures .fig-price {
  margin: 10px 0 0;
  font-size: 14px;
  color: var(--muted);
}
:root.gilded .fig-bars {
  list-style: none;
  margin: 20px 0 0;
  padding: 0;
  display: grid;
  gap: 9px;
}
:root.gilded .fig-bars li {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--chip-text);
}
/* The bar's LENGTH is per-note data and arrives as an inline --w; everything
   else about it is here. ⚠️ Never select on that attribute. */
:root.gilded .fig-bar {
  height: 6px;
  border-radius: 3px;
  background: var(--gold-fill);
  width: var(--w, 2%);
}
:root.gilded .fig-other .fig-bar { background: rgba(var(--gold-rgb), 0.35); }
:root.gilded .fig-n { font-variant-numeric: tabular-nums; color: var(--muted); }
:root.gilded .note-figures .fig-coverage,
:root.gilded .note-figures .fig-updated {
  margin: 14px 0 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--muted);
}
:root.gilded .note-figures .fig-updated { margin-top: 8px; }

@media (max-width: 600px) {
  :root.gilded .note-figures { padding: 20px 18px 18px; margin-top: 22px; }
  :root.gilded .note-figures .fig-count { font-size: 46px; }
  /* 96px, not 80: "Fragrance Oils" is the longest label any note produces and
     it wrapped to two lines at 80, which pushed that row out of line with the
     rest. The column stays FIXED rather than auto so the bars start at the same
     x on every row — an auto column would step in and out per note. */
  :root.gilded .fig-bars li { grid-template-columns: 96px minmax(0, 1fr) auto; }
  :root.gilded .note-cta-primary,
  :root.gilded .note-cta-secondary { font-size: 14px; padding: 11px 18px; }
}

/* ⚠️ THIS BLOCK SITS AFTER THE BASE RULES ON PURPOSE, AND IT WAS WRONG FIRST.
   Written above them it lost every property they also set — same specificity
   (0,2,0), so source order decides — and the CTA row rendered CENTRED beside a
   left-aligned h1. Exactly the mobile-blur trap this file already records.
   Anything added here that a base rule below also sets must stay below it. */
@media (min-width: 1300px) {
  :root.gilded .note-page .page { max-width: 1340px; }
  /* ⚠️ THE GRID MOVED FROM .note-hero TO .note-body, AND THAT IS THE WHOLE FIX.
     A grid is as tall as its tallest child, so while the card was the hero's
     second column the hero was 465px tall against 236px of text and the reading
     sheet began 289px below the Shop buttons. Measured at 1440 before the change:
     CTA bottom 298, sheet top 587. The hero is now text alone and the card shares
     a row with the sheet, so both start together.
     ⚠️ Nothing above the CTAs moves — breadcrumb 62, h1 162 — so §20.1's PIXEL
     stops still describe the hero they were solved for. Re-verified after. */
  :root.gilded .note-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 420px;
    gap: 44px;
    align-items: start;
  }
  /* The card is first in the DOM and second on screen: `order` rather than an
     explicit grid-column, so the single-column layout below needs no rule at all. */
  :root.gilded .note-figures { order: 2; }
  :root.gilded .note-sheet { order: 1; max-width: 820px; margin-right: auto; margin-top: 0; }
  :root.gilded .note-figures { margin-top: 0; }
  /* Left-aligned once there is a card to sit beside. Centring the h1 over a
     left-aligned sheet reads as a mistake, not as a choice. */
  :root.gilded .note-hero-text,
  :root.gilded .note-breadcrumb,
  :root.gilded .note-page .page h1 { text-align: left; justify-content: flex-start; }
  :root.gilded .note-ornament { margin-left: 0; }
  :root.gilded .note-cta-row { justify-content: flex-start; }
}

/* --- the index's header needs its BASE layout, not only the gilded overrides ---
   ❌ I COSTED THIS EXACT FAILURE FOR §8.2 AN HOUR EARLIER AND THEN WALKED INTO IT.
   `drafts\gilded-note-header-costing.md` records that the homepage header's gilded
   rules "are not self-sufficient — they lean on index.html's own base CSS", and
   names `display: flex` as the missing piece. The same is true here from the other
   side: `.note-header`'s layout lives in **scent-notes.css**, and
   `scent-notes.html` does NOT link it — the page carries its own inline <style>
   and pulls only gilded.css.

   So adding the header markup produced an unstyled header: the logo rendered at
   the <img>'s natural size, a 380px magnifier printed across the hero, with the
   wordmark and the search link run together beside it. §17's rules set its colours
   and §20.4 its width; neither supplies a box.

   ⚠️ LINKING scent-notes.css WAS THE OTHER OPTION AND IS WORSE. That file also
   defines `.page`, `.bg`, `h1`, `.subtitle`, `.intro` and `.search-cta`, every one
   of which this page already defines inline — so it would bring six collisions to
   fix one. The header's own box is five declarations; that is the whole gap. */
/* ⚠️ THE FIVE SCOPED RULES THAT SAT HERE ARE GONE — 2026-09-24. They gave the two
   index pages a header box and, by supplying only a box, left them rendering a
   plain 14px search link and an uncoloured 20px wordmark while the other 51 drew
   a pill and a 15px one. The header is now defined ONCE, unscoped, in §17, which
   is where the record of that split lives. These were (0,3,0) against the shared
   rule's (0,2,0), so leaving them would have kept the divergence they caused. */

/* =========================================================================
   §21  THE GUIDE ARTICLES' FULL-PAGE BACKGROUND — 2026-09-24

   `drafts\gilded-guide-background-costing.md`, option (b): the hero goes
   TRANSPARENT and keeps its fixed height, and the guide's photograph becomes one
   full-page fixed layer. Option (a) — hero photo plus page photo — is the same
   image at two scales with a seam between them.

   ⚠️ TRAP 1, AND IT IS THE REASON THE NOTE PAGES' ANSWER DOES NOT TRANSFER.
   .guide-hero is `height: clamp(240px, 40vh, 400px)`, so the band's height — and
   therefore where its text sits — CHANGES WITH VIEWPORT HEIGHT. §20.1 had to use
   PIXEL stops because the note-page hero occupies the same pixels at every window
   size; this hero does not. The stops below are solved after measuring, not
   carried over.

   ⚠️ TRAP 2: `.bg` AND THE SCRIM ARE BOTH z-index -1 AND THE PHOTO WINS.
   scent-notes.css sets `.bg { position: fixed; z-index: -1 }` and §5.1 sets the
   scrim to the same, so paint order decides and `.bg`, a later sibling, covers
   its own darkening. On the note pages that went live for a day before anything
   needed the scrim. Here it would be visible immediately, because the hero text
   sits on the photograph with nothing else behind it.
   ========================================================================= */

:root.gilded body.guide-page .bg {
  z-index: -2;
}

/* ⚠️ background-color, NOT just background-image. guide.css gives .guide-hero
   `background-color: #0f1410`, which is OPAQUE — keeping the height while leaving
   the colour would put a solid dark block over the very photograph this section
   exists to show. Stripping the inline background-image alone is not enough. */
:root.gilded body.guide-page .guide-hero {
  background-color: transparent;
}

/* --- the guide scrim, in PERCENTAGES, and that is the measured answer --------
   ⚠️ §20.1 HAD TO USE PIXEL STOPS AND THIS ONE MUST NOT. Measured on the
   rendered page across five viewport sizes, because the hero is 40vh and scales
   with the window where the note-page hero does not:

       sample        px range          % range
       eyebrow top   124-276  (152px)  20.0-27.6%   (7.6 pts)
       h1 top        169-321  (152px)  27.3-32.1%   (4.8 pts)
       h1 bottom     215-367           32.9-36.7%
       body top      282-434           43.4-45.5%   (2.1 pts)

   In pixels the text moves 152px; as a fraction of the viewport it barely moves.
   Percentages track this hero and pixels do not — the exact opposite of the note
   pages, and the reason the note-page answer was not carried over.

   ✅ THE ALPHAS ARE SOLVED FROM THE WHITE-PHOTO BOUND, NOT PICKED. Each hero
   sample sits directly on the photograph (§18 gave .guide-eyebrow a transparent
   ground, so guide.css's rgba(15,20,16,0.5) pill no longer applies):

       .guide-eyebrow   12px/400  floor 4.5  needs alpha >= 0.648   <- binding
       .guide-hero p    17px/400  floor 4.5  needs alpha >= 0.623
       .guide-hero h1   40px/600  floor 3.0  needs alpha >= 0.549

   So the band stays at or above 0.68 through 47%, which covers the lowest
   sample's worst case (the hero paragraph's bottom at 45.2% on a 620px-tall
   window), then fades out. Below that the opaque .guide-body covers the centre
   and the photograph runs at full strength in the margins, which is the point of
   making it full-page at all.

   Specificity: :root.gilded body::before is (0,2,2); this is (0,3,2). */
:root.gilded body.guide-page::before {
  background: linear-gradient(180deg,
    rgba(var(--ink-rgb), 0.78) 0%,
    rgba(var(--ink-rgb), 0.72) 18%,
    rgba(var(--ink-rgb), 0.68) 47%,
    rgba(var(--ink-rgb), 0.12) 56%,
    rgba(var(--ink-rgb), 0) 64%);
}

/* --- §20.6  the notes index: hero band and A–Z bar -------------------------
   `drafts\scentsearch-remaining-pages-build-spec.md` §2, items 1 and 3. Items 2
   (family chips) and 5 (the "+N more" tile) are NOT built — see below.

   ⚠️ EVERY RULE IS SCOPED TO body.scent-notes-index. §17.1 was written
   unscoped, reached the LIVE HOMEPAGE, and shipped a regression: the explore grid
   rendered 4 columns instead of 3 at 216px instead of 150, three names clipped,
   and the phone grid 2,073px tall. `.intro`, `.subtitle` and `.footer-links` are
   all shared with other pages. Nothing here may be written bare.

   ❌ FAMILY CHIPS ARE NOT BUILT, BY BRYAN'S DECISION (2026-09-24), AND THE
   MEASUREMENT IS THE ARGUMENT. 38 notes over 11 families: the largest is woody at
   7 and musky has exactly one, so a chip hides 35 of 38 rather than narrowing
   them. Every note page's breadcrumb carries a PLAIN-TEXT family segment for the
   same reason — with no filter to point at, a link there would lie.
   `tests\notes-index.js` [D3] is what stops §2.2 being "restored" later without
   that argument being met again. */

:root.gilded body.scent-notes-index .index-hero {
  margin-bottom: 26px;
}
:root.gilded body.scent-notes-index .index-eyebrow {
  margin: 0 0 10px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 3px;
  text-transform: uppercase;
  /* solid --gold-lt, not the gradient: §9b measured --gold-text-bright at 3.13:1
     on a photograph for a tracked caption this size and it had to become solid.
     Third time that has been the answer; treat it as the default. */
  color: var(--gold-lt);
}
:root.gilded body.scent-notes-index .index-standfirst {
  margin: 12px 0 0;
  max-width: 62ch;
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1.5;
  color: var(--chip-text);
}
:root.gilded body.scent-notes-index .index-meta {
  margin: 14px 0 0;
  font-family: var(--font-body);
  font-size: 13px;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--muted);
}

/* --- the A–Z jump bar ------------------------------------------------------
   ⚠️ A DEAD LETTER IS A <span>, NOT A DIMMED <a>. Eight letters carry no note
   (D K Q U W X Y Z). Rendering them as links would send a reader to an anchor
   that does not exist — the "link that lies" the Saved control is a <button> to
   avoid, and the reason the breadcrumb's family segment is plain text. */
:root.gilded body.scent-notes-index .az-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 4px;
  margin: 0 0 22px;
  padding: 12px 16px;
  border-radius: 12px;
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
}
:root.gilded body.scent-notes-index .az-label {
  margin-right: 8px;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--gold-lt);
}
:root.gilded body.scent-notes-index .az-letter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 28px;
  border-radius: 8px;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
  color: var(--chip-text);
}
:root.gilded body.scent-notes-index a.az-letter:hover,
:root.gilded body.scent-notes-index a.az-letter:focus-visible {
  background: rgba(var(--gold-rgb), 0.16);
  color: var(--gold-lt);
}
/* ⚠️ 0.5, NOT THE SPEC'S 0.35, AND THE REASON IS MEASURED. At 0.35 a dead letter
   reads 2.01:1 against the bar -- the only figure on this page under the 4.5
   floor. It is arguably exempt: it is aria-hidden, it is not a control, and WCAG
   sets no contrast requirement for an inactive component. But "exempt" is how a
   real failure gets waved through, and the on/off distinction does not need the
   extra dimness to work: at 0.5 a dead letter MEASURES 2.88:1 against LIVE
   letters at 13.78:1, which is still unmistakably off, and it is materially
   easier to read that the alphabet is complete. A spec value that costs
   legibility for no gain is the wrong value.
   ⚠️ 2.88 is the MEASURED figure; solving it by hand first gave 3.32, and the
   predicted number is not the one that belongs in a comment. */
:root.gilded body.scent-notes-index .az-letter-off {
  color: rgba(var(--muted-rgb), 0.5);
}

/* The jump target sits on the first tile of each letter, so a jump lands with the
   header still overhead; scroll-margin keeps the tile clear of it. */
:root.gilded body.scent-notes-index .note-tile[id^="az-"] {
  scroll-margin-top: 78px;
}

:root.gilded body.scent-notes-index .footer-links {
  margin-top: 34px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--muted);
}
:root.gilded body.scent-notes-index .footer-links a {
  color: var(--chip-text);
  text-decoration: none;
}
:root.gilded body.scent-notes-index .footer-links a:hover {
  color: var(--gold-lt);
}

@media (max-width: 600px) {
  :root.gilded body.scent-notes-index .az-bar { padding: 10px 12px; gap: 5px 3px; }
  :root.gilded body.scent-notes-index .az-letter { min-width: 25px; height: 25px; font-size: 13px; }
  :root.gilded body.scent-notes-index .index-standfirst { font-size: 16px; }
}

/* --- §20.5  pairing and confusion cards -----------------------------------
   Build spec §8.5: "Pairs well with becomes cards. Each card shows both notes'
   images side by side with a small + disc on the seam, then the pair name, the
   co-occurrence count and the existing one-line description. Easy to confuse
   with uses the same card."

   ✅ THE IMAGERY WORKS, AND I PREDICTED IT WOULD NOT. The note photographs are
   full-bleed LANDSCAPE backgrounds chosen to sit behind text, so a 64px square
   crop of one looked like a safe thing to rule out — and the spec's card was
   probably drawn around the curated square note set that was DECLINED on
   2026-09-19. Rendered and looked at instead: vanilla pods, amber, cedar logs,
   roses, a beach, laundry on a line, irises, pumpkins, rolled leather all read
   at 64px. Only musk is an ambiguous dark texture. The assertion was wrong and
   the contact sheet is what settled it.

   ⚠️ EVERY RULE IS SCOPED THROUGH .pair-card. A bare ".pair-name" is (0,2,0) and
   loses to ":root.gilded .page p" at (0,2,1) — which is exactly how the figures
   card silently rendered its gold count in --chip-text an hour earlier.

   ❌ THE PADDING-BOX LAYER MUST BE OPAQUE, AND THE FIRST VERSION MADE IT A 3.5%
   GOLD TINT. The wire is a GRADIENT painted on the border-box layer and covered
   everywhere except the 1px edge by the padding-box layer above it — so a
   near-transparent fill lets the full gold gradient show through the whole card.
   Rendered: seven cards as solid gold plates with cream text on them, barely
   readable. This file already states the pattern as
   `linear-gradient(--panel,--panel) padding-box, var(--wire) border-box`; the
   opaque half is not decoration. --ink-700 is the palette's lifted chip fill, so
   the card reads as raised off the sheet rather than tinted.

   ⚠️ THE CARD IS AN <a>, SO IT INHERITS NO LINK COLOUR BY ACCIDENT. Colour is
   set per part rather than on the anchor, or the description would take link
   gold and stop reading as prose. */

:root.gilded .pair-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 14px;
  margin: 14px 0 26px;
}

:root.gilded .pair-card {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 16px;
  text-decoration: none;
  border-radius: 14px;
  /* the wire edge: an OPAQUE padding-box fill over the gradient border-box layer */
  background:
    linear-gradient(var(--ink-700), var(--ink-700)) padding-box,
    var(--wire) border-box;
  border: 1px solid transparent;
}
:root.gilded .pair-card:hover {
  background:
    linear-gradient(var(--ink-600), var(--ink-600)) padding-box,
    var(--wire) border-box;
}

/* The two photographs, with the + sitting ON the seam rather than between them —
   the discs overlap by half, so the pair reads as one object. */
:root.gilded .pair-card .pair-imgs {
  position: relative;
  display: flex;
  align-items: center;
  flex: none;
}
:root.gilded .pair-card .pair-img {
  width: 58px;
  height: 58px;
  border-radius: 10px;
  background-size: cover;
  background-position: center;
  box-shadow: 0 0 0 1px rgba(var(--gold-rgb), 0.28);
}
/* ⚠️ THE NO-PHOTO STATE IS DESIGNED, NOT A GAP. tonka carries no photograph by
   an explicit decision, and it appears in 16 pair/confusion entries across the
   set. Its half shows the wire pattern, which is the same thing tonka.html says
   about itself. Do not fill it with a lookalike. */
:root.gilded .pair-card .pair-img-none {
  background: var(--wire);
  opacity: 0.5;
}
:root.gilded .pair-card .pair-plus {
  position: relative;
  z-index: 1;
  width: 22px;
  height: 22px;
  margin: 0 -11px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1;
  background: var(--gold-fill);
  color: var(--shadow-ink);
}

:root.gilded .pair-card .pair-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
:root.gilded .pair-card .pair-name {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--gold-lt);
}
:root.gilded .pair-card .pair-count {
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
:root.gilded .pair-card .pair-why {
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.45;
  color: var(--chip-text);
}

@media (max-width: 600px) {
  :root.gilded .pair-cards { grid-template-columns: 1fr; gap: 10px; }
  :root.gilded .pair-card { padding: 12px 13px; gap: 13px; }
  :root.gilded .pair-card .pair-img { width: 50px; height: 50px; }
  :root.gilded .pair-card .pair-name { font-size: 18px; }
}

/* --- §20.4  the header band runs full width -------------------------------
   Build spec §8.2, OPTION A of drafts\gilded-note-header-costing.md, chosen by
   Bryan 2026-09-24. It removes the seam and nothing else.

   `.note-header` carries max-width: 900px from scent-notes.css, so the band sat
   as a floating 900px bar while the reading sheet is 820 and the photograph is
   full-bleed. §17's --surface-glass is what made that limit VISIBLE -- the limit
   predates the redesign, the glass revealed it -- and §20.1's darker top band
   made it obvious.

   ⚠️ SCOPED TO body.note-page, AND THAT IS NOT OPTIONAL. `.note-header` is on
   51 pages: the 38 note pages, 12 guides and 404.shtml. An unscoped rule here is
   a decision about all of them, which is the .footer-links / .intro / .subtitle
   collision for the fourth time.

   ⚠️ IT DOES NOT MOVE THE BREADCRUMB, WHICH IS WHY NO CONTRAST FIGURE IS
   RE-SOLVED. Probed on the live page by injecting exactly this rule: the header
   spans 0 -> 1351 at a 1366 viewport and the breadcrumb still starts at y = 62.
   The 22px of top padding collapses into the band and the bar ends where it
   ended, so §20.1's PIXEL stops still describe the hero they were solved for.

   ❌ THIS IS NOT §8.2's "the same glass header bar as the home page", and the
   three things it leaves out are each a decision rather than an omission -- the
   nav, Saved, and the wordmark's gold. Costing has the measurements; the short
   version is that the homepage header's gilded rules silently assume a
   `display: flex` that lives in index.html, the Saved button is
   onclick="openSavedPanel()" on pages carrying no executable JavaScript at all,
   and adopting it would delete the logo mark from 38 pages. */
/* ⚠️ THIS RULE SETS NO BACKGROUND AND NO BORDER, DELIBERATELY. §17 already gives
   .note-header var(--surface-glass) (alpha 0.72) and the --line bottom border.
   A first version of this block restated the background as rgba(--ink, 0.55),
   copied from the homepage's .hero-header — which is LIGHTER, so it would have
   quietly cost the header's two links contrast while fixing a width. The seam is
   a width problem; only the width changes here. */
/* ⚠️ THE INDEX JOINS THIS SCOPE (step 5). It had NO header at all until
   2026-09-24 — the one page in the note family that linked nowhere from the top —
   and it now carries the same .note-header markup. Still NOT unscoped: the 12
   guides and 404.shtml also use .note-header, and their 900px band is a separate
   decision from this one. */
/* ⚠️ THE GUIDES JOIN THIS SCOPE (2026-09-24), AND §21 IS WHY. Their 900px
   centred band was a separate decision while their photograph lived inside the
   hero; a full-page photo turns it into the same floating-bar seam it was on the
   note pages. Bryan took the decision once §21 made it visible.
   ✅ 404.shtml JOINED ON 2026-09-24, AND ITS "served at the missing URL's own
   path" CAVEAT TURNED OUT TO BE ABOUT A DIFFERENT PROPERTY. That path governs how
   hrefs RESOLVE — which is why every href and both stylesheet links on that page
   are root-absolute, and why [C2] exists — and has nothing to do with how wide a
   band is. The two concerns are independent, so one was not a reason to hold the
   other.

   ⚠️ AND THE SEAM IS THERE WITHOUT A PHOTOGRAPH, which is what the check found.
   §20.4's stated justification was "a 900px centred band while the page is
   full-bleed", and 404.shtml has NO photo at all: its `.bg` sets `image: none`
   over a flat rgb(15,20,16). Measured at 1440 before the change: band 900px at
   x=263, content column 720px at x=353 — so the band was not full width, not
   aligned to the content, and a THIRD width overhanging it by 90px a side. On a
   flat ground a translucent --surface-glass band shows its left and right edges
   harder than it does over a photograph, because there is no texture to break
   them up. Looked at, not inferred.

   ⚠️ `doc-page` IS ON ELEVEN PAGES AND REACHES ONE ELEMENT. Measured: 404.shtml
   is the only one carrying .note-header; about, contact, privacy, terms,
   editorial-standards, for-brands, candle-care, fragrance-oil-uses,
   how-to-choose and how-smell-works carry no header at all. That is the inverse
   of the making.html case — there a shared class would have applied a SCRIM that
   darkens twice, so it got its own hook; here it would apply the same full-width
   band every other page already has, which is the desired treatment rather than
   a hazard. No new body class was invented for one element. */
:root.gilded body.note-page .note-header,
:root.gilded body.scent-notes-index .note-header,
:root.gilded body.guide-page .note-header,
:root.gilded body.guide-hub .note-header,
:root.gilded body.guides-index .note-header,
:root.gilded body.doc-page .note-header {
  max-width: none;
  height: 62px;
  padding: 0 48px;
  /* ⚠️ EXPLICIT, AND ONLY guides.html NEEDS IT — but it belongs on the shared
     rule rather than in a per-page exception. That page's inline <style> carries
     a BARE `header { text-align: center; margin-bottom: 28px }` for its h1 block,
     an element selector at (0,0,1) that also matches this band and would hang
     28px of dead air under it. Every other page here has no bare header rule, so
     this sets what is already true for them and states it instead of inheriting
     it. A header band has no bottom margin anywhere on this site. */
  margin-bottom: 0;
}

@media (max-width: 600px) {
  :root.gilded body.note-page .note-header,
  :root.gilded body.scent-notes-index .note-header,
  :root.gilded body.guide-page .note-header,
  :root.gilded body.guide-hub .note-header,
  :root.gilded body.guides-index .note-header,
  :root.gilded body.doc-page .note-header { padding: 0 16px; height: 56px; }
}

/* --- §20.1  the note-page scrim, top-anchored ------------------------------
   ✅ THE INLINE GRADIENT IS GONE, so this is the only thing between the photo
   and the eye. Until 2026-09-24 every note page was darkened TWICE — the
   inline rgba(15,20,16,0.78→0.88) on .bg that §8.1 and §5.1 both said to
   remove, plus the shared scrim over it — which is why the photograph read as
   near-black in the margins beside the reading sheet.

   ⚠️ THE SHARED SCRIM IS A FULL-PAGE WASH AND THAT IS NOW THE WRONG SHAPE.
   :root.gilded body::before runs 0.52 at the top to 0.86 at the bottom, which
   is right for the homepage, where text sits on the photograph the whole way
   down. On a note page the only text on the photograph is the header: the h1,
   the quick answer and the body all sit on the opaque sheet. A full-page wash
   would dim the picture in exactly the margins where it is supposed to run at
   full strength, so this overrides it with a band that fades out above the
   sheet.

   Specificity: :root.gilded body::before is (0,2,2); this is (0,3,2), so it
   wins without !important and without depending on source order.

   ⚠️ THE STOPS ARE IN PIXELS, NOT PERCENTAGES, AND THAT IS THE WHOLE POINT OF
   THIS RETUNE. Every other scrim in this file is a percentage of the viewport,
   which is right for the homepage, where the hero is sized against the fold.
   The note-page hero is not: it is a header bar, a 13px breadcrumb, an ornament
   and one line of display type, and it occupies THE SAME PIXELS whatever the
   window height. Measured on the rendered page at three sizes:

       viewport      breadcrumb      h1            sheet starts
       1366 x 1000   62-115px        162-226px     256px
       1366 x  700   62-115px        162-226px     256px
        390 x  844   56- 97px        134-205px     227px

   As a percentage the h1 swings from 16.2% to 32.3% across those three, so a
   percentage stop cannot cover it — the first version of §20.1 put 0.20 at 24%
   and 0 at 38%, and the h1 landed in the fade on every screen.

   ✅ THE ALPHAS ARE SOLVED FROM THE WHITE-PHOTO BOUND, NOT PICKED. Composited
   over pure white, --gold-lt (#f0d58a, L 0.679) and --chip-text (#e9dcb8,
   L 0.720) need:

       breadcrumb  13px/500   floor 4.5   alpha >= 0.68 (link) / 0.67 (crumb)
       h1          62px/600   floor 3.0   alpha >= 0.545

   These stops give 0.77 at the breadcrumb's middle and 0.655 at the h1's, so
   both clear with margin rather than by a hair.

   ⚠️ AND THE GRADIENT STILL DOES NOT CLEAR, EVEN AT 0.655 — measured, not
   inferred from §9b. --gold-text-bright bottoms out at #cf9d42 (L 0.376),
   which is 2.61:1 against that backdrop, under the 3.0 large-text floor. So the
   h1 stays solid --gold-lt (4.47:1) and §8.3's ".gold-text" is struck for the
   same reason A1's headline gold was.

   The band ends at 400px and the photograph runs at full strength below it,
   which is the margin beside the reading sheet — the thing the full-page
   background exists for. */
:root.gilded body.note-page::before {
  background: linear-gradient(180deg,
    rgba(var(--ink-rgb), 0.82) 0px,
    rgba(var(--ink-rgb), 0.74) 130px,
    rgba(var(--ink-rgb), 0.60) 235px,
    rgba(var(--ink-rgb), 0.14) 320px,
    rgba(var(--ink-rgb), 0) 400px);
}

/* --- §20.1a  the photo drops BELOW the scrim -------------------------------
   ❌ WITHOUT THIS LINE THE SCRIM ABOVE NEVER PAINTED A PIXEL, AND §20.1 SHIPPED
   LIVE ON 2026-09-24 IN THAT STATE. scent-notes.css sets `.bg { position:
   fixed; z-index: -1 }` and §5.1 sets the scrim to `position: fixed; z-index:
   -1` as well. Equal z-index in one stacking context is decided by paint order,
   and body::before is body's FIRST child while .bg is a later sibling — so the
   photograph painted on top of its own darkening.

   ⚠️ IT WAS INVISIBLE FOR A DAY BECAUSE NOTHING NEEDED THE SCRIM YET. Before
   step 2 the only text over the photograph was the header, which carries its
   own --surface-glass band; the h1, the quick answer and the body all sat on
   the opaque sheet. So a page with no scrim at all looked correct. Putting the
   breadcrumb and the h1 on the photograph is what made it matter.

   ⚠️ AND STEP 1's VERIFICATION COULD NOT HAVE CAUGHT IT: it confirmed the
   served CSS carried a four-stop gradient ending transparent, which was true.
   That is a check on the DECLARATION, not on the painted pixel — the same
   wrong-property family as the gradient-clipped wordmark and the "a token is
   not a request" font. The measurement that found it read the backdrop under
   the h1 as [255,255,255]: with the photo whitened, pure white means nothing
   at all sits between the photograph and the eye.

   ⚠️ SCOPED TO body.note-page ON PURPOSE, NOT APPLIED TO EVERY .bg. The guide
   hubs use the same class and still carry their per-page inline gradient — the
   generator only strips it from note pages — so dropping their photo below the
   scrim would darken them TWICE, which is the defect step 1 set out to end.
   .bg-slideshow already does exactly this on the homepage, for this reason. */
:root.gilded body.note-page .bg {
  z-index: -2;
}

/* =========================================================================
   §22  THE INSTAGRAM FOLLOW COMPONENTS — guide articles only
   =========================================================================

   Two link-only components, requested 2026-09-28. No script, no embed, no
   pixel: an <a href> and nothing else, on pages that carry NO executable
   JavaScript at all and should keep that property.

   ⚠️ THERE IS NO SHARED GUIDE TEMPLATE. The 11 articles are individual files,
   so the MARKUP is repeated per page and only these rules are shared. That is
   the trade: one set of rules, eleven insertions. A generator for 11 static
   pages would be a second thing to keep in step with the pages themselves.

   ⚠️ SCOPED TO body.guide-page, NOT LEFT BARE. .ig-follow and .ig-inline are
   unused anywhere on the site today, so a bare rule would collide with
   nothing — but .footer-links, .intro, .subtitle and .note-header each read
   that way right up until they didn't, and §18.9 exists because of it.

   ⚠️ THE BOX IS OPAQUE ON PURPOSE, and it is what makes the contrast figure a
   NUMBER rather than a bound. It sits inside .guide-body, which §21 already
   made an opaque reading surface — so no photograph can reach this text and
   the measurement does not depend on which one loaded. Same reasoning as the
   note pages' opaque reading sheet.

   ⚠️ NOT TERRACOTTA. --alert / .guide-safety is reserved for hot wax, open
   flame and lye. A marketing CTA borrowing the safety colour would make the
   one element that must not read as ordinary copy read as ordinary copy, from
   the other direction. This is gold, quiet, and clearly not a warning. */

:root.gilded body.guide-page .ig-follow {
  margin: 40px 0 8px;
  padding: 22px 24px;
  background: var(--panel);
  border: 1px solid rgba(var(--gold-rgb), 0.22);
  border-radius: 10px;
}

:root.gilded body.guide-page .ig-follow h2 {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--chip-text);
}

:root.gilded body.guide-page .ig-follow p {
  margin: 0 0 16px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
  max-width: 62ch;
}

/* A real <a>, styled as a pill. Never a <div> with a click handler — there is
   no handler to give it, and a link that looks like a button but cannot be
   opened in a new tab is a control that lies about itself. */
:root.gilded body.guide-page .ig-follow-btn {
  display: inline-block;
  padding: 10px 18px;
  border: 1px solid rgba(var(--gold-rgb), 0.45);
  border-radius: 999px;
  background: rgba(var(--gold-rgb), 0.08);
  color: var(--gold-lt);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-decoration: none;
}
:root.gilded body.guide-page .ig-follow-btn:hover,
:root.gilded body.guide-page .ig-follow-btn:focus-visible {
  background: rgba(var(--gold-rgb), 0.16);
  border-color: rgba(var(--gold-rgb), 0.7);
  outline: none;
}

/* The inline one. Quieter still: one line, a left rule rather than a full
   box, so it reads as an aside between sections and not as a second banner. */
:root.gilded body.guide-page .ig-inline {
  margin: 28px 0;
  padding: 12px 0 12px 16px;
  border-left: 2px solid rgba(var(--gold-rgb), 0.3);
}
:root.gilded body.guide-page .ig-inline p {
  margin: 0;
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
  max-width: 62ch;
}
:root.gilded body.guide-page .ig-inline a,
:root.gilded body.guide-page .ig-follow p a {
  color: var(--gold-lt);
  text-decoration: underline;
  text-underline-offset: 2px;
}

@media (max-width: 600px) {
  :root.gilded body.guide-page .ig-follow { padding: 18px 16px; }
  :root.gilded body.guide-page .ig-follow h2 { font-size: 19px; }
  :root.gilded body.guide-page .ig-follow-btn { display: block; text-align: center; }
}
