/** Shopify CDN: Minification failed

Line 1637:19 Expected identifier but found whitespace
Line 1637:69 Unexpected "`"

**/
/* ============================================================================
   NEAT VALUES — VISUAL OVERHAUL
   ----------------------------------------------------------------------------
   Baseline commit: 90c2682 ("baseline: Shrine PRO v1.6.1 as deployed")
   Tokens:          snippets/nv-tokens.liquid (loads BEFORE base.css)
   This file:       loads AFTER base.css, in <head>, in both layouts.

   ----------------------------------------------------------------------------
   HOW THIS FILE WINS
   ----------------------------------------------------------------------------
   Every component rule is scoped `body.nv ...` — specificity (0,2,1).

   That is not defensive over-qualification. 36 sections in this theme load
   their own stylesheet with {{ 'x.css' | asset_url | stylesheet_tag }} inside
   <body> — AFTER everything in <head>. On a tie, source order wins, so a
   plainly-written override here would apply on the homepage and silently lose
   on collection, account and search pages. `body.nv` beats them on
   specificity regardless of order.

   It is also the kill switch: remove `nv` from the body class in
   layout/theme.liquid and layout/password.liquid and this entire overhaul
   switches off. No deploy required.

   ----------------------------------------------------------------------------
   RULES FOR THIS FILE
   ----------------------------------------------------------------------------
   1. NO !important, except in three sanctioned places: the REDUCED MOTION
      block below (an accessibility reset must beat everything), and the two
      banner-fenced zones at the bottom (THIRD-PARTY and LEGACY). If you reach
      for it anywhere else, the selector is wrong.
   2. Mobile-first. Base rules are mobile; step up with min-width.
   3. Breakpoints: 750 and 990 ONLY. See KNOWN ISSUES below.
   4. Never edit assets/base.css. Override it here.
   5. This must stay a plain .css file. As .css.liquid, a Liquid error yields a
      SILENTLY EMPTY stylesheet rather than an error — an afternoon lost to
      "my CSS isn't applying". Liquid belongs in nv-tokens.liquid.
   6. No directional properties (margin-left, text-align: left, ...) — this
      file loads after rtl.css and would undo it if RTL is ever enabled.

   ----------------------------------------------------------------------------
   KNOWN ISSUES IN base.css (do not "fix" — just don't trip on them)
   ----------------------------------------------------------------------------
   - Six competing mobile breakpoints exist theme-wide: 740, 749, 750, 767,
     768, 769. base.css owns 214 queries at 749/750/990 and wins by weight of
     numbers, so those are canon. Never introduce a new value.
   - base.css has off-by-one overlaps: `max-width:750px` (x5) alongside
     `min-width:750px`, and `min-width:749px` (x2) alongside `max-width:749px`.
     At exactly 749px and 750px both rulesets can apply. This is why that
     1px band can look odd. It is pre-existing.
   - CSS custom properties CANNOT be used in media queries. The breakpoints
     above are a convention, not a token. `@media (min-width: var(--x))` is
     invalid CSS and fails silently.
   ============================================================================ */


/* ============================================================================
   MOTION
   Rebound here rather than in nv-tokens.liquid: base.css:1031-1035 declares
   these three at :root and loads after the snippet, so it would win there.
   ============================================================================ */
:root {
  --duration-short:   120ms;
  --duration-default: 220ms;
  --duration-long:    420ms;
  --nv-ease:     cubic-bezier(.2, 0, 0, 1);
  --nv-ease-out: cubic-bezier(.16, 1, .3, 1);
}


/* ============================================================================
   DESKTOP TOKEN STEP-UP
   ============================================================================ */
@media screen and (min-width: 750px) {
  :root {
    --nv-gutter:    3.2rem;
    --nv-section-y: 8.8rem;
    --nv-stack:     2.4rem;

    --nv-text-xl:  2.4rem;
    --nv-text-2xl: 3.2rem;
    --nv-text-3xl: 4.0rem;
    --nv-text-4xl: 5.6rem;

    --nv-price-size: 3.2rem;
  }
}


/* ============================================================================
   REDUCED MOTION
   ----------------------------------------------------------------------------
   Third sanctioned !important zone. This is the standard accessibility reset:
   it has to beat author styles unconditionally, including the fenced legacy
   and third-party rules below, so !important is correct here and only here
   outside the fences.

   Migrated and widened from the legacy block, which only covered four button
   selectors. base.css has just 2 prefers-reduced-motion queries of its own
   across 21,709 lines, so most of the theme's motion was unguarded.

   Animation and transition are neutralised globally. `transform: none` is NOT
   applied globally on purpose — plenty of elements use transform for layout
   (centring via translate, slider track positioning), and blanket-killing it
   would break them. It stays scoped to interactive hover/active states, which
   is what the legacy rules were actually protecting.
   ============================================================================ */
@media (prefers-reduced-motion: reduce) {
  body.nv *,
  body.nv *::before,
  body.nv *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0ms !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
  }

  html {
    scroll-behavior: auto !important;
  }

  body.nv .button:hover,
  body.nv .button:active,
  body.nv .main-product-atc:hover,
  body.nv .main-product-atc:active,
  body.nv .cart__checkout-button:hover,
  body.nv .cart__checkout-button:active,
  body.nv #CartDrawer-Checkout:hover,
  body.nv #CartDrawer-Checkout:active,
  body.nv .kaching-bundles-sticky-atc__button:hover,
  body.nv .kaching-bundles-sticky-atc__button:active {
    transform: none !important;
  }
}


/* ============================================================================
   COMPONENTS
   Populated in later phases. Order: buttons/forms, product card, header +
   drawer, footer, sticky ATC, touch targets.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   Generic buttons and form controls
   ----------------------------------------------------------------------------
   These are the site-wide primitives, NOT the buy buttons — those are handled
   separately further down. base.css already sizes .button at 49px and inputs
   at 45px, so this is purely about how they read: flatter, softer corners,
   a real focus ring, and a hover that lifts rather than glows.
   ---------------------------------------------------------------------------- */
body.nv .button,
body.nv .shopify-challenge__button,
body.nv .customer button {
  border-radius: var(--nv-radius-sm);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-wide);
  transition:
    background-color var(--duration-default) var(--nv-ease),
    box-shadow var(--duration-default) var(--nv-ease),
    transform var(--duration-default) var(--nv-ease-out);
}

body.nv .button:not([disabled]):hover,
body.nv .customer button:not([disabled]):hover {
  box-shadow: var(--nv-shadow-2);
}

/* base.css draws focus with an outline + offset; a ring reads cleaner against
   the warm ground and does not clip inside overflow:hidden containers. */
body.nv .button:focus-visible,
body.nv .field__input:focus-visible,
body.nv .select__select:focus-visible,
body.nv .customer button:focus-visible {
  box-shadow: var(--nv-focus-ring);
  outline: none;
}

body.nv .field__input,
body.nv .select__select,
body.nv .text-area {
  border-radius: var(--nv-radius-sm);
  transition:
    border-color var(--duration-default) var(--nv-ease),
    box-shadow var(--duration-default) var(--nv-ease);
}


/* ----------------------------------------------------------------------------
   Product card
   ----------------------------------------------------------------------------
   card_style is "card" and card_text_alignment is "center" in settings, so the
   card renders as a filled surface. Cream surface, soft elevation, and a
   restrained hover — base.css's default card shadow was a 15px blur at 10%
   opacity offset 2/6, which reads as a drop shadow rather than elevation.
   ---------------------------------------------------------------------------- */
body.nv .card-wrapper .card {
  border-radius: var(--nv-radius-md);
  overflow: hidden;
}

body.nv .card--card {
  box-shadow: var(--nv-shadow-1);
  transition:
    box-shadow var(--duration-default) var(--nv-ease),
    transform var(--duration-default) var(--nv-ease-out);
}

@media (hover: hover) and (pointer: fine) {
  body.nv .card-wrapper:hover .card--card {
    box-shadow: var(--nv-shadow-2);
    transform: translateY(-2px);
  }
}

body.nv .card__heading {
  font-size: var(--nv-text-base);
  font-weight: var(--nv-weight-medium);
  letter-spacing: var(--nv-tracking-normal);
  line-height: var(--nv-lh-snug);
}

body.nv .card__content {
  padding-block-start: var(--nv-space-xs);
}

body.nv .card-information > * + * {
  margin-block-start: var(--nv-space-3xs);
}

/* Quick-add sits inside the card; keep it visually secondary to the PDP CTA
   while still clearing 44px. */
body.nv .quick-add__submit {
  min-height: var(--nv-tap-min);
  border-radius: var(--nv-radius-sm);
  font-size: var(--nv-text-sm);
}


/* ----------------------------------------------------------------------------
   Page gutter
   ----------------------------------------------------------------------------
   base.css:399 hardcodes `padding: 0 1.5rem` on .page-width and steps to 5rem
   at 750px. 15px is tight on a phone; 20px gives the content room to breathe
   without losing grid width.
   ---------------------------------------------------------------------------- */
body.nv .page-width {
  padding-inline: var(--nv-gutter);
}


/* ----------------------------------------------------------------------------
   Price badges  (retired from the legacy fence)
   ----------------------------------------------------------------------------
   Colours come from --color-base-* rather than --color-foreground/-background
   on purpose. The sale badge carries a colour-scheme class (.color-accent-2),
   which remaps the semantic tokens locally — so reading the semantic ones here
   would tint the badge with whatever scheme it happens to sit in. The base
   tokens are global and give a consistent ink chip everywhere.

   The legacy version was a burgundy gradient (#7A2030 -> #5F1825) with three
   stacked shadows, 800 weight and a coloured glow. Flat ink reads as
   considered rather than shouty, and stops the badge competing with the
   buy button for attention.
   ---------------------------------------------------------------------------- */
body.nv .badge.price__badge-sale,
body.nv .badge.price__badge-sale.color-accent-2 {
  background: rgb(var(--nv-cta));
  background-color: rgb(var(--nv-cta));
  color: #FFFFFF;
  border: none;
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-wide);
  font-size: var(--nv-text-xs);
  padding: 0.7rem var(--nv-space-xs);
  border-radius: var(--nv-radius-xs);
  display: inline-flex;
  align-items: center;
  gap: var(--nv-space-3xs);
  text-transform: uppercase;
  white-space: nowrap;
  align-self: center;
  margin: 0;
  box-shadow:
    0 1px 2px rgba(var(--nv-shadow-color), .10),
    0 3px 8px rgba(var(--nv-cta), .32),
    0 8px 20px rgba(var(--nv-cta), .18);
  position: relative;
}

/* Kept from the legacy block: the discount glyph is intentionally suppressed
   so the badge reads as pure text. */
body.nv .badge.price__badge-sale .icon-discount {
  display: none;
}

body.nv .badge.price__badge-sale .badge__percentage-saved {
  font-weight: var(--nv-weight-bold);
  font-size: var(--nv-text-xs);
  letter-spacing: var(--nv-tracking-wide);
}

body.nv .badge.price__badge-sale .nowrap {
  display: inline-flex;
  align-items: center;
  gap: var(--nv-space-3xs);
}

/* Sold-out was 10px in the legacy block — under the 11px floor for this file. */
body.nv .badge.price__badge-sold-out {
  background: rgba(var(--color-base-text), .55);
  color: rgb(var(--color-base-background-1));
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-wide);
  font-size: var(--nv-text-2xs);
  padding: 0.6rem 1.1rem;
  border-radius: var(--nv-radius-xs);
  text-transform: uppercase;
  border: none;
}


/* ----------------------------------------------------------------------------
   Price block  (retired from the legacy fence)
   ----------------------------------------------------------------------------
   LOAD-BEARING: the .price__regular { display: none } rule below is NOT
   cosmetic. On a sale, this theme renders BOTH .price__regular and
   .price__sale. Without this rule the regular price appears a second time,
   beside the sale price. Do not remove it without checking an on-sale product.

   Sizing moved from a hardcoded 36px/900 to --nv-price-size (2.8rem mobile,
   3.2rem desktop) at weight 600. 900 on a 36px number was the least premium
   element on the page; the smaller, lighter figure still dominates the block
   because everything around it got quieter too.
   ---------------------------------------------------------------------------- */
body.nv .price.price--on-sale {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--nv-space-xs);
  margin: 0 0 var(--nv-space-sm);
}

body.nv .price.price--on-sale .price__container {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: nowrap;
  gap: var(--nv-space-xs);
  width: auto;
}

/* See the LOAD-BEARING note above. */
body.nv .price.price--on-sale .price__regular {
  display: none;
}

body.nv .price.price--on-sale .price__sale {
  display: inline-flex;
  align-items: baseline;
  gap: var(--nv-space-xs);
  margin: 0;
}

body.nv .price .price-item.price-item--sale.main-price,
body.nv .price .price-item--sale.accent-color-accent-1 {
  color: rgb(var(--color-base-text));
  font-family: var(--font-heading-family);
  font-size: var(--nv-price-size);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-tight);
  line-height: var(--nv-lh-tight);
}

body.nv .price__compare-price {
  display: inline-flex;
  align-items: baseline;
}

body.nv .price__compare-price s,
body.nv .price__compare-price .price-item--regular,
body.nv .price .main-comapre-price {
  font-size: var(--nv-text-base);
  font-weight: var(--nv-weight-regular);
  color: var(--nv-ink-4);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  letter-spacing: var(--nv-tracking-normal);
  font-family: var(--font-body-family);
}


/* ----------------------------------------------------------------------------
   Buy buttons — add to cart AND checkout  (retired from the legacy fence)
   ----------------------------------------------------------------------------
   The legacy fence styled these as two separate ~180-line blocks that were
   declaration-for-declaration identical apart from the selectors and which
   label element carried the arrow. They are one ruleset here.

   .main-product-atc is worn by BOTH the main product-form button and the
   sticky bar's button (blocks/product_sticky-atc.liquid gives it
   "sticky-atc__add_button button main-product-atc"). Sizing overrides for the
   sticky variant live in the sticky-ATC section, later in this file.

   The legacy version set --color-button and --color-base-accent-1 on the
   element itself with !important, which is why no palette change could ever
   reach these buttons. Same technique here minus the !important — element-level
   custom properties still beat :root, and body.nv (0,2,1) beats base.css's
   .button (0,1,0), so nothing has to shout.

   :not(.sticky-atc__add_button) matters. The sticky bar's button wears
   .main-product-atc too, but it lives in a horizontal bar alongside a
   thumbnail, title and price — applying width:100% and a 56px height to it
   would wreck that layout and fight base.css's own
   .sticky-atc--mobile-btn-full / --desktop-btn-full handling. The bar's
   button is styled separately in the MOBILE section below.
   ---------------------------------------------------------------------------- */
body.nv .main-product-atc:not(.sticky-atc__add_button),
body.nv #CartDrawer-Checkout,
body.nv .cart__checkout-button {
  --color-button: var(--nv-cta);
  --color-button-text: 255, 255, 255;
  --alpha-button-background: 1;
  --alpha-button-border: 0;

  background: rgb(var(--nv-cta));
  background-image: none;
  color: #FFFFFF;

  font-family: var(--font-heading-family);
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
  line-height: 1;

  border: none;
  border-radius: var(--nv-radius-sm);
  padding: 2rem var(--nv-space-md);
  min-height: 5.6rem;
  width: 100%;
  box-sizing: border-box;

  box-shadow: var(--nv-shadow-2);

  cursor: pointer;
  transition:
    transform var(--duration-default) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease),
    background-color var(--duration-default) var(--nv-ease);
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--nv-space-2xs);
  -webkit-appearance: none;
  appearance: none;
  -webkit-tap-highlight-color: transparent;
  outline: none;

  animation: nvAtcBreathe 6s ease-in-out infinite;
}

/* Softened from the legacy atcBreathe. The old version pulsed scale 1 -> 1.005
   AND swelled a six-layer shadow with a 32px -> 40px coloured glow every 4.5s;
   the shadow swell, not the scale, is what read as a throb. Scale is now
   1.003, the glow is gone, and the cycle is slower. Still catches the eye in
   peripheral vision; no longer looks like the page is breathing. */
@keyframes nvAtcBreathe {
  0%, 100% { transform: scale(1);     box-shadow: var(--nv-shadow-2); }
  50%      { transform: scale(1.003); box-shadow: var(--nv-shadow-3); }
}

/* Softened from atcShimmer: highlight opacity .22 -> .10, cycle 5.5s -> 8s.
   ----------------------------------------------------------------------------
   :not(.sticky-atc__add_button) IS LOAD-BEARING — do not drop it.

   The sizing rule above already excludes the sticky bar's button, and that rule
   is where `position: relative` and `overflow: hidden` are declared. This
   shimmer rule originally did NOT carry the same exclusion, so the pseudo
   element was created on the sticky bar's button but had no positioned,
   clipping parent to live in. It therefore resolved its containing block to the
   fixed <sticky-atc> bar and swept the FULL WIDTH of the bar (-100% -> 200%)
   before snapping back — a rogue band tracking across the bar on a loop, with
   nothing to clip it. Reported as "this weird border on desktop that animates
   across the bar then jumps back to the original position".

   The sticky bar's button is a scroll trigger, not the primary CTA; it gets no
   shimmer at all now, which is also why it needs no clipping context. */
body.nv .main-product-atc:not(.sticky-atc__add_button)::before,
body.nv #CartDrawer-Checkout::before,
body.nv .cart__checkout-button::before {
  content: '';
  position: absolute;
  top: 0;
  inset-inline-start: -100%;
  width: 55%;
  height: 100%;
  background: linear-gradient(115deg, transparent, rgba(255, 255, 255, .10), transparent);
  animation: nvAtcShimmer 8s ease-in-out infinite;
  pointer-events: none;
  z-index: 1;
}

@keyframes nvAtcShimmer {
  0%, 70% { inset-inline-start: -100%; }
  100%    { inset-inline-start: 200%; }
}

body.nv .main-product-atc:hover,
body.nv #CartDrawer-Checkout:hover,
body.nv .cart__checkout-button:hover {
  background: rgb(var(--nv-cta-hover));
  color: #FFFFFF;
  transform: translateY(-2px);
  box-shadow: var(--nv-shadow-3);
  animation: none;
}

body.nv .main-product-atc:active,
body.nv #CartDrawer-Checkout:active,
body.nv .cart__checkout-button:active {
  background: rgb(var(--nv-cta));
  transform: translateY(0) scale(.995);
  box-shadow: var(--nv-shadow-1);
  animation: none;
  transition-duration: var(--duration-short);
}

body.nv .main-product-atc:focus,
body.nv #CartDrawer-Checkout:focus,
body.nv .cart__checkout-button:focus {
  outline: none;
}

body.nv .main-product-atc:focus-visible,
body.nv #CartDrawer-Checkout:focus-visible,
body.nv .cart__checkout-button:focus-visible {
  box-shadow: var(--nv-focus-ring);
  outline: none;
  animation: none;
}

body.nv .main-product-atc:disabled,
body.nv .main-product-atc[disabled],
body.nv #CartDrawer-Checkout:disabled,
body.nv .cart__checkout-button:disabled,
body.nv .cart__checkout-button[disabled] {
  background: rgba(var(--color-base-text), .22);
  color: #FFFFFF;
  cursor: not-allowed;
  opacity: .75;
  transform: none;
  animation: none;
  box-shadow: none;
}

