/* ══════════════════════════════════════════════════════════════════════════
   RING BUILDER — assets/ring-builder.css
   Loaded by sections/ring-builder.liquid. Lives in assets/ rather than a
   {% stylesheet %} block (spec DEVIATION 3) so markup, JS and CSS could be
   built in parallel against the DOM manifest.

   Everything is scoped under .rb. Colour, type and easing come from the theme
   through the --rb-* aliases declared on .rb below — this file hardcodes
   exactly two colours, both flagged where they appear: the slider's neutral
   grey (#d9d9d9, which must NOT recolour with the brand or the unselected
   track stops reading as "off") and the mobile scrim's fallback.

   Reading order matches the spec: shell → rail → diamond view → slider →
   card → detail → review → mobile sheet → skeletons → breakpoints.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── tokens ────────────────────────────────────────────────────────────────
   Aliased once here rather than reaching for --color-* at 300 call sites, so
   the component can be lifted into another theme by rewriting this one block.
   The fallbacks are the Novara palette, present only so the builder still
   renders if it is dropped onto a page that never emitted :root. */
.rb {
  --rb-accent:   var(--color-accent, #83673D);      /* gold  */
  --rb-ink:      var(--color-text, #211D18);
  --rb-ink-soft: var(--color-text-muted, #6E6659);
  /* SWAPPED against the spec's palette, deliberately. The spec has white
     cards on a bone page; this theme reserves the bone tone (--color-surface)
     for a banded SECTION inside a page, so using it for a whole route made
     /build-a-ring the only page in the shop with a different background.
     Page takes the theme's page colour, panels take the tint — the same
     figure/ground separation, the right way up for this palette. */
  --rb-paper:    var(--color-surface, #F3EEE6);     /* cards, segments, panels */
  --rb-bone:     var(--color-bg, #FBF9F6);          /* the builder's page */
  --rb-line:     var(--color-border, #E7E0D4);
  --rb-font-heading: var(--font-heading);           /* Cormorant Garamond */
  --rb-font-body:    var(--font-body);              /* Karla */
  --rb-radius: 0;
  /* Thumb size lives here because the rail fill has to offset by half of it;
     two copies of this number drifting apart is exactly the bug above. */
  --rb-thumb: 24px;
  --rb-thumb-half: 12px;
  --rb-ease: cubic-bezier(.23,1,.32,1);
  --rb-dur: 200ms;

  /* The chevron geometry of the step rail, in one place: the point, the notch
     and the overlap are three numbers that must stay in lockstep or the 3px
     seam between segments closes up or gapes. overlap = point - seam. */
  --rb-chev: 22px;
  --rb-chev-lap: -19px;

  /* The card is the only element in the component that casts a drop shadow
     (§3.4). Black-alpha rather than the ink token on purpose: a shadow tinted
     with the brand brown reads as a smudge on the bone band, and neutral
     alpha survives a merchant recolouring the palette. */
  --rb-shadow:      0 1px 2px rgba(0,0,0,.05), 0 10px 24px rgba(0,0,0,.06);
  --rb-shadow-lift: 0 2px 5px rgba(0,0,0,.07), 0 18px 36px rgba(0,0,0,.10);
}

/* ── TRAP #1 ───────────────────────────────────────────────────────────────
   The three views are siblings toggled with the boolean `hidden` attribute.
   Every one of them carries a display of its own (grid/flex), and a plain
   `[hidden] { display: none }` loses to those on specificity — so all three
   views paint at once and the page is three screens tall. !important is the
   fix, and it is the single most important declaration in this file. */
.rb [hidden] { display: none !important; }

/* ══════════════════════════════════════════════════════════════════════════
   SHELL
   ══════════════════════════════════════════════════════════════════════════ */

/* The whole builder sits on a full-bleed bone band. The section itself is
   flush (no --section-spacing), so the vertical rhythm is declared here:
   24px of air above the rail, 72px below the pager so the last row of cards
   does not collide with the footer. */
.rb {
  width: 100%;
  background: var(--rb-bone);
  color: var(--rb-ink);
  font-family: var(--rb-font-body);
  padding: 24px var(--gutter, 20px) 72px;
}
.rb__inner {
  width: 100%;
  max-width: 1180px;
  margin-inline: auto;
}
.rb__view { display: block; }

/* A single focus treatment for the whole component, matching base.css: 2px
   gold, offset so it clears the element's own border. Declared once against a
   list rather than per-component, so nothing can be added later without one.
   Nowhere in this file is an outline removed without a replacement. */
.rb button:focus-visible,
.rb a:focus-visible,
.rb select:focus-visible,
.rb input:focus-visible,
.rb [role="button"]:focus-visible,
.rb__scrim:focus-visible {
  outline: 2px solid var(--rb-accent);
  outline-offset: 2px;
}

/* ══════════════════════════════════════════════════════════════════════════
   §3.1 STEP RAIL
   ══════════════════════════════════════════════════════════════════════════ */

/* The rail's own background is the border colour: the 3px gap left between
   each segment's point and the next segment's notch shows THROUGH to it, and
   that show-through IS the divider. There is no border anywhere on a step. */
.rb__rail {
  display: flex;
  background: var(--rb-line);
  margin-bottom: 28px;
}

.rb__step {
  position: relative;              /* so is-active can raise its z-index */
  flex: 1 1 0;
  min-width: 0;
  min-height: 80px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 18px 12px calc(var(--rb-chev) + 18px);
  background: var(--rb-paper);
  color: var(--rb-ink-soft);
  /* A hard stop: whatever the chip ends up carrying, it is clipped at the
     segment rather than spilling across the seam into the next one. */
  overflow: hidden;
  /* 22px point on the right, 22px notch on the left. */
  clip-path: polygon(
    0 0,
    calc(100% - var(--rb-chev)) 0,
    100% 50%,
    calc(100% - var(--rb-chev)) 100%,
    0 100%,
    var(--rb-chev) 50%
  );
  transition: background-color var(--rb-dur) var(--rb-ease),
              color var(--rb-dur) var(--rb-ease);
}
/* Pull each segment back into the previous one's point. 22 - 19 = the 3px seam. */
.rb__step + .rb__step { margin-left: var(--rb-chev-lap); }

/* The outer edges of the rail are straight, not chevroned — a notch on the
   first segment would leave a wedge of bone showing at the container's edge. */
.rb__step:first-child {
  padding-left: 22px;
  clip-path: polygon(
    0 0,
    calc(100% - var(--rb-chev)) 0,
    100% 50%,
    calc(100% - var(--rb-chev)) 100%,
    0 100%
  );
}
.rb__step:last-child {
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, var(--rb-chev) 50%);
}

.rb__step-num {
  font-family: var(--rb-font-heading);
  font-size: 30px;
  font-weight: 500;
  line-height: 1;
  flex: none;
}
.rb__step-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rb__step-l1,
.rb__step-l2 {
  font-size: 12px;
  line-height: 1.25;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Step 1's chip appears once a setting is chosen, step 2's once a stone is.
   Both are `hidden` in markup until then — handled by the [hidden] rule. */
/* Wraps on purpose. Between 820px and 1000px a segment is ~250px of usable
   width and has to carry a numeral, two label lines, a 40px thumbnail, a price
   and two links before the trailing icon. Without wrap the flex line overflows,
   the icon's margin-left:auto collapses to nothing, and "View" is painted on
   top of the icon — so the links drop to a second row instead. */
.rb__step-chip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px 10px;
  min-width: 0;
}
.rb__step-thumb {
  width: 40px;
  height: 40px;
  flex: none;
  background: var(--rb-bone) center / cover no-repeat;  /* JS writes the image */
  border: 1px solid var(--rb-line);
}
.rb__step-chip-price {
  font-family: var(--rb-font-heading);
  font-size: 17px;
  font-weight: 500;
  line-height: 1.1;
  color: var(--rb-ink);
}
.rb__step-links { display: flex; align-items: center; gap: 8px; }
/* On a touch screen the two links need room to become real targets; the
   gap has to grow with them or their tap areas overlap and the wrong one
   fires. Pointer devices keep the tight 8px chip. */
@media (pointer: coarse) { .rb__step-links { gap: 22px; } }
@media (pointer: coarse) { .rb__step-link::after { inset: -14px -10px; } }
.rb__step-link {
  position: relative;                /* anchors the enlarged hit area below */
  font-size: 11px;
  letter-spacing: .04em;
  color: var(--rb-accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}
/* The painted link is 11px type — about 25x18. That is a fine-looking chip and
   a poor target, so the TAP area is grown to 44px tall with a transparent
   overlay while the text keeps its size. Horizontal growth is capped at 4px a
   side because Change and View sit 8px apart; going wider would make the two
   targets overlap, which trades one problem for a worse one. */
.rb__step-link::after {
  content: "";
  position: absolute;
  inset: -14px -4px;                 /* 18 + 28 = 46 tall */
}
.rb__step-link:hover { color: var(--rb-ink); }
.rb__step-ic {
  margin-left: auto;             /* pinned to the segment's right edge */
  flex: none;
  display: flex;
  align-items: center;
  color: var(--rb-line);
}
.rb__step-ic svg { width: 26px; height: 26px; }

/* §16.14 — THE RULE THE REFERENCE OMITS.
   `is-done` is written by JS and had no styling at all, so a completed step
   looked identical to one never visited. It must read as *behind you* without
   competing with is-active's gold fill, so it takes the band's bone tone —
   recessed against the white of the pending steps — a gold numeral, and a 2px
   gold cap drawn as a background layer rather than a border (a border would
   shrink the content box by 2px and knock this segment out of alignment with
   the other two). Authored BEFORE .is-active so that a segment carrying both
   classes resolves to active on source order, not by accident. */
/* A completed step. The reference styles this state not at all, so this rule is
   ours — but the first version overreached: it tinted the segment bone (the
   same colour as the band behind it, so a done step dissolved into the page)
   and drew a 2px gold rule along the top edge. That rule followed the segment's
   full width THROUGH the chevron point, so it read as a stray gold bar floating
   over the rail rather than as progress.
   The cue is now carried by the numeral and the trailing glyph turning gold —
   quiet, and it cannot break geometry because it touches no box. */
.rb__step.is-done {
  color: var(--rb-ink-soft);
}
.rb__step.is-done .rb__step-num { color: var(--rb-accent); }
.rb__step.is-done .rb__step-ic  { color: var(--rb-accent); }

/* The only state the reference styles, and the loudest thing on the page. */
.rb__step.is-active {
  z-index: 2;
  background: var(--rb-accent);
  background-image: none;
  color: var(--rb-paper);
}
.rb__step.is-active .rb__step-num,
.rb__step.is-active .rb__step-chip-price,
.rb__step.is-active .rb__step-ic { color: var(--rb-paper); }
.rb__step.is-active .rb__step-link { color: var(--rb-paper); }
.rb__step.is-active .rb__step-thumb { border-color: var(--rb-paper); }

/* ══════════════════════════════════════════════════════════════════════════
   §3.2 DIAMOND VIEW — filters, results, pagination
   ══════════════════════════════════════════════════════════════════════════ */

/* Origin tabs are NOT a grid item — they are centred above the whole panel and
   sized to their content, which is why width:max-content rather than a width. */
.rb__origin {
  display: flex;
  width: max-content;
  max-width: 100%;
  margin: 0 auto 20px;
  border: 1px solid var(--rb-line);
  background: var(--rb-paper);
}
.rb__origin-tab {
  min-height: 44px;
  padding: 0 22px;
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rb-ink-soft);
  background: transparent;
  transition: background-color var(--rb-dur) var(--rb-ease),
              color var(--rb-dur) var(--rb-ease);
}
.rb__origin-tab:hover { color: var(--rb-ink); }
.rb__origin-tab.is-active,
.rb__origin-tab[aria-selected="true"] {
  background: var(--rb-ink);
  color: var(--rb-paper);
}

/* ── filter cards ───────────────────────────────────────────────────────── */
.rb__fcard {
  background: var(--rb-paper);
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  padding: 18px 22px;
  /* No shadow. The card in the results grid is the one element that has one. */
}
/* The shape card spans the full panel width and is a SIBLING of the grid, not
   a member of it — ten tiles in a 2-column grid cell would be unreadable. */
.rb__fcard--wide { margin-bottom: 16px; }

.rb__fgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.rb__fhead {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 24px;
  margin-bottom: 14px;
}
.rb__ftitle {
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: .02em;
  margin: 0;
}
.rb__fhead-ctl { margin-left: auto; display: flex; align-items: center; }
/* The mobile summary value ("D - G") is injected into every head by JS but
   must never show on desktop, where the control itself is visible. */
.rb__fsum { display: none; }

.rb__fbody { display: block; }

/* The six advanced cards join the same grid flow — no separate container and
   no animation, so opening the disclosure just reflows the grid. This is the
   only place `data-extra` is used, and it is CSS-only (§6.4). The class lands
   on an ancestor whose identity JS owns, so both shapes are matched. */
.rb__fcard[data-extra] { display: none; }
.rb.is-advanced-open .rb__fcard[data-extra],
.rb .is-advanced-open .rb__fcard[data-extra] { display: block; }

/* ── shape tiles ────────────────────────────────────────────────────────── */
.rb__shapes {
  display: grid;
  grid-template-columns: repeat(10, minmax(0, 1fr));
  gap: 8px;
}
.rb__shape {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  padding: 10px 4px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--rb-ink-soft);
  transition: border-color var(--rb-dur) var(--rb-ease),
              color var(--rb-dur) var(--rb-ease);
}
.rb__shape:hover { border-color: var(--rb-line); color: var(--rb-ink); }
.rb__shape[aria-pressed="true"] {
  border-color: var(--rb-accent);
  color: var(--rb-ink);
}
/* 64px is the design size, but 10 tiles inside a padded card on a 375px phone
   leaves ~66px of column — max-width lets the icon shrink instead of pushing
   the grid into a horizontal scroll. */
.rb__shape-ic {
  width: 64px;
  max-width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
}
/* The tiles are photographs of real stones shot on WHITE, and the panel behind
   them is cream — so each one landed as a visible white rectangle with a
   diamond floating in it, ten pale boxes in a row. `multiply` drops pure white
   to nothing and leaves the stone, which is what makes them read as cut-outs on
   the panel the way the reference does. It is safe for the greys inside the
   stone: multiply only darkens, and the facets are already darker than the
   cream they now blend against.
   NO `isolation: isolate` on the tile. It was the obvious guard — keep the
   blend off the card's border — but isolating creates a fresh stacking context
   whose backdrop is empty, so multiply had nothing to multiply against and the
   white rectangles survived untouched. The blend needs to reach the panel
   behind it to do anything at all. The border is unaffected because it is
   painted by the tile, not by the image. */
.rb__shape-ic img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  mix-blend-mode: multiply;
}
.rb__shape-lbl {
  font-size: 11px;
  line-height: 1.2;
  letter-spacing: .03em;
  text-align: center;
}

/* ── pills (lab filter + review option pills share one class) ───────────── */
.rb__pills { display: flex; flex-wrap: wrap; gap: 8px; }
.rb__pill {
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  background: var(--rb-paper);
  color: var(--rb-ink);
  font-size: 13px;
  padding: 8px 18px;
  min-height: 38px;
  transition: border-color var(--rb-dur) var(--rb-ease),
              color var(--rb-dur) var(--rb-ease),
              background-color var(--rb-dur) var(--rb-ease);
}
.rb__pill:hover { border-color: var(--rb-accent); }
/* Selection is carried by aria-pressed, not a class — the accessible state and
   the visual state are the same fact, so they cannot drift apart. */
.rb__pill[aria-pressed="true"] {
  border-color: var(--rb-accent);
  color: var(--rb-accent);
}
/* Unavailable combinations are rendered struck through rather than removed, so
   the shopper can see the option exists and that this pairing is what is out. */
.rb__pill[disabled],
.rb__pill:disabled {
  color: var(--rb-ink-soft);
  background: var(--rb-bone);
  border-color: var(--rb-line);
  text-decoration: line-through;
  cursor: not-allowed;
}
.rb__pill[disabled]:hover,
.rb__pill:disabled:hover { border-color: var(--rb-line); color: var(--rb-ink-soft); }

/* The lab pills invert the fill: bone at rest, white when pressed (§4.2). */
.rb__pill[data-rb-lab] { min-width: 88px; background: var(--rb-bone); }
.rb__pill[data-rb-lab][aria-pressed="true"] {
  background: var(--rb-paper);
  border-color: var(--rb-accent);
  color: var(--rb-accent);
}

/* ── the White | Fancy segment, which lives in the colour card's head ───── */
.rb__seg {
  display: flex;
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  overflow: hidden;
}
.rb__seg-btn {
  padding: 6px 12px;
  min-height: 30px;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--rb-ink-soft);
  background: var(--rb-paper);
  transition: background-color var(--rb-dur) var(--rb-ease),
              color var(--rb-dur) var(--rb-ease);
}
.rb__seg-btn:hover { color: var(--rb-ink); }
.rb__seg-btn.is-active { background: var(--rb-ink); color: var(--rb-paper); }

/* ── colour: two bodies in one card, swapped by the head segment ────────── */
/* Fancy is a MODE, not a filter card — both bodies live inside the colour card
   and the inactive one carries `hidden`, so the card never changes height
   category and the head segment stays put across the swap. */
.rb__colorwhite { display: block; }

/* ── fancy colour carousel (§4.4) ───────────────────────────────────────── */
.rb__colorfancy { display: flex; align-items: center; gap: 8px; }
/* One of the five things allowed to be round. */
.rb__fancy-nav {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid var(--rb-line);
  background: var(--rb-paper);
  color: var(--rb-ink);
  display: grid;
  place-items: center;
  font-size: 12px;
  line-height: 1;
}
.rb__fancy-nav:hover { border-color: var(--rb-accent); color: var(--rb-accent); }
.rb__fancy-nav[disabled] { opacity: .35; cursor: not-allowed; }
.rb__fancy-nav[disabled]:hover { border-color: var(--rb-line); color: var(--rb-ink); }

/* The track scrolls at every width — thirteen 84px tiles never fit a filter
   card, so hiding the scrollbar and snapping is the interaction, not a
   mobile-only fallback. */
.rb__fancy-track {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding-bottom: 2px;
}
.rb__fancy-track::-webkit-scrollbar { display: none; }
.rb__fancy-tile {
  flex: 0 0 84px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px 4px;
  border: 1px solid var(--rb-line);
  background: var(--rb-paper);
  color: var(--rb-ink-soft);
  transition: border-color var(--rb-dur) var(--rb-ease),
              background-color var(--rb-dur) var(--rb-ease),
              color var(--rb-dur) var(--rb-ease);
}
.rb__fancy-tile:hover { border-color: var(--rb-accent); color: var(--rb-ink); }
/* Pressed: gold border, a 7% gold wash and an inset gold ring. The ring is an
   inset outline rather than an inset box-shadow so the component keeps exactly
   one box-shadow on a box (the result card). :focus-visible is authored after
   this, so a pressed-and-focused tile still shows the focus ring. */
.rb__fancy-tile[aria-pressed="true"] {
  border-color: var(--rb-accent);
  background: color-mix(in srgb, var(--rb-accent) 7%, var(--rb-paper));
  color: var(--rb-ink);
  outline: 1px solid var(--rb-accent);
  outline-offset: -3px;
}
.rb__fancy-tile:focus-visible { outline: 2px solid var(--rb-accent); outline-offset: 2px; }
.rb__fancy-dot {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--dot, var(--rb-line));   /* per-tile custom property */
  border: 1px solid rgba(0,0,0,.10);        /* keeps a white dot visible */
}
.rb__fancy-lbl {
  font-size: 10px;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.2;
}

/* ── tools row: Advanced Filters + Reset, both pushed right ─────────────── */
.rb__ftools {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 16px;
  margin-top: 16px;
}
.rb__advanced {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  font-size: 12px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--rb-ink);
}
.rb__advanced:hover { color: var(--rb-accent); }
/* Chevron drawn from two borders rather than shipped as an icon — it has to
   rotate, and a glyph would need a font that carries it. */
