/** Shopify CDN: Minification failed

Line 959:48 Unexpected "`"
Line 964:71 Unterminated string token

**/
.footer__title {
  font-weight: bold;
  font-size: var(--body-font-size) !important;
}

.type-banner .promo-grid__container {
  align-items: center;
  height: 100%;
}

.type-banner__text {
  flex-direction: column;
  gap: 1rem;
}

.flex-grid__item--75 .type-banner__text,
.flex-grid__item--100 .type-banner__text {
  flex-direction: row;
}

.type-banner__text h2,
.type-banner__text p {
  margin: 0;
}

.type-banner__text .element-button {
  margin: 0;
}

/* Collection hero scrim + ink. Brandon 2026-09-03: "Contrast check the font colors in the
   hero and optimize if need be."

   MEASURED BEFORE, with a11y/qc_bb_hero_contrast.js - the worst 5% of the pixels each glyph
   actually sits on, not an ancestor-walk guess (/collections/floating-shelf-brackets @1440):
     h1, white                     1.51:1  needs 3:1    FAIL
     breadcrumb "Home", #1c1d1d     4.1:1  needs 4.5:1  FAIL
     breadcrumb "Collections"      4.83:1  needs 4.5:1  passed only by where it landed

   Two causes:
   (1) The breadcrumb component exposes no colour hook - element.breadcrumbs.liquid does
       `--_color-primary: var(--color-primary)`, the near-black #1c1d1d. So the hero carried
       WHITE title ink and NEAR-BLACK breadcrumb ink over the same photograph, and no scrim
       can satisfy both at once.
   (2) The stock scrim is `linear-gradient(45deg, <colour>, transparent 50%)` - a bottom-LEFT
       CORNER wash. Its depth under any given glyph depends on how far right the title runs,
       so a longer title walks straight out of it. Measured across three collections and two
       widths, a 45deg scrim swung between 3.5:1 and 12.8:1 for that reason.

   Fix: one ink colour, and a VERTICAL scrim. The text band is always at the bottom of the
   hero (.collection-hero__content is align-items: flex-end), so a `to top` gradient's depth
   no longer depends on title length or viewport width. Setting --color-primary here also
   feeds the breadcrumb's "/" divider, which derives itself at 30% of that same colour.

   THE STOPS ARE IN PIXELS, NOT PERCENTAGES - and that is the whole point. A percentage
   version of this gradient shipped first and was WRONG: percentages are of the HERO, which is
   400px on desktop but 200px on mobile, while the text band is roughly the same physical
   height either way. On the short hero the text therefore sat proportionally much higher, up
   where the gradient had already faded, and measured 2.48:1 and 2.81:1 on the breadcrumbs at
   390px - a fail shipped behind a clean desktop pass. Pixel stops make the scrim depth track
   the TEXT BAND, which is the thing that needs covering. Do not "tidy" these back into
   percentages.

   MEASURED AFTER, all 12 combinations of {390, 768, 1024, 1440} x the three collections that
   have a hero photo - every one PASS, worst case 6.93:1 against a 4.5:1 requirement:
     390 : 7.72 / 6.93 / 7.34      768 : 8.86 / 8.86 / 13.13
     1024: 7.11 / 7.23 / 10.80     1440: 7.11 / 7.11 / 10.32
   A 45deg variant was measured and REJECTED - it passed on average but bottomed out at 3.5:1
   on floating-cabinet-brackets @1024.

   The `--color-secondary: rgb(0,0,0, 0.6)` this rule used to carry is DELETED, not moved. It
   only ever fed the stock gradient that is overridden just below. Proven dead by substitution
   rather than by reading the CSS: restoring it to the root #ffffff rendered pixel-identical
   (ImageChops diff bbox None). Do not re-add it - it would be a misleading no-op.

   If a future Expanse upgrade resets this, re-run:
     MSYS_NO_PATHCONV=1 node Desktop/bracket-burl/a11y/qc_bb_hero_contrast.js \
       /collections/floating-shelf-brackets <outdir> 1440
   and check QC_HERO_NEGATIVE=1 still FAILS before believing a green result. */
.collection-hero__content.overlay {
  --element-text-color: rgb(255,255,255);
  --color-primary: #ffffff;
}

.collection-hero__content.overlay::after {
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.82) 0px,
    rgba(0, 0, 0, 0.66) 110px,
    rgba(0, 0, 0, 0.24) 190px,
    rgba(0, 0, 0, 0) 280px
  );
}