body.nv .main-product-atc:disabled::before,
body.nv .main-product-atc[disabled]::before,
body.nv #CartDrawer-Checkout:disabled::before,
body.nv .cart__checkout-button:disabled::before {
  display: none;
}

/* Label sits above the shimmer layer. */
body.nv .main-product-atc .main-atc__label,
body.nv .main-product-atc .button__label,
body.nv #CartDrawer-Checkout .button__label,
body.nv .cart__checkout-button .button__label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--nv-space-2xs);
  position: relative;
  z-index: 2;
  color: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  line-height: inherit;
  margin: 0;
  background: transparent;
}

body.nv .main-product-atc .main-atc__label__text {
  position: relative;
  z-index: 2;
  color: inherit;
  display: inline-flex;
  align-items: center;
}

/* The arrow is kept — it was a deliberate affordance in the original.
   The ATC hangs it off .main-atc__label__text; the checkout buttons hang it
   off .button__label. Both are listed so the two stay identical. */
body.nv .main-product-atc .main-atc__label__text::after,
body.nv #CartDrawer-Checkout .button__label::after,
body.nv .cart__checkout-button .button__label::after {
  content: ' \2192';
  display: inline-block;
  margin-inline-start: var(--nv-space-2xs);
  font-weight: var(--nv-weight-semibold);
  opacity: .85;
  transition: transform var(--duration-default) var(--nv-ease);
}

body.nv .main-product-atc:hover .main-atc__label__text::after,
body.nv #CartDrawer-Checkout:hover .button__label::after,
body.nv .cart__checkout-button:hover .button__label::after {
  transform: translateX(4px);
  opacity: 1;
}

body.nv .main-product-atc:active .main-atc__label__text::after,
body.nv #CartDrawer-Checkout:active .button__label::after,
body.nv .cart__checkout-button:active .button__label::after {
  transform: translateX(2px);
}

body.nv .main-product-atc .loading-overlay__spinner,
body.nv #CartDrawer-Checkout .loading-overlay__spinner,
body.nv .cart__checkout-button .loading-overlay__spinner {
  position: relative;
  z-index: 2;
}

body.nv .main-product-atc .spinner .path,
body.nv #CartDrawer-Checkout .spinner .path,
body.nv .cart__checkout-button .spinner .path {
  stroke: #FFFFFF;
}

body.nv .main-product-atc .main-atc__error {
  position: relative;
  z-index: 2;
  color: #FFFFFF;
  font-weight: var(--nv-weight-semibold);
}

/* Desktop step-up for the buy buttons. The base rules above are the mobile
   tier — the legacy CSS had this the other way round, with a max-width:767px
   block cutting desktop values down. Same end sizes, mobile-first, and no
   767px seam. */
@media screen and (min-width: 750px) {
  body.nv .main-product-atc,
  body.nv #CartDrawer-Checkout,
  body.nv .cart__checkout-button {
    font-size: var(--nv-text-base);
    min-height: 6rem;
    padding: 2.2rem var(--nv-space-md);
  }
}


/* ============================================================================
   SHARED COMPONENT LANGUAGE  —  site-wide
   ----------------------------------------------------------------------------
   Originally scoped to the homepage so it could be judged before the proven
   funnel moved. That scope turned into a bug:

   testimonial-carousel is used by the homepage AND every product template.
   With the review-card rules pinned to the homepage, product pages fell back
   to the section's own stylesheet — pale yellow rating pill, bare verified
   text, no card treatment. The same component rendering two different ways
   purely because of where the rules were scoped.

   Now site-wide. Genuinely page-specific rules keep their own scope: the hero
   targets .hero-banner__* (homepage-only by markup) and product rules sit
   under body.nv-tpl--product further down.

   Nothing here changes copy, section order, or any merchant setting.
   ============================================================================ */

/* ---- Section rhythm ---------------------------------------------------------
   spacing_sections is 0 and stays 0 (raising it double-pads the 58 sections
   that carry their own padding). Instead the homepage gets its air here, where
   it can be tuned per-section without touching any JSON.
   -------------------------------------------------------------------------- */
body.nv .shopify-section {
  --nv-band: transparent;
}

/* min-block-size, NOT padding. An earlier pass set padding-block here, which
   REPLACED the per-section padding merchants had already tuned — several
   sections got visibly shorter as a result. A minimum can only ever add
   height, so a section that was already generous is left exactly as it was. */
body.nv main > .shopify-section {
  min-block-size: 0;
}

/* NO inter-section margin. It was added to restore height after the padding
   mistake, but every section already carries its own padding — stacking a
   margin on top produced a dead white band between the hero and the section
   below it. Sections provide their own rhythm; this only zeroes any inherited
   margin so nothing doubles up. */
body.nv main > .shopify-section + .shopify-section {
  margin-block-start: 0;
}

/* ---- Section headings -------------------------------------------------------
   EVERY homepage section names its heading differently — .title, .furniture-
   heading, .product-showcase__title, .cf-title, .reviews-header h2,
   .results__content__heading. An earlier pass only caught `.title`, which is
   why "Our Favorites" went large while "Browse Collections" stayed small. The
   full list is enumerated here so the page has ONE heading size, not six.
   -------------------------------------------------------------------------- */
body.nv .title,
body.nv .title-wrapper h2,
body.nv .section-heading,
body.nv .furniture-heading,
body.nv .product-showcase__title,
body.nv .collection__title > .title,
body.nv .results__content__heading,
body.nv .reviews-header h2,
body.nv .cf-title {
  font-size: clamp(2.8rem, 4.4vw, 5.2rem);
  font-weight: var(--nv-weight-bold);
  letter-spacing: -0.03em;
  line-height: 1.04;
  margin-block-end: var(--nv-space-md);
  text-wrap: balance;
}

@media screen and (min-width: 750px) {
  body.nv .title,
  body.nv .title-wrapper h2,
  body.nv .furniture-heading,
  body.nv .product-showcase__title,
  body.nv .reviews-header h2,
  body.nv .cf-title {
    margin-block-end: var(--nv-space-lg);
  }
}

body.nv .title-wrapper {
  margin-block-end: var(--nv-space-lg);
}

/* ---- Hero -------------------------------------------------------------------
   The previous rule targeted .hero-section2__heading, which does not exist —
   the class is .hero-banner__heading. That is why the hero never changed.
   -------------------------------------------------------------------------- */
body.nv .hero-banner__heading {
  font-size: clamp(4.2rem, 8vw, 9.6rem);
  font-weight: var(--nv-weight-bold);
  letter-spacing: -0.04em;
  line-height: 0.95;
  margin-block-end: var(--nv-space-md);
  text-wrap: balance;
}

body.nv .hero-banner__subheading {
  font-size: clamp(1.6rem, 1.6vw, 2.1rem);
  line-height: var(--nv-lh-body);
  max-width: 46ch;
  margin-block-end: var(--nv-space-lg);
  opacity: .92;
}

/* Hero CTA — the accent's first appearance on the page. It was a flat white
   slab, which disappeared into a pale photo. Coral pill, real elevation. */
body.nv .hero-banner__button {
  background: rgb(var(--nv-cta));
  color: #FFFFFF;
  border: none;
  border-radius: var(--nv-radius-pill);
  padding: 1.8rem 3.4rem;
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  box-shadow: var(--nv-shadow-3);
  transition:
    background-color var(--duration-default) var(--nv-ease),
    transform var(--duration-default) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease);
}

@media (hover: hover) and (pointer: fine) {
  body.nv .hero-banner__button:hover {
    background: rgb(var(--nv-cta-hover));
    transform: translateY(-3px);
    box-shadow: var(--nv-shadow-4);
  }
}

/* ---- Collection circles (Browse Collections) --------------------------------
   Static circles read as clip-art. A ring that closes in on hover, plus a
   lift, makes them feel like controls rather than decoration.
   -------------------------------------------------------------------------- */
body.nv .collection-title {
  font-size: var(--nv-text-lg);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-normal);
  line-height: var(--nv-lh-snug);
}

/* CLIPPING FIX. .collections-carousel is `overflow-x: auto; overflow-y: hidden`.
   Per spec, once one axis is auto/scroll the other can never compute to
   `visible` — so the hover lift and the ring were being sliced off. The only
   real fix is interior room: pad the scroll box, then pull the same amount
   back with negative margin so the section's own spacing is unchanged. */
body.nv .collections-carousel {
  padding-block: var(--nv-space-md);
  margin-block: calc(var(--nv-space-md) * -1);
  /* The section draws a visible grey scrollbar under the circles. It has
     prev/next arrows either side, so the bar is redundant and reads as a
     stray rule across the layout. Scrolling still works — by drag, wheel,
     trackpad and keyboard. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

body.nv .collections-carousel::-webkit-scrollbar {
  display: none;
}

body.nv .collection-image-wrapper {
  transition:
    transform var(--duration-default) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease),
    border-color var(--duration-default) var(--nv-ease);
}

/* Bigger on desktop — 180px circles were small for a five-across row. */
@media screen and (min-width: 990px) {
  body.nv .collection-item {
    width: 26rem;
  }
  body.nv .collection-image-wrapper {
    width: 24rem;
    height: 24rem;
  }
  body.nv .collection-title {
    font-size: var(--nv-text-xl);
  }
}

@media (hover: hover) and (pointer: fine) {
  body.nv .collection-item:hover .collection-image-wrapper {
    transform: translateY(-6px) scale(1.03);
    box-shadow:
      0 0 0 3px rgb(var(--color-base-background-1)),
      0 0 0 5px rgba(var(--nv-cta), .55),
      var(--nv-shadow-3);
  }
  body.nv .collection-item:hover .collection-title {
    color: rgb(var(--nv-cta));
  }
}

body.nv .collection-title {
  transition: color var(--duration-default) var(--nv-ease);
}

/* "View collection" reads as fine print; make it a deliberate affordance. */
body.nv .view-collection-link {
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  color: var(--nv-ink-3);
  transition: color var(--duration-default) var(--nv-ease);
}

@media (hover: hover) and (pointer: fine) {
  body.nv .collection-item:hover .view-collection-link {
    color: rgb(var(--nv-cta));
  }
}

body.nv .carousel-nav {
  width: var(--nv-tap-min);
  height: var(--nv-tap-min);
  border-radius: 50%;
  background: rgb(var(--color-base-background-1));
  border: 1px solid var(--nv-line);
  box-shadow: var(--nv-shadow-2);
  transition:
    transform var(--duration-default) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease);
}

@media (hover: hover) and (pointer: fine) {
  body.nv .carousel-nav:hover {
    transform: scale(1.08);
    box-shadow: var(--nv-shadow-3);
  }
}

/* ---- Product / collection cards ---------------------------------------------
   White cards on the grey band, with a real border rather than relying on a
   shadow alone. The image scales inside a fixed frame on hover — the card
   itself does not move, so nothing reflows and nothing shifts under a thumb.
   -------------------------------------------------------------------------- */
body.nv .card-wrapper .card,
body.nv .card--card {
  background: rgb(var(--color-base-background-1));
  border: 1px solid var(--nv-line);
  border-radius: var(--nv-radius-md);
  box-shadow: none;
  overflow: hidden;
  transition:
    border-color var(--duration-default) var(--nv-ease),
    box-shadow var(--duration-default) var(--nv-ease);
}

body.nv .card__media,
body.nv .card .media {
  overflow: hidden;
}

body.nv .card__media img,
body.nv .card .media img {
  transition: transform var(--duration-long) var(--nv-ease-out);
}

@media (hover: hover) and (pointer: fine) {
  body.nv .card-wrapper:hover .card,
  body.nv .card-wrapper:hover .card--card {
    border-color: rgba(var(--color-foreground), .22);
    box-shadow: var(--nv-shadow-2);
  }
  body.nv .card-wrapper:hover .card__media img,
  body.nv .card-wrapper:hover .card .media img {
    transform: scale(1.04);
  }
}

body.nv .card__content {
  padding: var(--nv-space-sm);
}

body.nv .card__heading,
body.nv .card__heading a {
  font-size: var(--nv-text-base);
  font-weight: var(--nv-weight-medium);
  line-height: var(--nv-lh-snug);
  letter-spacing: var(--nv-tracking-normal);
}

/* ---- Vendor / eyebrow -------------------------------------------------------
   The "NEAT VALUES" line above a product title reads as an afterthought at
   default size. Small-caps with real tracking turns it into a brand signal.
   -------------------------------------------------------------------------- */
body.nv .caption-with-letter-spacing,
body.nv .card__vendor,
body.nv .product-showcase__vendor {
  font-size: var(--nv-text-2xs);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--nv-ink-3);
}

/* ============================================================================
   CUSTOMER REVIEWS — full rebuild
   ----------------------------------------------------------------------------
   The section ships its own <style> in <body>, which loads AFTER this file.
   body.nv .review-card is (0,3,1) against its (0,1,0), so we win on
   specificity without !important.

   Social proof is the most persuasive block on the page and it was rendering
   as flat panels. This gives it depth, a real hierarchy, and a live signal.
   ============================================================================ */

body.nv .reviews-section {
  background: rgb(var(--color-base-background-2));
  padding-block: var(--nv-space-2xl);
}

/* Rating pill — was pale yellow, which read as a warning. Now an ink chip. */
body.nv .reviews-rating {
  background: rgb(var(--color-base-background-1));
  color: rgb(var(--color-base-text));
  border-radius: var(--nv-radius-pill);
  padding: 1rem 2rem;
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-normal);
  box-shadow:
    0 0 0 1px rgba(var(--nv-shadow-color), .06),
    0 2px 6px rgba(var(--nv-shadow-color), .07),
    0 10px 28px rgba(var(--nv-shadow-color), .10);
}

body.nv .reviews-rating .rating-stars svg,
body.nv .reviews-rating svg {
  fill: #F5B301;
}

/* ---- The card --------------------------------------------------------------- */
body.nv .review-card {
  background: rgb(var(--color-base-background-1));
  border: 1px solid var(--nv-line);
  border-radius: var(--nv-radius-lg);
  overflow: hidden;
  box-shadow: var(--nv-shadow-1);
  transition:
    transform var(--duration-default) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease),
    border-color var(--duration-default) var(--nv-ease);
}

@media (hover: hover) and (pointer: fine) {
  body.nv .review-card:hover {
    transform: translateY(-6px);
    box-shadow: var(--nv-shadow-3);
    border-color: rgba(var(--color-foreground), .2);
  }
  body.nv .review-card:hover img {
    transform: scale(1.05);
  }
}

body.nv .review-card img {
  transition: transform var(--duration-long) var(--nv-ease-out);
}

/* The card is now a flex column, which makes the photo a flex item and so
   shrinkable. Nothing should ever compress it: the fixed 260px crop is what
   holds the three photos on one line across the row, so never let the photo
   absorb the column's spare height. */
body.nv .review-card img {
  flex: 0 0 auto;
}

body.nv .review-content {
  padding: var(--nv-space-md);
}

body.nv .review-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--nv-space-2xs);
  margin-block-end: var(--nv-space-sm);
}

body.nv .review-card .stars svg {
  fill: #F5B301;
  width: 1.6rem;
  height: 1.6rem;
}

body.nv .review-card .name {
  font-weight: var(--nv-weight-bold);
  font-size: var(--nv-text-sm);
  letter-spacing: var(--nv-tracking-normal);
  background: none;
  padding: 0;
}

/* ---- Verified buyer — the live signal ---------------------------------------
   A green pill with a genuinely pulsing dot. The dot animates a box-shadow
   ring rather than width/height so it never triggers layout, and the halo is
   drawn on a ::after so the dot itself stays crisp.
   -------------------------------------------------------------------------- */
body.nv .review-card .verified {
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  background: rgba(22, 163, 74, .10);
  color: #15803D;
  border: 1px solid rgba(22, 163, 74, .22);
  border-radius: var(--nv-radius-pill);
  padding: .45rem 1rem .45rem .8rem;
  font-size: 0.95rem;
  font-weight: var(--nv-weight-bold);
  letter-spacing: .05em;
  text-transform: uppercase;
  white-space: nowrap;
}

body.nv .review-card .verified .dot {
  position: relative;
  flex: 0 0 auto;
  width: .7rem;
  height: .7rem;
  border-radius: 50%;
  background: #16A34A;
}

body.nv .review-card .verified .dot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: #16A34A;
  animation: nvVerifiedPulse 2s cubic-bezier(.4, 0, .6, 1) infinite;
}

@keyframes nvVerifiedPulse {
  0%   { transform: scale(1);   opacity: .65; }
  70%  { transform: scale(2.6); opacity: 0;   }
  100% { transform: scale(2.6); opacity: 0;   }
}

body.nv .review-content p {
  font-size: var(--nv-text-sm);
  line-height: 1.65;
  color: var(--nv-ink-2);
}

/* ---- Purchased-product footer -----------------------------------------------
   Was a coral wash (var(--nv-cta-tint)). Changed to neutral grey: a warm
   pink-red panel sitting directly under review copy reads as a warning or an
   error state, which is the last association you want on a five-star review.

   Deliberately NOT green either. Green already carries exactly one meaning on
   this card — the VERIFIED BUYER pill and its pulsing dot. Repeating it on the
   panel below spends that signal on decoration and leaves two near-but-not-
   quite-matching greens stacked in one card, which reads as accidental. A
   green wash behind a price also implies savings, a claim this panel isn't
   making: it states what the reviewer bought, not that it was a deal.

   Neutral grey makes the panel recede so the photo, the stars and the green
   verification do the talking, and it matches the #F5F5F5 ground already
   chosen for the site. The accent survives where it belongs — on the price.
   Derived from --color-foreground so it holds up in any colour scheme, with a
   literal fallback so it can never render as untinted white.
   -------------------------------------------------------------------------- */
body.nv .review-product {
  background: rgba(var(--color-foreground), .04);
  border: 1px solid var(--nv-hairline, #E5E5E5);
  border-radius: var(--nv-radius-md);
  padding: var(--nv-space-xs) var(--nv-space-sm);
  margin-block-start: var(--nv-space-sm);
}

body.nv .review-product strong {
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-normal);
}

body.nv .review-product .price {
  font-weight: var(--nv-weight-bold);
  color: rgb(var(--nv-cta));
  font-size: var(--nv-text-sm);
}

body.nv .review-product .date {
  color: var(--nv-ink-3);
  font-size: var(--nv-text-2xs);
}

/* ---- Carousel arrows -------------------------------------------------------- */
body.nv .reviews-nav {
  width: var(--nv-tap-min);
  height: var(--nv-tap-min);
  border-radius: 50%;
  background: rgb(var(--color-base-background-1));
  border: 1px solid var(--nv-line);
  box-shadow: var(--nv-shadow-2);
  transition:
    transform var(--duration-default) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease);
}

@media (hover: hover) and (pointer: fine) {
  body.nv .reviews-nav:hover {
    transform: scale(1.08);
    box-shadow: var(--nv-shadow-3);
  }
}


/* ============================================================================
   REMAINING BUY / SUBMIT ACTIONS
   ----------------------------------------------------------------------------
   Three buttons were still rendering outside the accent because each sits in a
   section with its own hand-written CSS and its own class name, so none of
   them were picked up by the shared buy-button ruleset:

     .product-showcase__add-to-cart   white outline  (a real add-to-cart!)
     .cf-button                       black          (contact submit)
     .footer-newsletter__button       grey           (newsletter signup)

   Every primary action on the page should be the same colour, or the accent
   stops meaning "this is the thing to press".
   ============================================================================ */