.rb__advanced::after {
  content: "";
  width: 7px;
  height: 7px;
  margin-top: -4px;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--rb-dur) var(--rb-ease);
}
.rb__advanced[aria-expanded="true"]::after { margin-top: 2px; transform: rotate(-135deg); }

.rb__reset {
  min-height: 44px;
  font-size: 12.5px;
  color: var(--rb-accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.rb__reset:hover { color: var(--rb-ink); }

/* ── quick carat chips — the other round thing ──────────────────────────── */
.rb__quick {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-top: 22px;
}
.rb__quick-chip {
  border: 1px solid var(--rb-line);
  border-radius: 999px;
  background: var(--rb-paper);
  color: var(--rb-ink);
  font-size: 12px;
  letter-spacing: .04em;
  padding: 9px 18px;
  min-height: 40px;
  transition: border-color var(--rb-dur) var(--rb-ease),
              color var(--rb-dur) var(--rb-ease);
}
.rb__quick-chip:hover { border-color: var(--rb-accent); color: var(--rb-accent); }
.rb__quick-chip[aria-pressed="true"],
.rb__quick-chip.is-active { border-color: var(--rb-accent); color: var(--rb-accent); }

/* ── count, results, pager ──────────────────────────────────────────────── */
.rb__count {
  text-align: center;
  margin: 26px 0 0;
  font-size: 13px;
  letter-spacing: .08em;
  color: var(--rb-ink-soft);
}
.rb__results { margin-top: 26px; }

.rb__aip { margin-bottom: 42px; }
.rb__aip-title {
  font-family: var(--rb-font-heading);
  font-size: 30px;
  font-weight: 500;
  line-height: 1.15;
  text-align: center;
  margin: 0 0 20px;
}
/* The ✦ is the only gold thing in the heading. Markup wraps it in a bare span;
   if it does not, the heading simply renders all-ink and nothing breaks. */
.rb__aip-title span { color: var(--rb-accent); }
/* Exactly four, never fluid — the AI picks are a fixed-size editorial row. */
.rb__aip-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 22px;
}
.rb__all-title {
  font-family: var(--rb-font-heading);
  font-size: 26px;
  font-weight: 500;
  text-align: center;
  margin: 0 0 20px;
}
/* The results grid is fluid and is never overridden at any breakpoint — one
   declaration covers 375px to 4K. min() with 100% is what stops the 230px
   minimum from forcing a horizontal scroll on the narrowest phone. */
.rb__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(230px, 100%), 1fr));
  gap: 22px;
}
.rb__status {
  padding: 40px 20px;
  text-align: center;
  color: var(--rb-ink-soft);
  font-size: 14px;
  line-height: 1.6;
}
/* Empty-success and API-failure share this node; when JS clears it, it must
   not leave 80px of dead space above the pager. */