.element-input:not(.element-input--transparent) {
  --element-input-color-primary: var(--root-color-primary, #000);
  --element-input-color-secondary: var(--root-color-secondary, #fff);
}

.newsletter__input-group .element-input~button {
  --element-button-height: initial;
}

.newsletter-section .element-text.element-text--heading-h0 {
  font-size: var(--element-text-font-size--heading-2xl);
  line-height: var(--element-text-line-height--heading-2xl);
}

.newsletter-section .element-text.element-text--heading-h1 {
  font-size: var(--element-text-font-size--heading-xl);
  line-height: var(--element-text-line-height--heading-xl);
}

.newsletter-section .element-text.element-text--heading-h2 {
  font-size: var(--element-text-font-size--heading-lg);
  line-height: var(--element-text-line-height--heading-lg);
}

.newsletter-section .element-text.element-text--heading-h3 {
  font-size: var(--element-text-font-size--heading-md);
  line-height: var(--element-text-line-height--heading-md);
}

.text-with-icons__block-text a,
.newsletter-section__content a {
  text-decoration: underline;
}

/* ==========================================================================
   JIS customisations re-applied for Expanse 10.0.0            2026-08-12
   Everything above this line is stock 10.0 (byte-identical to the stock
   portion of our 9.1 file, verified before appending).

   Where 10.0 renamed the markup a rule targeted, the new selector is used and
   the old one is noted. The variant picker is the only part that moved: 10.0
   replaced .variant-button-wrap / label.element-radio with the kit component
   system (.kit-fieldset / label.kit-radio), so the option-grid rules below are
   re-expressed, NOT copied.
   ========================================================================== */

/* Footer color breakup: was one flat black slab (newsletter band + legal band
   both on scheme 9's black). Newsletter band now uses navy with a bronze seam,
   so the footer reads as two intentional zones instead of one giant block. */
.footer__section--border:has(.footer__subscribe) {
  background: #232F3E;
  border-top: 2px solid #886848;
  padding: 22px 0;
}

.footer__section--border:has(.footer__subscribe) .footer__subscribe,
.footer__section--border:has(.footer__subscribe) a {
  color: #fff;
}

.newsletter__input-group .element-button {
  --element-button-color-primary: #886848 !important;
  --element-button-color-secondary: #fff !important;
  background: #886848 !important;
  color: #fff !important;
}

/* Newsletter band (navy, matches Module 07/resources above it) hits the legal
   band's true black (#000) with no transition â€” same seam class as nav-into-hero
   at the top of the page. Same fix: gradient from the navy at the top edge into
   the real black by 200px down, same technique, mirrored at the bottom of the page. */
.site-footer .footer__section--menus {
  padding-top: 36px;
  background: linear-gradient(to bottom, #232F3E, #000000 200px);
}

.site-footer > .footer__section:not(.footer__section--menus) {
  padding: 22px 0;
}

/* Cart drawer shipping/tax note rendered under Brandon's 13px readability floor
   (was inheriting --element-text-font-size--body-sm, ~11.2px). Scoped here
   instead of the shared token so badges/breadcrumbs/pagination/chips elsewhere
   that also use that token are untouched. */
.cart__item-row--footer small {
  font-size: 13px;
}

/* Cart drawer subtotal cents (element.price's global sup{font-size:60%} rule
   shrank the "00" in $0.00 to ~8.4px, under the 13px floor). Scoped to the
   subtotal price only via the data-subtotal attribute passed from
   header.cart-drawer.liquid, so product/PDP prices elsewhere keep the
   60% superscript treatment. */
.element-price[data-subtotal] sup {
  font-size: 13px;
  top: -0.2em;
}

/* Policy pages (Admin > Settings > Policies) wrap every list item as
   <li><p>text</p></li>. The theme's .rte rule gives every <p> a 25px
   bottom margin meant for real paragraphs, so each bullet/number inherited
   full paragraph spacing instead of tight list spacing. Scoped to <p>
   nested in <li> so real paragraph spacing elsewhere in rich text is untouched. */
.rte li p {
  margin-bottom: 0;
}

/* ==========================================================================
   PDP: buy button above the fold                                  2026-08-06
   Bracket & Burl. Ported to Expanse 10.0 on 2026-08-12: layout-stack shadow
   DOM and element-select quantity are unchanged, but the option pills are now
   kit radios (see the block marked "option pills" below).

   Pairs with the script appended to snippets/section.main-product.liquid.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Stack rhythm.
   .product-single__meta is a <layout-stack>: :host{display:contents} plus a
   flex container inside a SHADOW ROOT whose gap is var(--_gap) ->
   var(--layout-stack-gap-size-sm) -> 16px. The shadow root is unreachable
   from here, but custom properties inherit through it, so re-declaring the
   token on the host is the supported way to change the gap.
   Nested stacks (the title block, the price block) re-inherit the token, so
   put it back for every descendant stack - only the top-level rhythm changes.

   NOTE 10.0: layout.stack is untouched by the kit migration - the kit system
   introduced a SEPARATE .kit-stack (a plain flex div, no shadow root) used
   only inside the variant picker. Both exist. This rule still targets
   layout-stack and is unaffected.
   -------------------------------------------------------------------------- */
.product-single__meta {
  --layout-stack-gap-size-sm: 10px;
}
.product-single__meta layout-stack {
  --layout-stack-gap-size-sm: 16px;
}

/* --------------------------------------------------------------------------
   2. Option pills, compact and capped.                    RE-EXPRESSED FOR 10.0
   9.1 wrapped each option in .variant-button-wrap and drew each value as
   label.element-radio. 10.0 renders one <fieldset class="kit-fieldset"> per
   option, with a <legend class="kit-fieldset__legend"> and the values as
   label.kit-radio inside a .kit-stack flex row. Neither 9.1 class exists in
   10.0 - verified by string search, both return zero hits - so these are new
   selectors, not a copy.

   The pills' container is now ALREADY a wrapping flex row (.kit-stack--wrap),
   so the script tags that existing element with .jis-grid rather than building
   a div and reparenting - one less place for the picker's re-render to fight
   us. The declarations below still stand on their own so the rule holds if the
   class ever lands on a plain div again.
   13px is our readability floor.
   -------------------------------------------------------------------------- */
.product-single__meta .kit-fieldset__legend {
  margin-block-end: 5px;
}
.jis-grid label.kit-radio {
  margin: 0;
}
.jis-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  align-items: flex-start;
}

/* An option with exactly one value is dead UI - the script tags it .jis-solo.
   display:none (not removal) so the auto-selected radio stays in the DOM and
   the theme's variant resolution is unaffected.
   10.0: the tagged element is the <fieldset>, not .variant-button-wrap. */
.kit-fieldset.jis-solo {
  display: none;
}

/* The cap lives on .jis-grid, never on the <fieldset>: Chrome ignores
   overflow:hidden on a fieldset and the pills spill over the next option's
   label. Still true in 10.0, and now doubly relevant - in 10.0 the option
   wrapper IS a fieldset. */
.jis-grid.jis-capped {
  overflow: hidden;
}
.jis-more {
  display: block;
  margin: 6px 0 0;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  font-size: 13px;
  line-height: 1.3;
  text-align: left;
  text-decoration: underline;
  cursor: pointer;
  opacity: 0.75;
}
.jis-more:hover,
.jis-more:focus-visible {
  opacity: 1;
}

/* Selected-option indicator - TRIED, SHIPPED, REVERTED SAME DAY 2026-08-12.
   Kept as a comment because the defect it targeted is still open and the next
   attempt must not repeat this mistake.

   The problem it addressed (gauntlet MAJOR 1): stock 10.0 marks the chosen value
   with fill alone - rgb(217,217,217) on rgb(255,255,255) is 1.41:1, where WCAG 2.2
   SC 1.4.11 wants 3:1 for a state indicator. Live 9.1 is 1.39:1, so it is a
   standing defect, not upgrade damage.

   What was tried:
     .jis-grid label.kit-radio input[type='radio']:checked ~ .kit-button
       { box-shadow: inset 0 0 0 2px #131a22; }

   Why it was reverted - Brandon on live: "When I click to a different wood species
   the first one is not deselected." The ring reads live `:checked`, which flips the
   instant a radio is tapped. 10.0's grey selected-fill does NOT - it comes from the
   server-rendered section markup, so it only moves when the section HTML swaps
   (~330ms measured, longer on a slow phone). In that window the OLD pill is still
   filled while the NEW one is already ringed: two pills look selected at once.

   The ring did not create that lag - it made an existing lag visible. On a
   180-variant configurator a visibly wrong selection is worse than the
   low-contrast state it replaced.

   For the next attempt: drive the indicator from the SAME source as the fill so
   the two can never disagree - i.e. restyle whatever the server-rendered selected
   state already is, rather than adding a second, faster mechanism on :checked.
   Verify by changing a variant and asserting exactly ONE pill reads as selected at
   +0ms, +150ms and +2s.

   ---------------------------------------------------------------------------
   SECOND ATTEMPT, SHIPPED 2026-08-12. THE DIAGNOSIS ABOVE IS WRONG - keep reading.

   The fill is NOT server-rendered and does NOT lag. It is
   `.kit-radio__input:checked + .kit-button` (head.styles.kit-tokens.liquid:66) - the
   LIVE pseudo-class, the same source any ring would use. Both flip at t=0. Measured.

   What actually happens is `kit.button.liquid:101`:
   `transition: background var(--kit-transition-normal)`, 150ms. Mid-transition BOTH
   pills report an intermediate oklab() background - the old one fading out, the new
   one fading in - and against a 1.30:1 fill a half-faded pill is indistinguishable
   from a selected one. That crossfade is what Brandon saw. The `checked` ATTRIBUTE
   does lag until the section swaps (~400ms), but nothing is styled from it, so that
   lag is invisible.

   So the fix is not "drive it from the same source" - it already was. It is: make the
   indicator instant and high-contrast, and stop the fill crossfading.

   `box-shadow`, not `outline`, for one measured reason: box-shadow is absent from the
   transition list, so the ring flips with no interpolation. (An earlier draft of this
   comment claimed outline had to stay free for the focus ring. Checked, and that is
   not true here - see the focus note below.)

   SEPARATE PRE-EXISTING DEFECT FOUND WHILE DOING THIS - not fixed, not caused by it.
   Option pills have NO visible keyboard focus indicator (WCAG 2.2 SC 2.4.7). Measured
   on an unchecked pill with `:focus-visible` matching and the radio as
   document.activeElement: the button computes `outline-style: none` and its box-shadow
   is unchanged. The theme's focus ring lives on `.kit-radio__indicator`
   (kit.radio.liquid), but the variant picker passes custom `content`, so that indicator
   element never renders. `outline` on these buttons is therefore unused and available
   if someone wants to add a focus ring - that is a separate visual change needing its
   own sign-off, so it is flagged, not silently shipped.

   Verified on the rendered draft, Wood Species (15 values) and Depth (6), sampled at
   +0/+50/+150/+400/+900/+2000ms: exactly ONE pill reads as filled AND ringed at every
   sample, t=0 included. Ring is opaque rgb(19,26,34) - no alpha, so no compositing
   trap - giving 17.4:1 against page white and 13.4:1 against the pill's own fill,
   where SC 1.4.11 asks 3:1. */
/* The ring colour is the theme's own primary token, NOT a hand-picked hex. The first
   attempt used #131a22, which is a fourth near-black in a component whose fill
   (`color-mix(in srgb, #1c1d1d 15%, transparent)`) and text (`rgb(28,29,29)`) both
   already derive from `--kit-color-primary` = #1c1d1d. Caught by the gauntlet's brand
   pass. A token also ports to CJ and CMS unchanged, where a hex would not. */
kit-variant-picker .kit-radio__input:checked + .kit-button:not(:disabled) {
  box-shadow: inset 0 0 0 2px var(--kit-color-primary);
}

/* A matching `:checked + .kit-swatch` ring was written and then REMOVED: no product in
   the published B&B catalogue can render a swatch. Checked every option name across
   /products.json - Depth, Length, Mounting Substrate, Quantity of Kits, Quantity of
   Pairs, Thickness, Title, Wood Species - and swatches need either `value.swatch` or an
   option matching `legacy_swatch_triggers` (default 'color,colour'). None qualify, so
   the rule was dead CSS shipping untested. If a colour/finish option is ever added,
   add the ring back and VERIFY it - an offset ring on a round swatch is not the same
   geometry problem as an inset ring on a rectangular pill.

   Kills the 150ms crossfade that made the outgoing pill still read as selected.
   Both pills style from the same live :checked, so with no interpolation the
   handover is atomic. The .kit-swatch selector is retained here deliberately: it costs
   nothing and a future swatch should not crossfade either. */
kit-variant-picker .kit-radio .kit-button,
kit-variant-picker .kit-radio .kit-swatch {
  transition: none;
}

/* --------------------------------------------------------------------------
   3. Buy box.
   The form is already flex/column with a 10px gap. The shipping-policy note
   ships ABOVE Add to cart, which costs 26px of the first screen for a line
   nobody reads before deciding - move it under the buttons.

   .shopify-product-form is generated by Shopify's {% form 'product' %} tag,
   not by theme markup, so it does not appear in theme source in either
   version. 10.0 still uses that tag (snippets/form.product.liquid). Confirm
   on the rendered page, not by grep.
   -------------------------------------------------------------------------- */
.product-single__meta .shopify-product-form > .product__policies {
  order: 9;
  margin: 0;
}
.product-single__meta .shopify-product-form > .shopify-payment-terms {
  order: 10;
}

/* The number is self-evidently the price; the theme's "Price" heading just
   costs a line. The visually-hidden "Regular price" stays inside the block, so
   screen readers lose nothing. Matches Cabinet Jewels and Centurymade.
   Verified: block.product-price.liquid still emits .label.variant__label in 10.0. */
block-price > .label.variant__label {
  display: none;
}

/* Add to cart and Shop Pay must read as one pair: same width, height, radius.
   Height and radius are driven by script off the theme's own --size-12 token
   so both buttons land on the value the theme already intended for the wallet
   button (48px), which also clears the 44px touch-target floor. */
.product-single__meta .shopify-payment-button,
.product-single__meta .add-to-cart {
  width: 100%;
}

/* Add to cart must out-weigh selection state. Gauntlet MAJOR-2, 2026-08-12
   (GAUNTLET-item2-pill-indicator-2026-08-12.md): once the selected option pills gained
   a 2px near-black ring, buy-box weight ranked Buy-with-Shop > three ringed pills >
   Add to cart, which carried only a 1px 20%-alpha inset hairline. The primary CTA was
   the faintest interactive element in the column.

   The cause is a modifier, not a missing style: the button ships as
   `element-button element-button--inverted`, and `--inverted` swaps the theme's own
   primary button (solid `--element-button-color-primary` #1c1d1d with #ffffff text)
   into white-with-dark-text. Un-inverting it restores the theme's intended primary
   treatment rather than inventing a colour - a filled block always out-weighs an
   outlined pill, so the hierarchy resolves without touching the ring.

   Tokens, not hexes, for the same reason as the pill ring: this must port to CJ and CMS.
   `:not([disabled])` so the sold-out / unavailable state keeps the theme's own
   disabled styling and does not read as a live CTA. */
.product-single__meta .add-to-cart.element-button--inverted:not([disabled]),
.product-single__meta .add-to-cart.element-button--inverted:not([disabled]):hover {
  background: var(--element-button-color-primary, #1c1d1d);
  box-shadow: none;
}

.product-single__meta .add-to-cart.element-button--inverted:not([disabled]),
.product-single__meta .add-to-cart.element-button--inverted:not([disabled]) span,
.product-single__meta .add-to-cart.element-button--inverted:not([disabled]):hover span {
  color: var(--element-button-color-secondary, #fff);
}

/* The sticky bar's button is brought onto the same token further down the file, in the
   .jis-stickybuy__btn block - NOT duplicated here. An earlier draft of this change added
   a second .jis-stickybuy__btn rule at this position and it silently lost on source
   order to the original ~270 lines below, at equal specificity. */

/* --------------------------------------------------------------------------
   4. Quantity, Amazon style.
   The theme ships a native <select> reading "Quantity: N" - right idea, but
   it stops at 10 and a native popup's height is browser-controlled, so it
   cannot be capped to five rows. The script swaps in a listbox that lists
   1-30 and shows five, and moves it onto its own line directly above the
   buttons (Amazon's arrangement) instead of sharing the price row.
   All of block-quantity-selector / .price-quantity-row / [data-quantity-select]
   / .element-select__wrapper / .product__quantity survive in 10.0.
   -------------------------------------------------------------------------- */
.price-quantity-row__quantity:empty {
  display: none;
}
/* Products that sell by pack tier ("Qty Pairs" / "Qty Kits" variant options)
   get no quantity control of ours at all - the pack option is the quantity, and
   a second numeric selector under it reads as two quantity pickers. The script
   adds this class; the hidden input[name=quantity] rides along at 1 and still
   submits from inside a display:none container. */
.price-quantity-row__quantity.jis-qty-hidden,
block-quantity-selector.jis-qty-hidden {
  display: none;
}
.jis-qtyline {
  order: -1;
}
/* The theme renders the native control in one of two shapes depending on the
   product: a <select> (.element-select__wrapper) or a -/+ stepper wrapped in
   .product__quantity. Only the select was hidden, so on every stepper product
   our listbox and the theme's stepper both showed - two quantity boxes stacked.
   Found on the PLY-LOC shelves 2026-08-10. Hide both shapes. The submitting
   input[name=quantity] lives inside .product__quantity and still posts from
   inside a display:none container, same as the pack-SKU case above. */
.jis-qtyline .element-select__wrapper,
.jis-qtyline .product__quantity {
  display: none;
}
.jis-qty {
  position: relative;
  display: inline-block;
}
.jis-qty__trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  padding: 0 14px;
  font-size: 13px;
  line-height: 1.2;
  text-align: left;
  color: inherit;
  background: none;
  /* 2px near-black, matching a SELECTED option pill.        2026-08-21
     Brandon: make the quantity box carry the same border weight as the
     Depth / Wood Species / Thickness boxes. Measured first: the box was
     already 1px, marginally DARKER than an unselected pill
     (rgba(0,0,0,.28) vs #1c1d1d @ 15%), so matching the unselected
     hairline would have made it lighter, not heavier. The weight he is
     pointing at is the selected pill's ring
     (`kit-variant-picker .kit-radio__input:checked + .kit-button`, ~200
     lines up) - 2px, opaque, on --kit-color-primary.

     A real border rather than that rule's inset box-shadow: an inset shadow
     here would be drawn inside the 14px padding and read as a gap. Measured
     the geometry cost first - the button computes box-sizing:border-box, so
     min-height pins the height at 40px either way; the shrink-to-fit width
     grows 2px (110.9 -> 112.9), which is free, the control sits alone on its
     own line above Add to cart. The
     token, not a hex, for the same reason as the pill ring - it ports.
     B&B ONLY: Brandon scoped this to Bracket & Burl, so it is deliberately
     NOT mirrored to Cabinet Jewels or Century Made. */
  border: 2px solid var(--kit-color-primary, #1c1d1d);
  border-radius: var(--element-button-radius, 2px);
  cursor: pointer;
}
.jis-qty__caret {
  width: 0;
  height: 0;
  margin-top: 2px;
  border: 4px solid transparent;
  border-top-color: currentColor;
}
.jis-qty__list {
  position: absolute;
  z-index: 30;
  left: 0;
  top: calc(100% + 4px);
  min-width: 100%;
  margin: 0;
  padding: 4px 0;
  list-style: none;
  max-height: 170px; /* ~5 rows; the script measures the real figure at open */
  overflow-y: auto;
  background: var(--color-background, #fff);
  border: 1px solid rgba(0, 0, 0, 0.18);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
  text-align: left;
}
.jis-qty__list[hidden] {
  display: none;
}
.jis-qty__list li {
  padding: 7px 16px;
  font-size: 13px;
  line-height: 1.3;
  white-space: nowrap;
  cursor: pointer;
}
.jis-qty__list li.is-active {
  background: rgba(0, 0, 0, 0.07);
}
.jis-qty__list li[aria-selected='true'] {
  font-weight: 600;
}

/* --------------------------------------------------------------------------
   4b. Narrative lede.
   The product's own hook and Overview prose, lifted above the options. Clamped
   to three lines rather than word-capped: Add to cart has to stay above the
   760px fold and the worst product leaves only ~102px, so the lede's cost has
   to be a constant, not a function of how long the copy happens to be.
   -------------------------------------------------------------------------- */
.product-single__meta .jis-lede {
  margin: 0;
}
.product-single__meta .jis-lede h2,
.product-single__meta .jis-lede h3 {
  font-size: 18px;
  line-height: 1.3;
  margin: 0 0 5px;
}
/* Lede matches body copy at every breakpoint. Bound to the theme's own body
   token rather than a hardcoded px so it tracks the type scale modifier instead
   of stranding at a fixed size the way the original 14px did (Brandon
   2026-08-10). Resolves to 18px desktop / 16px mobile. */
.product-single__meta .jis-lede__body {
  font-size: var(--element-text-font-size--body-md, 18px);
  line-height: 1.5;
}
.product-single__meta .jis-lede__body p {
  margin: 0 0 6px;
}
.product-single__meta .jis-lede__body > *:last-child {
  margin-bottom: 0;
}
.product-single__meta .jis-lede__body.is-clamped {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.jis-lede__more {
  display: block;
  margin: 4px 0 0;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  font-size: 13px;
  line-height: 1.3;
  text-align: left;
  text-decoration: underline;
  cursor: pointer;
  opacity: 0.75;
}
.jis-lede__more:hover,
.jis-lede__more:focus-visible {
  opacity: 1;
}

/* Specifications, relocated above the buy box. Sized as reference data, not as
   narrative: smaller than the lede and tightly set, so it informs without
   competing with the story above it.
   (The move itself is currently disabled - Brandon reversed it 2026-08-07 -
   but the styling is kept so re-enabling is a one-line change.) */
.product-single__meta .jis-specs {
  margin: 12px 0 0;
}
.product-single__meta .jis-specs h2,
.product-single__meta .jis-specs h3 {
  font-size: 15px;
  line-height: 1.3;
  margin: 0 0 5px;
}
.product-single__meta .jis-specs ul {
  margin: 0;
  padding-left: 18px;
  font-size: 13px;
  line-height: 1.45;
}
.product-single__meta .jis-specs li {
  margin: 0 0 3px;
}
.product-single__meta .jis-specs li:last-child {
  margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   5. Desktop type hierarchy.
   The title is the theme's heading-lg (32px). Shrinking it to 28px holds our
   longest shelf names to two lines. The description's headings inherit the
   same scale, so they have to come down too or a section heading outranks the
   product name (Brandon caught exactly this on Cabinet Jewels).
   -------------------------------------------------------------------------- */
@media only screen and (min-width: 769px) {
  .product-single__meta h1.element-text--heading-lg {
    font-size: 28px;
    line-height: 1.22;
  }
}
/* NOTE the selector: this theme's description renders inside
   <element-text-rte class="element-text element-text--rte">, NOT a .rte
   wrapper, so the Cabinet Jewels / Centurymade `.rte h2` rules match nothing
   here. Without this the description's "Overview" ships at 32px against a 28px
   product title - the exact inversion Brandon caught on Cabinet Jewels.
   The doubled class is deliberate: the theme's own rule is
   `.element-text.element-text--rte h2` (0,2,1), so a single-class selector
   loses on specificity no matter where it sits in the cascade.
   element-text-rte survives the 10.0 kit migration untouched. */
.product-single__meta element-text-rte.element-text--rte h2 {
  font-size: 20px;
  line-height: 1.3;
  margin: 28px 0 10px;
}
.product-single__meta element-text-rte.element-text--rte h2:first-child {
  margin-top: 0;
}
.product-single__meta element-text-rte.element-text--rte h3 {
  font-size: 16px;
  line-height: 1.35;
  margin: 20px 0 8px;
}
.product-single__meta element-text-rte.element-text--rte h4,
.product-single__meta element-text-rte.element-text--rte h5,
.product-single__meta element-text-rte.element-text--rte h6 {
  font-size: 14px;
  margin: 16px 0 6px;
}

/* --------------------------------------------------------------------------
   6. Sticky buy bar (phones only).
   On a phone the gallery plus header own the first ~450px, so no amount of
   tightening puts the buy box on the first screen. This bar keeps a buy
   button reachable once the real one scrolls away.
   -------------------------------------------------------------------------- */
.jis-stickybuy {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: none;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  background: var(--color-background, #fff);
  border-top: 1px solid rgba(0, 0, 0, 0.12);
  box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.09);

  /* The bar is a full-width fixed strip that `measureWidgets()` lifts clear of
     the chat bubble, so it hovers OVER page content rather than sitting flush at
     the bottom edge. With pointer-events on the container, every tap landing in
     that ~65px band hit the bar's own padding and died - reported on live
     2026-08-12 as "lots of things went unresponsive on mobile". Hit-testing
     confirmed it: elementFromPoint in the band returned div.jis-stickybuy, not
     the content behind it.

     Pass taps through the container and re-enable them only on the real
     controls, so the bar's padding and its ~44px of bare edge stop eating taps
     meant for the page.

     Written as two flat rules, not CSS nesting: nesting needs Chrome 120+ /
     Safari 17.2+, and a phone that does not support it would silently drop the
     pass-through and keep the dead band. Not worth the risk on a live fix. */
  pointer-events: none;
}
.jis-stickybuy > * {
  pointer-events: auto;
}
.jis-stickybuy__price {
  flex: 0 0 auto;
  font-size: 15px;
  font-weight: 600;
  white-space: nowrap;
}
.jis-stickybuy__btn {
  flex: 1 1 auto;
  margin: 0;
  padding: 12px 16px;
  font-size: 14px;
  line-height: 1.2;
  min-height: 44px;
  cursor: pointer;
  /* Was --color-background / --color-foreground. 10.0 defines NEITHER on this element,
     so both fell through to their fallbacks and the bar's button rendered #131a22 - a
     different near-black from the inline Add to cart (#1c1d1d) and from the pill ring.
     Measured 2026-08-12. Now on the same --element-button-color-* pair the inline CTA
     uses, so the two Add-to-cart buttons match by construction, not coincidence. */
  color: var(--element-button-color-secondary, #fff);
  background: var(--element-button-color-primary, #1c1d1d);
  border: 1px solid transparent;
  border-radius: var(--element-button-radius, 2px);
}
.jis-stickybuy__btn[disabled] {
  opacity: 0.5;
  cursor: default;
}
@media only screen and (max-width: 768px) {
  .jis-stickybuy.is-visible {
    display: flex;
  }
}
/* never sit on top of an open drawer, modal or the cart */
.js-drawer-open .jis-stickybuy,
.modal--is-active .jis-stickybuy,
body:has(dialog[open]) .jis-stickybuy {
  display: none !important;
}

@media only screen and (max-width: 768px) {
  /* the breadcrumb costs a line of the first screen on phones */
  .product-single__meta > nav[aria-label='breadcrumbs'] {
    display: none;
  }
  .product-single__meta {
    --layout-stack-gap-size-sm: 9px;
  }
}


/* ==========================================================================
   2026-08-10b - Brandon: image frame, section spacing, mobile header overflow
   ========================================================================== */

/* Primary product image frame. The theme drew a 1px #E4E4E4 edge - 1.27:1
   against white, effectively invisible. 2px in a warm tone from the brand's
   own family (#886848) reads at 2.05:1, versus 1.72:1 for the CMS frame this
   is matching. Selector repeats the class to clear the theme's
   `.product__main-photos .product-slideshow` (0,2,0). Mobile is left alone -
   the theme deliberately sets border:0 under 768px. */
.product__main-photos .product-slideshow.product-slideshow {
  border-width: 2px;
  border-style: solid;
  border-color: #C2B3A1;
}

/* Standard 28px between a section divider and the heading under it, matching
   the 28px the in-description <hr>s already use. The divider's parent here is
   a <layout-stack> whose 10px gap lives in a shadow root and cannot be
   selected, so 18px + that 10px = the 28px standard. Measured, not assumed. */
.product-single__meta hr.jis-divider {
  margin-bottom: 18px;
}

/* Mobile nav: drop the collection thumbnails.        BRANDON'S CALL 2026-08-12
   header.mobile-nav.liquid renders a 40x40 "Collection link image" beside any
   menu item that resolves to a collection, sourced from
   `subcollection_drop.products.first.featured_media.preview_image`. On B&B that
   produced exactly two - FLOATING SHELVES (PLY-LOC-Full.jpg) and BRACKETS
   (EZ-LOC1210-10W.jpg). Brandon asked for them gone.

   BOTH rules are required. The image is `position:absolute; left:15px` and the
   theme gives its sibling label `padding-left:45px` to clear it, so hiding the
   image alone leaves every collection link indented 45px into empty space.

   !important because the theme's own rules live in an inline <style> inside the
   snippet, which is emitted AFTER this stylesheet in the document - equal
   specificity would lose on source order.

   Done here rather than by editing header.mobile-nav.liquid so the stock file
   stays pristine for the next Expanse upgrade (this is exactly what
   overrides.css is for). If the thumbnails are ever wanted back, delete this
   block - nothing else was touched. */
.slide-nav__image {
  display: none !important;
}
.slide-nav__image + span {
  padding-left: 0 !important;
}

/* M7. Mobile header overflowed 6px on 9.1: 17px page gutter + 180px logo +
   4 x 46px icon buttons = 381px in a 375px viewport. Trimming the icons' side
   padding to 6px makes them 38px (still well over the 24px WCAG 2.2 minimum).

   STILL LOAD-BEARING ON 10.0 - measured 2026-08-12. This block was deleted and
   then RESTORED the same day, because deleting it for real reintroduced the
   overflow:

     M7 removed  ->  icon padding computes 10px, mobile page overflow 6px
     M7 present  ->  icon padding computes 6px,  mobile page overflow 0px

   A/B on the actual variable, nothing else changed. So neither of the two
   reasons it looked dead actually covers it: 10.0 does ship
   `calc(var(--site-nav-item-padding) / 2)` under max-width:768px, and
   `.site-nav` is display:none at mobile, but the icons row alone still
   overflows at the theme's default padding.

   How the wrong answer was reached, so nobody repeats it: the first test
   SIMULATED the removal by injecting the theme default with !important and read
   back 0 overflow. A simulated toggle is not the same as the deletion - delete
   the rule for real, reload, and measure. Also remember there are TWO copies
   (here and an injected <style> in snippets/section.main-product.liquid);
   neutralising one leaves the other applying 6px, which hides the effect
   entirely. */
@media only screen and (max-width: 768px) {
  .site-header .site-nav__link--icon {
    padding-left: 6px;
    padding-right: 6px;
  }
}


/* ===========================================================================
   JIS 2026-09-03. Two-row header + the REI nav:utility type ratio.
   Brandon: "I want the two level nav on bracket and burl that we use on the
   other sites. logo / search with breadcrumb on the left of top line and a11y
   sign in and cart on the right side of the same line. Right below that have
   the nav." Plus the REI relationship asked for across all four stores.

   THE LAYOUT ITSELF IS NOT CSS. It comes from ONE stock setting -
   sections/header-group.json, header.settings.main_menu_alignment: "below"
   (was "left"). On this theme that single setting gates THREE things at once:
   the two-row layout, the header search field, and un-hiding the icon labels
   (header.icons.liquid sets hide_icon_labels = false only when
   nav_position == 'below'). Cabinet Jewels and Century Made Shop are both set
   the same way. Do not try to reproduce this in CSS.

   RESULT, measured live: row 1 = logo at x40 + search field at x360 + icon row
   right-aligned; row 2 = nav at y80; header 134px tall. No horizontal overflow
   at 1440 / 1280 / 1100 / 1024 / 960 / 900 / 800 / 769 / 390.
   =========================================================================== */

/* --- the icon row must not be squeezed ------------------------------------
   MEASURED: at 1024 the icon row was compressed 196px -> 154px and the cart
   link's right edge landed at 1026 in a 1024 viewport - 2px of horizontal
   overflow on a live page. This is the same failure C10 fixed on Cabinet
   Jewels when it got this header, and it was predicted for B&B.

   With flex-shrink: 0 the icon row holds 184px and the search field absorbs
   the difference (470 -> 440px, far above the 120px usable floor). At every
   other width the row is already 135px and this rule changes nothing. */
.header-item--icons {
  flex-shrink: 0;
}

/* --- "Sign in" is hidden below 1024 ---------------------------------------
   The span is OURS (added in customer-account.liquid), so unlike the theme's
   Cart label it carries no visually-hidden class of its own and would show at
   every width. Measured before this rule: 19px of horizontal overflow at 900.

   Visually hidden rather than display:none, deliberately - the theme's own
   `small--hide` uses display:none, which removes an element from the
   ACCESSIBILITY TREE, not just from view. Cabinet Jewels' trigger measured an
   accessible name of "" at 390 for exactly that reason. This keeps the name.

   1024 matches Cabinet Jewels, Cabinet Screws and Century Made Shop. */
@media only screen and (max-width: 1023px) {
  .site-nav__icons shopify-account .site-nav__icon-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* --- REI's nav : utility ratio -------------------------------------------
   MEASURED on rei.com at 1440 (headless gets a bot challenge - read through a
   real browser): nav 16px / Sign in 14px / Cart 14px -> utility / nav = 0.875.
   B&B's nav is 18px DM Sans rgb(215,211,203), so utility lands at 15.75px. */
.site-nav__icons .site-nav__link--cart {
  font-size: 15.75px;
}

/* "Sign in" is SLOTTED into <shopify-account>'s open shadow root, so its
   flattened-tree parent is inside that component and it inherits a UA font -
   measured here at 13.33px Arial beside an 18px DM Sans "Cart", the same trap
   Cabinet Jewels and Cabinet Screws both hit.

   Set on the SLOTTED SPAN, never on the label: a document rule targeting the
   label loses on font-size even with !important (only an inline style wins),
   while setting the span works and the label inherits it. */
.site-nav__icons shopify-account > span[slot='signed-out-avatar'] {
  font-size: 15.75px;
  font-family: 'DM Sans', sans-serif;
  color: rgb(215, 211, 203);
}


/* ===================================================================
   ACCESSIBILITY: TEXT SIZE KNOB (--ts)   appended 2026-09-03
   Two blocks below. Both are INERT at --ts: 1, asserted by qc_ts_inert.js
   (which also counts PARSED rules - a stray `*/` makes CSS error recovery
   eat a block, and a discarded block is indistinguishable from an inert
   one, so a PASS with 0 rules parsed means nothing).

   ts-response.css is GENERATED (gen_ts_css.py) - do not hand-edit it here;
   re-run the generator against the theme's JIS-authored files instead.
   =================================================================== */
/* ===================================================================
   --ts response for this store's OWN hard-px type.  GENERATED -
   do not hand-edit; re-run gen_ts_css.py against the theme's files.

   Every rule restates a declaration that already exists in a JIS
   section or snippet, multiplied by the text-size knob. At --ts: 1
   each one computes to exactly its original value, so this block is
   INERT until a shopper asks for larger text.

   95 declarations, from 7 files.
   =================================================================== */
:root .bb-custom-order-v1{font-size:calc((17px) * var(--ts)) !important}
:root .bb-custom-order-v1 .eyebrow{font-size:calc((13px) * var(--ts)) !important}
:root .bb-custom-order-v1 .lede{font-size:calc((19px) * var(--ts)) !important}
:root .bb-custom-order-v1 .btn{font-size:calc((15px) * var(--ts)) !important}
:root .bb-custom-order-v1 .finenote{font-size:calc((13px) * var(--ts)) !important}
:root .bb-custom-order-v1 .note{font-size:calc((14px) * var(--ts)) !important}
:root .bb-custom-order-v1 label.f-label{font-size:calc((13px) * var(--ts)) !important}
:root .bb-custom-order-v1 .f-input,:root .bb-custom-order-v1 select.f-input,:root .bb-custom-order-v1 textarea.f-input{font-size:calc((13.5px) * var(--ts)) !important}
:root .bb-custom-order-v1 textarea.f-input{font-size:calc((14.5px) * var(--ts)) !important}
:root .bb-custom-order-v1 .row-remove{font-size:calc((15px) * var(--ts)) !important}
:root .bb-custom-order-v1 .add-line{font-size:calc((13.5px) * var(--ts)) !important}
:root .bb-custom-order-v1 .summary-box ol{font-size:calc((13.5px) * var(--ts)) !important}
:root .bb-custom-order-v1 .dropzone{font-size:calc((13px) * var(--ts)) !important}
:root .bb-custom-order-v1 .trust-list{font-size:calc((13.5px) * var(--ts)) !important}
:root .bb-home-v3{font-size:calc((17px) * var(--ts)) !important}
:root .bb-home-v3 .eyebrow{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .lede{font-size:calc((19px) * var(--ts)) !important}
:root .bb-home-v3 .btn{font-size:calc((15px) * var(--ts)) !important}
:root .bb-home-v3 .chip{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .tag{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 details.sources summary{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .note{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .finenote{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .spec-btn{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .tape .labels{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .tape .readout{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .grove h4{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .grove button{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .board-stamp{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .board-cta{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .tile h3{font-size:calc((22px) * var(--ts)) !important}
:root .bb-home-v3 .tile p{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .tile .mono-cta{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .trust-item{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .spec-links a{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .price-card .was-amt{font-size:calc((20px) * var(--ts)) !important}
:root .bb-home-v3 .price-card .win-row .amt{font-size:calc((40px) * var(--ts)) !important}
:root .bb-home-v3 .price-card .price-tagline{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .reel-meta .reel-name{font-size:calc((24px) * var(--ts)) !important}
:root .bb-home-v3 .reel-meta .mono{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .hero-v2-copy .from-price{font-size:calc((18px) * var(--ts)) !important}
:root .bb-home-v3 .buybox .spec{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .buybox .price-now{font-size:calc((34px) * var(--ts)) !important}
:root .bb-home-v3 .buybox .price-sub{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .buy-row select{font-size:calc((15px) * var(--ts)) !important}
:root .bb-home-v3 .buybox .pdp-link{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .buybox .buy-facts li{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .bracket-hint{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .incl h3{font-size:calc((22px) * var(--ts)) !important}
:root .bb-home-v3 .incl li{font-size:calc((17px) * var(--ts)) !important}
:root .bb-home-v3 .incl ol li::before{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .facts .card h3{font-size:calc((22px) * var(--ts)) !important}
:root .bb-home-v3 .facts .card p{font-size:calc((16px) * var(--ts)) !important}
:root .bb-home-v3 .facts .card a.more{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .ask{font-size:calc((17px) * var(--ts)) !important}
:root .bb-home-v3 .bk-card h3{font-size:calc((20px) * var(--ts)) !important}
:root .bb-home-v3 .bk-card p{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .bk-card .bk-price{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .all-link a{font-size:calc((14px) * var(--ts)) !important}
:root .bb-home-v3 .guide-group h3{font-size:calc((22px) * var(--ts)) !important}
:root .bb-home-v3 .guide-group h3 span{font-size:calc((15px) * var(--ts)) !important}
:root .bb-home-v3 .guide-group .g-model{font-size:calc((16px) * var(--ts)) !important}
:root .bb-home-v3 .guide-group .g-files a{font-size:calc((15px) * var(--ts)) !important}
:root .bb-home-v3 .guides-all a{font-size:calc((15px) * var(--ts)) !important}
:root .bb-mission-v1{font-size:calc((17px) * var(--ts)) !important}
:root .bb-mission-v1 .eyebrow{font-size:calc((13px) * var(--ts)) !important}
:root .bb-mission-v1 .btn{font-size:calc((15px) * var(--ts)) !important}
:root .bbrc h2{font-size:calc((clamp(26px,3.4vw,40px)) * var(--ts)) !important}
:root .bbrc h3{font-size:calc((19px) * var(--ts)) !important}
:root .bbrc p{font-size:calc((16px) * var(--ts)) !important}
:root .bbrc .eyebrow{font-size:calc((13px) * var(--ts)) !important}
:root .bbrc .spec{font-size:calc((13.5px) * var(--ts)) !important}
:root .bbrc .finenote{font-size:calc((13px) * var(--ts)) !important}
:root .bbrc .cta{font-size:calc((13.5px) * var(--ts)) !important}
:root .bbrc .cta-ghost{font-size:calc((13.5px) * var(--ts)) !important}
:root .m01__eyebrow{font-size:calc((13px) * var(--ts)) !important}
:root .m01__card--intro p{font-size:calc((13.5px) * var(--ts)) !important}
:root .m01__qmark{font-size:calc((120px) * var(--ts)) !important}
:root .m01__q-eyebrow{font-size:calc((13px) * var(--ts)) !important}
:root .m01__quote{font-size:calc((22.5px) * var(--ts)) !important}
:root .m01__card-title{font-size:calc((15px) * var(--ts)) !important}
:root .m01__card-copy{font-size:calc((13.5px) * var(--ts)) !important}
:root .m01__card-link{font-size:calc((13px) * var(--ts)) !important}
:root .m02__name{font-size:calc((13px) * var(--ts)) !important}
:root .m02__rest{font-size:calc((15.5px) * var(--ts)) !important}
:root .m03__specs li{font-size:calc((13.5px) * var(--ts)) !important}
:root .m04__list li{font-size:calc((15.5px) * var(--ts)) !important}
:root .m05__label h2{font-size:calc((clamp(22px,2.6vw,31px)) * var(--ts)) !important}
:root .m05__hint{font-size:calc((13px) * var(--ts)) !important}
:root .bbhs__dot{font-size:calc((15px) * var(--ts)) !important}
:root .bbhs__card h3{font-size:calc((15px) * var(--ts)) !important}
:root .bbhs__card p{font-size:calc((13px) * var(--ts)) !important}
:root .m06__item summary{font-size:calc((17px) * var(--ts)) !important}
:root .m06__ans p{font-size:calc((15.5px) * var(--ts)) !important}
:root .m06__sources summary{font-size:calc((13px) * var(--ts)) !important}
:root .jis-cart-clear{font-size:calc((13px) * var(--ts)) !important}
:root .cart__item-row--footer small{font-size:calc((13px) * var(--ts)) !important}
:root .element-price[data-subtotal] sup{font-size:calc((13px) * var(--ts)) !important}
:root .jis-more{font-size:calc((13px) * var(--ts)) !important}
:root .jis-qty__trigger{font-size:calc((13px) * var(--ts)) !important}
:root .jis-qty__list li{font-size:calc((13px) * var(--ts)) !important}
:root .product-single__meta .jis-lede h2,:root .product-single__meta .jis-lede h3{font-size:calc((18px) * var(--ts)) !important}
:root .jis-lede__more{font-size:calc((13px) * var(--ts)) !important}
:root .product-single__meta .jis-specs h2,:root .product-single__meta .jis-specs h3{font-size:calc((15px) * var(--ts)) !important}
:root .product-single__meta .jis-specs ul{font-size:calc((13px) * var(--ts)) !important}
:root .product-single__meta element-text-rte.element-text--rte h2{font-size:calc((20px) * var(--ts)) !important}
:root .product-single__meta element-text-rte.element-text--rte h3{font-size:calc((16px) * var(--ts)) !important}
:root .jis-stickybuy__price{font-size:calc((15px) * var(--ts)) !important}
:root .jis-stickybuy__btn{font-size:calc((14px) * var(--ts)) !important}
:root .site-nav__icons .site-nav__link--cart{font-size:calc((15.75px) * var(--ts)) !important}
:root .site-nav__icons shopify-account > span[slot='signed-out-avatar']{font-size:calc((15.75px) * var(--ts)) !important}
:root .footer__base-links{font-size:calc((13px) * var(--ts)) !important}
@media (max-width:860px) {
  :root .bb-custom-order-v1 h1{font-size:calc((40px) * var(--ts)) !important}
  :root .bb-custom-order-v1 h2{font-size:calc((32px) * var(--ts)) !important}
  :root .bb-home-v3 h1{font-size:calc((44px) * var(--ts)) !important}
  :root .bb-home-v3 .buybox .price-now{font-size:calc((30px) * var(--ts)) !important}
  :root .bb-mission-v1 h1{font-size:calc((40px) * var(--ts)) !important}
  :root .bb-mission-v1 h2{font-size:calc((34px) * var(--ts)) !important}
}
@media only screen and (min-width: 769px) {
  :root .product-single__meta h1.element-text--heading-lg{font-size:calc((28px) * var(--ts)) !important}
}

/* ===================================================================
   BRACKET & BURL - the --ts response for type this store does NOT own.

   gen_ts_css.py scrapes hard-px font-size declarations out of the
   JIS-authored sections and snippets (bb/ts-response.css, 95 rules).
   What it can never reach is handled here, each with its reason.

   Same contract as the generated block: at --ts: 1 every rule computes
   to exactly its original value, so this file is INERT at rest. That is
   asserted by qc_ts_inert.js, not assumed.
   =================================================================== */


/* -------------------------------------------------------------------
   0. THE KNOB'S DEFAULT.

   Declared in CSS as well as by the panel's boot script, so every rule in
   these blocks resolves even if the script is blocked, deferred, or the
   panel is removed later. Without a definition `calc((14px) * var(--ts))`
   is INVALID and the browser drops the whole declaration - for the type
   floor rules that would not merely fail to scale, it would DELETE the
   floor and ship smaller text than we have today.
   ------------------------------------------------------------------- */
:root {
  --ts: 1;
}


/* -------------------------------------------------------------------
   1. EXPANSE 10'S TYPE TOKENS.  The big one.

   Most elements that will not answer the knob are not hard-px at all -
   they resolve to `var(--element-text-font-size--*)`, or they INHERIT
   from a parent that does. Scaling the eleven tokens is one block that
   moves the whole family instead of dozens of brittle per-element rules.

   MEASURED ON BRACKET & BURL, not copied from Cabinet Jewels:
   `qc_type_tokens.js` at 1440 and 390 on theme 147994050634 returned the
   same eleven names and the same two value sets CJ has. Both stores are
   on Expanse 10.0.x, so that is the expected answer - but it is written
   down here because it was checked, not because it was assumed.

   DELIBERATELY NOT `html { font-size: calc(16px * var(--ts)) }`. That is
   the usual trick and it would scale these tokens too, since they are
   rem-based - but it would ALSO scale every rem-based padding, margin,
   gap and max-width in the theme, turning a text-size control into a
   whole-layout zoom.

   THE TOKENS ARE BREAKPOINT-DEPENDENT. Expanse defines one set below
   769px and a larger set at and above it. A single unconditional block
   using the DESKTOP values moves elements with the knob AT REST - it did
   on CJ, 27 on home and 44 on the PDP. Both sets are restated here, each
   behind the theme's own breakpoint.

   `!important` on a custom property is legal and is required: Expanse
   declares the same tokens at the same specificity, and without it the
   later declaration wins. If a THIRD breakpoint is ever added upstream,
   !important would hide it - re-run qc_type_tokens.js after any upgrade.
   ------------------------------------------------------------------- */

/* Below 769px - Expanse's mobile set. */
:root {
  --element-text-font-size--body-lg:     calc(1.125rem * var(--ts)) !important;
  --element-text-font-size--body-md:     calc(1rem * var(--ts)) !important;
  --element-text-font-size--body-sm:     calc(0.875rem * var(--ts)) !important;
  --element-text-font-size--body-xs:     calc(0.75rem * var(--ts)) !important;
  --element-text-font-size--heading-2xl: calc(3rem * var(--ts)) !important;
  --element-text-font-size--heading-xl:  calc(2rem * var(--ts)) !important;
  --element-text-font-size--heading-lg:  calc(1.5rem * var(--ts)) !important;
  --element-text-font-size--heading-md:  calc(1.25rem * var(--ts)) !important;
  --element-text-font-size--heading-sm:  calc(1.125rem * var(--ts)) !important;
  --element-text-font-size--heading-xs:  calc(1rem * var(--ts)) !important;
  --element-text-font-size--heading-2xs: calc(0.875rem * var(--ts)) !important;
}

/* 769px and up - Expanse's desktop set. The max() forms are multiplied
   whole, which is valid and keeps each token's own floor intact. */
@media only screen and (min-width: 769px) {
  :root {
    --element-text-font-size--body-lg:     calc(1.25rem * var(--ts)) !important;
    --element-text-font-size--body-md:     calc(1.125rem * var(--ts)) !important;
    --element-text-font-size--body-sm:     calc(1rem * var(--ts)) !important;
    --element-text-font-size--body-xs:     calc(0.875rem * var(--ts)) !important;
    --element-text-font-size--heading-2xl: calc(max(4rem, 1.125rem) * var(--ts)) !important;
    --element-text-font-size--heading-xl:  calc(max(3rem, 1.125rem) * var(--ts)) !important;
    --element-text-font-size--heading-lg:  calc(max(2rem, 1.125rem) * var(--ts)) !important;
    --element-text-font-size--heading-md:  calc(max(1.5rem, 1.125rem) * var(--ts)) !important;
    --element-text-font-size--heading-sm:  calc(max(1.25rem, 1.125rem) * var(--ts)) !important;
    --element-text-font-size--heading-xs:  calc(max(1.125rem, 1.125rem) * var(--ts)) !important;
    --element-text-font-size--heading-2xs: calc(max(1rem, 1.125rem) * var(--ts)) !important;
  }
}


/* -------------------------------------------------------------------
   2. THE DECLARATION gen_ts_css.py REFUSED.

   Reported as `SKIPPED, not a selector`. It is not malformed - it exceeds
   the generator's 160-character selector cap, and the console message
   truncates at 70 characters so it reads as garbage. Left unhandled it is
   a silent hole in the PDP description.

   Its h2 (20px) and h3 (16px) siblings are short enough to have been
   generated; only the h4/h5/h6 group is over the cap.
   ------------------------------------------------------------------- */
:root .product-single__meta element-text-rte.element-text--rte h4,
:root .product-single__meta element-text-rte.element-text--rte h5,
:root .product-single__meta element-text-rte.element-text--rte h6 {
  font-size: calc((14px) * var(--ts)) !important;
}


/* -------------------------------------------------------------------
   3. STRAGGLERS THE SCRAPER CANNOT SEE BY DESIGN.

   Both found by qc_bb_stragglers.js, which injects the two blocks and
   THEN turns the knob. Its first version only turned the knob against a
   theme carrying no --ts rules, so every element on the page reported
   STUCK - including all the ones the generated block handles. An
   instrument that cannot tell a real straggler from an unloaded
   stylesheet is worse than no instrument.
   ------------------------------------------------------------------- */

/* THE PDP PRICE. The one element an inline style puts out of reach.
   Measured on /products/ply-loc-60-...: the markup carries
   `style="--element-text-font-size--body-lg: var(--size-5-5)"` directly on
   the price span, and an inline custom-property declaration outranks any
   stylesheet - so the :root token block above is bypassed for this element
   and only this element. Three instances at 22px on the PDP; the other two
   .element-price spans on the same page have no inline style and already
   scale 18px -> 23.04px correctly.

   Re-declared at the point of USE against the theme's own `--size-5-5`
   rather than against the body-lg token. That matters: multiplying the
   token here would DOUBLE-scale on any price that does NOT carry the
   inline override, because the token is already scaled above. `--size-5-5`
   is not something this file touches, so it cannot compound. */
:root .block-price .element-price,
:root .block-price__regular .element-price {
  font-size: calc(var(--size-5-5, 22px) * var(--ts)) !important;
}

/* THE SECOND "Sign in" LABEL, at 13.3333px.
   It sits in the `shopify-account` custom element's signed-out-avatar SLOT
   and inherits from inside that element, so no CSS rule anywhere declares
   it. Being in a shadow tree it is also invisible to the coverage probe's
   `document.querySelectorAll('body *')` - its ABSENCE from the miss list
   was never evidence that it scales. [[shopify-account-slotted-label-unstylable]]

   The header's own visible "Sign in" (15.75px, .site-nav__icon-label)
   already scales correctly and is NOT touched here.

   Any literal px value would ROUND 13.3333 to 13.33, which the inertness
   test correctly reads as a change at rest. `1em` is the parent's own size,
   so at --ts: 1 this computes to exactly the inherited value and is inert
   by construction. */
:root shopify-account span:not([class]) {
  font-size: calc(1em * var(--ts)) !important;
}

/* Price cents (2026-09-26 QC): collection/search/cart cards rendered the cents
   superscript at 10.8px desktop / 9.6px phone. House floor is 14px. */
:root .element-price sup{font-size:calc(14px * var(--ts)) !important}

/* Header search field (2026-09-27): light field so it reads on the dark header. */
.site-header__search-island input.search__input{background:#F4F2EE !important;color:#131A22 !important;box-shadow:inset 0 0 0 1px #D7D3CB !important}
.site-header__search-island input.search__input::placeholder{color:#4A5058 !important;opacity:1 !important}
.site-header__search-island input.search__input:focus-visible{outline:2px solid #D8C0A0 !important;outline-offset:2px}
.site-header__search-island .element-button--search svg{color:#131A22 !important}


/* ==========================================================================
   OPTION SWATCHES - CMS parity                                    2026-10-01

   centurymadeshop.com shows a material as a row of 34px round swatches and
   spells the chosen value out in the option label ("Material: Maple"). This
   store now does the same for its swatched option. The markup comes from
   snippets/jis-option-swatch.liquid (which options, and what each swatch
   shows) via kit.product-variant-picker[.button].liquid: a kit.swatch with
   .jis-swatch inside label.kit-radio, the value name in the label's title
   and in visually-hidden text.

   The ring is box-shadow, never outline, and nothing here transitions. A ring
   that eases in or out reads as two selected values for a moment after a
   click (B&B 2026-08-12 - the "Selected-option indicator" note in B&B's
   overrides.css).
   ========================================================================== */
.kit-swatch.jis-swatch {
  width: 34px;
  height: 34px;
  border: 0;
  outline: none;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15);
  transition: none;
}
.kit-radio .kit-swatch.jis-swatch:hover {
  outline: none;
}
.kit-radio__input:checked ~ .kit-swatch.jis-swatch {
  outline: none;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15),
    0 0 0 2px var(--kit-color-secondary, #fff),
    0 0 0 4px var(--kit-color-primary);
}
@media (hover: hover) and (pointer: fine) {
  .kit-radio:hover .kit-radio__input:not(:checked) ~ .kit-swatch.jis-swatch:not(.kit-swatch--disabled) {
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15),
      0 0 0 2px var(--kit-color-secondary, #fff),
      0 0 0 3px var(--kit-color-primary);
  }
}
.kit-radio__input:focus-visible ~ .kit-swatch.jis-swatch {
  outline: 2px solid var(--kit-color-focus);
  outline-offset: 5px;
}
/* the row: CMS spacing, and the label is the hit area for the round swatch */
.jis-grid:has(> label.kit-radio .jis-swatch) {
  gap: 8px;
}
label.kit-radio:has(.jis-swatch) {
  padding: 0;
  border-radius: 50%;
}
/* kit separates the label from the chosen value with " - " (an em dash);
   CMS uses a colon: "Finish: Black Bronze". */
.kit-fieldset__legend > .kit-fieldset__legend-selection.kit-text::before {
  content: ':\00a0';
}