body.nv .product-showcase__add-to-cart,
body.nv .cf-button,
body.nv .footer-newsletter__button {
  background: rgb(var(--nv-cta));
  border-color: rgb(var(--nv-cta));
  color: #FFFFFF;
  transition:
    background-color var(--duration-default) var(--nv-ease),
    transform var(--duration-default) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease);
}

@media (hover: hover) and (pointer: fine) {
  body.nv .product-showcase__add-to-cart:hover,
  body.nv .cf-button:hover,
  body.nv .footer-newsletter__button:hover {
    background: rgb(var(--nv-cta-hover));
    border-color: rgb(var(--nv-cta-hover));
    color: #FFFFFF;
    transform: translateY(-2px);
    box-shadow: var(--nv-shadow-2);
  }
}


/* ---- Header -----------------------------------------------------------------
   Height is deliberately untouched — see the note in nv-tokens.liquid about the
   .section-header min-height regression. This adds definition only.
   -------------------------------------------------------------------------- */
body.nv .section-header .header {
  border-bottom: 1px solid var(--nv-line);
}

body.nv .header__menu-item {
  font-weight: var(--nv-weight-medium);
  letter-spacing: var(--nv-tracking-normal);
}


/* ============================================================================
   WORDMARK
   ----------------------------------------------------------------------------
   settings.logo points at shopify://shop_images/NeatValuesLogo.png, which is
   not in this store's Files yet — so header-logo.liquid falls through to
   rendering shop.name as plain text. That text IS the logo right now, and it
   was rendering as ordinary body copy.

   Treated as a real wordmark: heavy, tightly tracked, with a full stop set in
   the CTA colour. The dot is a ::after on the link, so it costs no markup and
   disappears automatically the moment a real logo image is uploaded (the
   :not(.media) guard below).

   If you upload a logo later, delete nothing — these rules stop applying on
   their own.
   ============================================================================ */
body.nv .header__heading-link:not(.media) {
  color: rgb(var(--color-base-text));
  text-decoration: none;
  padding-block: 0;
  display: inline-flex;
  align-items: baseline;
  transition: opacity var(--duration-default) var(--nv-ease);
}

/* The shop name renders as <span class="h2"> inside the link, and .h2 carries
   its own font-size from base.css — so the type has to be set on the span or
   it is silently overridden. */
body.nv .header__heading-link:not(.media) .h2 {
  font-family: var(--font-heading-family);
  font-size: 2.4rem;
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1;
  margin: 0;
}

body.nv .header__heading-link:not(.media)::after {
  content: '.';
  color: rgb(var(--nv-cta));
  margin-inline-start: .1rem;
  font-size: 2.4rem;
  font-weight: 700;
  line-height: 1;
}

@media screen and (min-width: 990px) {
  body.nv .header__heading-link:not(.media) .h2,
  body.nv .header__heading-link:not(.media)::after {
    font-size: 2.8rem;
  }
}

@media (hover: hover) and (pointer: fine) {
  body.nv .header__heading-link:not(.media):hover {
    opacity: .7;
  }
}

/* Footer wordmark gets the same treatment so the brand reads consistently at
   both ends of the page. */
body.nv .footer-brand__name {
  font-family: var(--font-heading-family);
  font-weight: 700;
  letter-spacing: -0.035em;
}

body.nv .footer-brand__name::after {
  content: '.';
  color: var(--nv-footer-muted);
}


/* ============================================================================
   BUY-BUTTON HOVER — label was disappearing into the button
   ----------------------------------------------------------------------------
   The theme ships a hover effect (settings.action_btn_hover = "center"). How it
   works, from base.css:2266-2304:

     at rest   ::after is a full-bleed fill in --color-button, scaleX(1)
     on hover  ::after scales to 0, revealing the element's own background,
               and base.css:2296 sets the LABEL to rgb(var(--color-button))

   That is coherent for a stock button: white background is revealed and the
   label turns the button colour. But the CTA rules set --color-button to the
   coral accent AND paint the element background coral, so on hover the fill
   vanished and a coral label sat on a coral background. Invisible text.

   Rather than fight the effect, the CTA opts out of it: the fill is held at
   scaleX(1) in the hover colour, and the label is forced white on top. The
   ::after carries z-index:-1 in base.css, so the label always stacks above it.

   Specificity note: base.css's rule is (0,4,1) once :hover and the :not()
   argument are counted. Adding body.nv + the element selector takes these to
   (0,4,2), which is the minimum that reliably wins.
   ============================================================================ */
body.nv.action-btns--center button.main-product-atc:hover,
body.nv.action-btns--left button.main-product-atc:hover,
body.nv.action-btns--center button.cart__checkout-button:hover,
body.nv.action-btns--left button.cart__checkout-button:hover,
body.nv.action-btns--center button#CartDrawer-Checkout:hover,
body.nv.action-btns--left button#CartDrawer-Checkout:hover {
  color: #FFFFFF;
}

body.nv button.main-product-atc:hover::after,
body.nv button.cart__checkout-button:hover::after,
body.nv button#CartDrawer-Checkout:hover::after {
  transform: scaleX(1);
  background-color: rgb(var(--nv-cta-hover));
}


/* ============================================================================
   PRODUCT PAGE  —  scoped to body.nv-tpl--product
   ----------------------------------------------------------------------------
   template.name is "product" for all 34 product templates regardless of their
   suffix, so this one scope covers every one of them.

   Presentation only. No copy, block order or setting is touched.
   ============================================================================ */

/* ---- Price -----------------------------------------------------------------
   DO NOT set product price font-size here. It will not work.

   blocks/product_price.liquid emits its own <style> inside <body> using an ID
   selector built from the block id:

     #main-price-XXXX__price .price__sale .price-item--sale { font-size: 2rem }

   That is specificity (1,2,0). Nothing selector-based in this file can beat an
   ID — only another ID or !important, and neither belongs here.

   The sizes are merchant settings, so they are set properly instead:
     regular_price_size / sale_price_size / compare_price_size
   now 54 / 54 / 24 px across all 33 product templates. The schema caps were
   12-32px, which could not express a deals-led price, so they were raised to
   90 / 90 / 48. Merchants can now tune the price in the theme editor.

   Weight, tracking and layout below are safe — the inline rule only sets
   font-size, so these do not collide.
   -------------------------------------------------------------------------- */
body.nv-tpl--product .price .price-item.main-price {
  font-weight: var(--nv-weight-bold);
  letter-spacing: -0.035em;
  line-height: 0.95;
}

/* The struck-through original is hidden on the product page. The saving is
   already stated once, more forcefully, by the SAVE xx% badge beside the
   price — showing both made the block busy and repeated the same fact twice.

   HIDDEN, NOT REMOVED. The badge computes its percentage from
   compare_at_price, so the value has to keep rendering for the badge to have
   anything to calculate. Deleting the markup would take the badge with it.
   The badge is a sibling of .price__compare-price, not a child, so hiding
   this element does not touch it.

   Still announced to screen readers via the .visually-hidden labels the block
   emits alongside it, so the original price is not lost to assistive tech. */
/* SHOWN AGAIN. Hiding it left "$9.99" beside a "SAVE 80%" badge with nothing
   the 80% referred to — the badge asserted a saving the page never evidenced.
   The struck original is the evidence, and it is what makes the badge legible
   at a glance rather than a claim the shopper has to take on trust. */
body.nv-tpl--product .product-page-price .price__compare-price {
  display: inline-flex;
  align-items: baseline;
}

/* Deliberately ~40% of the price. Big enough to read, quiet enough that the
   number being CHARGED stays the one the eye lands on first. */
body.nv-tpl--product .product-page-price .price__compare-price s,
body.nv-tpl--product .product-page-price .main-comapre-price {
  font-size: clamp(1.5rem, 3.4vw, 1.9rem);
  font-weight: var(--nv-weight-regular);
  color: var(--nv-ink-4);
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
  letter-spacing: var(--nv-tracking-normal);
}

body.nv-tpl--product .price.price--on-sale,
body.nv-tpl--product .price__container {
  gap: var(--nv-space-sm);
  align-items: baseline;
  flex-wrap: wrap;
}

/* The save badge has to hold its own next to a 64px number. */
body.nv-tpl--product .badge.price__badge-sale {
  font-size: var(--nv-text-sm);
  padding: 0.9rem var(--nv-space-sm);
  align-self: center;
}

/* ---- Product title ---------------------------------------------------------- */
/* Weight, not size, was what made this shout. At 700 with -0.03em tracking the
   title competed with the price for first read; the price should win, because
   it is the thing being decided. 600 and looser tracking keeps the title
   legible at the same size while letting the number lead. */
body.nv-tpl--product .product__title,
body.nv-tpl--product .product__title h1 {
  font-size: clamp(2.8rem, 3.4vw, 4.2rem);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: -0.018em;
  line-height: 1.12;
  text-wrap: balance;
}

/* ---- Rating row ------------------------------------------------------------
   Stars, then the score, then the count — the order shoppers read. */
body.nv-tpl--product .rating-stars__value {
  font-weight: var(--nv-weight-bold);
  color: rgb(var(--color-base-text));
  font-variant-numeric: tabular-nums;
}
body.nv-tpl--product .rating-stars__label {
  color: var(--nv-ink-3);
  font-weight: var(--nv-weight-regular);
}
body.nv-tpl--product .rating-stars-and-text {
  gap: 0.2rem;
  align-items: center;
}

/* ---- Media ------------------------------------------------------------------ */
body.nv-tpl--product .product__media-wrapper .media,
body.nv-tpl--product .product__media-item .media {
  border-radius: var(--nv-radius-md);
  overflow: hidden;
}

/* ---- Reviews at the foot of the product page --------------------------------
   The product page uses the `testimonials` section, which renders
   .multicolumn-card.testimonial-card — a different component from the
   homepage's .review-card. Matching the two so the two pages read as one site.
   -------------------------------------------------------------------------- */
body.nv-tpl--product .testimonial-card,
body.nv-tpl--product .multicolumn-card {
  background: rgb(var(--color-base-background-1));
  border: 1px solid var(--nv-line);
  border-radius: var(--nv-radius-lg);
  overflow: hidden;
  box-shadow: var(--nv-shadow-1);
  transition:
    transform var(--duration-default) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease),
    border-color var(--duration-default) var(--nv-ease);
}

@media (hover: hover) and (pointer: fine) {
  body.nv-tpl--product .testimonial-card:hover,
  body.nv-tpl--product .multicolumn-card:hover {
    transform: translateY(-6px);
    box-shadow: var(--nv-shadow-3);
    border-color: rgba(var(--color-foreground), .2);
  }
  body.nv-tpl--product .multicolumn-card:hover .media img {
    transform: scale(1.05);
  }
}

body.nv-tpl--product .multicolumn-card .media img {
  transition: transform var(--duration-long) var(--nv-ease-out);
}

body.nv-tpl--product .multicolumn-card__info {
  padding: var(--nv-space-md);
}

/* Same gold as the homepage review stars. */
body.nv-tpl--product .rating-stars svg,
body.nv-tpl--product .rating-stars__container--overlay svg {
  fill: #F5B301;
  color: #F5B301;
}

body.nv-tpl--product .multicolumn-text {
  font-size: var(--nv-text-sm);
  line-height: 1.65;
  color: var(--nv-ink-2);
}

/* ---- Feature columns (custom-columns-new) -----------------------------------
   The "360 Degree Rotation / Universal Compatibility" blocks. The copy was
   fine; the headings had no hierarchy above the body text under them.
   -------------------------------------------------------------------------- */
body.nv-tpl--product .custom-columns__column h3,
body.nv-tpl--product .custom-columns__column .h3 {
  font-size: var(--nv-text-xl);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-tight);
  line-height: var(--nv-lh-snug);
  margin-block-end: var(--nv-space-2xs);
}

body.nv-tpl--product .icons-with-text__icon__icon {
  transition: transform var(--duration-default) var(--nv-ease-out);
}

@media (hover: hover) and (pointer: fine) {
  body.nv-tpl--product .custom-columns__column:hover .icons-with-text__icon__icon {
    transform: scale(1.12) rotate(-4deg);
  }
}

/* ---- Video slider ("See It In Action") ---------------------------------------
   Bare rectangles. Rounded, framed and lifted so they read as playable cards
   rather than pasted screenshots.
   -------------------------------------------------------------------------- */
body.nv-tpl--product .image-slide__image {
  border-radius: var(--nv-radius-md);
  overflow: hidden;
  box-shadow: var(--nv-shadow-1);
  transition:
    transform var(--duration-default) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease);
}

@media (hover: hover) and (pointer: fine) {
  body.nv-tpl--product .image-slide:hover .image-slide__image {
    transform: translateY(-4px);
    box-shadow: var(--nv-shadow-3);
  }
}

/* ---- Delivery timeline -------------------------------------------------------
   The Ordered / Order Ready / Delivered strip sits directly under the CTA, so
   it is a trust signal right at the decision point. Dates get weight, labels
   step back so the two read as a hierarchy rather than one grey block.

   Note for future greps: this block writes class='...' with SINGLE quotes,
   so a search for class=" will miss it entirely.
   -------------------------------------------------------------------------- */
body.nv-tpl--product .shipping-checkpoint__top,
body.nv-tpl--product .shipping-checkpoint__top strong {
  font-weight: var(--nv-weight-bold);
  font-size: var(--nv-text-base);
  color: var(--nv-ink-1);
}

body.nv-tpl--product .shipping-checkpoint__bottom {
  font-size: var(--nv-text-sm);
  color: var(--nv-ink-3);
}


/* ============================================================================
   MOBILE
   ----------------------------------------------------------------------------
   Mobile is the priority traffic source for this store. Everything here is a
   fix for something measurably wrong, not a preference.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   Sticky add-to-cart bar
   ----------------------------------------------------------------------------
   Three real defects in base.css:13562-13592:

   1. No env(safe-area-inset-bottom). The bar is `bottom: 0` with 15px of
      bottom padding, so on notched iPhones the home indicator sits on top of
      the button. The theme has zero references to safe-area-inset anywhere.

   2. `.sticky-atc .button` sets min-height:auto, line-height:2.75em,
      font-size:1.5rem -> a real tap height of ~41px, under the 44px minimum,
      on the single most important control on a product page.

   3. z-index: 2, which is also what .scroll-to-top-btn and .music-player use
      (base.css:2067). Same stacking level, both fixed to the bottom, so the
      floating button lands on top of the bar.
   ---------------------------------------------------------------------------- */
body.nv .sticky-atc {
  z-index: var(--nv-z-sticky-atc);
  padding-block: var(--nv-space-2xs);
  padding-bottom: calc(var(--nv-space-xs) + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--nv-hairline);
  box-shadow: var(--nv-shadow-3);
}

/* Width is deliberately not set — base.css owns that via the
   .sticky-atc--mobile-btn-full / --desktop-btn-full classes the block emits
   from its own settings, and overriding it here would ignore the merchant's
   choice. This only fixes the tap target. */
body.nv .sticky-atc .button {
  min-height: var(--nv-tap-min);
  line-height: 1.2;
  padding-inline: var(--nv-space-md);
}

/* base.css declares --sticky-atc-offset on .scroll-to-top-btn and
   .music-player and reads it in their `bottom` calc, but NOTHING in the theme
   ever assigns it a non-zero value — it is dead in every one of its four
   declaration sites. Give it a real value when a sticky bar is present so the
   floating buttons clear it instead of overlapping. */
@supports selector(:has(*)) {
  body.nv:has(.sticky-atc) .scroll-to-top-btn,
  body.nv:has(.sticky-atc) .music-player {
    --sticky-atc-offset: 7rem;
  }
}

/* ----------------------------------------------------------------------------
   Simplified bar: thumbnail + product name + "Choose bundle"
   ----------------------------------------------------------------------------
   Price, sale badge and variant picker are switched OFF in the block settings
   on every product template rather than hidden here, so the theme editor tells
   the truth about what the bar shows. This section only lays out what remains.

   Belt-and-braces against the rogue sweeping band: even though the shimmer rule
   above now excludes this button, anything that manages to create a pseudo
   element on it would be unclipped for the same reason. Deny both outright. */
   NOTE ON CLASSES: the block swaps the button entirely depending on `function`.
   add_to_cart emits  .sticky-atc__add_button.button.main-product-atc
   scroll_*    emits  .sticky-atc__scroll-btn.button   (no .main-product-atc)
   Both are styled here so switching the setting never drops the styling. */
body.nv .sticky-atc .sticky-atc__add_button::before,
body.nv .sticky-atc .sticky-atc__add_button::after,
body.nv .sticky-atc .sticky-atc__scroll-btn::before,
body.nv .sticky-atc .sticky-atc__scroll-btn::after {
  content: none;
  animation: none;
}

/* Full-bleed bar.

   The block hardcodes `class='sticky-atc-container page-width'`, and
   base.css:396 gives .page-width `max-width: var(--page-width); margin: 0 auto;
   padding: 0 1.5rem`. On a wide screen that centres the bar's contents inside
   1280px and leaves dead gutters either side, so the thumbnail floats in from
   the left edge and the button stops well short of the right.

   All three are overridden so the contents run nearly edge to edge, held off
   the glass by --nv-bleed-pad — a deliberately small 1.2rem (12px) rather than
   .page-width's 1.5rem, so the bar still reads as full-bleed but the thumbnail
   and the button are not touching the screen edge.

   env(safe-area-inset-*) is added on top. It is 0px on ordinary screens and
   only becomes non-zero on a notched phone in landscape — without it the
   button would sit under the notch or the rounded corner. */
body.nv .sticky-atc .sticky-atc-container,
body.nv .sticky-atc .sticky-atc-container.page-width {
  --nv-bleed-pad: 1.2rem;

  display: flex;
  align-items: center;
  gap: var(--nv-space-sm);

  max-width: none;
  width: 100%;
  margin-inline: 0;
  padding-left: calc(var(--nv-bleed-pad) + env(safe-area-inset-left, 0px));
  padding-right: calc(var(--nv-bleed-pad) + env(safe-area-inset-right, 0px));
  box-sizing: border-box;
}

/* base.css:13700 adds padding-right:1rem to .sticky-atc__left at >=750px,
   which would reintroduce a gap between the title and the button. The flex
   gap already handles that spacing. */
body.nv .sticky-atc .sticky-atc__left {
  padding-right: 0;
}

/* Thumbnail: fixed square, never distorts a non-square source. */
body.nv .sticky-atc__image {
  flex: 0 0 auto;
  width: 4.8rem;
  height: 4.8rem;
  border-radius: var(--nv-radius-xs);
  overflow: hidden;
  background: rgba(var(--color-base-text), .04);
}

body.nv .sticky-atc__image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Title takes the slack; the button keeps its intrinsic width. */
body.nv .sticky-atc__left {
  flex: 1 1 auto;
  min-width: 0;
}