.rb__status:empty { display: none; padding: 0; }

.rb__pager {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 22px;
  margin-top: 34px;
}
.rb__pager-btn {
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--rb-line);
  background: var(--rb-paper);
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rb-ink);
  transition: border-color var(--rb-dur) var(--rb-ease),
              color var(--rb-dur) var(--rb-ease);
}
.rb__pager-btn:hover { border-color: var(--rb-accent); color: var(--rb-accent); }
.rb__pager-btn[disabled] { opacity: .4; cursor: not-allowed; }
.rb__pager-btn[disabled]:hover { border-color: var(--rb-line); color: var(--rb-ink); }
.rb__pageinfo { font-size: 12.5px; color: var(--rb-ink-soft); letter-spacing: .04em; }

/* ══════════════════════════════════════════════════════════════════════════
   §3.3 THE DUAL-RANGE SLIDER
   Two <input type=range> stacked in the same box. The [data-hi] input IS the
   visible rail — it carries the three-stop gradient, so there is no separate
   track element to keep in sync. [data-lo] is collapsed to height 0 and
   contributes nothing but its thumb, two pixels lower so the two thumbs read
   as sitting on the same line. Both inputs are pointer-events:none; only the
   thumbs take the pointer, which is what lets the two overlap without the top
   one swallowing every drag aimed at the bottom one.
   JS writes --lo and --hi on the wrapper as percentages.
   ══════════════════════════════════════════════════════════════════════════ */

.rb__slider {
  --lo: 0%;
  --hi: 100%;
  position: relative;
  /* The 9px sides reserve half a thumb at each end so a thumb parked at min or
     max cannot overhang the card's padding. */
  padding: 16px 9px 4px;
  min-height: 38px;
}

.rb__slider input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  position: absolute;
  left: 9px;
  width: calc(100% - 18px);
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  pointer-events: none;
  -webkit-tap-highlight-color: transparent;
}

/* The rail. Three hard stops, no soft edges: grey up to --lo, ink across the
   selection, grey again after --hi. */
.rb__slider input[data-hi] {
  top: 16px;
  height: 4px;
  z-index: 5;
  /* Stops are computed from the THUMB CENTRE, not from a raw percentage of the
     track. The browser lays the thumb out inside the track — at the minimum its
     left edge is on the track edge, so its centre is half a thumb in — and a
     plain percentage therefore ended the ink half a thumb short of the handle,
     leaving a dark stub sticking out beside it. That error is half the thumb
     width, so growing the thumb to 24px for the touch target made a 9px
     artefact a 12px one, which is when it became obvious.
     --lo-f / --hi-f are unitless 0..1 fractions written by the JS. */
  background: linear-gradient(
    to right,
    #d9d9d9 0,                                          /* the one allowed hardcode */
    #d9d9d9 calc(var(--rb-thumb-half) + (100% - var(--rb-thumb)) * var(--lo-f, 0)),
    var(--rb-ink) calc(var(--rb-thumb-half) + (100% - var(--rb-thumb)) * var(--lo-f, 0)),
    var(--rb-ink) calc(var(--rb-thumb-half) + (100% - var(--rb-thumb)) * var(--hi-f, 1)),
    #d9d9d9 calc(var(--rb-thumb-half) + (100% - var(--rb-thumb)) * var(--hi-f, 1)),
    #d9d9d9 100%
  );
}
.rb__slider input[data-lo] {
  top: 18px;
  height: 0;
  z-index: 6;
}

/* Thumbs. WebKit and Gecko cannot share a selector list — one unknown
   pseudo-element invalidates the whole rule — so the two are written out in
   full, twice, deliberately. */
/* The thumb is the ONLY part of the slider that takes pointer events (the
   input itself is pointer-events:none), so it is the real target — and an 18px
   circle is under the 24px WCAG 2.5.8 minimum. A 3px transparent ring grows the
   target to 24px while background-clip keeps the PAINTED circle at 18px, so the
   control is easier to grab and looks identical. */