body.nv .sticky-atc__title {
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  line-height: 1.25;
  letter-spacing: 0;
  margin: 0;
  color: rgb(var(--color-base-text));

  /* Two lines on mobile, one on desktop — long titles must not push the
     button off-screen or grow the bar's height. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

body.nv .sticky-atc__button {
  flex: 0 0 auto;
}

body.nv .sticky-atc .sticky-atc__add_button,
body.nv .sticky-atc .sticky-atc__scroll-btn {
  background: rgb(var(--nv-cta));
  color: #FFFFFF;
  border: none;
  border-radius: var(--nv-radius-sm);
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
  box-shadow: var(--nv-shadow-1);
  padding-inline: var(--nv-space-lg);
  transition:
    background-color var(--duration-default) var(--nv-ease),
    box-shadow var(--duration-default) var(--nv-ease);
}

body.nv .sticky-atc .sticky-atc__add_button:hover,
body.nv .sticky-atc .sticky-atc__scroll-btn:hover {
  background: rgb(var(--nv-cta-hover));
  color: #FFFFFF;
  box-shadow: var(--nv-shadow-2);
}

/* The label span must stay white — base.css:2296 recolours button labels to
   rgb(var(--color-button)) on hover, which on a coral CTA paints coral on
   coral. Same class of bug already fixed on the main add-to-cart. */
body.nv .sticky-atc .sticky-atc__add_button:hover .sticky-atc__label__text,
body.nv .sticky-atc .sticky-atc__scroll-btn:hover .sticky-atc__label__text,
body.nv .sticky-atc .sticky-atc__add_button .sticky-atc__label__text,
body.nv .sticky-atc .sticky-atc__scroll-btn .sticky-atc__label__text {
  color: #FFFFFF;
}

@media (min-width: 750px) {
  body.nv .sticky-atc__image {
    width: 5.6rem;
    height: 5.6rem;
  }

  body.nv .sticky-atc__title {
    font-size: var(--nv-text-base);
    -webkit-line-clamp: 1;
  }
}


/* ----------------------------------------------------------------------------
   Cart drawer full-width bug
   ----------------------------------------------------------------------------
   settings_data.json sets the drawer's mobile_width to "full", but base.css
   only honours it below 501px (base.css:6920). Between 501px and 749px —
   large phones in landscape, small tablets — it silently falls back to a
   40rem panel with a 30px peek strip, despite the setting saying otherwise.
   Re-gate to the theme's real mobile edge.
   ---------------------------------------------------------------------------- */
@media screen and (max-width: 749px) {
  body.nv .cart-drawer--mobile-width-full .cart-drawer {
    width: 100%;
  }
  body.nv .cart-drawer--mobile-width-full .drawer__inner {
    max-width: 100%;
    width: 100%;
  }
}


/* ----------------------------------------------------------------------------
   iOS focus zoom
   ----------------------------------------------------------------------------
   Safari zooms the viewport when a form control under 16px receives focus,
   and does not zoom back out. base.css:2550 correctly gives .field__input
   1.6rem, but .select__select (base.css:2585) is 1.4rem — so every variant
   dropdown on a product page triggers it.

   max() rather than a flat 1.6rem so this survives someone lowering
   body_scale, which would otherwise drag rem below 16px again.

   NOT fixed with maximum-scale=1 on the viewport meta: zoom-locking is an
   accessibility regression and the current meta tag is correct.
   ---------------------------------------------------------------------------- */
body.nv input,
body.nv select,
body.nv textarea,
body.nv .select__select,
body.nv .field__input {
  font-size: max(16px, var(--nv-text-base));
}


/* ----------------------------------------------------------------------------
   Mobile body copy
   ----------------------------------------------------------------------------
   theme.liquid:365 ships body at 1.5rem (15px), stepping up to 1.6rem only
   from 750px. Mobile is the majority of this store's traffic and it was
   reading at 15px. 16px is the floor for comfortable body copy and it also
   means every inheriting control clears the iOS zoom threshold by default.
   ---------------------------------------------------------------------------- */
body.nv {
  font-size: var(--nv-text-base);
}


/* ----------------------------------------------------------------------------
   Touch targets
   ----------------------------------------------------------------------------
   Enumerated, not blanket. A global min-height on `a` would wreck inline links
   inside rich text. Each of these was measured; see the DIAGNOSTICS block at
   the bottom of this file for the tool used to find them.

   The theme's global primitives are already fine — .button is 49px, header
   icons are 44x44, inputs are 45px. The failures are concentrated in the
   components that matter most on a phone.
   ---------------------------------------------------------------------------- */

/* base.css:8700 re-declares this after :8581 and drops the row to ~36px. */
body.nv .menu-drawer__menu-item {
  padding-block: 1.4rem;
  min-height: var(--nv-tap-min);
  display: flex;
  align-items: center;
}

/* base.css:2020 — 3.5rem + 12px text. */
body.nv .button--tertiary {
  min-height: var(--nv-tap-min);
  font-size: var(--nv-text-xs);
}

/* base.css:2953 gives 4rem on mobile, then base.css:6649 SHRINKS it to 3.5rem
   from 750px up. Floor both. */
body.nv cart-remove-button .button,
body.nv .cart-remove-button .button {
  min-width: var(--nv-tap-min);
  min-height: var(--nv-tap-min);
}

/* base.css:7955 — 2.8rem. */
body.nv .upsell__add-btn {
  min-height: var(--nv-tap-min);
}

/* base.css:10891 — 2.5rem. */
body.nv .product-popup-modal__button {
  min-height: var(--nv-tap-min);
}

/* base.css:6976 sizes this 44x44 correctly but positions it at right:-10px,
   so ~10px of the target hangs outside the panel and is not tappable. */
body.nv .drawer__close {
  inset-inline-end: 0;
}

/* Footer legal links sit in a tight row. Padding + negative margin reaches a
   44px target without moving anything visually. */
body.nv .footer-bottom__legal a {
  display: inline-block;
  padding-block: 1rem;
  margin-block: -1rem;
}


/* ----------------------------------------------------------------------------
   Hover on touch
   ----------------------------------------------------------------------------
   base.css has 123 lines using :hover but only 5 @media (hover: hover) guards.
   On a touchscreen a :hover style latches after a tap and stays until the user
   taps elsewhere. These are the ones that visibly stick.
   ---------------------------------------------------------------------------- */
@media (hover: none) {
  /* base.css:2194 — icon jumps to scale(1.07) and stays there. */
  body.nv .header__icon:hover .icon {
    transform: none;
  }
  /* base.css:8705 — tapped drawer row keeps a grey wash. */
  body.nv .menu-drawer__menu-item:hover,
  body.nv .menu-drawer__close-button:hover {
    background-color: transparent;
  }
}


/* ============================================================================
   DIAGNOSTICS — keep commented. Uncomment one at a time during QA, screenshot,
   then re-comment. None of these should ever ship uncommented.
   ============================================================================ */

/* Tripwire: if the body class is ever rewritten by an app or the theme editor,
   the whole overhaul silently reverts. This makes that loud.
body:not(.nv) { outline: 3px dashed magenta; }
*/

/* Find sub-44px tap targets. Do NOT ship as a real rule — a blanket
   min-height on `a` destroys inline links inside rich text.
@media (pointer: coarse) {
  body.nv a, body.nv button, body.nv summary, body.nv select {
    outline: 1px solid magenta;
    min-height: 44px;
  }
}
*/


/* ============================================================================
   THIRD-PARTY  —  Kaching Bundles sticky bar
   ----------------------------------------------------------------------------
   All that remains of the 775-line inline block that used to sit at the top of
   <body> in layout/theme.liquid. Everything else has been tokenized into the
   COMPONENTS section above; this is the deliberate residue.

   THIS STAYS, WITH ITS !important INTACT. These are not design decisions —
   they are layout defences against markup shipped by an app we do not control
   and cannot re-render. The `min-width: 0` rule near the bottom is
   load-bearing: without it the bundle title refuses to wrap and pushes the
   button off-screen on narrow phones. The original author left a comment
   saying so, and it has been kept.

   Do not tokenize these. Do not remove the !important. Recolour only, and
   re-test on a bundle-enabled product page at 360 / 375 / 430px after any
   change — including after Shopify app updates, since the DOM can change
   underneath these selectors without warning.

   Retired from this fence, in order:
     1. reduced-motion  -> generalized, near the top of this file
     2. save badge      -> COMPONENTS
     3. price block     -> COMPONENTS (incl. the load-bearing
                           .price__regular { display: none } guard)
     4. add to cart     -> COMPONENTS
     5. checkout        -> merged into the shared buy-button ruleset
     6. mobile tier     -> inverted to mobile-first; 767px seam removed
   ============================================================================ */

/* ------------------------------------------------------------
   KEYFRAME RETAINED FOR THE STILL-FENCED KACHING RULES BELOW.
   Defined originally inside the ADD TO CART block. The Kaching sticky bar
   still references it by name, and a CSS animation pointing at an undefined
   keyframe name does nothing at all, with no error — so removing it would
   quietly kill that animation with no warning.

   atcBreathe lived here too and has now been removed: nothing references it
   since the checkout button was merged into the tokenized ruleset above.
   Retire this one the day the Kaching block goes.
------------------------------------------------------------ */
@keyframes atcShimmer {
  0%, 65% { left: -100%; }
  100% { left: 200%; }
}

/* ============================================================
   KACHING BUNDLES STICKY — deep navy, compact, mobile-fixed
============================================================ */
html body button.kaching-bundles-sticky-atc__button,
html body .kaching-bundles-sticky-atc button.kaching-bundles-sticky-atc__button {
  --color-button: 30, 58, 79 !important;
  --color-base-accent-1: 30, 58, 79 !important;
  --color-button-text: 255, 255, 255 !important;

  background: linear-gradient(180deg, #2E2E2E 0%, #141414 50%, #000000 100%) !important;
  background-color: #141414 !important;
  background-image: linear-gradient(180deg, #2E2E2E 0%, #141414 50%, #000000 100%) !important;

  color: #FFFFFF !important;
  font-family: 'Archivo', sans-serif !important;
  font-size: 13px !important;
  font-weight: 700 !important;
  letter-spacing: 1.4px !important;
  text-transform: uppercase !important;
  text-decoration: none !important;
  text-shadow: 0 1px 2px rgba(90, 26, 8, 0.45) !important;
  line-height: 1 !important;

  border: none !important;
  border-color: transparent !important;
  border-width: 0 !important;
  border-radius: 6px !important;
  padding: 13px 22px !important;
  min-height: 44px !important;
  width: auto !important;
  box-sizing: border-box !important;

  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    inset 0 -1px 0 rgba(90, 26, 8, 0.35),
    0 3px 12px rgba(120, 38, 14, 0.4),
    0 0 24px rgba(242, 84, 45, 0.18) !important;

  cursor: pointer !important;
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.3s ease, background 0.3s ease !important;
  position: relative !important;
  overflow: hidden !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 6px !important;
  -webkit-appearance: none !important;
  appearance: none !important;
  -webkit-tap-highlight-color: transparent !important;
  outline: none !important;
}

html body button.kaching-bundles-sticky-atc__button::before {
  content: '' !important;
  position: absolute !important;
  top: 0 !important;
  left: -100% !important;
  width: 50% !important;
  height: 100% !important;
  background: linear-gradient(115deg, transparent, rgba(255, 255, 255, 0.18), transparent) !important;
  animation: atcShimmer 6s ease-in-out infinite !important;
  pointer-events: none !important;
  z-index: 1 !important;
}

html body button.kaching-bundles-sticky-atc__button::after {
  display: inline-block !important;
  margin-left: 4px !important;
  font-weight: 600 !important;
  opacity: 0.85 !important;
  transition: transform 0.25s ease, opacity 0.2s ease !important;
  position: relative !important;
  z-index: 2 !important;
  flex-shrink: 0 !important;
}

html body button.kaching-bundles-sticky-atc__button:hover {
  transform: translateY(-2px) scale(1.01) !important;
  background: linear-gradient(180deg, #FF6A45 0%, #2E2E2E 50%, #141414 100%) !important;
  background-color: #2E2E2E !important;
  background-image: linear-gradient(180deg, #FF6A45 0%, #2E2E2E 50%, #141414 100%) !important;
  color: #FFFFFF !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.32),
    inset 0 -1px 0 rgba(90, 26, 8, 0.35),
    0 6px 18px rgba(120, 38, 14, 0.5),
    0 12px 28px rgba(90, 26, 8, 0.28),
    0 0 36px rgba(242, 84, 45, 0.28) !important;
}

html body button.kaching-bundles-sticky-atc__button:hover::after {
  transform: translateX(4px) !important;
  opacity: 1 !important;
}

html body button.kaching-bundles-sticky-atc__button:active {
  transform: translateY(0) scale(0.99) !important;
  background: linear-gradient(180deg, #000000 0%, #D63F1B 50%, #C0330F 100%) !important;
  background-color: #D63F1B !important;
  background-image: linear-gradient(180deg, #000000 0%, #D63F1B 50%, #C0330F 100%) !important;
  box-shadow:
    inset 0 2px 6px rgba(60, 18, 6, 0.55),
    inset 0 -1px 0 rgba(0, 0, 0, 0.25),
    0 1px 4px rgba(120, 38, 14, 0.25) !important;
  transition-duration: 0.05s !important;
}

html body button.kaching-bundles-sticky-atc__button:active::after {
  transform: translateX(1px) !important;
}

html body button.kaching-bundles-sticky-atc__button:focus { outline: none !important; }
html body button.kaching-bundles-sticky-atc__button:focus-visible {
  outline: 2px solid #000000 !important;
  outline-offset: 2px !important;
}

html body button.kaching-bundles-sticky-atc__button:disabled,
html body button.kaching-bundles-sticky-atc__button[disabled] {
  background: #C5C5C5 !important;
  background-color: #C5C5C5 !important;
  background-image: none !important;
  color: #FFFFFF !important;
  cursor: not-allowed !important;
  opacity: 0.7 !important;
  transform: none !important;
  text-shadow: none !important;
}
html body button.kaching-bundles-sticky-atc__button:disabled::before,
html body button.kaching-bundles-sticky-atc__button[disabled]::before {
  display: none !important;
}

/* ============================================================
   MOBILE — KACHING ONLY
   The price, badge, ATC and checkout rules that used to live in this block
   have been retired. They were a max-width:767px tier bolted onto
   desktop-first base values; the tokenized versions above are mobile-first,
   so those sizes are now the default and desktop steps UP at min-width:750px.
   That also removes 767px, which was a seam: between 750 and 767 the old CSS
   gave desktop price/button sizing but mobile grid sizing.

   767 / 430 / 360 survive here on purpose. For Kaching they are not layout
   breakpoints — they are overflow guards against third-party markup we do
   not control.
============================================================ */
@media (max-width: 767px) {
  /* Kaching — button keeps its natural content width, never shrinks */
  html body button.kaching-bundles-sticky-atc__button,
  html body .kaching-bundles-sticky-atc button.kaching-bundles-sticky-atc__button,
  html body div[class*="kaching-bundles-sticky"] button.kaching-bundles-sticky-atc__button {
    font-size: 11px !important;
    padding: 10px 16px !important;
    min-height: 42px !important;
    letter-spacing: 0.6px !important;
    width: auto !important;
    max-width: none !important;
    min-width: 0 !important;
    flex: 0 0 auto !important;
    flex-shrink: 0 !important;
    flex-grow: 0 !important;
    white-space: nowrap !important;
    overflow: visible !important;
    text-overflow: clip !important;
    box-sizing: border-box !important;
  }
  html body button.kaching-bundles-sticky-atc__button::after {
    margin-left: 4px !important;
    font-size: 1em !important;
    flex-shrink: 0 !important;
  }

  /* Allow title containers to shrink so the title wraps to multiple lines
     instead of pushing the button off-screen. This is the actual fix. */
  html body [class*="kaching-bundles-sticky"] *:not(button):not(img):not(picture):not(source):not(svg):not(path) {
    min-width: 0 !important;
  }
}

@media (max-width: 430px) {
  html body button.kaching-bundles-sticky-atc__button {
    font-size: 10.5px !important;
    padding: 9px 14px !important;
    letter-spacing: 0.5px !important;
  }
}

@media (max-width: 360px) {
  html body button.kaching-bundles-sticky-atc__button {
    font-size: 10px !important;
    padding: 8px 12px !important;
    letter-spacing: 0.4px !important;
  }
  html body button.kaching-bundles-sticky-atc__button::after {
    display: none !important;
  }
}


/* End of the third-party fence. Nothing below this line. */


/* ============================================================================
   MEMBERSHIP HUB
   ============================================================================
   Components for sections/nv-membership-hero, nv-savings-calculator,
   nv-member-catalog and nv-member-status.

   Scoped body.nv (0,2,1) like everything else in this file, so these beat both
   base.css and the 36 section stylesheets that load in <body>. No !important.

   Vertical padding comes from per-section --nv-*-pt/--nv-*-pb custom properties
   that each section emits from its own merchant settings, rather than being
   hardcoded here. Replacing merchant-tuned padding with a blanket value is a
   mistake this overhaul already made once on the homepage.

   Local semantic colours. Positive/negative need to read instantly and neither
   exists in the token layer, so they are defined here rather than reaching for
   the coral CTA, which means "buy" everywhere else on the site.
   ---------------------------------------------------------------------------- */
body.nv .nv-mhero,
body.nv .nv-calc,
body.nv .nv-mcat,
body.nv .nv-mstat {
  --nv-pos: #1B7F4B;
  --nv-pos-tint: rgba(27, 127, 75, .08);
  --nv-neg: #A6402F;
  --nv-neg-tint: rgba(166, 64, 47, .07);
}

/* ---------------------------------------------------------------- HERO ---- */
body.nv .nv-mhero {
  padding-top: var(--nv-mhero-pt, 4.4rem);
  padding-bottom: var(--nv-mhero-pb, 3.9rem);
}

/* Was capped at 76rem, which left the hero as a narrow column stranded in the
   middle of a 1280px page — the headline wrapped after three words while empty
   gutters ran down both sides. Now it uses the page width properly, with the
   measure controlled per-element instead of by squeezing the whole block. */
body.nv .nv-mhero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--nv-space-sm);
  max-width: 112rem;
}

body.nv .nv-mhero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--nv-space-2xs);
  margin: 0;
  font-size: var(--nv-text-2xs);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--nv-ink-3);
}

body.nv .nv-mhero__eyebrow--live {
  color: var(--nv-pos);
}

/* Shared "live" dot. Also used by the catalog and status headers so an active
   membership reads the same way wherever it is surfaced. */
body.nv .nv-mhero__dot,
body.nv .nv-mcat__dot,
body.nv .nv-mstat__dot {
  inline-size: 0.8rem;
  block-size: 0.8rem;
  border-radius: 50%;
  background: var(--nv-pos);
  flex: 0 0 auto;
  animation: nvMemberPulse 2.4s ease-in-out infinite;
}

@keyframes nvMemberPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(27, 127, 75, .45); }
  70%      { box-shadow: 0 0 0 0.7rem rgba(27, 127, 75, 0); }
}

body.nv .nv-mhero__heading {
  margin: 0;
  font-size: var(--nv-text-3xl);
  line-height: var(--nv-lh-tight);
  letter-spacing: var(--nv-tracking-tight);
  color: var(--nv-ink-1);
  text-wrap: balance;
}

body.nv .nv-mhero__sub {
  margin: 0;
  /* 62ch keeps a comfortable reading measure while letting the block itself be
     wide — the previous 54rem hard cap was what made the hero look pinched. */
  max-width: 62ch;
  font-size: var(--nv-text-lg);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-2);
}

body.nv .nv-mhero__sub p { margin: 0; }

body.nv .nv-mhero__price {
  display: flex;
  align-items: baseline;
  gap: var(--nv-space-2xs);
  margin-top: var(--nv-space-2xs);
}