.rb__slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    var(--rb-paper) 0 7px,
    #d9d9d9 7px 9px,
    transparent 9px);
  border: 0;
  /* §3.3 pins a soft lift on the thumb so it separates from the ink rail it
     sits on. It is the only shadow outside the result card, and it is on a
     control, not a box. */
  box-shadow: 0 1px 3px rgba(0,0,0,.22);
  cursor: grab;
  pointer-events: auto;
}
.rb__slider input[type="range"]::-moz-range-thumb {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    var(--rb-paper) 0 7px,
    #d9d9d9 7px 9px,
    transparent 9px);
  border: 0;
  box-shadow: 0 1px 3px rgba(0,0,0,.22);
  cursor: grab;
  pointer-events: auto;
}
.rb__slider input[type="range"]:active::-webkit-slider-thumb { cursor: grabbing; }
.rb__slider input[type="range"]:active::-moz-range-thumb { cursor: grabbing; }

/* Firefox paints its own track over the element background, so it has to be
   cleared or the gradient rail disappears in Gecko only. */
.rb__slider input[type="range"]::-moz-range-track {
  height: 4px;
  background: transparent;
  border: 0;
}

/* A 2px outline around a 0-height input is invisible, so the focus ring is
   moved onto the thumb: gold border plus a gold halo. The outline is removed
   ONLY because this replaces it. */
.rb__slider input[type="range"]:focus-visible { outline: none; }
.rb__slider input[type="range"]:focus-visible::-webkit-slider-thumb {
  background: radial-gradient(circle at 50% 50%, var(--rb-paper) 0 7px, var(--rb-accent) 7px 9px, transparent 9px);
  box-shadow: 0 0 0 2px var(--rb-paper), 0 0 0 4px var(--rb-accent);
}
.rb__slider input[type="range"]:focus-visible::-moz-range-thumb {
  background: radial-gradient(circle at 50% 50%, var(--rb-paper) 0 7px, var(--rb-accent) 7px 9px, transparent 9px);
  box-shadow: 0 0 0 2px var(--rb-paper), 0 0 0 4px var(--rb-accent);
}

/* ── tick labels (ordinal sliders only) ─────────────────────────────────── */
.rb__ticks {
  display: flex;
  justify-content: space-between;
  /* Clears the 18px thumb, which overhangs the 4px rail by 7px on each side. */
  margin-top: 14px;
}
.rb__tick {
  position: relative;
  width: 20px;
  font-size: 11px;
  line-height: 1.2;
  text-align: center;
  color: var(--rb-ink-soft);
  transition: color var(--rb-dur) var(--rb-ease);
}
.rb__tick.is-in { color: var(--rb-ink); }
/* Each label punches a 5×8px white notch UP through the rail, so the rail
   reads as a scale with divisions rather than one continuous bar.
   z-index 7 is load-bearing: the rail is a positioned input at z-index 5 and
   the lo thumb at 6, so a notch left at auto paints UNDERNEATH both and the
   divisions simply never appear. Above the thumbs is harmless — the notch is
   white and so is the thumb's fill, and at 8px tall it never reaches the
   thumb's border. */
.rb__tick::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -15px;
  z-index: 7;
  width: 5px;
  height: 8px;
  transform: translateX(-50%);
  background: var(--rb-paper);
}
/* Suppressed at both ends — a notch there would slice the rail's own tips off. */
.rb__tick:first-child::before,
.rb__tick:last-child::before { content: none; }

/* ── number inputs (numeric sliders only) ───────────────────────────────── */
.rb__nums { display: flex; gap: 10px; margin-top: 12px; }
.rb__num {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  height: 42px;
  padding: 0 10px;
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  background: var(--rb-paper);
}
.rb__num input {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  border: 0;
  background: transparent;
  font-size: 13px;
  text-align: right;
  color: var(--rb-ink);
  -moz-appearance: textfield;
  appearance: textfield;
}
/* Spinners stripped: the value is driven by the slider, and a pair of arrows
   inside a 42px box crowds the unit suffix off the end. */
.rb__num input::-webkit-outer-spin-button,
.rb__num input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* The input fills the 42px box edge to edge, so an outline on it would be
   clipped by the wrapper. It moves to the wrapper instead — removed only
   because it is replaced one line down. */
.rb__num input:focus-visible { outline: none; }
.rb__num:focus-within {
  border-color: var(--rb-accent);
  outline: 2px solid var(--rb-accent);
  outline-offset: 2px;
}
.rb__num-unit { flex: none; font-size: 12px; color: var(--rb-ink-soft); }
/* Price is the one unit that precedes its number. Selected off the §6 data
   attribute via a sibling combinator rather than :has(), so it needs no parent
   selector and no extra class in the markup contract. */
.rb__num input[data-rb-num^="price"] { text-align: left; }
.rb__num input[data-rb-num^="price"] ~ .rb__num-unit { order: -1; }

/* ══════════════════════════════════════════════════════════════════════════
   §3.4 THE DIAMOND CARD — the one element with a shadow
   ══════════════════════════════════════════════════════════════════════════ */

.rb__card {
  display: flex;
  flex-direction: column;
  background: var(--rb-paper);
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  box-shadow: var(--rb-shadow);
  cursor: pointer;
  transition: transform var(--rb-dur) var(--rb-ease),
              box-shadow var(--rb-dur) var(--rb-ease),
              border-color var(--rb-dur) var(--rb-ease);
}
.rb__card:hover {
  transform: translateY(-2px);
  box-shadow: var(--rb-shadow-lift);
  border-color: var(--rb-accent);
}
.rb__card:focus-visible { outline: 2px solid var(--rb-accent); outline-offset: 2px; }

.rb__card-media {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--rb-bone);   /* also the no-image placeholder — no icon, no text */
}
.rb__card-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* "Pill" here means a small dark label, not a rounded one: --rb-radius is 0
   and the badge is not on the short list of round things. */
.rb__card-badge {
  position: absolute;
  right: 8px;
  bottom: 8px;
  padding: 4px 8px;
  font-size: 9px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rb-paper);
  background: rgba(33,29,24,.72);
  background: color-mix(in srgb, var(--rb-ink) 72%, transparent);
}
.rb__card-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 14px 16px;
}
.rb__card-cert { font-size: 10.5px; color: var(--rb-ink-soft); letter-spacing: .02em; }
/* There is no title on this card. This line IS the identifier. */
.rb__card-spec { font-size: 12px; line-height: 1.45; color: var(--rb-ink); }
.rb__card-price {
  margin-top: 2px;
  font-family: var(--rb-font-heading);
  font-size: 23px;
  font-weight: 500;
  line-height: 1.1;
}

/* ══════════════════════════════════════════════════════════════════════════
   §3.5 DETAIL VIEW — three columns
   ══════════════════════════════════════════════════════════════════════════ */

/* Not a modal. The detail view REPLACES the results grid in place and the step
   rail stays visible above it, which is why there is no overlay, no trap and
   no fixed positioning anywhere in this block. */
.rb__dv { display: block; }

.rb__dv-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 10px;
  margin-bottom: 22px;
  border-bottom: 1px solid var(--rb-line);
}
.rb__dv-back {
  min-height: 44px;
  font-size: 11.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--rb-ink-soft);
}
.rb__dv-back:hover { color: var(--rb-accent); }
.rb__dv-close {
  width: 44px;
  height: 44px;
  font-size: 22px;
  line-height: 1;
  color: var(--rb-ink-soft);
}
.rb__dv-close:hover { color: var(--rb-accent); }