body.nv .nv-mhero__price-amount {
  font-family: var(--font-heading-family);
  font-size: 4.4rem;
  font-weight: var(--nv-weight-bold);
  line-height: 1;
  letter-spacing: var(--nv-tracking-tight);
  color: var(--nv-ink-1);
}

body.nv .nv-mhero__price-cycle {
  font-size: var(--nv-text-base);
  color: var(--nv-ink-3);
}

/* The cadence line is the honesty beat: 13 charges, not 12. Deliberately
   legible rather than fine print. */
body.nv .nv-mhero__cadence {
  margin: 0;
  font-size: var(--nv-text-sm);
  color: var(--nv-ink-3);
}

body.nv .nv-mhero__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--nv-space-xs);
  width: 100%;
  margin-top: var(--nv-space-2xs);
}

body.nv .nv-mhero__cta,
body.nv .nv-mstat__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--nv-tap-min);
  padding: 1.6rem var(--nv-space-xl);
  border: none;
  border-radius: var(--nv-radius-sm);
  background: rgb(var(--nv-cta));
  color: #FFFFFF;
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
  box-shadow: var(--nv-shadow-2);
  transition:
    background-color var(--duration-default) var(--nv-ease),
    box-shadow var(--duration-default) var(--nv-ease),
    transform var(--duration-default) var(--nv-ease-out);
}

body.nv .nv-mhero__cta:hover,
body.nv .nv-mstat__cta:hover {
  background: rgb(var(--nv-cta-hover));
  color: #FFFFFF;
  box-shadow: var(--nv-shadow-3);
  transform: translateY(-2px);
}

body.nv .nv-mhero__fineprint {
  margin: 0;
  max-width: 52rem;
  font-size: var(--nv-text-xs);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
}

body.nv .nv-mhero__fineprint a { color: inherit; }

/* Three stat tiles rather than three floating numbers. Equal columns with
   hairline dividers give the row structure at full width, where the old
   free-flowing flex layout just drifted apart. */
body.nv .nv-mhero__proof {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  margin: var(--nv-space-md) 0 0;
  padding: var(--nv-space-md) 0 0;
  border-top: 1px solid var(--nv-hairline);
  width: 100%;
  max-width: 78rem;
  list-style: none;
}

body.nv .nv-mhero__proof-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  padding: 0 var(--nv-space-2xs);
  border-inline-start: 1px solid var(--nv-hairline);
}

body.nv .nv-mhero__proof-item:first-child {
  border-inline-start: none;
}

body.nv .nv-mhero__proof-num {
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-2xl);
  font-weight: var(--nv-weight-bold);
  line-height: 1;
  letter-spacing: var(--nv-tracking-tight);
  color: rgb(var(--nv-cta));
}

body.nv .nv-mhero__proof-label {
  font-size: var(--nv-text-xs);
  line-height: var(--nv-lh-snug);
  color: var(--nv-ink-3);
  text-align: center;
  text-wrap: balance;
}

/* ---------------------------------------------------------- CALCULATOR ---- */
body.nv .nv-calc {
  padding-top: var(--nv-calc-pt, 4.4rem);
  padding-bottom: var(--nv-calc-pb, 4.4rem);
}

body.nv .nv-calc__inner {
  max-width: 82rem;
}

body.nv .nv-calc__heading {
  margin: 0 0 var(--nv-space-2xs);
  font-size: var(--nv-text-2xl);
  line-height: var(--nv-lh-snug);
  letter-spacing: var(--nv-tracking-tight);
  color: var(--nv-ink-1);
  text-align: center;
}

body.nv .nv-calc__intro {
  margin: 0 auto var(--nv-space-lg);
  max-width: 50rem;
  font-size: var(--nv-text-base);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
  text-align: center;
}

body.nv .nv-calc__intro p { margin: 0; }

body.nv .nv-calc__widget {
  display: grid;
  gap: var(--nv-space-md);
  padding: var(--nv-space-md);
  border: 1px solid var(--nv-hairline);
  border-radius: var(--nv-radius-lg);
  background: rgb(var(--color-background));
  box-shadow: var(--nv-shadow-2);
}

body.nv .nv-calc__controls {
  display: grid;
  gap: var(--nv-space-md);
}

body.nv .nv-calc__label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--nv-space-sm);
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-medium);
  color: var(--nv-ink-2);
}

body.nv .nv-calc__value {
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-lg);
  font-weight: var(--nv-weight-bold);
  color: rgb(var(--nv-cta));
  font-variant-numeric: tabular-nums;
}

/* 4.4rem tap target on the thumb — this is the primary interaction of the
   section and mobile is the priority surface. */
body.nv .nv-calc__range {
  width: 100%;
  margin: var(--nv-space-2xs) 0 0;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  cursor: pointer;
}

body.nv .nv-calc__range::-webkit-slider-runnable-track {
  height: 0.6rem;
  border-radius: var(--nv-radius-pill);
  background: rgba(var(--color-base-text), .10);
}

body.nv .nv-calc__range::-moz-range-track {
  height: 0.6rem;
  border-radius: var(--nv-radius-pill);
  background: rgba(var(--color-base-text), .10);
}

body.nv .nv-calc__range::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 2.4rem;
  height: 2.4rem;
  margin-top: -0.9rem;
  border: none;
  border-radius: 50%;
  background: rgb(var(--nv-cta));
  box-shadow: var(--nv-shadow-2);
}

body.nv .nv-calc__range::-moz-range-thumb {
  width: 2.4rem;
  height: 2.4rem;
  border: none;
  border-radius: 50%;
  background: rgb(var(--nv-cta));
  box-shadow: var(--nv-shadow-2);
}

body.nv .nv-calc__range:focus-visible {
  outline: none;
}

body.nv .nv-calc__range:focus-visible::-webkit-slider-thumb {
  box-shadow: var(--nv-focus-ring);
}

body.nv .nv-calc__hint {
  margin: var(--nv-space-2xs) 0 0;
  font-size: var(--nv-text-xs);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
}

body.nv .nv-calc__result {
  display: grid;
  gap: var(--nv-space-sm);
  padding: var(--nv-space-md);
  border-radius: var(--nv-radius-md);
  background: var(--nv-pos-tint);
  transition: background-color var(--duration-default) var(--nv-ease);
}

body.nv .nv-calc__result--negative {
  background: var(--nv-neg-tint);
}

body.nv .nv-calc__ledger {
  display: grid;
  gap: var(--nv-space-2xs);
}

body.nv .nv-calc__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--nv-space-sm);
  font-size: var(--nv-text-sm);
  color: var(--nv-ink-2);
}

body.nv .nv-calc__row--fee {
  padding-top: var(--nv-space-2xs);
  border-top: 1px solid var(--nv-hairline);
}

body.nv .nv-calc__row-num {
  font-variant-numeric: tabular-nums;
  font-weight: var(--nv-weight-semibold);
  white-space: nowrap;
}

body.nv .nv-calc__net {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--nv-space-2xs);
  padding-top: var(--nv-space-sm);
  border-top: 2px solid var(--nv-hairline-strong);
}

body.nv .nv-calc__net-label {
  font-size: var(--nv-text-sm);
  color: var(--nv-ink-2);
}

body.nv .nv-calc__net-num {
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-3xl);
  font-weight: var(--nv-weight-bold);
  line-height: 1;
  color: var(--nv-pos);
  font-variant-numeric: tabular-nums;
}

body.nv .nv-calc__result--negative .nv-calc__net-num {
  color: var(--nv-neg);
}

body.nv .nv-calc__net-cycle {
  font-size: var(--nv-text-sm);
  color: var(--nv-ink-3);
}

body.nv .nv-calc__year,
body.nv .nv-calc__verdict {
  margin: 0;
  font-size: var(--nv-text-sm);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-2);
}

body.nv .nv-calc__verdict {
  font-weight: var(--nv-weight-semibold);
}

body.nv .nv-calc__disclaimer {
  margin: var(--nv-space-sm) 0 0;
  font-size: var(--nv-text-xs);
  color: var(--nv-ink-4);
  text-align: center;
}

/* ------------------------------------------------------- MEMBER CATALOG ---- */
body.nv .nv-mcat {
  padding-top: var(--nv-mcat-pt, 4.4rem);
  padding-bottom: var(--nv-mcat-pb, 4.4rem);
}

body.nv .nv-mcat__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--nv-space-2xs);
  margin-bottom: var(--nv-space-lg);
  text-align: center;
}

body.nv .nv-mcat__heading {
  margin: 0;
  font-size: var(--nv-text-2xl);
  line-height: var(--nv-lh-snug);
  letter-spacing: var(--nv-tracking-tight);
  color: var(--nv-ink-1);
}

body.nv .nv-mcat__sub {
  max-width: 50rem;
  font-size: var(--nv-text-base);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
}

body.nv .nv-mcat__sub p { margin: 0; }

body.nv .nv-mcat__status {
  display: inline-flex;
  align-items: center;
  gap: var(--nv-space-2xs);
  margin: 0;
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  color: var(--nv-pos);
}

body.nv .nv-mcat__grid {
  display: grid;
  grid-template-columns: repeat(var(--nv-mcat-cols, 2), minmax(0, 1fr));
  gap: var(--nv-space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

body.nv .nv-mcat__item {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--nv-hairline);
  border-radius: var(--nv-radius-md);
  background: rgb(var(--color-background));
  overflow: hidden;
  transition:
    box-shadow var(--duration-default) var(--nv-ease),
    transform var(--duration-default) var(--nv-ease-out);
}

body.nv .nv-mcat__item:hover {
  box-shadow: var(--nv-shadow-3);
  transform: translateY(-2px);
}

body.nv .nv-mcat__link {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-2xs);
  padding: var(--nv-space-2xs) var(--nv-space-2xs) var(--nv-space-xs);
  text-decoration: none;
  color: inherit;
}

body.nv .nv-mcat__media {
  aspect-ratio: 1 / 1;
  border-radius: var(--nv-radius-sm);
  overflow: hidden;
  background: rgba(var(--color-base-text), .04);
}

body.nv .nv-mcat__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

body.nv .nv-mcat__media-empty {
  display: block;
  width: 100%;
  height: 100%;
}

body.nv .nv-mcat__title {
  margin: 0;
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  line-height: var(--nv-lh-snug);
  color: var(--nv-ink-1);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

body.nv .nv-mcat__prices {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  margin-top: auto;
}

body.nv .nv-mcat__list {
  font-size: var(--nv-text-sm);
  color: var(--nv-ink-3);
  text-decoration: line-through;
}

/* Unlocked: the member price is the loudest thing on the card. */
body.nv .nv-mcat__member {
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-xl);
  font-weight: var(--nv-weight-bold);
  line-height: 1.1;
  color: rgb(var(--nv-cta));
  font-variant-numeric: tabular-nums;
}

/* Locked: same weight and size so the gap between list and member price still
   reads as real, but the glyphs are obscured. Blur alone is not enough — it can
   be lifted with devtools — which is fine, because the number is not a secret.
   The DISCOUNT is enforced at checkout, not here. */
body.nv .nv-mcat__member--locked {
  color: var(--nv-ink-4);
  letter-spacing: 0.1em;
  user-select: none;
}

body.nv .nv-mcat__save {
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-semibold);
  color: var(--nv-pos);
}

body.nv .nv-mcat__save--locked {
  color: var(--nv-ink-3);
  font-weight: var(--nv-weight-medium);
}

body.nv .nv-mcat__unlock {
  display: block;
  margin: 0 var(--nv-space-2xs) var(--nv-space-2xs);
  padding: 1.1rem var(--nv-space-xs);
  border-radius: var(--nv-radius-sm);
  background: var(--nv-cta-tint);
  color: rgb(var(--nv-cta));
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  text-align: center;
  text-decoration: none;
  transition: background-color var(--duration-default) var(--nv-ease);
}

body.nv .nv-mcat__unlock:hover {
  background: var(--nv-cta-tint-strong);
  color: rgb(var(--nv-cta));
}

body.nv .nv-mcat__empty {
  margin: 0;
  padding: var(--nv-space-xl) var(--nv-space-sm);
  border: 1px dashed var(--nv-hairline-strong);
  border-radius: var(--nv-radius-md);
  text-align: center;
  font-size: var(--nv-text-sm);
  color: var(--nv-ink-3);
}

body.nv .nv-mcat__foot {
  display: flex;
  justify-content: center;
  margin-top: var(--nv-space-lg);
}

body.nv .nv-mcat__viewall {
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  color: rgb(var(--nv-cta));
  text-underline-offset: 0.3em;
}

/* -------------------------------------------------------- MEMBER STATUS ---- */
body.nv .nv-mstat {
  padding-top: var(--nv-mstat-pt, 3.4rem);
  padding-bottom: var(--nv-mstat-pb, 2.2rem);
}

body.nv .nv-mstat__card {
  display: grid;
  gap: var(--nv-space-md);
  padding: var(--nv-space-md);
  border: 1px solid var(--nv-hairline);
  border-radius: var(--nv-radius-lg);
  background: rgb(var(--color-background));
  box-shadow: var(--nv-shadow-2);
}

body.nv .nv-mstat__top {
  display: grid;
  gap: var(--nv-space-2xs);
}

body.nv .nv-mstat__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--nv-space-2xs);
  margin: 0;
  font-size: var(--nv-text-2xs);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--nv-pos);
}

body.nv .nv-mstat__heading {
  margin: 0;
  font-size: var(--nv-text-2xl);
  line-height: var(--nv-lh-tight);
  letter-spacing: var(--nv-tracking-tight);
  color: var(--nv-ink-1);
}

body.nv .nv-mstat__lede {
  margin: 0;
  font-size: var(--nv-text-base);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-2);
}

body.nv .nv-mstat__facts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--nv-space-sm);
  margin: 0;
  padding: var(--nv-space-sm) 0;
  border-block: 1px solid var(--nv-hairline);
}

body.nv .nv-mstat__fact { margin: 0; }

body.nv .nv-mstat__fact-label {
  margin: 0 0 0.2rem;
  font-size: var(--nv-text-2xs);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  color: var(--nv-ink-3);
}

body.nv .nv-mstat__fact-value {
  margin: 0;
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-xl);
  font-weight: var(--nv-weight-bold);
  line-height: 1.1;
  color: var(--nv-ink-1);
}

body.nv .nv-mstat__fact-sub {
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-regular);
  color: var(--nv-ink-3);
}

body.nv .nv-mstat__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--nv-space-sm);
}

body.nv .nv-mstat__secondary {
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  color: var(--nv-ink-2);
  text-underline-offset: 0.3em;
}

/* Cancellation copy is normal body text, not fine print. Making it hard to
   read is a dark pattern and a compliance risk on a recurring charge. */
body.nv .nv-mstat__manage-text {
  margin: 0;
  font-size: var(--nv-text-xs);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
}

body.nv .nv-mstat__manage-text a {
  color: var(--nv-ink-2);
}

body.nv .nv-mstat__card--pending {
  gap: 0;
  padding: var(--nv-space-sm) var(--nv-space-md);
  border-style: dashed;
  box-shadow: none;
}

body.nv .nv-mstat__pending-text {
  margin: 0;
  font-size: var(--nv-text-sm);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-2);
}

/* --------------------------------------------------------------- 750px ---- */
@media (min-width: 750px) {
  /* Scales past the --nv-text-4xl ceiling deliberately: this is the one H1 on
     a page whose entire job is to sell a recurring charge, and at 3.8rem it
     read as a section heading rather than the top of the page. */
  body.nv .nv-mhero__heading {
    font-size: clamp(4.4rem, 4.6vw, 6.2rem);
    max-width: 18ch;
  }
  body.nv .nv-mhero__sub { font-size: var(--nv-text-xl); }
  body.nv .nv-mhero__price-amount { font-size: 6.4rem; }
  body.nv .nv-mhero__price-cycle { font-size: var(--nv-text-lg); }
  body.nv .nv-mhero__cadence { font-size: var(--nv-text-base); }
  body.nv .nv-mhero__proof-num { font-size: var(--nv-text-3xl); }
  body.nv .nv-mhero__proof-label { font-size: var(--nv-text-sm); }
  body.nv .nv-mhero__proof { padding-top: var(--nv-space-lg); }
  body.nv .nv-mhero__cta { padding-inline: var(--nv-space-2xl); }

  body.nv .nv-calc__heading { font-size: var(--nv-text-3xl); }
  body.nv .nv-calc__widget {
    grid-template-columns: 1.1fr 1fr;
    align-items: start;
    gap: var(--nv-space-xl);
    padding: var(--nv-space-xl);
  }
  body.nv .nv-calc__result { align-self: stretch; }

  body.nv .nv-mcat__heading { font-size: var(--nv-text-3xl); }
  body.nv .nv-mcat__grid { gap: var(--nv-space-md); }
  body.nv .nv-mcat__title { font-size: var(--nv-text-base); }

  body.nv .nv-mstat__card {
    padding: var(--nv-space-xl);
  }
  body.nv .nv-mstat__facts {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
  body.nv .nv-mstat__heading { font-size: var(--nv-text-3xl); }
}

/* Honour the existing reduced-motion contract in this file. */
@media (prefers-reduced-motion: reduce) {
  body.nv .nv-mhero__dot,
  body.nv .nv-mcat__dot,
  body.nv .nv-mstat__dot {
    animation: none !important;
  }
  body.nv .nv-mcat__item:hover,
  body.nv .nv-mhero__cta:hover,
  body.nv .nv-mstat__cta:hover {
    transform: none !important;
  }
}


/* ============================================================================
   SAVINGS CALCULATOR — single-number result
   ============================================================================
   Supersedes the ledger layout above. The earlier version showed six numbers
   moving at once (two savings rows, the fee, a net, an annual figure and a
   verdict) which read as an invoice and buried the good news under arithmetic.

   These rules come later in the file and win on source order at equal
   specificity. The superseded .nv-calc__ledger / __row / __net / __year /
   __verdict rules above are left in place rather than deleted — they cost
   nothing once nothing carries those classes, and removing them is a separate,
   riskier edit across a 2,900-line file.
   ---------------------------------------------------------------------------- */
body.nv .nv-calc__result {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  padding: var(--nv-space-lg) var(--nv-space-md);
  border-radius: var(--nv-radius-md);
  background: var(--nv-pos-tint);
  text-align: center;
  transition: background-color var(--duration-default) var(--nv-ease);
}

/* Zero state is neutral, never alarming. A red negative on a page selling a
   membership tells the visitor to leave. */
body.nv .nv-calc__result--zero {
  background: rgba(var(--color-base-text), .05);
}

body.nv .nv-calc__result-label {
  margin: 0;
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  color: var(--nv-ink-3);
}

/* The one number the section exists to show. */
body.nv .nv-calc__result-num {
  margin: var(--nv-space-2xs) 0 0;
  font-family: var(--font-heading-family);
  font-size: clamp(4.8rem, 9vw, 7.2rem);
  font-weight: var(--nv-weight-bold);
  line-height: 1;
  letter-spacing: var(--nv-tracking-tight);
  color: var(--nv-pos);
  font-variant-numeric: tabular-nums;
}

body.nv .nv-calc__result--zero .nv-calc__result-num {
  color: var(--nv-ink-3);
}

body.nv .nv-calc__result-unit {
  margin: var(--nv-space-2xs) 0 0;
  font-size: var(--nv-text-lg);
  font-weight: var(--nv-weight-medium);
  color: var(--nv-ink-2);
}

body.nv .nv-calc__result-note {
  margin: var(--nv-space-sm) 0 0;
  max-width: 34ch;
  font-size: var(--nv-text-sm);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
}

/* Sliders get more room now that the result column is simpler. */
body.nv .nv-calc__controls {
  gap: var(--nv-space-lg);
  align-content: center;
}

body.nv .nv-calc__label {
  align-items: center;
  font-size: var(--nv-text-base);
}

body.nv .nv-calc__value {
  font-size: var(--nv-text-2xl);
}

@media (min-width: 750px) {
  body.nv .nv-calc__widget {
    grid-template-columns: 1fr 1fr;
    align-items: stretch;
  }
  body.nv .nv-calc__result-num { font-size: 7.2rem; }
}


/* ============================================================================
   LISTICLE PAGES
   ============================================================================
   31 templates (page.listicle-*.json) were the last part of the site running on
   the pre-overhaul visual system. They are the ad landing pages, so they are
   the first thing a cold visitor sees — every one of them was inheriting raw
   base.css while the rest of the site had been rebuilt.

   Sections involved, all verified present in the rendered HTML rather than
   guessed from filenames: product-feature (the repeating block, 4 per page),
   promo-offer, promo-banner, loved-by-customers, product-image-marquee.

   ----------------------------------------------------------------------------
   WHY THIS TOUCHES NO COLOURS

   These pages carry ~84 inline style attributes emitted from section settings,
   and they are almost entirely `color:` and `background-color:` — 67 and 11
   respectively on a sampled page. Inline styles beat any selector here short of
   !important, which is banned outside the three sanctioned fences in this file.

   Fighting them would mean either losing (rules silently dead) or escalating
   (!important, and overriding a merchant's deliberate colour choice). So this
   block changes type scale, weight, tracking, rhythm, radius, elevation and
   motion — everything that carries a page's quality and that nothing is
   setting inline. Colour stays the merchant's.

   Section padding is also left alone: padding-top / padding-bottom arrive
   inline from each section's own settings, and blanket-replacing merchant-tuned
   padding is a mistake this overhaul already made once on the homepage.
   ---------------------------------------------------------------------------- */

/* ---------------------------------------------------------- PRODUCT FEATURE - */
body.nv .product-feature-container {
  max-width: 108rem;
}

body.nv .product-feature-grid {
  gap: var(--nv-space-lg);
  align-items: center;
}

/* The media is the hook on these pages. Given a real card treatment so it reads
   as a product shot rather than a pasted image. */
body.nv .product-feature-media {
  border-radius: var(--nv-radius-lg);
  overflow: hidden;
  background: rgba(var(--color-base-text), .04);
  box-shadow: var(--nv-shadow-2);
  transition:
    box-shadow var(--duration-default) var(--nv-ease),
    transform var(--duration-default) var(--nv-ease-out);
}

body.nv .product-feature-media img,
body.nv .product-feature-media video,
body.nv .product-feature-webp {
  display: block;
  width: 100%;
  height: auto;
}

@media (hover: hover) and (pointer: fine) {
  body.nv .product-feature-section:hover .product-feature-media {
    box-shadow: var(--nv-shadow-3);
    transform: translateY(-3px);
  }
}

body.nv .product-feature-content {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-sm);
}

/* Type only — the colour on these headings arrives inline from section
   settings and is deliberately left where the merchant set it. */
body.nv .product-feature-heading {
  font-size: var(--nv-text-2xl);
  font-weight: var(--nv-weight-bold);
  line-height: var(--nv-lh-tight);
  letter-spacing: var(--nv-tracking-tight);
  margin: 0;
  text-wrap: balance;
}

body.nv .product-feature-description {
  font-size: var(--nv-text-base);
  line-height: var(--nv-lh-body);
  max-width: 60ch;
}

body.nv .product-feature-description p {
  margin: 0 0 var(--nv-space-xs);
}

body.nv .product-feature-description p:last-child {
  margin-bottom: 0;
}

/* Both CTAs on these pages get the same treatment as the site's buy actions, so
   a visitor arriving cold sees one button language rather than three. */
body.nv .product-feature-button,
body.nv .product-feature-atc-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--nv-tap-min);
  padding: 1.5rem var(--nv-space-xl);
  border: none;
  border-radius: var(--nv-radius-sm);
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
  box-shadow: var(--nv-shadow-2);
  cursor: pointer;
  transition:
    box-shadow var(--duration-default) var(--nv-ease),
    transform var(--duration-default) var(--nv-ease-out);
}

body.nv .product-feature-button:hover,
body.nv .product-feature-atc-button:hover {
  box-shadow: var(--nv-shadow-3);
  transform: translateY(-2px);
}

body.nv .product-feature-atc-wrapper {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--nv-space-sm);
}

body.nv .product-feature-variant-select {
  min-height: var(--nv-tap-min);
  border-radius: var(--nv-radius-sm);
  border: 1px solid var(--nv-hairline-strong);
  padding: 0 var(--nv-space-md) 0 var(--nv-space-sm);
  /* 16px minimum or iOS zooms the whole page on tap — the same defect fixed on
     the product page variant picker. */
  font-size: max(16px, 1.6rem);
}

/* ------------------------------------------------------------- PROMO OFFER - */
body.nv .promo-offer-card {
  border-radius: var(--nv-radius-lg);
  box-shadow: var(--nv-shadow-3);
  overflow: hidden;
}

body.nv .promo-offer-heading {
  font-size: var(--nv-text-2xl);
  font-weight: var(--nv-weight-bold);
  line-height: var(--nv-lh-tight);
  letter-spacing: var(--nv-tracking-tight);
  text-wrap: balance;
}

body.nv .promo-offer-badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--nv-space-2xs);
}

body.nv .promo-offer-button {
  min-height: var(--nv-tap-min);
  border-radius: var(--nv-radius-sm);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  box-shadow: var(--nv-shadow-2);
  transition:
    box-shadow var(--duration-default) var(--nv-ease),
    transform var(--duration-default) var(--nv-ease-out);
}

body.nv .promo-offer-button:hover {
  box-shadow: var(--nv-shadow-3);
  transform: translateY(-2px);
}

body.nv .promo-offer-guarantee {
  font-size: var(--nv-text-xs);
  line-height: var(--nv-lh-body);
}

/* ------------------------------------------------------------ PROMO BANNER - */
/* The byline row is the credibility device on a listicle — it is what makes the
   page read as editorial rather than as an ad. Tightened rather than restyled. */
body.nv .promo-banner-author-wrap {
  display: flex;
  align-items: center;
  gap: var(--nv-space-xs);
}

body.nv .promo-banner-author-avatar {
  border-radius: 50%;
  overflow: hidden;
  flex: 0 0 auto;
  box-shadow: var(--nv-ring);
}

body.nv .promo-banner-author-name {
  font-weight: var(--nv-weight-semibold);
  letter-spacing: 0;
}

body.nv .promo-banner-author-updated,
body.nv .promo-banner-author-caption {
  font-size: var(--nv-text-xs);
  line-height: var(--nv-lh-snug);
}

body.nv .promo-banner-heading {
  font-weight: var(--nv-weight-bold);
  line-height: var(--nv-lh-tight);
  letter-spacing: var(--nv-tracking-tight);
  text-wrap: balance;
}

body.nv .promo-banner-button {
  min-height: var(--nv-tap-min);
  border-radius: var(--nv-radius-sm);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  box-shadow: var(--nv-shadow-2);
}

/* ------------------------------------------------------ LOVED BY CUSTOMERS - */
/* Star colour is set inline per star (30 of them on a page), so only the
   surrounding card and type are touched here. */
body.nv .lbc-title {
  font-weight: var(--nv-weight-semibold);
  line-height: var(--nv-lh-snug);
  letter-spacing: 0;
}

body.nv .lbc-text {
  font-size: var(--nv-text-sm);
  line-height: var(--nv-lh-body);
}

body.nv .lbc-name {
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-semibold);
  letter-spacing: var(--nv-tracking-wide);
}

/* ------------------------------------------------------------ 750px and up - */
@media (min-width: 750px) {
  body.nv .product-feature-grid { gap: var(--nv-space-2xl); }
  body.nv .product-feature-heading { font-size: var(--nv-text-3xl); }
  body.nv .product-feature-description { font-size: var(--nv-text-lg); }
  body.nv .promo-offer-heading { font-size: var(--nv-text-3xl); }
}

@media (prefers-reduced-motion: reduce) {
  body.nv .product-feature-section:hover .product-feature-media,
  body.nv .product-feature-button:hover,
  body.nv .product-feature-atc-button:hover,
  body.nv .promo-offer-button:hover {
    transform: none !important;
  }
}


/* ============================================================================
   CANCELLATION REQUEST
   ============================================================================
   Styled to be genuinely easy to complete. A cancellation page that is hard to
   read or hard to submit does not retain anyone — it converts a cancellation
   into a chargeback, which costs more than the subscription was worth.

   So: full-size body type, 44px+ controls, 16px inputs (below that iOS zooms
   the page on focus), a submit button that looks like every other primary
   action on the site, and terms in readable type rather than fine print.
   ---------------------------------------------------------------------------- */
body.nv .nv-cx {
  padding-top: var(--nv-cx-pt, 5rem);
  padding-bottom: var(--nv-cx-pb, 5rem);
}

body.nv .nv-cx__inner {
  max-width: 62rem;
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-lg);
}

body.nv .nv-cx__head {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-2xs);
}

body.nv .nv-cx__heading {
  margin: 0;
  font-size: var(--nv-text-3xl);
  line-height: var(--nv-lh-tight);
  letter-spacing: var(--nv-tracking-tight);
  color: var(--nv-ink-1);
}

body.nv .nv-cx__intro {
  font-size: var(--nv-text-lg);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-2);
  max-width: 54ch;
}

body.nv .nv-cx__intro p { margin: 0; }

body.nv .nv-cx__card {
  padding: var(--nv-space-lg);
  border: 1px solid var(--nv-hairline);
  border-radius: var(--nv-radius-lg);
  background: rgb(var(--color-background));
  box-shadow: var(--nv-shadow-2);
}

body.nv .nv-cx__form {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-md);
}

body.nv .nv-cx__field {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-3xs);
}

body.nv .nv-cx__label {
  display: flex;
  align-items: baseline;
  gap: var(--nv-space-2xs);
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  color: var(--nv-ink-1);
}

body.nv .nv-cx__req { color: rgb(var(--nv-cta)); }

body.nv .nv-cx__opt {
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-regular);
  color: var(--nv-ink-3);
  text-transform: lowercase;
}

/* max(16px, …) or iOS zooms the whole page the moment the field is focused —
   the same defect fixed on the product variant picker and the listicles. */
body.nv .nv-cx__input {
  width: 100%;
  min-height: var(--nv-tap-min);
  padding: var(--nv-space-2xs) var(--nv-space-xs);
  border: 1px solid var(--nv-hairline-strong);
  border-radius: var(--nv-radius-sm);
  background: rgb(var(--color-background));
  color: var(--nv-ink-1);
  font-family: inherit;
  font-size: max(16px, 1.6rem);
  line-height: 1.4;
  box-sizing: border-box;
  transition: border-color var(--duration-short) var(--nv-ease), box-shadow var(--duration-short) var(--nv-ease);
}

body.nv .nv-cx__input:focus {
  outline: none;
  border-color: rgb(var(--nv-cta));
  box-shadow: 0 0 0 3px var(--nv-cta-tint);
}

body.nv .nv-cx__textarea {
  min-height: 10rem;
  resize: vertical;
  padding-top: var(--nv-space-xs);
}

body.nv .nv-cx__hint {
  margin: 0;
  font-size: var(--nv-text-xs);
  color: var(--nv-ink-3);
}

body.nv .nv-cx__submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 5.2rem;
  padding: 0 var(--nv-space-xl);
  border: none;
  border-radius: var(--nv-radius-sm);
  background: rgb(var(--nv-cta));
  color: #FFFFFF;
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: var(--nv-shadow-2);
  transition: background-color var(--duration-default) var(--nv-ease), box-shadow var(--duration-default) var(--nv-ease);
}

body.nv .nv-cx__submit:hover {
  background: rgb(var(--nv-cta-hover));
  color: #FFFFFF;
  box-shadow: var(--nv-shadow-3);
}

body.nv .nv-cx__submit:focus-visible {
  outline: none;
  box-shadow: var(--nv-focus-ring);
}

/* Readable, not fine print. These are real terms of the plan. */
body.nv .nv-cx__terms {
  margin: 0;
  font-size: var(--nv-text-sm);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
}

body.nv .nv-cx__success {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-2xs);
  padding: var(--nv-space-md);
  border-radius: var(--nv-radius-md);
  background: rgba(27, 127, 75, .08);
}

body.nv .nv-cx__success-title {
  margin: 0;
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-xl);
  font-weight: var(--nv-weight-bold);
  color: #1B7F4B;
}

body.nv .nv-cx__success-text {
  margin: 0;
  font-size: var(--nv-text-base);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-2);
}

body.nv .nv-cx__errors {
  padding: var(--nv-space-sm) var(--nv-space-md);
  border-radius: var(--nv-radius-md);
  background: rgba(166, 64, 47, .07);
}

body.nv .nv-cx__errors-title {
  margin: 0 0 var(--nv-space-3xs);
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-bold);
  color: #A6402F;
}

body.nv .nv-cx__errors-list {
  margin: 0;
  padding-inline-start: var(--nv-space-sm);
  font-size: var(--nv-text-sm);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-2);
}

body.nv .nv-cx__alt {
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-3xs);
  padding-top: var(--nv-space-md);
  border-top: 1px solid var(--nv-hairline);
}

body.nv .nv-cx__alt-title {
  margin: 0;
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  color: var(--nv-ink-1);
}

body.nv .nv-cx__alt-text,
body.nv .nv-cx__alt-terms {
  margin: 0;
  font-size: var(--nv-text-sm);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
}

body.nv .nv-cx__alt-text a { color: rgb(var(--nv-cta)); }

@media (min-width: 750px) {
  body.nv .nv-cx__card { padding: var(--nv-space-xl); }
  body.nv .nv-cx__heading { font-size: var(--nv-text-4xl); }
}


/* ============================================================================
   VIP CLUB PAGE
   ============================================================================
   nv-vip-hero / nv-vip-benefits / nv-vip-timeline / nv-vip-terms.

   The hero is the only full-dark surface in the theme. That is deliberate — it
   is the one page whose job is to sell a recurring charge, and the contrast
   buys attention that the rest of the site does not need. Everything below it
   returns to the normal light ground so the terms read as information rather
   than as more marketing.
   ---------------------------------------------------------------------------- */
body.nv .nv-vh,
body.nv .nv-vb,
body.nv .nv-vt,
body.nv .nv-vx {
  --nv-vip-ink: #141414;
  --nv-vip-dark: #131313;
  --nv-vip-tint: #FBF2EF;
  --nv-vip-pos: #1B7F4B;
}

/* -------------------------------------------------------------- HERO ------ */
body.nv .nv-vh {
  padding-top: var(--nv-vh-pt, 6.7rem);
  padding-bottom: var(--nv-vh-pb, 6.7rem);
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(242, 84, 45, .16) 0%, rgba(242, 84, 45, 0) 55%),
    var(--nv-vip-dark);
  color: #FFFFFF;
}

body.nv .nv-vh__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--nv-space-sm);
  max-width: 88rem;
}

body.nv .nv-vh__eyebrow {
  margin: 0;
  font-size: var(--nv-text-2xs);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-eyebrow);
  text-transform: uppercase;
  color: rgb(var(--nv-cta));
}

body.nv .nv-vh__heading {
  margin: 0;
  font-size: var(--nv-text-3xl);
  line-height: 1.05;
  letter-spacing: var(--nv-tracking-tight);
  color: #FFFFFF;
  text-wrap: balance;
}

/* The terms, sitting directly under the headline. An offer this size followed
   immediately by price, cadence and exit is what separates it from a trap. */
body.nv .nv-vh__pill {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--nv-space-2xs);
  margin: var(--nv-space-2xs) 0 0;
  padding: var(--nv-space-2xs) var(--nv-space-md);
  border: 1px solid rgba(242, 84, 45, .45);
  border-radius: var(--nv-radius-pill);
  background: rgba(242, 84, 45, .12);
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  color: #FFFFFF;
}

body.nv .nv-vh__pill-dot { color: rgba(255, 255, 255, .45); }

body.nv .nv-vh__sub {
  margin: 0;
  max-width: 60ch;
  font-size: var(--nv-text-base);
  line-height: var(--nv-lh-body);
  color: rgba(255, 255, 255, .72);
}

body.nv .nv-vh__sub p { margin: 0; }

body.nv .nv-vh__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--nv-space-xs);
  margin-top: var(--nv-space-2xs);
}

body.nv .nv-vh__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--nv-tap-min);
  padding: 1.5rem var(--nv-space-xl);
  border-radius: var(--nv-radius-pill);
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
  transition:
    background-color var(--duration-default) var(--nv-ease),
    transform var(--duration-default) var(--nv-ease-out);
}

body.nv .nv-vh__cta--primary {
  background: rgb(var(--nv-cta));
  color: #FFFFFF;
  border: 1px solid rgb(var(--nv-cta));
}

body.nv .nv-vh__cta--primary:hover {
  background: rgb(var(--nv-cta-hover));
  color: #FFFFFF;
  transform: translateY(-2px);
}

body.nv .nv-vh__cta--ghost {
  background: transparent;
  color: #FFFFFF;
  border: 1px solid rgba(255, 255, 255, .30);
}

body.nv .nv-vh__cta--ghost:hover {
  background: rgba(255, 255, 255, .08);
  color: #FFFFFF;
}

body.nv .nv-vh__trust {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--nv-space-sm) var(--nv-space-lg);
  margin: var(--nv-space-md) 0 0;
  padding: var(--nv-space-md) 0 0;
  border-top: 1px solid rgba(255, 255, 255, .12);
  width: 100%;
  list-style: none;
}

body.nv .nv-vh__trust-item {
  display: inline-flex;
  align-items: center;
  gap: var(--nv-space-3xs);
  font-size: var(--nv-text-xs);
  color: rgba(255, 255, 255, .78);
}

body.nv .nv-vh__tick {
  inline-size: 1.6rem;
  block-size: 1.6rem;
  color: rgb(var(--nv-cta));
  flex: 0 0 auto;
}

/* ---------------------------------------------------------- BENEFITS ------ */
body.nv .nv-vb {
  padding-top: var(--nv-vb-pt, 6.2rem);
  padding-bottom: var(--nv-vb-pb, 6.2rem);
}

body.nv .nv-vb__head,
body.nv .nv-vt__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--nv-space-2xs);
  margin-bottom: var(--nv-space-xl);
  text-align: center;
}

body.nv .nv-vb__eyebrow,
body.nv .nv-vt__eyebrow,
body.nv .nv-vx__eyebrow {
  margin: 0;
  font-size: var(--nv-text-2xs);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-eyebrow);
  text-transform: uppercase;
  color: rgb(var(--nv-cta));
}

body.nv .nv-vb__heading,
body.nv .nv-vt__heading,
body.nv .nv-vx__heading {
  margin: 0;
  font-size: var(--nv-text-2xl);
  line-height: var(--nv-lh-tight);
  letter-spacing: var(--nv-tracking-tight);
  color: var(--nv-ink-1);
  text-wrap: balance;
}