/* rail | stage | info. minmax(0,…) on both flexible tracks because a grid
   track's automatic minimum is its content, and a wide <img> or a long
   measurements string would otherwise blow the column out. */
.rb__dv-body {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr) minmax(0, 380px);
  gap: 24px 32px;
  align-items: start;
}

/* The 72px rail is the media tab strip. Selection is border-only — a fill
   would compete with the photo inside the thumb. */
.rb__dv-rail { display: flex; flex-direction: column; gap: 8px; }
.rb__dv-tab,
.rb__dv-cert {
  position: relative;
  width: 72px;
  height: 72px;
  flex: none;
  display: grid;
  place-items: center;
  overflow: hidden;
  padding: 0;
  border: 1px solid var(--rb-line);
  background: var(--rb-paper);
  color: var(--rb-ink-soft);
  font-size: 10px;
  letter-spacing: .06em;
  text-transform: uppercase;
  transition: border-color var(--rb-dur) var(--rb-ease);
}
.rb__dv-tab img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.rb__dv-tab:hover,
.rb__dv-cert:hover { border-color: var(--rb-accent); color: var(--rb-ink); }
.rb__dv-tab.is-active { border-color: var(--rb-ink); }
.rb__dv-cert svg { width: 26px; height: 26px; }

.rb__dv-stage {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--rb-bone);
}
.rb__dv-stage img,
.rb__dv-stage video,
.rb__dv-stage iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  object-fit: cover;
}

.rb__dv-info { display: flex; flex-direction: column; gap: 10px; }
.rb__dv-certid {
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rb-ink-soft);
}
.rb__dv-title {
  font-family: var(--rb-font-heading);
  font-size: 32px;
  font-weight: 500;
  line-height: 1.12;
  margin: 0;
}
.rb__dv-price {
  font-family: var(--rb-font-heading);
  font-size: 26px;
  font-weight: 500;
  line-height: 1.1;
}

.rb__dv-sum {
  margin-top: 6px;
  padding-top: 14px;
  border-top: 1px solid var(--rb-line);
}
.rb__dv-sum-row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 5px 0;
  font-size: 13.5px;
}
.rb__dv-sum-row--total {
  margin-top: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--rb-line);
  font-family: var(--rb-font-heading);
  font-size: 19px;
  font-weight: 500;
}

.rb__dv-cta {
  width: 100%;
  min-height: 48px;
  margin-top: 12px;
  background: var(--rb-ink);
  color: var(--rb-paper);
  border: 1px solid var(--rb-ink);
  border-radius: var(--rb-radius);
  font-size: 12px;
  letter-spacing: .16em;
  text-transform: uppercase;
  transition: background-color var(--rb-dur) var(--rb-ease),
              border-color var(--rb-dur) var(--rb-ease);
}
.rb__dv-cta:hover { background: var(--rb-accent); border-color: var(--rb-accent); }

.rb__dv-spec-h {
  margin-top: 18px;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--rb-ink-soft);
}
/* Two columns of label/value rows, not a two-column table: fifteen rows in one
   column would push the CTA off a laptop screen. */
.rb__dv-spec {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 24px;
  margin: 8px 0 0;
}
.rb__dv-spec-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--rb-line);
  font-size: 12.5px;
}
.rb__dv-spec-row dt { color: var(--rb-ink-soft); margin: 0; }
.rb__dv-spec-row dd { margin: 0; text-align: right; color: var(--rb-ink); }

.rb__dv-certlink {
  margin-top: 14px;
  font-size: 12.5px;
  color: var(--rb-accent);
  text-decoration: underline;
  text-underline-offset: 3px;
  align-self: start;
}
.rb__dv-certlink:hover { color: var(--rb-ink); }

/* ══════════════════════════════════════════════════════════════════════════
   §3.6 REVIEW VIEW
   ══════════════════════════════════════════════════════════════════════════ */

.rb__rv {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  gap: 40px;
  align-items: start;
}

/* Thumbs sit ABOVE the stage here — the opposite of the detail view's side
   rail. Deliberate: the review column is already tall and a side rail would
   narrow the one image the shopper is buying from. */
.rb__rv-media { display: flex; flex-direction: column; gap: 12px; }
.rb__rv-thumbs { display: flex; gap: 8px; }
/* The strip is omitted entirely when there is only one image; :empty covers
   the case where JS writes nothing rather than removing the node. */
.rb__rv-thumbs:empty { display: none; }
.rb__rv-thumb {
  position: relative;
  width: 64px;
  height: 64px;
  flex: none;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--rb-line);
  background: var(--rb-bone);
  transition: border-color var(--rb-dur) var(--rb-ease);
}
.rb__rv-thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.rb__rv-thumb:hover { border-color: var(--rb-accent); }
.rb__rv-thumb.is-active { border-color: var(--rb-ink); }
.rb__rv-stage {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--rb-bone);
}
.rb__rv-stage img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* The panel's padding is a variable for one reason: the dividers below are
   full-bleed by pulling out with negative margins EXACTLY equal to it, and at
   ≤560px the padding drops to 16px. Tying both to one token is what stops the
   rules from hanging 8px proud of the card on a phone. */
.rb__rv-panel {
  --rb-rv-pad: 24px;
  padding: var(--rb-rv-pad);
  background: var(--rb-paper);
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
}
.rb__rv-h {
  font-family: var(--rb-font-heading);
  font-size: 27px;
  font-weight: 500;
  line-height: 1.15;
  margin: 0;
}
.rb__rv-rule {
  height: 0;
  border-top: 1px solid var(--rb-line);
  margin: 18px calc(var(--rb-rv-pad) * -1);
}

.rb__rv-block { display: flex; flex-direction: column; gap: 8px; }
.rb__rv-block-head { display: flex; align-items: center; gap: 8px; }
/* A typographic glyph, not a drawn dot: painting a circle here would make it a
   sixth round thing in a component whose radius contract lists exactly five. */