body.nv .nv-vb__lede,
body.nv .nv-vt__lede {
  margin: 0;
  max-width: 54ch;
  font-size: var(--nv-text-base);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
}

body.nv .nv-vb__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--nv-space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

body.nv .nv-vb__card {
  padding: var(--nv-space-md);
  border: 1px solid var(--nv-hairline);
  border-radius: var(--nv-radius-md);
  background: var(--nv-vip-tint);
}

body.nv .nv-vb__icon {
  inline-size: 2.6rem;
  block-size: 2.6rem;
  color: rgb(var(--nv-cta));
  margin-bottom: var(--nv-space-sm);
}

body.nv .nv-vb__icon svg { width: 100%; height: 100%; }

body.nv .nv-vb__card-title {
  margin: 0 0 var(--nv-space-3xs);
  font-size: var(--nv-text-base);
  font-weight: var(--nv-weight-bold);
  letter-spacing: 0;
  color: var(--nv-ink-1);
}

body.nv .nv-vb__card-text {
  font-size: var(--nv-text-sm);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
}

body.nv .nv-vb__card-text p { margin: 0; }

/* ---------------------------------------------------------- TIMELINE ------ */
body.nv .nv-vt {
  padding-top: var(--nv-vt-pt, 6.2rem);
  padding-bottom: var(--nv-vt-pb, 6.2rem);
}

body.nv .nv-vt__steps {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--nv-space-lg);
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: none;
}

body.nv .nv-vt__step {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--nv-space-2xs);
}

body.nv .nv-vt__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 3.2rem;
  block-size: 3.2rem;
  border-radius: 50%;
  background: rgb(var(--nv-cta));
  color: #FFFFFF;
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-bold);
  margin-bottom: var(--nv-space-2xs);
}

body.nv .nv-vt__step-title {
  margin: 0;
  font-size: var(--nv-text-base);
  font-weight: var(--nv-weight-bold);
  color: var(--nv-ink-1);
}

body.nv .nv-vt__step-text {
  margin: 0;
  font-size: var(--nv-text-sm);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
}

/* The amount, attached to the step. "$0" on the steps where nothing is taken
   is the whole point — it answers the question that drives chargebacks. */
body.nv .nv-vt__badge {
  display: inline-block;
  margin-top: var(--nv-space-3xs);
  padding: 0.4rem var(--nv-space-2xs);
  border-radius: var(--nv-radius-xs);
  background: var(--nv-vip-tint);
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-semibold);
  color: var(--nv-ink-2);
}

body.nv .nv-vt__badge--charge {
  background: rgb(var(--nv-cta));
  color: #FFFFFF;
}

/* ------------------------------------------------------------- TERMS ------ */
body.nv .nv-vx {
  padding-top: var(--nv-vx-pt, 6.2rem);
  padding-bottom: var(--nv-vx-pb, 6.2rem);
  background: rgb(var(--color-base-background-2));
}

body.nv .nv-vx__inner { max-width: 92rem; }

body.nv .nv-vx__heading { text-align: center; margin-bottom: var(--nv-space-lg); }

body.nv .nv-vx__compare {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--nv-space-sm);
  margin-bottom: var(--nv-space-2xl);
}

body.nv .nv-vx__col {
  padding: var(--nv-space-md);
  border: 1px solid var(--nv-hairline);
  border-radius: var(--nv-radius-lg);
  background: rgb(var(--color-background));
}

body.nv .nv-vx__col--member {
  background: var(--nv-vip-dark);
  border-color: transparent;
  color: #FFFFFF;
}

body.nv .nv-vx__col-title {
  margin: 0;
  font-size: var(--nv-text-lg);
  font-weight: var(--nv-weight-bold);
  color: var(--nv-ink-1);
}

body.nv .nv-vx__col-price {
  margin: 0 0 var(--nv-space-sm);
  font-size: var(--nv-text-xs);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  color: var(--nv-ink-3);
}

body.nv .nv-vx__col-flag {
  margin: 0 0 var(--nv-space-sm);
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  color: rgb(var(--nv-cta));
}

body.nv .nv-vx__list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--nv-space-2xs);
}

body.nv .nv-vx__list li {
  display: flex;
  align-items: flex-start;
  gap: var(--nv-space-2xs);
  padding-bottom: var(--nv-space-2xs);
  border-bottom: 1px solid var(--nv-hairline);
  font-size: var(--nv-text-sm);
  color: var(--nv-ink-2);
}

body.nv .nv-vx__col--member .nv-vx__list li {
  color: rgba(255, 255, 255, .88);
  border-bottom-color: rgba(255, 255, 255, .12);
}

body.nv .nv-vx__x { color: var(--nv-ink-4); font-weight: var(--nv-weight-bold); }
body.nv .nv-vx__tick { color: rgb(var(--nv-cta)); font-weight: var(--nv-weight-bold); }

body.nv .nv-vx__col-foot {
  margin: var(--nv-space-sm) 0 0;
  font-size: var(--nv-text-xs);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  color: var(--nv-ink-4);
  text-align: center;
}

body.nv .nv-vx__terms {
  display: flex;
  flex-direction: column;
  align-items: center;
}

body.nv .nv-vx__heading--terms { margin-bottom: var(--nv-space-md); }

body.nv .nv-vx__table-wrap {
  width: 100%;
  padding: var(--nv-space-md);
  border: 1px solid var(--nv-hairline);
  border-radius: var(--nv-radius-lg);
  background: rgb(var(--color-background));
  overflow-x: auto;
}

body.nv .nv-vx__table { margin: 0; display: flex; flex-direction: column; }

body.nv .nv-vx__row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.2rem;
  padding: var(--nv-space-xs) 0;
  border-bottom: 1px solid var(--nv-hairline);
}

body.nv .nv-vx__row:last-child { border-bottom: none; }

body.nv .nv-vx__row dt {
  font-size: var(--nv-text-xs);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  color: var(--nv-ink-3);
}

body.nv .nv-vx__row dd {
  margin: 0;
  font-size: var(--nv-text-sm);
  font-weight: var(--nv-weight-semibold);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-1);
}

body.nv .nv-vx__row dd a { color: rgb(var(--nv-cta)); }

body.nv .nv-vx__note {
  margin: var(--nv-space-sm) 0 0;
  max-width: 62ch;
  font-size: var(--nv-text-xs);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-3);
  text-align: center;
}

/* The exit, given real weight. Burying it is what the regulation is about, and
   a visible exit is also what makes the offer above it believable. */
body.nv .nv-vx__cancel {
  width: 100%;
  margin-top: var(--nv-space-lg);
  padding: var(--nv-space-md);
  border: 1px solid rgba(242, 84, 45, .35);
  border-radius: var(--nv-radius-lg);
  background: var(--nv-vip-tint);
  text-align: center;
}

body.nv .nv-vx__cancel-title {
  margin: 0 0 var(--nv-space-3xs);
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-lg);
  font-weight: var(--nv-weight-bold);
  color: var(--nv-ink-1);
}

body.nv .nv-vx__cancel-text {
  margin: 0 auto var(--nv-space-sm);
  max-width: 54ch;
  font-size: var(--nv-text-sm);
  line-height: var(--nv-lh-body);
  color: var(--nv-ink-2);
}

body.nv .nv-vx__cancel-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--nv-tap-min);
  padding: 1.4rem var(--nv-space-xl);
  border-radius: var(--nv-radius-pill);
  background: rgb(var(--nv-cta));
  color: #FFFFFF;
  font-family: var(--font-heading-family);
  font-size: var(--nv-text-xs);
  font-weight: var(--nv-weight-bold);
  letter-spacing: var(--nv-tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
}

body.nv .nv-vx__cancel-cta:hover { background: rgb(var(--nv-cta-hover)); color: #FFFFFF; }

/* --------------------------------------------------------------- 750px ---- */
@media (min-width: 750px) {
  body.nv .nv-vh__heading { font-size: clamp(4.2rem, 5vw, 6rem); }
  body.nv .nv-vh__sub { font-size: var(--nv-text-lg); }
  body.nv .nv-vh__pill { font-size: var(--nv-text-base); }

  body.nv .nv-vb__heading,
  body.nv .nv-vt__heading,
  body.nv .nv-vx__heading { font-size: var(--nv-text-3xl); }

  body.nv .nv-vb__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--nv-space-md); }
  body.nv .nv-vt__steps { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--nv-space-md); }
  body.nv .nv-vx__compare { grid-template-columns: 1fr 1fr; gap: var(--nv-space-md); }
  body.nv .nv-vx__col { padding: var(--nv-space-xl); }
  body.nv .nv-vx__table-wrap { padding: var(--nv-space-xl); }
  body.nv .nv-vx__cancel { padding: var(--nv-space-xl); }

  /* Label and value side by side once there is room for both. */
  body.nv .nv-vx__row {
    grid-template-columns: 16rem 1fr;
    gap: var(--nv-space-md);
    align-items: baseline;
  }
}

@media (prefers-reduced-motion: reduce) {
  body.nv .nv-vh__cta:hover { transform: none !important; }
}

/* ============================================================================
   VIP PAGE — CONTRAST RHYTHM AND CARD SCALE
   ============================================================================
   Measured off the built page, not guessed. Two faults:

   1. NO CONTRAST RHYTHM. Only .nv-vh (dark) and .nv-vx (grey) declared a
      background. .nv-vb, .nv-vt and .nv-mcat declared none, so the page ran
      dark -> white -> white -> white -> white -> grey: four unbroken white
      bands where the section boundaries simply disappeared.

      Restored to an alternating ladder, which is the rhythm the page had
      before the new sections landed:
        hero DARK / benefits WHITE / timeline GREY / catalog WHITE /
        terms GREY / legal + FAQ WHITE

   2. CARDS A TIER TOO SMALL. The .nv-vx cards had settled on the house
      pattern -- white ground, hairline border, --nv-radius-lg, stepping to
      --nv-space-xl on desktop. The newer cards never got it: .nv-vb__card and
      .nv-mcat__item sat at --nv-radius-md, .nv-vt__step was bare text rather
      than a card at all, and none of them stepped up past --nv-space-md, so
      they read as chips beside the sections above them.

   Section padding is deliberately NOT touched here: each section emits its own
   --nv-*-pt / --nv-*-pb from merchant settings, and a blanket rule would both
   use the wrong variable and discard tuned values.
   ---------------------------------------------------------------------------- */

/* ---- 1. the alternating bands ---- */
body.nv .nv-vb,
body.nv .nv-mcat {
  background: rgb(var(--color-background));
}

body.nv .nv-vt {
  background: rgb(var(--color-base-background-2));
}

/* The member status card only renders for signed-in members, which drops a
   white band between two others. An explicit ground keeps the ladder intact in
   both states instead of the rhythm changing once someone joins. */
body.nv .nv-mstat {
  background: rgb(var(--color-background));
}

/* ---- 2. the house card pattern, applied to the three that missed it ---- */
body.nv .nv-vb__card,
body.nv .nv-mcat__item {
  border-radius: var(--nv-radius-lg);
}

body.nv .nv-vb__card {
  padding: var(--nv-space-lg);
}

/* The icon was scaled for a chip; at card size it under-reads. */
body.nv .nv-vb__icon {
  inline-size: 3.2rem;
  block-size: 3.2rem;
  margin-bottom: var(--nv-space-md);
}

/* The timeline carries the charge schedule -- the one thing a visitor comes
   back to re-read -- yet it was the only band on the page rendered as loose
   text. On the grey ground it now sits on, white cards give it the same weight
   as the comparison columns it is explaining. */
body.nv .nv-vt__step {
  padding: var(--nv-space-md);
  border: 1px solid var(--nv-hairline);
  border-radius: var(--nv-radius-lg);
  background: rgb(var(--color-background));
}

@media (min-width: 750px) {
  body.nv .nv-vb__card,
  body.nv .nv-vt__step,
  body.nv .nv-mstat__card {
    padding: var(--nv-space-xl);
  }

  body.nv .nv-vb__grid,
  body.nv .nv-vt__steps {
    gap: var(--nv-space-lg);
  }
}

/* ----------------------------------------------------------------------------
   .nv-vx — SPLIT THE ONE GREY BAND INTO TWO
   ----------------------------------------------------------------------------
   This single section was stacking three separate card surfaces on one grey
   ground: the two comparison columns, the white terms table, and the coral
   cancel panel. Three panels floating on one band, with nothing to say where
   one idea ended and the next began.

   The section is one Liquid section, so the split is done in CSS. .nv-vx__terms
   (eyebrow + heading + table + note + cancel — the whole "in writing" half,
   since .nv-vx__cancel nests INSIDE it) becomes a white band; the comparison
   columns keep the grey. One coherent group per band.

   The band is painted with a wide box-shadow rather than the usual
   `width: 100vw` / `margin-inline: calc(50% - 50vw)` full-bleed. 100vw includes
   the scrollbar gutter, and nothing in this theme sets overflow-x on html or
   body, so that idiom would push a horizontal scrollbar onto every desktop
   view of this page. box-shadow does not participate in layout, so it cannot;
   clip-path then trims it vertically to the element box, letting it spread
   sideways only. vmax (not vw) keeps it wide enough in portrait too.
   ---------------------------------------------------------------------------- */
body.nv .nv-vx__terms {
  position: relative;
  width: 100%;
  background: rgb(var(--color-background));
  box-shadow: 0 0 0 100vmax rgb(var(--color-background));
  clip-path: inset(0 -100vmax);
  padding-top: var(--nv-space-2xl);
  /* Exactly the section padding, no more. Adding --nv-space-2xl on top of it
     here stacked a second gap under the cancel panel: the band already ends
     flush with the section, so the section padding IS the breathing room. */
  padding-bottom: var(--nv-vx-pb, 6.2rem);
  margin-bottom: calc(var(--nv-vx-pb, 6.2rem) * -1);
}

/* Carry the band down through the section's own bottom padding, then pull the
   box back up so layout is untouched. Without this the band stops short and
   leaves an empty grey strip between it and the white legal section below,
   which reads as a rendering fault rather than a divider. The merchant's
   padding value is reused, never replaced. */

/* Same treatment upward if the comparison block is toggled off and the terms
   become the first thing in the section. */
body.nv .nv-vx__terms:first-child {
  padding-top: calc(var(--nv-space-2xl) + var(--nv-vx-pt, 6.2rem));
  margin-top: calc(var(--nv-vx-pt, 6.2rem) * -1);
}

/* The table was a white card; on a white band it would be a card you cannot
   see. Its rows already carry hairline rules, so it reads better as a plain
   specification list — which also drops the page from three panels to one. */
body.nv .nv-vx__table-wrap {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}

@media (min-width: 750px) {
  /* overrides the blanket .nv-vx__table-wrap desktop padding set earlier */
  body.nv .nv-vx__table-wrap { padding: 0; }
}

/* ----------------------------------------------------------------------------
   LEGAL FOOTNOTE — PADDED LIKE A SECTION IT NO LONGER IS
   ----------------------------------------------------------------------------
   .vip-compliance carries `padding: 8rem 2rem` because it was built to hold a
   heading plus four transparency cards. On the membership page every one of
   those is toggled off and the block renders a single paragraph, but it kept
   the full 80px top and bottom — which, stacked under the terms band, was most
   of the dead space between the cancel panel and the disclaimer.

   The --legal-only modifier existed in the markup with no rule behind it. This
   is that rule. Top padding comes off entirely: the white band above already
   ends with the section's own padding, so the footnote just needs to sit under
   it rather than add a second gap.
   ---------------------------------------------------------------------------- */
body.nv .vip-compliance--legal-only {
  padding-top: 0;
  padding-bottom: var(--nv-space-2xl);
}

/* ----------------------------------------------------------------------------
   FAQ ACCORDIONS
   ----------------------------------------------------------------------------
   Scoped to .collapsible-content deliberately. .accordion / .accordion-v2 /
   .product__accordion are shared with product pages, custom-columns and the
   complementary-products block, so an unscoped rule would restyle accordions
   well outside this page.

   The rows were hairline-divided text: nothing to click toward, no open state,
   and a caret that never moved. They are now cards that respond — matching the
   card language the rest of this page already uses.
   ---------------------------------------------------------------------------- */
body.nv .collapsible-content .product__accordion {
  margin-bottom: var(--nv-space-2xs);
  border: 1px solid var(--nv-hairline);
  border-radius: var(--nv-radius-lg);
  background: rgb(var(--color-background));
  overflow: hidden;
  transition:
    border-color var(--duration-default) var(--nv-ease),
    box-shadow var(--duration-default) var(--nv-ease);
}

/* No data-border-type override is needed here. base.css draws the row divider
   from .accordion-v2[data-border-type="bottom"] at (0,2,0); the card rule above
   is (0,3,1), so its border shorthand already replaces that divider on all four
   sides. An explicit attribute rule would outrank the card, and the border-top
   reset it needed would strip the top edge off every card. */

@media (hover: hover) and (pointer: fine) {
  body.nv .collapsible-content .product__accordion:hover {
    border-color: var(--nv-hairline-strong);
    box-shadow: var(--nv-shadow-1);
  }
}

/* The open row is the one being read — give it the accent and lift it clear of
   the closed rows around it. */
body.nv .collapsible-content .product__accordion:has(.accordion__details[open]) {
  border-color: rgba(242, 84, 45, .35);
  box-shadow: var(--nv-shadow-2);
}

body.nv .collapsible-content .accordion__summary {
  gap: var(--nv-space-sm);
  justify-content: space-between;
  padding: var(--nv-space-md);
  list-style: none;
}

body.nv .collapsible-content .accordion__summary::-webkit-details-marker { display: none; }

body.nv .collapsible-content .accordion__summary:focus-visible {
  outline: none;
  box-shadow: var(--nv-focus-ring);
  border-radius: var(--nv-radius-lg);
}

body.nv .collapsible-content .accordion__title {
  max-width: none;
  font-size: 1.7rem;
  font-weight: var(--nv-weight-bold);
  line-height: 1.35;
}

/* The caret sat static, so nothing on the row said it could open. */
body.nv .collapsible-content .accordion__collapse-icon {
  color: var(--nv-ink-3);
  transition:
    transform var(--duration-default) var(--nv-ease),
    color var(--duration-default) var(--nv-ease);
}

body.nv .collapsible-content .accordion__details[open] .accordion__collapse-icon {
  transform: rotate(180deg);
  color: rgb(var(--nv-cta));
}

body.nv .collapsible-content .accordion__content__inner {
  padding: 0 var(--nv-space-md) var(--nv-space-md);
  color: var(--nv-ink-2);
  line-height: var(--nv-lh-body);
}

@media (prefers-reduced-motion: reduce) {
  body.nv .collapsible-content .product__accordion,
  body.nv .collapsible-content .accordion__collapse-icon {
    transition: none;
  }
}



/* ============================================================================
   VARIANT PICKER — part of the buying module, not a form above it
   ============================================================================
   It read as a stray form because it spoke a different language from the
   bundle directly beneath it: black selected state against the bundle's coral,
   a tinted select, its own spacing, its own label style. Same card, same
   radius, same selected colour, and the two read as one control.

   Selected colour is the important one. Two different meanings of "chosen" on
   one screen — black here, coral there — makes the shopper re-learn the page
   halfway down it.
   ========================================================================== */
body.nv-tpl--product variant-selects {
  display: block;
  margin-bottom: var(--nv-space-xs);
}

body.nv-tpl--product .product-form__input {
  margin-bottom: var(--nv-space-xs);
  padding: 0;
  border: 0;
}
body.nv-tpl--product .product-form__input:last-child { margin-bottom: 0; }

/* Same eyebrow as the bundle's tier labels. */
body.nv-tpl--product .product-form__input .form__label {
  font-size: 1.05rem;
  font-weight: var(--nv-weight-semibold);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--nv-ink-3);
  margin-bottom: var(--nv-space-3xs, .4rem);
}

/* ---- pills ---- */
body.nv-tpl--product .product-form__input--pills label:not(.form__label) {
  border: 1.5px solid var(--nv-hairline-strong);
  border-radius: var(--nv-radius-pill, 999rem);
  padding: .85rem 1.6rem;
  font-size: var(--nv-text-xs, 1.3rem);
  font-weight: var(--nv-weight-semibold);
  background: rgb(var(--color-background));
  color: rgb(var(--color-base-text));
  transition:
    border-color var(--duration-short) var(--nv-ease),
    background var(--duration-short) var(--nv-ease),
    box-shadow var(--duration-short) var(--nv-ease);
}
body.nv-tpl--product .product-form__input--pills label:not(.form__label):hover {
  border-color: rgba(var(--nv-cta), .55);
}
body.nv-tpl--product .product-form__input--pills input:checked + label,
body.nv-tpl--product .product-form__input--pills input[checked] + label {
  background: var(--nv-cta-tint);
  border-color: rgb(var(--nv-cta));
  color: rgb(var(--color-base-text));
  box-shadow: 0 0 0 1px rgb(var(--nv-cta)), 0 3px 10px rgba(var(--nv-cta), .16);
}
body.nv-tpl--product .product-form__input--pills input:focus-visible + label {
  outline: 2px solid rgb(var(--nv-cta));
  outline-offset: 2px;
}

/* ---- dropdown ---- */
/* DOUBLE BORDER FIX. base.css:2391 draws this control's outline on .select:after
   — an inset pseudo-element ring, not a border property. A border added to
   .select__select therefore sits INSIDE that ring and you see two concentric
   outlines. The ring is restyled instead and the select carries none. */
body.nv-tpl--product .product-form__input--dropdown .select {
  border-radius: var(--nv-radius-md);
  background: rgb(var(--color-background));
}
body.nv-tpl--product .product-form__input--dropdown .select__select {
  border: 0;
  background: transparent;
  border-radius: var(--nv-radius-md);
  font-size: var(--nv-text-xs, 1.3rem);
  font-weight: var(--nv-weight-semibold);
  padding: 1.1rem 4rem 1.1rem 1.4rem;
  min-height: 4.6rem;
}
body.nv-tpl--product .product-form__input--dropdown .select:after {
  border-radius: var(--nv-radius-md);
  box-shadow: 0 0 0 1.5px var(--nv-hairline-strong);
  transition: box-shadow var(--duration-short) var(--nv-ease);
}
body.nv-tpl--product .product-form__input--dropdown .select:hover:after {
  box-shadow: 0 0 0 1.5px rgba(var(--nv-cta), .55);
}
/* focus-within, because the ring belongs to the wrapper but focus lands on the
   select inside it. */
body.nv-tpl--product .product-form__input--dropdown .select:focus-within:after {
  box-shadow: 0 0 0 1.5px rgb(var(--nv-cta)), 0 0 0 4px rgba(var(--nv-cta), .16);
}
/* The generic field focus ring would be a third outline on top of that. */
body.nv-tpl--product .product-form__input--dropdown .select__select:focus,
body.nv-tpl--product .product-form__input--dropdown .select__select:focus-visible {
  box-shadow: none;
  outline: none;
}

/* The bundle follows immediately, so the gap between them is the seam that
   decides whether this reads as one module or two. */
body.nv-tpl--product variant-selects + .shopify-block,
body.nv-tpl--product .product-form__input + .shopify-block {
  margin-top: var(--nv-space-xs);
}


/* ============================================================================
   COLOUR SWATCHES
   ============================================================================
   The option is now rendered by the theme's swatch picker rather than as name
   pills. A colour is the thing being chosen, so showing the colour is both
   faster to scan and one less place for the page to look like a form.

   The value is not lost: the field label reads "COLOR - ORANGE" via the
   picker's [name] - [selected] template, and the per-swatch name stays in the
   accessibility tree, clipped rather than removed.
   ========================================================================== */
/* Block, not flex. As a flex row the label and the swatches shared a line, and
   the selected ring — a box-shadow, which draws outside the box and takes no
   layout space — printed straight over the end of the label text.
   The label owns its own line and the ring has clearance on every side. */
body.nv-tpl--product .product-form__input:has(.color-swatch) {
  display: block;
}
body.nv-tpl--product .product-form__input .form__label,
body.nv-tpl--product .product-form__input:has(.color-swatch) .form__label {
  display: block;
  inline-size: 100%;
  margin-bottom: 1rem;
}

body.nv-tpl--product .color-swatch {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  /* 1.4rem clears the 4px selected ring plus a real gap between discs. */
  margin: 0 1.4rem 0.8rem 0;
  line-height: 0;
}
body.nv-tpl--product .color-swatch input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
body.nv-tpl--product .color-swatch_hidden-label {
  position: absolute;
  inset: 0;
  margin: 0;
  cursor: pointer;
  z-index: 1;
}

body.nv-tpl--product .color-swatch .color-swatch__image {
  inline-size: 3.4rem;
  block-size: 3.4rem;
  border-radius: 50%;
  overflow: hidden;
  padding: 0;
  /* An inner hairline rather than a border, so white and off-white read as
     discs on a white card instead of disappearing. */
  box-shadow: inset 0 0 0 1px rgba(var(--nv-shadow-color), .18);
  transition:
    transform var(--duration-short) var(--nv-ease-out),
    box-shadow var(--duration-short) var(--nv-ease);
}
body.nv-tpl--product .color-swatch .color-swatch__custom-color {
  inline-size: 100%;
  block-size: 100%;
  border-radius: 50%;
  /* Shorthand, not background-color: one value in the list is a gradient. */
  background: var(--bg-color);
}
body.nv-tpl--product .color-swatch .color-swatch__image img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* Selected ring floats clear of the disc on its own gap, so it stays visible
   against a black swatch and a white one alike. */
body.nv-tpl--product .color-swatch input:checked ~ .color-swatch__image {
  box-shadow:
    inset 0 0 0 1px rgba(var(--nv-shadow-color), .18),
    0 0 0 2px rgb(var(--color-background)),
    0 0 0 4px rgb(var(--nv-cta));
}
body.nv-tpl--product .color-swatch input:focus-visible ~ .color-swatch__image {
  box-shadow:
    0 0 0 2px rgb(var(--color-background)),
    0 0 0 4px rgb(var(--nv-cta));
}

@media (hover: hover) and (pointer: fine) {
  body.nv-tpl--product .color-swatch:hover .color-swatch__image {
    transform: scale(1.07);
    box-shadow:
      inset 0 0 0 1px rgba(var(--nv-shadow-color), .18),
      0 2px 8px rgba(var(--nv-shadow-color), .18);
  }
  body.nv-tpl--product .color-swatch input:checked ~ .color-swatch__image,
  body.nv-tpl--product .color-swatch:hover input:checked ~ .color-swatch__image {
    box-shadow:
      inset 0 0 0 1px rgba(var(--nv-shadow-color), .18),
      0 0 0 2px rgb(var(--color-background)),
      0 0 0 4px rgb(var(--nv-cta));
  }
}

body.nv-tpl--product .color-swatch.disabled .color-swatch__image,
body.nv-tpl--product .color-swatch input.disabled ~ .color-swatch__image {
  opacity: .35;
}

/* Clipped, not removed — screen readers still announce the colour name. */
body.nv-tpl--product .color-swatch .color-swatch__label {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  body.nv-tpl--product .color-swatch:hover .color-swatch__image { transform: none; }
}


/* ============================================================================
   CLUB ENROLMENT LINE — PRESENT IN THE CART, INVISIBLE IN IT
   ============================================================================
   The $0 enrolment product has to BE in the cart: Paysight's Physical
   Fulfillment mapping reads it to create the subscription, so removing it
   removes the membership. But as its own row it read like a second thing the
   shopper was buying. It is hidden here and surfaced instead as a sub-line
   under the product it was bought with (snippets/cart-drawer.liquid).

   Counts are handled separately and natively: the product carries the theme's
   "cart-hidden" tag, which cart-drawer, cart-icon-bubble and header all already
   subtract from the item count.
   ========================================================================== */
body.nv .cart-item--product-neat-values-club-enrollment {
  display: none;
}



/* ============================================================================
   REVIEW CARDS — ONE BASELINE
   ----------------------------------------------------------------------------
   The purchased-product panel was flowing directly after the review copy, so
   its top edge landed wherever that particular review happened to stop. Three
   cards side by side produced three different panel heights and three
   different baselines — the single detail that makes a row of cards read as
   assembled rather than designed.

   The card becomes a column, the content stretches to fill it, and the panel
   takes margin-block-start: auto so it is pushed to the floor of whatever
   height the row settles on. No fixed heights anywhere: a long review still
   grows the whole row, and a short one no longer leaves its panel stranded
   mid-card. The minimum gap above the panel now comes from the paragraph's
   own bottom margin, since the auto margin has replaced the panel's.
   ============================================================================ */
body.nv .review-card {
  display: flex;
  flex-direction: column;
  scroll-snap-align: start;
}

body.nv .review-content {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
}

body.nv .review-content p {
  flex: 0 1 auto;
  margin-block-end: var(--nv-space-sm);
}

body.nv .review-product {
  margin-block-start: auto;
}

/* ---- The track was clipping the cards' own hover lift -----------------------
   .reviews-carousel sets overflow-x: auto, and per spec a non-visible value on
   one axis forces the other to auto — so the vertical axis was clipping too.
   With only 10px of padding, the card's -6px hover lift and its shadow-3 halo
   were being cut off at the top and bottom of the track. The padding is now
   sized to the shadow rather than to the card, and scroll-snap gives the
   sideways drag a deliberate stop instead of a free glide.
   -------------------------------------------------------------------------- */
body.nv .reviews-carousel {
  padding: var(--nv-space-sm) var(--nv-space-2xs) var(--nv-space-lg);
  scroll-snap-type: x proximity;
  scroll-padding-inline-start: var(--nv-space-2xs);
}


/* ============================================================================
   SLIDER ARROWS — ONE TREATMENT SITE-WIDE
   ----------------------------------------------------------------------------
   Splide builds its arrows in JS, so they were never reached by any hand-
   written section CSS and rendered as the library's default dark discs sitting
   on top of the media. The review carousel's own arrows had already been
   rebuilt as light, bordered, shadowed circles; this gives every other slider
   on the site that same object so the page stops using two different controls
   for the same gesture.
   ============================================================================ */
body.nv .splide__arrow {
  width: var(--nv-tap-min);
  height: var(--nv-tap-min);
  background: rgb(var(--color-base-background-1));
  border: 1px solid var(--nv-line);
  border-radius: 50%;
  box-shadow: var(--nv-shadow-2);
  opacity: 1;
  transition:
    transform var(--duration-default) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease),
    opacity var(--duration-default) var(--nv-ease);
}

body.nv .splide__arrow svg {
  width: 1.5rem;
  height: 1.5rem;
  fill: rgb(var(--color-base-text));
}

@media (hover: hover) and (pointer: fine) {
  body.nv .splide__arrow:hover:not(:disabled) {
    transform: scale(1.08);
    box-shadow: var(--nv-shadow-3);
  }
}

body.nv .splide__arrow:disabled {
  opacity: .28;
  box-shadow: none;
}


/* ============================================================================
   VIDEO SLIDES
   ----------------------------------------------------------------------------
   The UGC clips were square-cornered and sat flat on the page while every
   other card on the site is rounded and lifted, so the one section made of
   real customer footage looked the least considered. Corners and a shadow put
   them on the same shelf as the review cards.

   The play button was a flat coral ring drawn straight onto the video, which
   disappears over a bright frame. A frosted disc behind it keeps it legible on
   any poster image without covering the footage.
   ============================================================================ */
body.nv .image-slide__image {
  border-radius: var(--nv-radius-lg);
  overflow: hidden;
  box-shadow: var(--nv-shadow-1);
}

body.nv .image-slide .play-button {
  background: rgba(255, 255, 255, .16);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  box-shadow: 0 2px 12px rgba(0, 0, 0, .28);
  transition:
    transform var(--duration-default) var(--nv-ease-out),
    background-color var(--duration-default) var(--nv-ease);
}

@media (hover: hover) and (pointer: fine) {
  body.nv .internal-video__play:hover .play-button {
    transform: scale(1.09);
    background: rgba(255, 255, 255, .30);
  }
}

@media (prefers-reduced-motion: reduce) {
  body.nv .splide__arrow,
  body.nv .image-slide .play-button {
    transition: none;
  }
  body.nv .splide__arrow:hover:not(:disabled),
  body.nv .internal-video__play:hover .play-button {
    transform: none;
  }
}


/* ============================================================================
   PRODUCT PAGE BELOW THE FOLD — ONE PASS OVER EVERY SECTION
   ----------------------------------------------------------------------------
   Everything under the buy box was theme-default: flat glyphs, library dots,
   solid black discs, body copy at whatever size the section shipped with. Each
   section was individually fine and collectively looked assembled from parts.
   This gives them one vocabulary — tinted chips, coral as the single accent,
   and the same type ramp the buy box already uses.
   ============================================================================ */

/* ---- The UGC reel ----------------------------------------------------------
   Two portrait clips side by side on a phone is a contact sheet, not a feed.
   The slide count is now one with a slice of the next showing (set on the
   section, since Splide reads per-page from JS and no stylesheet can reach it).
   What CSS does here is the rest of the feed grammar: the neighbours recede so
   the clip you are on owns the screen, and the card carries real depth.
   -------------------------------------------------------------------------- */
body.nv .image-slide__image {
  box-shadow: var(--nv-shadow-2);
}

body.nv splide-component[data-image-slider] .splide__slide .image-slide__image {
  transition:
    opacity var(--duration-long) var(--nv-ease),
    transform var(--duration-long) var(--nv-ease-out),
    box-shadow var(--duration-default) var(--nv-ease);
}

@media screen and (max-width: 749px) {
  body.nv splide-component[data-image-slider] .splide__slide:not(.is-active) .image-slide__image {
    opacity: .45;
    transform: scale(.94);
  }
}

/* ---- Slider dots -----------------------------------------------------------
   Splide's default pagination is equal grey circles that scale up when active,
   which reads as a library control. A track of small dots with the current one
   drawn out into a coral pill is the pattern every social app uses, and it
   says position rather than just count.
   -------------------------------------------------------------------------- */
body.nv .splide__pagination {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: .8rem;
  margin-block-start: var(--nv-space-sm);
  padding: 0;
}

/* The theme paints the visible dot on ::before and leaves the button itself
   transparent as a larger hit area. Styling the button therefore drew a pill
   BEHIND the theme's dot instead of replacing it. All of this belongs on
   ::before; the button keeps its hit area untouched. */
body.nv .splide__pagination__page {
  margin: 0;
  border: 0;
  background: transparent;
  opacity: 1;
  transform: none;
}

body.nv .splide__pagination__page::before {
  opacity: 1;
  border-radius: var(--nv-radius-pill);
  /* NOT --color-foreground. The dots wrapper runs an inverted scheme, which is
     exactly why the theme draws its own dots from --color-background: inside
     here, foreground IS white. Faint coral is immune to the swap and ties the
     inactive track to the pill it fills in. */
  background: rgba(var(--nv-cta), .28);
  transition:
    width var(--duration-default) var(--nv-ease-out),
    background-color var(--duration-default) var(--nv-ease);
}

body.nv .splide__pagination__page.is-active::before {
  width: 2.4rem;
  height: var(--pagination-dot-height);
  border-radius: var(--nv-radius-pill);
  background: rgb(var(--nv-cta));
  opacity: 1;
}

/* ---- Feature icons become chips --------------------------------------------
   The glyphs were bare coral symbols floating next to their headings at the
   same optical weight as punctuation. A tinted disc gives each one a footprint,
   turns the column into a scannable list, and is the single change that stops
   these sections reading as a stock theme. Shared by the feature columns and
   the icons-with-content block so both speak the same way.
   -------------------------------------------------------------------------- */
body.nv-tpl--product .icons-with-text__icon__title {
  display: flex;
  align-items: center;
  gap: var(--nv-space-2xs);
}

body.nv-tpl--product .icons-with-text__icon__icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 4.4rem;
  block-size: 4.4rem;
  border-radius: 50%;
  background: rgba(var(--nv-cta), .10);
  box-shadow: inset 0 0 0 1px rgba(var(--nv-cta), .16);
}

body.nv-tpl--product .icons-with-text__icon__icon .material-icon {
  font-size: 2.2rem;
  line-height: 1;
}

/* ---- Feature list rhythm ---------------------------------------------------
   Three stacked items with nothing between them ran together as one grey
   block. Hairlines and real spacing make them three things.
   -------------------------------------------------------------------------- */
body.nv-tpl--product .icon-with-content__icons .icons-with-text__icon-item + .icons-with-text__icon-item {
  margin-block-start: var(--nv-space-md);
  padding-block-start: var(--nv-space-md);
  border-block-start: 1px solid var(--nv-hairline);
}

body.nv-tpl--product .icons-with-text__icon__text .rte p,
body.nv-tpl--product .icon-with-content__text p {
  font-size: var(--nv-text-sm);
  line-height: 1.7;
  color: var(--nv-ink-2);
}

/* ---- Delivery timeline -----------------------------------------------------
   Three solid black discs directly under a coral button — the heaviest objects
   in the buy column were the ones that are not the decision. They become quiet
   neutral chips, and only the last step, the one the shopper actually wants,
   carries the accent. Progressive emphasis instead of three equal shouts.
   -------------------------------------------------------------------------- */
body.nv-tpl--product .shipping-checkpoint__icon {
  background: rgba(var(--color-foreground), .06);
  color: var(--nv-ink-1);
  box-shadow: inset 0 0 0 1px var(--nv-hairline);
}

body.nv-tpl--product .shipping-checkpoint:last-of-type .shipping-checkpoint__icon {
  background: rgba(var(--nv-cta), .12);
  color: rgb(var(--nv-cta));
  box-shadow: inset 0 0 0 1px rgba(var(--nv-cta), .22);
}

body.nv-tpl--product .shipping-checkpoints__bar {
  background: var(--nv-hairline);
}

@media (prefers-reduced-motion: reduce) {
  body.nv splide-component[data-image-slider] .splide__slide .image-slide__image,
  body.nv .splide__pagination__page::before {
    transition: none;
  }
  body.nv splide-component[data-image-slider] .splide__slide:not(.is-active) .image-slide__image {
    transform: none;
  }
}


/* ---- Feature-column images -------------------------------------------------
   The product shot between the feature blocks was a bare <img> with square
   corners, sitting between rounded icon chips above it and a rounded video
   reel below. Same radius as the reel and the review cards so the page has one
   corner, not three.
   -------------------------------------------------------------------------- */
body.nv-tpl--product .custom-columns__column .content-wrapper > img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  border-radius: var(--nv-radius-lg);
}