.rb__rv-glyph {
  flex: none;
  font-size: 9px;
  line-height: 1;
  color: var(--rb-ink);
}
.rb__rv-label {
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--rb-ink-soft);
}
.rb__rv-links { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.rb__rv-links a {
  font-size: 11.5px;
  color: var(--rb-accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}
.rb__rv-links a:hover { color: var(--rb-ink); }

.rb__rv-name {
  font-family: var(--rb-font-heading);
  font-size: 23px;
  font-weight: 500;
  line-height: 1.15;
  color: var(--rb-accent);       /* the setting name is gold and links to the PDP */
}
.rb__rv-name:hover { color: var(--rb-ink); }
.rb__rv-price {
  font-family: var(--rb-font-heading);
  font-size: 30px;
  font-weight: 500;
  line-height: 1.05;
}
.rb__rv-price-note { font-size: 12.5px; color: var(--rb-ink-soft); }

.rb__rv-opts { display: flex; flex-direction: column; gap: 14px; margin-top: 6px; }
.rb__rv-opt-row { display: flex; flex-direction: column; gap: 8px; }
.rb__rv-opt-name { font-size: 12.5px; color: var(--rb-ink-soft); }
.rb__rv-opt-pills { display: flex; flex-wrap: wrap; gap: 8px; }

.rb__rv-field { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.rb__rv-flabel { font-size: 12.5px; color: var(--rb-ink-soft); }
/* The chevron is two clipped gradients rather than an inline SVG, so it takes
   its colour from --rb-accent instead of baking a gold hex into a data URI. */
.rb__rv-select {
  width: 100%;
  height: 46px;
  padding: 0 40px 0 14px;
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  background-color: var(--rb-paper);
  color: var(--rb-ink);
  font-size: 13.5px;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--rb-accent) 50%),
    linear-gradient(135deg, var(--rb-accent) 50%, transparent 50%);
  background-position: calc(100% - 21px) calc(50% - 1px), calc(100% - 15px) calc(50% - 1px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
.rb__rv-select:hover { border-color: var(--rb-accent); }
.rb__rv-select[disabled] { opacity: .5; cursor: not-allowed; }
.rb__rv-check {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  font-size: 12.5px;
  color: var(--rb-ink-soft);
  cursor: pointer;
}
.rb__rv-check input { width: 16px; height: 16px; accent-color: var(--rb-accent); cursor: pointer; }
.rb__rv-input {
  width: 100%;
  height: 46px;
  padding: 0 14px;
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  background: var(--rb-paper);
  color: var(--rb-ink);
  font-size: 13.5px;
}
.rb__rv-input:hover { border-color: var(--rb-accent); }

.rb__rv-cert { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--rb-ink-soft); }
.rb__rv-spec { font-size: 13px; line-height: 1.5; }
.rb__rv-dprice {
  font-family: var(--rb-font-heading);
  font-size: 30px;
  font-weight: 500;
  line-height: 1.05;
}
.rb__rv-ships { font-size: 12px; line-height: 1.5; color: var(--rb-ink-soft); }

/* Subtotal is right-aligned and the CTA is full-width directly beneath it —
   the price lands where the eye already is, then the button spans the panel. */
.rb__rv-total {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 16px;
  margin-top: 20px;
}
.rb__rv-total-l {
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--rb-ink-soft);
}
.rb__rv-total-v {
  font-family: var(--rb-font-heading);
  font-size: 30px;
  font-weight: 500;
  line-height: 1.05;
  text-align: right;
}
.rb__rv-add {
  width: 100%;
  min-height: 50px;
  margin-top: 14px;
  background: var(--rb-ink);
  color: var(--rb-paper);
  border: 1px solid var(--rb-ink);
  border-radius: var(--rb-radius);
  font-size: 12px;
  letter-spacing: .16em;
  text-transform: uppercase;
  transition: background-color var(--rb-dur) var(--rb-ease),
              border-color var(--rb-dur) var(--rb-ease);
}
.rb__rv-add:hover { background: var(--rb-accent); border-color: var(--rb-accent); }
.rb__rv-add[disabled] { opacity: .5; cursor: not-allowed; }
.rb__rv-add[disabled]:hover { background: var(--rb-ink); border-color: var(--rb-ink); }
.rb__rv-note {
  margin-top: 10px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--color-sale);   /* the theme's one error tone — base.css uses it bare */
}
.rb__rv-note:empty { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   §3.7 MOBILE SHEET PARTS THAT MUST NOT LIVE IN A MEDIA QUERY
   ══════════════════════════════════════════════════════════════════════════ */

/* TRAP #10. JS creates a DONE button inside every filter card except shape, on
   every viewport, because it has no idea how wide the screen is. If this rule
   were written inside the ≤768px block, desktop would inherit the browser
   default (visible) and every filter card on a laptop would sprout a 46px ink
   button. Base, outside every media query, deliberately. */
.rb__done { display: none; }

/* The scrim is appended to <body>, NOT to .rb — so it cannot be scoped under
   .rb (a descendant selector would never match) and it cannot read the --rb-*
   aliases either, which is why it reaches for --color-text directly. The rgba
   line is a fallback for engines without color-mix; both are the ink at 45%. */
.rb__scrim {
  position: fixed;
  inset: 0;
  z-index: 1000;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  cursor: pointer;
  background: rgba(33,29,24,.45);
  background: color-mix(in srgb, var(--color-text, #211D18) 45%, transparent);
}

/* ══════════════════════════════════════════════════════════════════════════
   §3.9 SKELETONS
   ══════════════════════════════════════════════════════════════════════════ */

/* Eight of these go into the results grid while a fetch is in flight. They
   inherit the grid's sizing, so they land exactly where the cards will. */
.rb__skel {
  aspect-ratio: 1 / 1;
  border: 1px solid var(--rb-line);
  border-radius: var(--rb-radius);
  background: linear-gradient(100deg,
    var(--rb-bone) 18%,
    var(--rb-paper) 38%,
    var(--rb-bone) 58%);
  background-size: 220% 100%;
  animation: rb-sweep 1.2s linear infinite;
}
/* The same sweep, per image. Cards are inserted long after load, so JS wires
   this with a MutationObserver; CSS only owns the two end states. */
.rb__card-media.is-loading {
  background: linear-gradient(100deg,
    var(--rb-bone) 18%,
    var(--rb-paper) 38%,
    var(--rb-bone) 58%);
  background-size: 220% 100%;
  animation: rb-sweep 1.2s linear infinite;
}
.rb__card-media.is-loaded { animation: none; background: var(--rb-bone); }

@keyframes rb-sweep {
  from { background-position: 120% 0; }
  to   { background-position: -120% 0; }
}
@keyframes rb-sheet-up {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

/* ══════════════════════════════════════════════════════════════════════════
   §3.8 BREAKPOINTS — authored in DESCENDING width order.
   1000 → 820 → 768 → 560. The reference authored 768 last and inherited a
   collision: the 560 rules landed first in the cascade and the mobile filter
   model then partially re-overrode them on phones. Descending order means a
   narrower query always wins on source order, which is the only ordering that
   composes.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── ≤1000px ─────────────────────────────────────────────────────────────
   Both two-up layouts lose their side column. The detail view keeps its rail
   beside the stage — only the info column drops. */
@media (max-width: 1000px) {
  .rb__rv {
    grid-template-columns: minmax(0, 1fr);
    gap: 32px;
  }
  .rb__dv-body { grid-template-columns: 72px minmax(0, 1fr); }
  .rb__dv-info { grid-column: 1 / -1; max-width: none; }
}

/* ── ≤820px ─────────────────────────────────────────────────────────────
   Tablet. The filter grid goes single-column, and both fixed-count grids step
   down one notch. The step rail keeps its chevrons but sheds the thumbnail —
   40px of image inside a 33%-wide segment leaves no room for the price. */
@media (max-width: 820px) {
  .rb__fgrid { grid-template-columns: minmax(0, 1fr); }
  .rb__shapes { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .rb__aip-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Three chevrons across a tablet leaves ~230px of usable width per segment.
     The numeral, the two label lines and the icon hold the first row; the chip
     is given the whole second row rather than being squeezed in beside a
     nowrap label it cannot push. The spec drops the thumbnail here for the
     same reason — price and links stay, and now they are readable. */
  .rb__step {
    flex-wrap: wrap;
    padding: 10px 14px 10px calc(var(--rb-chev) + 12px);
    gap: 4px 10px;
  }
  .rb__step:first-child { padding-left: 16px; }
  .rb__step-num { font-size: 22px; }
  .rb__step-l1,
  .rb__step-l2 { font-size: 10px; }
  .rb__step-chip { flex-basis: 100%; }
  .rb__step-thumb { display: none; }
  .rb__step-chip-price { font-size: 15px; }

  .rb__dv-title { font-size: 26px; }
}

/* ── ≤768px ─────────────────────────────────────────────────────────────
   THE MOBILE FILTER MODEL (§3.7). This is the only breakpoint that changes the
   interaction, not just the layout: a filter card stops being a card and
   becomes a 44px head row whose value is summarised in gold on the right.
   Tapping one promotes it to a bottom sheet over a scrim. */
@media (max-width: 768px) {
  .rb__fgrid { gap: 8px; }

  /* Collapsed: the head IS the card. */
  .rb__fcard { padding: 0 14px; }
  .rb__fhead {
    min-height: 44px;
    margin-bottom: 0;
    cursor: pointer;
  }
  .rb__fbody { display: none; }
  .rb__fsum {
    display: block;
    margin-left: auto;
    max-width: 60%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12px;
    color: var(--rb-accent);
    text-align: right;
  }
  /* The colour card's White|Fancy segment cannot sit in a collapsed head next
     to the summary value — it belongs to the open sheet. */
  .rb__fhead-ctl { display: none; }

  /* THE SHAPE CARD IS EXEMPT. Its tiles stay inline and visible, its head is
     not a tap target, and it gets no summary value — shape is the filter
     shoppers reach for first and burying it behind a sheet costs conversions.
     Written after the collapse rules so it wins on source order. */
  .rb__fcard--wide { padding: 14px; }
  .rb__fcard--wide .rb__fhead { min-height: 0; margin-bottom: 12px; cursor: default; }
  .rb__fcard--wide .rb__fbody { display: block; }
  .rb__fcard--wide .rb__fsum { display: none; }
  .rb__fcard--wide .rb__fhead-ctl { display: flex; }

  /* Opened: pinned to the bottom of the viewport. One of the two
     fixed-position declarations this component is allowed — the other is the
     scrim it sits on. */
  .rb__fcard.is-sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1001;
    max-height: 82vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 18px 18px calc(18px + env(safe-area-inset-bottom, 0px));
    /* The one non-zero radius in the whole component: 14px on the top corners
       only, so the sheet reads as rising off the bottom edge. */
    border-radius: 14px 14px 0 0;
    animation: rb-sheet-up 280ms var(--rb-ease);
  }
  .rb__fcard.is-sheet .rb__fhead {
    min-height: 0;
    margin-bottom: 14px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--rb-line);
    cursor: default;
  }
  .rb__fcard.is-sheet .rb__fbody { display: block; }
  .rb__fcard.is-sheet .rb__fsum { display: none; }
  .rb__fcard.is-sheet .rb__fhead-ctl { display: flex; }

  /* DONE only exists visually inside an open sheet. Base is display:none, far
     above this block. */
  .rb__fcard.is-sheet .rb__done {
    display: block;
    width: 100%;
    min-height: 46px;
    margin-top: 18px;
    background: var(--rb-ink);
    color: var(--rb-paper);
    border: 1px solid var(--rb-ink);
    border-radius: var(--rb-radius);
    font-size: 12px;
    letter-spacing: .16em;
    text-transform: uppercase;
  }

  /* Tools and chips stop hugging the right edge once the grid is one column. */
  .rb__ftools { justify-content: space-between; }
}

/* ── ≤560px ─────────────────────────────────────────────────────────────
   Phone. The step rail gives up on chevrons entirely — three interlocking
   segments at 320px are 90px each and the clip-paths eat 22 of them. */
@media (max-width: 560px) {
  .rb__rail { display: block; background: transparent; }
  .rb__step {
    width: 100%;
    min-height: 56px;
    margin-left: 0;
    padding: 10px 16px;
    clip-path: none;
  }
  .rb__step:first-child { padding-left: 16px; clip-path: none; }
  .rb__step:last-child { clip-path: none; }
  .rb__step + .rb__step { margin-left: 0; border-top: 1px solid var(--rb-line); }

  .rb__shapes { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .rb__shape { padding: 8px 2px; }

  /* Panel padding tightens — and the full-bleed rules follow it, because both
     read the same token. */
  .rb__rv-panel { --rb-rv-pad: 16px; }
  .rb__rv-price,
  .rb__rv-dprice,
  .rb__rv-total-v { font-size: 26px; }

  /* Detail view collapses to one column: stage first, then the rail reflowed
     into a horizontal 60px strip, then the info. */
  .rb__dv-body { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .rb__dv-stage { order: 1; }
  .rb__dv-rail {
    order: 2;
    flex-direction: row;
    gap: 8px;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .rb__dv-rail::-webkit-scrollbar { display: none; }
  .rb__dv-tab,
  .rb__dv-cert { width: 60px; height: 60px; }
  .rb__dv-info { order: 3; grid-column: auto; }
  .rb__dv-spec { grid-template-columns: minmax(0, 1fr); }
  .rb__dv-title { font-size: 24px; }

  .rb__pager { gap: 14px; }
}

/* ── motion opt-out ────────────────────────────────────────────────────────
   base.css already ships the theme's one global reduced-motion rule, but it
   only clamps animation-duration to .001ms — an infinite sweep collapsed to a
   near-zero duration still leaves the skeleton frozen at the END of its
   gradient, and the sheet still snaps in from a translate. These two need the
   animation cancelled outright, not shortened.
   It wins: base.css's rule is `*` (specificity 0,0,0) with !important; these
   are class selectors (0,1,0 / 0,2,0) with !important, and among !important
   declarations the more specific one takes it regardless of file order. */
@media (prefers-reduced-motion: reduce) {
  .rb__skel {
    animation: none !important;
    background: var(--rb-bone) !important;
  }
  .rb__card-media.is-loading {
    animation: none !important;
    background: var(--rb-bone) !important;
  }
  .rb__fcard.is-sheet { animation: none !important; }
}
