/** Shopify CDN: Minification failed

Line 1571:2 Unexpected "}"

**/
/* Tokens live in nd-global.css so every section can read them. */


/* The horizontal lockdown that used to live here is in nd-global.css now,
   loaded from theme.liquid on every template. Scoping it to this stylesheet
   fixed this page and left the rest of the site sliding. */

/* ---------------------------------------------------------------- shell */

.nd-product {
  /* Last line of defence against a sideways page: whatever a scroller inside
     does, the section itself never spills. clip rather than hidden, because
     hidden would make this a scroll container and break sticky descendants. */
  overflow-x: clip;
  font-family: var(--font-body-family);
  font-weight: var(--font-body-weight);
  color: var(--nd-ink);
}

.nd-product__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.5rem;
}

@media screen and (min-width: 750px) {
  .nd-product__grid {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    gap: 4.8rem;
    align-items: start;
  }
}

/* A grid item's min-width resolves to auto, which means it refuses to shrink
   below its content -- so the gallery's ten slides push the column wider than
   the track and the whole page gains a horizontal scroll. Both columns are
   pinned to zero so the scrollers inside do the scrolling instead. */
.nd-product__media,
.nd-product__info {
  min-width: 0;
}
.nd-product__media {
    position: sticky;
    top: 20px;
    display: flex;
    gap: 15px;
    flex-direction: column;
}
.nd-product__info {
  max-width: 46rem;
}

/* ---------------------------------------------------------------- media */

/* One row of thumbs, always. The previous shape was a single wrapping flex
   container holding both the full-size slides and the thumbs, so ten thumbs
   at 6.4rem overran the column and broke onto a second row. Stage and strip
   are separate scrollers now, and neither can wrap. */

.nd-media {
  /* Positioned for the bestseller mark, which is absolute inside it. */
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  margin: 0;
}
.nd-media__thumb-picture {
  display: block;
  width: 100%;
  height: 100%;
}

.nd-media__thumb-picture .nd-media__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The bestseller mark ---------------------------------------------------
 *
 * Frosted rather than solid, so it sits on the photograph instead of
 * punching a hole in it, and small enough that the product is still the
 * first thing read. The star is the accent's only appearance up here,
 * which is what keeps it a mark rather than a second offer.
 */
.nd-badge {
  position: absolute;
  z-index: 3;
  /* Into the corner, not floating inside it.
   *
   * Flush to the top and right edges of the stage, so it reads as part
   * of the picture rather than a sticker laid on top of one. Right, not
   * left: the slides carry their own headlines across the top left, and
   * a mark there cut into six of the ten. */
  top: 0;
  right: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0;
  padding: 0.75rem 1.3rem;
  /* No border and no shadow now that it meets the frame: an outline on
     two edges that have nothing beyond them draws the seam it is meant
     to hide. */
  border: 0;
  /* Only the inner corner is rounded. The stage clips its own top right
     curve, so rounding that corner here just doubled the arc. */
  border-radius: 0 0 0 var(--nd-radius);
  /* Light frosted glass, with the type in ink.
   *
   * A white veil thick enough to rescue a dark slide also erases itself on
   * a white one, so tone alone cannot both carry the words and draw the
   * panel. The veil is therefore set for legibility and the hairline draws
   * the shape.
   *
   * Ink and not white type, which is the one change from the reference.
   * White reads beautifully over mid-toned photography; this gallery is
   * mostly cut-outs on white, where white type has nothing to sit on. */
  background-color: rgba(255, 255, 255, 0.5);
  /* `brightness` is what makes this look like glass on a white slide.
   *
   * A white veil over a white photograph is just white -- the panel had no
   * tone of its own and only the hairline said it was there. Darkening the
   * backdrop before the veil composites over it gives the glass a tint it
   * keeps on any ground: a white slide settles near 232 against a 246
   * ground, which is a visible pane, and the darkest slide in the gallery
   * still lifts to about 140, where ink type clears AA.
   *
   * `saturate` above 1 is the other half of the effect -- over anything
   * with colour in it, the pane picks that colour up rather than greying
   * it out, which is what makes it read as glass rather than as a scrim. */
  -webkit-backdrop-filter: blur(18px) saturate(1.6) brightness(0.85);
  backdrop-filter: blur(18px) saturate(1.6) brightness(0.85);
  /* The hairline is what makes it a panel on a white slide, and it is ink
     rather than white because a white edge on white is no edge. Only the
     two inner sides carry it -- beyond the other two is the frame. */
  box-shadow:
    inset 1px 0 0 rgba(0, 0, 0, 0.09),
    inset 0 -1px 0 rgba(0, 0, 0, 0.09);
  font-family: var(--font-heading-family);
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  line-height: 1;
  text-transform: uppercase;
  color: var(--nd-ink);
  /* Inert, so it cannot take a tap meant for the gallery under it. */
  pointer-events: none;
}

.nd-badge__star {
  width: 1.25rem;
  height: 1.25rem;
  fill: var(--nd-select, #c1502e);
}

.nd-media__stage {
  display: flex;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* A box with overflow-x:auto computes overflow-y to auto as well, which on
     any platform with gutter scrollbars reserves a strip of height under the
     images. Pinning the cross axis is what keeps the strip the height of a
     thumbnail and nothing more. */
  overflow-y: hidden;
  /* Without this, swiping past the last slide hands the gesture to the page
     and the whole document slides sideways under your thumb. */
  /* x only. The shorthand sets both axes, and this track cannot scroll
     vertically -- so `contain` on y told the browser not to chain a
     vertical wheel to the page either, and the page stopped scrolling
     whenever the pointer was over the row. Horizontal still contains,
     which is what stops a sideways flick triggering a back-navigation. */
  overscroll-behavior-x: contain;
  overscroll-behavior-y: auto;
  /* One axis only, so a diagonal drag is not split between this scroller and
     the page. */
  touch-action: pan-x pan-y pinch-zoom;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  border-radius: var(--nd-radius);
  background-color: var(--nd-surface);
  scrollbar-width: none;
}

.nd-media__stage::-webkit-scrollbar {
  display: none;
}

.nd-media__slide {
  position: relative;
  flex: 0 0 100%;
  scroll-snap-align: center;
  /* Set by the section, so the gallery's shape is a choice rather than a
     fact of the stylesheet. Square unless told otherwise. */
  aspect-ratio: var(--nd-media-ratio, 1 / 1);
}

.nd-media__slide .nd-media__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* The clip is sized exactly like the photographs beside it and fitted the
   same way: contain, not cover. This product's clip happens to be square, so
   it fills the slide with nothing to letterbox -- but contain is the right
   rule for the next clip as much as this one, because cover would crop the
   frame of something shot to be watched. Where there is a letterbox it takes
   the stage's own surface, which is why no background is set here. */
.nd-media__slide .nd-media__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.nd-media__embed {
  width: 100%;
  height: 100%;
}

.nd-media__embed iframe {
  width: 100%;
  height: 100%;
  border: 0;
}

/* A poster frame looks like a photograph, so the strip needs to say which
   one is the clip. Bottom right, small, and over the ink veil so it reads on
   a light frame and a dark one alike. */
.nd-media__thumb-play {
  position: absolute;
  right: 0.4rem;
  bottom: 0.4rem;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: var(--nd-radius-pill);
  background-color: rgba(0, 0, 0, 0.62);
}

.nd-media__thumb-play::after {
  content: '';
  position: absolute;
  /* Optically centred, not measured: a triangle's mass sits left of its
     bounding box, so a centred box reads as off centre. */
  top: 50%;
  left: 54%;
  width: 0;
  height: 0;
  transform: translate(-50%, -50%);
  border-style: solid;
  border-width: 0.35rem 0 0.35rem 0.55rem;
  border-color: transparent transparent transparent #fff;
}

.nd-media__thumbs {
  display: flex;
  align-items: flex-start;
  gap: 0.8rem;
  margin: 0;
  padding: 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  overflow-y: hidden;
  /* x only. The shorthand sets both axes, and this track cannot scroll
     vertically -- so `contain` on y told the browser not to chain a
     vertical wheel to the page either, and the page stopped scrolling
     whenever the pointer was over the row. Horizontal still contains,
     which is what stops a sideways flick triggering a back-navigation. */
  overscroll-behavior-x: contain;
  overscroll-behavior-y: auto;
  touch-action: pan-x pan-y pinch-zoom;
  list-style: none;
  scrollbar-width: none;
}

.nd-media__thumbs::-webkit-scrollbar {
  display: none;
}

/* The thumb is sized here and nowhere else: a fixed box on the list item, and
   the anchor and image both told to fill it exactly. Height was being left to
   the image's aspect-ratio, which meant any inherited rule on img -- or the
   flex line stretching the item -- could change it. Now nothing can. */
.nd-media__thumb-item {
  flex: 0 0 auto;
  width: 6.4rem;
  height: 6.4rem;
}

/* iOS hands a drag that begins on a link or an image to its own gesture --
   the link preview and the image drag -- and the horizontal scroller under
   it never sees the move. Chrome does not do this, which is why the strip
   tested fine here and was dead on the phone. Naming the axis on the child
   as well as the scroller, and refusing the callout and the drag, is what
   gives the gesture back to the list. */
.nd-media__thumb,
.nd-media__thumb .nd-media__img {
  touch-action: pan-x pan-y pinch-zoom;
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
  -webkit-user-select: none;
  user-select: none;
}

/* position:relative is load-bearing, not decoration.
 *
 * This element holds a .visually-hidden span, and Dawn styles that as
 * position:absolute. An absolutely positioned box is clipped only by an
 * ancestor that is its containing block -- an ancestor's overflow does
 * nothing otherwise. With no positioned parent these labels resolved against
 * the page itself, ignored the scroller clipping their thumbnails, and sat
 * out at x=448 through 664 on a 390px screen. That, and nothing else, is what
 * made the document 664px wide and the whole page swipe sideways.
 *
 * Measured in headless Chrome at 390px: document scrollWidth 664 against a
 * body and #MainContent of exactly 390. Every guard on html, body and
 * MainContent was correct and none of them could ever have caught this. */
.nd-media__thumb {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border: 1px solid var(--nd-rule);
  border-radius: var(--nd-radius-sm);
  background-color: var(--nd-surface);
  transition: border-color var(--nd-speed) ease;
}

.nd-media__thumb .nd-media__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.nd-media__thumb.is-active {
  border-color: var(--nd-ink);
}

.nd-media__thumb:focus-visible {
  outline: 2px solid var(--nd-ink);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .nd-media__stage {
    scroll-behavior: auto;
  }
}

/* -------------------------------------------------------------- the top */

.nd-eyebrow {
  /* Sits under the title now rather than above it, so the space goes over. */
  margin: 0.6rem 0 0;
  font-family: var(--font-heading-family);
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--nd-ink-2);
}

.nd-title {
  margin: 0;
  font-family: var(--nd-font-heading);
  font-size: clamp(2.8rem, 5.5vw, 4rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.01em;
  color: var(--nd-ink);
}

.nd-rating {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  /* Leads the column now, so it takes the tighter gap the eyebrow had. */
  margin: 0 0 0.8rem;
  /* Quieter than the title it introduces. At 13px it was competing with
     the spec line under the name for second place in the column; the
     rating is a credential, and a credential is read once. */
  font-size: 1.2rem;
  color: var(--nd-ink-2);
}

.nd-rating__stars {
  display: inline-flex;
  gap: 0.2rem;
}

/* The rating stars take the star token, not the ink.
   --nd-star is #c1502e and always has been; these were filled with
   --nd-ink, which is why the palette change reached the Okendo widget's
   stars further down the page and never reached these. */
.nd-star {
  /* Down with the words beside them -- five stars held at their old size
     against smaller type would make the row read as stars with a caption
     rather than as one line. */
  width: 1.25rem;
  height: 1.25rem;
  fill: var(--nd-star);
}

.nd-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  /* Spacing above is owned by whatever precedes these, so that one gap is
     stated in one place rather than added from both sides. */
  margin: 0 0 2.4rem;
  padding: 0;
  list-style: none;
}

.nd-pill {
  /* Smaller, and the chip comes in with it. Left at its old padding
     around 10px type the chip reads as a button with a caption in it
     rather than as a note about the product. */
  padding: 0.5rem 1rem;
  border-radius: var(--nd-radius-pill);
  /* Outlined, with the text at full strength.
  
     These were grey text on a grey chip, which the eye skips -- and the fix
     was a graphite fill carrying amber, which over-corrected. Reading the
     two side by side on the page made the diagnosis obvious: the claim was
     weak because the TEXT was grey, not because the chip had no fill. A
     filled chip is a heavy answer to a type problem, and it made the second
     darkest thing above the fold a piece of secondary information -- more
     visual weight per square millimetre than the Add to cart button.
     
     Outline instead, and now the accent carries the text. The old accent
     could not: it was 1.6:1 on white, which is the whole reason a graphite
     fill was ever reached for. This one is 4.71:1 and needs no ground.
     
     The palette brief assigns the tint to chip fills, and these are chips --
     but the benefits panel directly below already holds that tint, and two
     tinted blocks stacked read as one warm mass rather than as two levels.
     That was tested rather than assumed. So the chips take the accent as
     TEXT with the tint only as their border: branded, legible, and still a
     lighter level than the panel under them. One line if the fill is wanted
     instead. */
  background-color: transparent;
  border: 1px solid var(--nd-pop-soft-rule);
  color: var(--nd-pop);
  font-family: var(--font-heading-family);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* This line names what is in the bundle, directly under the product name --
   the place a subtitle answers "what is it", which for a bundle is the same
   question as "what do I get". It replaced a category label that said the
   product was a portable ergonomic workstation; the three benefits under it
   establish portable and ergonomic between them, and four named objects say
   more than the phrase did.

   The pills used to carry "4 pieces", which raised the question this answers
   and then did not answer it. That pill is gone rather than left restating
   a line two centimetres above it. */
.nd-intro {
  /* 10px over, 18px under. The gap over is the smaller of the two because a
     subtitle belongs to the name above it and not to the pills below -- that
     is the whole rhythm of this column: things that belong together sit
     closer, and an even gap everywhere is what makes a page read as a
     template. It previously had no bottom margin at all against pills that
     have no top margin, so the two groups touched. */
  margin: 1rem 0 1.8rem;
  font-size: 1.4rem;
  line-height: 1.5;
  /* The full ink, not the secondary. This line is the specification --
     what is actually in the box -- and it is read, not skimmed past. */
  color: var(--nd-ink);
}

/* ------------------------------------------------------------ benefits */

/* Three outcomes, one line each. They replace the intro paragraph rather than
   joining it: the eyebrow already says what the thing is and the pills already
   carry its numbers, so a sentence restating the composition was the third
   description above the fold. Three short claims in its place cost about a
   line of height and say considerably more.

   Same drawn tick as the benefit list inside the accordion, so a reader meets
   one mark on this page rather than two that nearly match. */

/* The benefits sit in a panel, and the panel is the only warm thing above
   the fold.
   
   Everything up here was ink and grey on white, which scans as one block --
   the eye is given nothing to land on between the name and the tiles. One
   tinted panel fixes that, and it matters WHICH element gets it: these three
   lines are the argument, where the chips above them are specifications. If
   colour is spent once above the fold it belongs on the reason to buy, not
   on the weight in kilograms.
   
   The chips stay outlined for the same reason. Rendering both tinted showed
   them reading as one confused warm mass rather than two levels. */
.nd-benefits {
  margin: 0 0 2.4rem;
  padding: 1.6rem 1.6rem 1.6rem 1.4rem;
  border: 1px solid var(--nd-pop-soft-rule);
  border-radius: var(--nd-radius);
  background-color: var(--nd-pop-soft);
  list-style: none;
}

/* Deeper amber than the panel, never the vivid one -- see the token. */
.nd-benefits .nd-icon__accent {
  fill: var(--nd-pop-deep);
}

.nd-benefits .nd-icon__accentstroke {
  stroke: var(--nd-pop-deep);
}

.nd-benefits__item {
  position: relative;
  padding-left: 2.6rem;
  /* A shade more air than before: inside a panel the lines are read as a
     group, and the group needs to breathe where a bare list did not. */
  margin-bottom: 1.1rem;
  font-size: 1.5rem;
  line-height: 1.45;
  color: var(--nd-ink);
}

/* An icon needs more room than a drawn tick, and the line is set against the
   icon's optical centre rather than its box -- these marks are top-weighted,
   so centring them on the box drops them below the text. */
.nd-benefits__item--icon {
  padding-left: 3.2rem;
  min-height: 2.2rem;
}

.nd-benefits__icon {
  position: relative;
  top: -0.1rem;
  left: 0.2rem;
  width: 2.2rem;
  height: 2.2rem;
}





.nd-benefits__item:last-child {
  margin-bottom: 0;
}

.nd-benefits__tick {
  position: absolute;
  top: 0.5rem;
  left: 0.4rem;
  width: 0.5rem;
  height: 0.9rem;
  border: solid var(--nd-mark);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}

/* --------------------------------------------------------------- tiers */

.nd-tiers {
  margin: 3.2rem 0 0;
  padding: 0;
  border: 0;
}

.nd-tiers__legend {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.8rem;
  width: 100%;
  padding: 0;
  margin-bottom: 1rem;
}

.nd-tiers__label {
  font-family: var(--font-heading-family);
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--nd-ink-2);
}

.nd-tiers__value {
  font-size: 1.3rem;
  color: var(--nd-ink);
}

.nd-tiers__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.8rem;
}

.nd-tier {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--nd-rule);
  border-radius: var(--nd-radius);
  background-color: var(--nd-ground);
  cursor: pointer;
  transition: border-color var(--nd-speed) ease, background-color var(--nd-speed) ease;
}

.nd-tier__input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.nd-tier__cap {
  display: block;
  padding: 0.5rem 0.4rem;
  background-color: var(--nd-ink);
  color: var(--nd-ground);
  font-family: var(--font-heading-family);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  line-height: 1.3;
  text-align: center;
  text-transform: uppercase;
}

/* Amber needs dark ink. The other cap is near-black with white on it, and
   carrying that same white onto amber would have been unreadable -- which is
   why this rule sets both and not just the fill. */
.nd-tier--gold .nd-tier__cap {
  background-color: var(--nd-pop);
  color: var(--nd-pop-ink);
}

/* The first tile carries no badge, but the element is still emitted so every
   tile's name sits on one line. Empty, it keeps its height and draws nothing
   -- no fill, no divider, no text to align. Collapsing it instead would put
   the first name a whole bar higher than the other two. */
.nd-tier__cap:empty {
  background-color: transparent;
  border-bottom-color: transparent;
}

/* A zero-width space rather than a min-height: an empty block with padding is
   only as tall as its padding, and a hard-coded height would drift the moment
   the cap's font size or line-height changed. This gives it a real line box,
   so it is exactly as tall as a bar with words in it and stays that way. */
.nd-tier__cap:empty::before {
  content: '\200B';
}

.nd-tier__body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  padding: 1rem 0.6rem 1.1rem;
  text-align: center;
}

/* "3 pack" on one baseline rather than the numeral stacked over the word.
   Stacked, it cost every tile a line and read as a promo counter; set on
   a line it reads as a quantity, which is what it is -- and the three
   tiles lose 15px each without a word coming off them. */
.nd-tier__name {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.3rem;
  line-height: 1;
}

/* The count is the only thing that differs between the three tiles, so it is
   the biggest thing on them. It was the smallest. */
.nd-tier__count {
  font-family: var(--font-heading-family);
  font-weight: 700;
  font-size: 2.2rem;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--nd-ink);
}

.nd-tier__unit-word {
  margin-top: 0;
  font-size: 1.1rem;
  line-height: 1.2;
  color: var(--nd-ink-2);
}

.nd-tier__total {
  margin-top: 0.4rem;
  font-family: var(--font-heading-family);
  font-size: 1.8rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--nd-ink);
  font-variant-numeric: tabular-nums;
}

.nd-tier__was {
  font-size: 1.2rem;
  color: var(--nd-ink-2);
  font-variant-numeric: tabular-nums;
}

/* A badge rather than a strip across the foot. The saving is the argument
   for the second and third tiles, and a full-width band in a pale tint reads
   as a caption where a pill reads as a mark on the tile. */
.nd-tier__save {
  align-self: center;
  margin-top: 0.55rem;
  /* The percentage is gone, so the pill no longer has to be squeezed to
     fit a 115px tile -- "Save $158" is short. It gets its horizontal
     padding back and loses height instead: smaller type, tighter block,
     less of the tile spent on a mark about the tile. */
  padding: 0.3rem 0.7rem;
  border-radius: var(--nd-radius-pill);
  background-color: var(--nd-save-soft);
  font-family: var(--font-heading-family);
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.2;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--nd-save);
  white-space: nowrap;
}

.nd-tier__benefit {
  display: block;
  margin-top: auto;
  padding: 0.5rem 0.4rem;
  background-color: var(--nd-save-soft);
  color: var(--nd-save);
  font-family: var(--font-heading-family);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.3;
  text-align: center;
}

/* The chosen tile fills its badge. The saving is the reason to move up a
   tile, so on the tile you are on it stops being an offer and becomes a fact
   -- and it is the one filled mark in the group, which is what makes the
   selection readable at a glance. */
.nd-tier.is-selected .nd-tier__save,
.nd-tier:has(.nd-tier__input:checked) .nd-tier__save {
  background-color: var(--nd-save);
  color: var(--nd-paper);
}

.nd-tier.is-selected {
  border-color: var(--nd-ink);
  outline: 1px solid var(--nd-ink);
  outline-offset: -2px;
}

.nd-tier:has(.nd-tier__input:checked) {
  border-color: var(--nd-ink);
  outline: 1px solid var(--nd-ink);
  outline-offset: -2px;
}

.nd-tier:has(.nd-tier__input:focus-visible) {
  outline: 2px solid var(--nd-ink);
  outline-offset: 2px;
}

.nd-tier--soldout {
  cursor: default;
  background-color: var(--nd-surface);
  color: var(--nd-ink-2);
}

.nd-tier--soldout .nd-tier__name,
.nd-tier--soldout .nd-tier__total {
  color: var(--nd-ink-2);
  text-decoration: line-through;
}

/* ------------------------------------------------- tiers: shared parts */

/* Thumbnails, grouping and the gift line, in both layouts.
 *
 * The body is two groups -- what you get, and what it costs -- so stacked
 * rows can put the price on the right. Side by side the groups dissolve and
 * their pieces return to the tile's one centred column in the order they
 * always had: count, price, was, saving. */
.nd-tier__main,
.nd-tier__price {
  display: contents;
}

.nd-tier__name  { order: 1; }
.nd-tier__total { order: 2; }
.nd-tier__was   { order: 3; }
.nd-tier__save  { order: 4; }

/* Hidden side by side: the tile's own outline says which one is chosen. */
.nd-tier__radio {
  display: none;
}

/* A thumbnail at the head of the tile, inset so it reads as a picture of
   the pack rather than as the tile's background. Contained on white, so a
   cut-out shot of any proportion sits whole instead of being cropped. */
.nd-tier__thumb {
  display: block;
  margin: 0.8rem 0.8rem 0;
  overflow: hidden;
  border-radius: 0.8rem;
  background-color: var(--nd-surface);
  aspect-ratio: 1;
}

.nd-tier__thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* The tile's body sits closer to a picture above it than to a bare edge. */
.nd-tier__thumb + .nd-tier__body {
  padding-top: 0.7rem;
}

/* Side by side a tile can only say "Free gift"; the named gift and its
   picture belong to the stacked row, which has the width for them. */
.nd-tier__benefit-long,
.nd-tier__gift-img,
.nd-tier__benefit--long-only {
  display: none;
}

/* ---------------------------------------------------------- tiers: stacked */

/* One pack per row, read down the left edge.
 *
 * The same language as the add-on rows further down the column -- circle,
 * picture, name, price on the right -- so the buy box reads as one system
 * rather than a grid of cards followed by a list. Each row answers the
 * question in the order a buyer asks it: how many, what it saves, what it
 * costs. */
.nd-tiers--vertical .nd-tiers__grid {
  grid-template-columns: minmax(0, 1fr);
  /* Room for the badge that sits on each row's top edge. */
  gap: 1.2rem;
}

.nd-tiers--vertical .nd-tier {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  align-items: center;
  column-gap: 1rem;
  /* 9px, down from 13. The row is a full-width label, so it stays an easy
     target at any height the contents allow -- 62px here, well over the
     44px floor -- and every pixel under that is height the buy box was
     spending on air. */
  padding: 0.9rem 0.9rem 0.9rem 0.9rem;
  /* The badge hangs over the top edge, so the row cannot clip. */
  overflow: visible;
}

/* The badge becomes a tag on the row's top edge instead of a bar across the
   tile. A bar across a full-width row is a banner; a tag says the same thing
   at the size of a label. */
.nd-tiers--vertical .nd-tier__cap {
  position: absolute;
  top: 0;
  right: 1.4rem;
  z-index: 1;
  padding: 0.2rem 0.65rem;
  border-radius: var(--nd-radius-pill);
  transform: translateY(-50%);
  font-size: 0.9rem;
  line-height: 1.2;
  white-space: nowrap;
}

.nd-tiers--vertical .nd-tier__cap:empty {
  display: none;
}

/* A tagged row takes a little more room at the top. The tag hangs half
   over the edge and the price sits top-right under it. */
.nd-tiers--vertical .nd-tier--capped {
  padding-top: 1.3rem;
}

.nd-tiers--vertical .nd-tier__radio {
  position: relative;
  display: block;
  width: 1.9rem;
  height: 1.9rem;
  border: 1px solid var(--nd-hairline-strong, rgba(0, 0, 0, 0.28));
  border-radius: 50%;
  background-color: var(--nd-surface);
  transition: border-color var(--nd-speed) ease;
}

.nd-tiers--vertical .nd-tier__radio::after {
  content: '';
  position: absolute;
  inset: 0.4rem;
  border-radius: 50%;
  background-color: var(--nd-ink);
  transform: scale(0);
  transition: transform var(--nd-speed) ease;
}

.nd-tiers--vertical .nd-tier.is-selected .nd-tier__radio,
.nd-tiers--vertical .nd-tier:has(.nd-tier__input:checked) .nd-tier__radio {
  border-color: var(--nd-ink);
}

.nd-tiers--vertical .nd-tier.is-selected .nd-tier__radio::after,
.nd-tiers--vertical .nd-tier:has(.nd-tier__input:checked) .nd-tier__radio::after {
  transform: scale(1);
}

/* 44px, the add-on rows' size. The picture is the tallest thing in the
   row, so it sets the row's height: at 56 it made every pack 84px tall
   for a thumbnail that reads just as clearly at 44. */
.nd-tiers--vertical .nd-tier__thumb {
  width: 4.4rem;
  margin: 0;
  border-radius: 0.6rem;
  border: 1px solid var(--nd-rule);
}

/* With no thumbnail the row keeps its columns: the body takes the picture's
   track as well, so the text starts right after the circle instead of a
   picture-sized gap. */
.nd-tiers--vertical .nd-tier__radio + .nd-tier__body {
  grid-column: 2 / -1;
}

.nd-tiers--vertical .nd-tier__body,
.nd-tiers--vertical .nd-tier__thumb + .nd-tier__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 1rem;
  padding: 0;
  text-align: left;
}

.nd-tiers--vertical .nd-tier__main,
.nd-tiers--vertical .nd-tier__price {
  display: flex;
  gap: 0.3rem;
}

.nd-tiers--vertical .nd-tier__main {
  align-items: center;
  min-width: 0;
}

.nd-tiers--vertical .nd-tier__price {
  align-items: flex-end;
  gap: 0.5rem;
}
span.nd-tier__price__detail {
  display: flex;
  align-items: center;
  gap: 5px;
}
.nd-tiers--vertical .nd-tier__price {
  flex-direction: column;
}
.nd-tiers--vertical .nd-tier__name {
  justify-content: flex-start;
}

.nd-tiers--vertical .nd-tier__count {
  font-size: 1.8rem;
}

.nd-tiers--vertical .nd-tier__unit-word {
  font-size: 1.3rem;
  color: var(--nd-ink);
}

.nd-tiers--vertical .nd-tier__save {
  align-self: flex-start;
  margin-top: 0;
  padding: 0.2rem 0.6rem;
}

.nd-tiers--vertical .nd-tier__total {
  margin-top: 0;
  font-size: 1.7rem;
  line-height: 1.1;
}

/* Leading set, not inherited: at the body's 1.8 the struck price was a
   22px line for 12px type, which made the price block the tallest thing
   in the row after the picture and pushed it up under the tag. */
.nd-tiers--vertical .nd-tier__was {
  font-size: 1.2rem;
  line-height: 1.2;
}

/* The gift as a compact pill under the pack's name, with its picture.
 *
 * It was a band across the whole foot of the row -- 358px wide and 44px
 * tall for five words. A pill sized to its own text says the same thing,
 * sits in the same column as the name and saving it belongs to, and costs
 * the row about half the height. */
.nd-tiers--vertical .nd-tier__benefit,
.nd-tiers--vertical .nd-tier__benefit--long-only {
  grid-column: 2 / -1;
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  max-width: 100%;
  margin: 0.6rem 0 0;
  padding: 0.2rem 0.8rem 0.2rem 0.2rem;
  border-radius: var(--nd-radius-pill);
  font-size: 1.1rem;
  letter-spacing: 0.01em;
  line-height: 1.2;
  text-align: left;
}

/* With a thumbnail, the pill starts under the name rather than under the
   picture, so it reads as part of the pack's text. */
.nd-tiers--vertical .nd-tier__thumb ~ .nd-tier__benefit {
  grid-column: 3 / -1;
}

/* Without a gift picture there is nothing on the left to hug, so the pill
   takes even padding. */
.nd-tiers--vertical .nd-tier__benefit:not(:has(.nd-tier__gift-img)) {
  padding-left: 0.8rem;
}

.nd-tiers--vertical .nd-tier__benefit-short {
  display: none;
}

.nd-tiers--vertical .nd-tier__benefit-long {
  display: inline;
}

.nd-tiers--vertical .nd-tier__gift-img {
  flex: 0 0 auto;
  display: block;
  width: 2rem;
  height: 2rem;
  overflow: hidden;
  border-radius: 50%;
  background-color: var(--nd-surface);
}

.nd-tiers--vertical .nd-tier__gift-img img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* ----------------------------------------------------------------- atc */

.nd-atc {
  margin: 2rem 0 0;
}

.nd-atc__error {
  margin-bottom: 1rem;
  font-size: 1.3rem;
  color: var(--nd-tint-ink);
}

.nd-atc__btn {
  position: relative;
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  padding: 1.7rem 2rem;
  border: 0;
  border-radius: var(--nd-radius-sm);
  background-color: var(--nd-ink);
  color: var(--nd-ground);
  font-family: var(--font-heading-family);
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--nd-speed) ease, color var(--nd-speed) ease;
}

.nd-atc__btn[disabled] {
  background-color: var(--nd-ink-2);
  cursor: default;
}

.nd-atc__label {
  display: inline-flex;
  align-items: center;
  gap: 0.8rem;
}

.nd-atc__price {
  font-variant-numeric: tabular-nums;
}

.nd-atc__sep {
  opacity: 0.6;
}

.nd-atc__btn.loading .nd-atc__label {
  visibility: hidden;
}

.nd-atc__btn > .loading__spinner {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 2rem;
  height: 2rem;
  margin: -1rem 0 0 -1rem;
}



.nd-atc__note {
  margin: 0.6rem 0 0;
  font-size: 1.3rem;
  text-align: center;
  color: var(--nd-ink-2);
}

/* -------------------------------------------------------------- delivery */

/* Deliberately the quietest line in the buy box. It answers "when", which is
   the question the tiles and the button cannot, and it does it in one line at
   caption size -- a delivery promise set loud reads as a sales tactic, set
   quietly it reads as a fact. Only the dates carry the ink. */

.nd-delivery {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  margin: 1.2rem 0 0;
  font-size: 1.3rem;
  line-height: 1.4;
  color: var(--nd-ink-2);
}

.nd-delivery__text {
  min-width: 0;
}

.nd-delivery strong {
  font-weight: 600;
  color: var(--nd-ink);
}

.nd-delivery__dot {
  flex: 0 0 auto;
  width: 0.6rem;
  height: 0.6rem;
  border-radius: var(--nd-radius-pill);
  background-color: var(--nd-mark);
}

/* --------------------------------------------------------------- quotes */

/* One quote at a time, swiped. It sits between the delivery line and the
   guarantees because that is the reassurance beat -- after the decision, not
   in the middle of it. Nothing autoplays: a carousel rotating on its own two
   centimetres under the button is motion competing with the action beside it,
   and it moves the words a reader is halfway through.

   Kept deliberately short. The stars share the attribution line rather than
   taking a row above the quote, the panel's padding is tighter than the other
   cards, and the quote runs at a closer line-height -- together about a third
   off the height, with nothing removed and nothing truncated. */

.nd-quotes {
  margin: 2.4rem 0 0;
  padding: 1.4rem;
  border: 1px solid var(--nd-rule);
  border-radius: var(--nd-radius);
  background-color: var(--nd-surface);
}

.nd-quotes__track {
  display: flex;
  gap: 1.4rem;
  margin: 0;
  padding: 0;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  list-style: none;
  scrollbar-width: none;
}

.nd-quotes__track::-webkit-scrollbar {
  display: none;
}

.nd-quote {
  flex: 0 0 100%;
  scroll-snap-align: start;
}

.nd-quote__text {
  margin: 0 0 0.8rem;
  font-size: 1.35rem;
  line-height: 1.45;
  color: var(--nd-ink);
}

/* Stars and name on one row: the two things that say who said this, reading
   as one unit rather than bracketing the words. */
.nd-quote__by {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0;
}

.nd-quote__stars {
  display: flex;
  flex: 0 0 auto;
  gap: 0.15rem;
}

.nd-quote__stars .nd-star {
  width: 1.1rem;
  height: 1.1rem;
  fill: var(--nd-mark);
}

/* The attribution is the part that makes a quote worth printing -- a name and
   a place read as a person, an unattributed sentence reads as copywriting. */
.nd-quote__author {
  font-size: 1.15rem;
  font-style: normal;
  line-height: 1.3;
  color: var(--nd-ink-2);
}

.nd-quotes__dots {
  display: flex;
  gap: 0.6rem;
  margin-top: 1rem;
}

.nd-quotes__dot {
  /* Same reason as .nd-media__thumb above: it contains a visually-hidden
     label, so it has to be the containing block for it. */
  position: relative;
  width: 0.5rem;
  height: 0.5rem;
  padding: 0;
  border: 0;
  border-radius: var(--nd-radius-pill);
  background-color: var(--nd-rule);
  cursor: pointer;
  transition: background-color var(--nd-speed) ease;
}

.nd-quotes__dot.is-active {
  background-color: var(--nd-mark);
}

.nd-quotes__dot:focus-visible {
  outline: 2px solid var(--nd-ink);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .nd-quotes__track {
    scroll-behavior: auto;
  }
}

/* --------------------------------------------------------------- addons */

/* Two rows, 56px each. Sits with the button because that is the moment
   someone is already saying yes -- an add-on offered before the decision
   competes with it, and one offered after the cart is a different visit.
   No card and no heading, so it reads as part of the buy box rather than a
   section that has pushed its way in. */

/* A panel, not a rule.
 *
 * This was a heading and two rows hanging off a hairline, which is why it
 * read as loose copy at the bottom of the column rather than as an offer.
 * Everything else in the buy box is an object with an edge -- the tiles,
 * the accordions, the gift note -- and this was the one thing that was
 * not. */
.nd-addons {
  margin: 1.6rem 0 0;
  /* Tighter than the tiles above it. A row of accessories that takes
     more of the column than the product's own price ladder is the wrong
     way round -- it was 273px against 202. */
  padding: 1rem;
  border: 1px solid var(--nd-hairline);
  border-radius: var(--nd-radius);
  background-color: var(--nd-ground);
}

.nd-addons__label {
  margin: 0 0 0.4rem;
  font-family: var(--font-heading-family);
  font-weight: 600;
  font-size: 1.1rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--nd-ink-2);
}

/* Divided, not spaced. Two rows floating in a gap read as two separate
   suggestions; ruled, they read as a short list of things that go with


/* Contain, not cover. These are product shots on white and cover was
   cutting the keyboard off at both ends; the whole object on its own
   ground is what makes a 48px square look considered rather than

   The clipping that used to be described here went with the two-across
   layout, and it was never live in the first place -- it sat inside this
   comment run.


/* Outline, not filled. There is one filled button in this column and it is
   the one that sells the desk.
 *
 * And a hairline outline rather than a full-ink one: at 7rem wide with a
 * black border on both rows, these were the heaviest marks in the panel
 * and drew the eye away from the row that actually carries the offer. */
.nd-addons__add {
  flex: 0 0 auto;
  min-width: 6.4rem;
  padding: 0.8rem 1.2rem;
  border: 1px solid var(--nd-ink);
  border-radius: var(--nd-radius-pill);
  background-color: var(--nd-ink);
  font-family: var(--font-heading-family);
  font-weight: 600;
  font-size: 1.15rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--nd-paper);
  cursor: pointer;
  transition: background-color var(--nd-speed) ease, color var(--nd-speed) ease, opacity var(--nd-speed) ease;
}

/* The plus rides inside the idle label, so it disappears with the word it
   belongs to the moment the row is confirmed. */
.nd-addons__add-idle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.nd-addons__plus {
  width: 1rem;
  height: 1rem;
}

.nd-addons__add-done {
  display: none;
}

/* The accent, not the ink. Now that resting is black, a black confirmed
   state is indistinguishable from an untouched button. */
.nd-addons__add.is-added {
  border-color: var(--nd-select);
  background-color: var(--nd-select);
  color: var(--nd-paper);
}

.nd-addons__add.is-added .nd-addons__add-idle {
  display: none;
}

.nd-addons__add.is-added .nd-addons__add-done {
  display: inline;
}

.nd-addons__add[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}

.nd-addons__add:focus-visible {
  outline: 2px solid var(--nd-ink);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .nd-addons__add {
    transition: none;
  }
}

/* --------------------------------------------------------------- trust */

/* Three thin pills, one line, always.
 *
 * Equal thirds rather than content width, so the row cannot wrap whatever the
 * labels say -- content-width pills fit on one phone and break onto two on a
 * narrower one, and the label that decides it is "1 year warranty".
 *
 * The icons are gone, and that is what buys the fit. Three icons cost about
 * 60px of a 360px row, which is the difference between one line and two, and
 * a pill this small is read as a shape and a word rather than studied. */

.nd-trust {
  display: flex;
  flex-wrap: nowrap;
  /* No space-between. Content-width pills spread across the row left big
     gaps between three small shapes; letting them grow instead fills the
     same width with the pills themselves. */
  gap: 0.4rem;
  margin: 1.2rem 0 0;
  padding: 0;
  border: 0;
  list-style: none;
}

/* Sized so three pills with icons still hold one row at 360px, which is the
   narrowest phone worth designing for. 13px icon, 10.5px label, 7px of side
   padding: about 313px of content in 330px of room. Any larger and the row
   breaks, which is the thing this was asked not to do. */
.nd-trust__item {
  display: flex;
  /* Grow from content width rather than to equal thirds. Thirds made every
     pill 117px and quietly ellipsed the longest label; growing from content
     means each pill is at least as wide as its own words and then shares out
     what is left, so they fill the row without any of them being cut. */
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  min-width: 0;
  padding: 0.65rem 0.45rem;
  border: 1px solid var(--nd-rule);
  border-radius: var(--nd-radius-pill);
}

.nd-trust__icon {
  flex: 0 0 auto;
  width: 1.3rem;
  height: 1.3rem;
}

.nd-trust__label {
  overflow: hidden;
  font-size: 1.05rem;
  line-height: 1.3;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--nd-ink);
}

.nd-trust + .nd-addons {
  margin-top: 1.6rem;
}

/* ------------------------------------------------------ return promise */

/* The returns guarantee, stated rather than badged.
 *
 * Every store has a "30-day returns" pill, including the ones that make a
 * return as hard as they can. A block that says what actually happens, and
 * for how long, is what separates meaning it from saying it -- so this is
 * the one trust signal on the page that gets a panel of its own.
 *
 * Green because it is the colour of "this is safe", muted to sage so it
 * reads as reassurance rather than as a sale. Both colours are block
 * settings, so it can be tested without code. */
.nd-promise {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 1.2rem;
  margin: 1rem 0 0;
  padding: 1.4rem 1.6rem;
  border: 1px solid rgba(47, 91, 62, 0.16);
  border: 1px solid color-mix(in srgb, var(--nd-promise-ink, #2f5b3e) 16%, transparent);
  border-radius: var(--nd-radius);
  background-color: var(--nd-promise-bg, #edf3ee);
}

/* The mark sits on a white disc, which is what lifts it off the tint --
   an icon straight on the green reads as part of the background. */
.nd-promise__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 4.2rem;
  height: 4.2rem;
  border-radius: 50%;
  background-color: #ffffff;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--nd-promise-ink, #2f5b3e) 14%, transparent);
  color: var(--nd-promise-ink, #2f5b3e);
}

.nd-promise__icon {
  width: 1.9rem;
  height: 1.9rem;
}

.nd-promise__body {
  min-width: 0;
}

.nd-promise__heading {
  margin: 0;
  font-family: var(--nd-font-heading);
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.25;
  color: var(--nd-promise-ink, #2f5b3e);
}

/* Ink, not the green: the heading carries the colour, the sentence carries
   the terms, and terms should be the easiest thing on the block to read. */
.nd-promise__text {
  margin: 0.3rem 0 0;
  font-size: 1.3rem;
  line-height: 1.45;
  color: var(--nd-ink);
}

/* ------------------------------------------------------------- motion */

@media (prefers-reduced-motion: reduce) {
  .nd-media__thumb,
  .nd-tier,
  .nd-atc__btn,
  }

/* --------------------------------------------------------------- sticky */

/* The bar that arrives when the real buy box has scrolled away.
 *
 * It never renders its own prices from JavaScript beyond mirroring the tile
 * that is selected, and its button submits the page's own form through the
 * form attribute. One source of truth for the tier, one path to the cart --
 * a sticky bar that keeps its own state is a sticky bar that eventually
 * disagrees with the page above it.
 */

/* The spacer the script appends, holding open exactly the bar's height at the
   foot of the document. flex-shrink is off because Dawn's body is a flex
   column and would otherwise squeeze it back to nothing. */
/* Dawn's base.css carries `div:empty { display: none }`, and a spacer is by
   definition an empty div -- so the theme hid the one element whose entire
   job is to occupy space. That selector is (0,1,1) and a bare class is
   (0,1,0), so Dawn won and the element computed to display:none while its
   height computed to 155px, which is why it looked set and measured as
   nothing.
   
   Matching :empty as well takes it to (0,2,0) and settles it on weight
   rather than on load order. */
.nd-sticky__spacer,
.nd-sticky__spacer:empty {
  display: block;
  flex: 0 0 auto;
  width: 100%;
}

/* The script moves this to the body on load. It has to live outside the
   product section: that section is clipped, and a clipped ancestor is a
   containing block for fixed descendants in Safari. */
.nd-sticky {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 20;
  padding: 1rem 1.5rem calc(1rem + env(safe-area-inset-bottom));
  border-top: 1px solid var(--nd-rule);
  background-color: var(--nd-ground);
  transform: translateY(100%);
  transition: transform 260ms ease;
}

.nd-sticky.is-in {
  transform: translateY(0);
}

.nd-sticky__inner {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  max-width: var(--page-width);
  margin: 0 auto;
}

/* ---- tiers */

.nd-sticky__tiers {
  display: flex;
  gap: 0.6rem;
}

.nd-sticky__tier {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  padding: 0.9rem 0.4rem;
  border: 1px solid var(--nd-rule);
  border-radius: var(--nd-radius-pill);
  background-color: transparent;
  font-family: var(--font-heading-family);
  font-weight: 600;
  font-size: 1.25rem;
  color: var(--nd-ink);
  cursor: pointer;
  transition: background-color var(--nd-speed) ease, border-color var(--nd-speed) ease, color var(--nd-speed) ease;
}

.nd-sticky__tier.is-on {
  border-color: var(--nd-ink);
  background-color: var(--nd-ink);
  color: var(--nd-ground);
}

.nd-sticky__tier[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}

/* The badge sits on the pill's top edge rather than above it, so the row
   keeps one height whether a pill carries a badge or not. */
.nd-sticky__badge {
  position: absolute;
  top: -0.9rem;
  left: 50%;
  transform: translateX(-50%);
  padding: 0.15rem 0.6rem;
  border-radius: var(--nd-radius-pill);
  background-color: var(--nd-save-soft);
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
  color: var(--nd-save);
}

.nd-sticky__tier.is-on .nd-sticky__badge {
  background-color: var(--nd-save);
  color: var(--nd-ground);
}

/* ---- thumbs */

/* Beside the price, not on a row of their own. A full-width row holding one
   32px thumbnail is mostly empty band, and it grew emptier the fewer units
   were selected.
   
   Overlapped, and in a slot of a fixed width. Laid out side by side they were
   32px wider at three than at one, and every one of those pixels came out of
   the checkout button -- the button changed size depending on the tier, which
   is the one element on this bar that should never move. A fixed slot means
   the count can change and the row cannot. */
.nd-sticky__thumbs {
  display: flex;
  flex: 0 0 4.8rem;
  width: 4.8rem;
  flex-wrap: nowrap;
  gap: 0;
}

/* The hidden attribute has to be honoured explicitly. A UA stylesheet sets
   [hidden] to display:none at the lowest possible weight, and any author rule
   that names display -- this one said block -- beats it. The script was
   hiding the extra thumbnails correctly the whole time and all nine were
   drawing anyway. */
.nd-sticky__thumb[hidden] {
  display: none;
}

/* Each one sits over the last, with a ring in the bar's own colour so the
   overlap reads as a stack rather than as one smudged picture. */
.nd-sticky__thumb + .nd-sticky__thumb {
  margin-left: -1.8rem;
}

.nd-sticky__thumb {
  box-shadow: 0 0 0 2px var(--nd-ground);
}

.nd-sticky__thumb {
  display: block;
  width: 2.8rem;
  height: 2.8rem;
  overflow: hidden;
  border: 1px solid var(--nd-rule);
  border-radius: var(--nd-radius-sm);
  background-color: var(--nd-surface);
}

.nd-sticky__thumb-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---- price and button */

.nd-sticky__buy {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.nd-sticky__price {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  margin: 0;
  font-family: var(--font-heading-family);
  font-weight: 700;
  font-size: 1.8rem;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--nd-ink);
}

.nd-sticky__note {
  font-family: var(--font-body-family);
  font-weight: 400;
  font-size: 1.1rem;
  line-height: 1.3;
  color: var(--nd-ink-2);
}

.nd-sticky__submit {
  flex: 1 1 auto;
  min-height: 4.8rem;
  padding: 0 1.6rem;
  border: 0;
  border-radius: var(--nd-radius-sm);
  background-color: var(--nd-ink);
  font-family: var(--font-heading-family);
  font-weight: 600;
  font-size: 1.3rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--nd-ground);
  cursor: pointer;
  transition: opacity var(--nd-speed) ease;
}

.nd-sticky__submit:hover {
  opacity: 0.88;
}

.nd-sticky__submit:focus-visible,
.nd-sticky__tier:focus-visible {
  outline: 2px solid var(--nd-ink);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .nd-sticky,
  .nd-sticky__tier,
  .nd-sticky__submit {
    transition: none;
  }
}


/* What comes free with the selected tier -------------------------------
 *
 * A small rectangle on the left, above the button. It is not a badge and
 * not a banner: it states one fact at the moment the fact becomes true,
 * and it is sized so that turning it on moves the button by one line.
 *
 * Left-aligned and inline-flex, so its width is its content's. Stretched
 * to the column it would read as a second call to action competing with
 * the black button directly beneath it.
 */
.nd-gift {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  align-self: flex-start;
  margin: 0 0 0.9rem;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--nd-select-soft);
  border-radius: var(--nd-radius-sm);
  background-color: var(--nd-select-soft);
  font-family: var(--font-heading-family);
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.3;
  /* Small text on the tint takes the deep shade. The primary is 3.64:1
     here and 11-12px type needs 4.5:1 -- measured, and the reason this is
     not simply --nd-select. */
  color: var(--nd-select-deep);
}

/* A `display` of any kind on the element itself outranks the browser's
   own `[hidden] { display: none }`, so the note kept showing its icon on
   the tiers that include nothing. Stated here rather than swapping the
   attribute for a class, because `hidden` is what tells a screen reader
   the note is not there either. */
.nd-gift[hidden] {
  display: none;
}

.nd-gift__icon {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
}


/* Add both -------------------------------------------------------------
 *
 * One row under the two add-ons, and the only tinted thing in the block,
 * so the block still reads as a quiet list with one offer at the end
 * rather than as a third product competing with the two above it.
 *
 * A button rather than a checkbox pair: the whole row is the action, and
 * there is only one of them.
 */
.nd-pair {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  /* The last row already contributes its own 1rem of bottom padding, so
     this is the second half of the gap and not the whole of it. */
  margin-top: 0.4rem;
  padding: 1rem 1.2rem;
  border: 1px solid var(--nd-select-soft);
  border-radius: var(--nd-radius-sm);
  background-color: var(--nd-select-soft);
  font-family: var(--font-heading-family);
  text-align: left;
  cursor: pointer;
  transition: background-color var(--nd-speed) ease, border-color var(--nd-speed) ease;
}

.nd-pair__text {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.6rem;
}

.nd-pair__label {
  font-size: 1.3rem;
  font-weight: 600;
  color: var(--nd-ink);
}

/* The deep shade, not the primary. 11px on the tint measures 3.64:1
   against the 4.5:1 small text needs. */
.nd-pair__save {
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--nd-select-deep);
}

.nd-pair__prices {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.nd-pair__now {
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--nd-ink);
}

.nd-pair__was {
  font-size: 1.2rem;
  color: var(--nd-ink-2);
}

/* Confirmation replaces the prices rather than joining them -- once it is
   in the cart the arithmetic is the cart's job, not this row's. */
.nd-pair__done {
  display: none;
  font-size: 1.3rem;
  font-weight: 600;
  color: var(--nd-select-deep);
}

.nd-pair.is-added {
  border-color: var(--nd-select);
  cursor: default;
}

.nd-pair.is-added .nd-pair__prices,
.nd-pair.is-added .nd-pair__save {
  display: none;
}

.nd-pair.is-added .nd-pair__done {
  display: block;
}

.nd-pair:focus-visible {
  outline: 2px solid var(--nd-ink);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .nd-pair {
    transition: none;
  }
}


/* Complete the setup, as options ---------------------------------------
 *
 * The whole row is the label, so the picture and the name are part of
 * the target rather than decoration beside a small box. 44px of height
 * comes from the padding the row already had.

/* The input itself is taken out of the flow rather than hidden, so it
   still takes focus and still answers to the keyboard. */
.nd-addons__check {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.nd-addons__box {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 1px solid var(--nd-hairline-strong, rgba(0, 0, 0, 0.28));
  border-radius: 0.5rem;
  background-color: var(--nd-ground);
  color: transparent;
  transition: background-color var(--nd-speed) ease, border-color var(--nd-speed) ease;
}

.nd-addons__box svg {
  width: 1.3rem;
  height: 1.3rem;
}

.nd-addons__check:checked + .nd-addons__box {
  border-color: var(--nd-ink);
  background-color: var(--nd-ink);
  color: var(--nd-paper);
}

.nd-addons__check:focus-visible + .nd-addons__box {
  outline: 2px solid var(--nd-ink);
  outline-offset: 2px;
}

/* A ticked row lifts a little, so the panel shows what is coming with

/* Both together ---------------------------------------------------------
   A statement, not a button: with the rows tickable, "add both" is just
   both ticks, and a control for it would be a third way to do one thing. */
.nd-addons__pair {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 1rem 0 0;
  padding: 0.7rem 0.9rem;
  border-radius: var(--nd-radius-sm);
  background-color: var(--nd-select-soft);
  font-family: var(--font-heading-family);
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--nd-select-deep);
}

.nd-addons__pair[hidden] {
  display: none;
}

.nd-addons__pair-icon {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
}

@media (prefers-reduced-motion: reduce) {
  .nd-addons__box {
    transition: none;
  }
}


/* Add-ons, as stacked rows ---------------------------------------------
 *
 * Two cards side by side gave each about 150px to hold a picture, a name
 * and a price, and no room at all to say what the thing does. A
 * full-width row has space for the line that sells it, and reading down
 * a short list is easier than reading across a pair.
 *
 * The whole row is still the label, so the photograph is part of the
 * target.
 */
.nd-addons__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.6rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nd-addons__opt {
  display: block;
  cursor: pointer;
}

.nd-addons__card {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.9rem;
  /* Tighter all round. Two rows at 72px each pushed the button down the
     column for a choice most people skip; at 54 they are still a
     comfortable target because the whole row is the label. */
  padding: 0.9rem;
  border: 1px solid var(--nd-hairline);
  border-radius: var(--nd-radius-sm);
  background-color: var(--nd-ground);
  transition: border-color var(--nd-speed) ease, box-shadow var(--nd-speed) ease;
}

/* Chosen. The border does the work -- a second fill inside a panel that
   already has an edge is one layer too many. */
.nd-addons__check:checked + .nd-addons__card {
  border-color: var(--nd-ink);
  box-shadow: inset 0 0 0 1px var(--nd-ink);
}

.nd-addons__check:focus-visible + .nd-addons__card {
  outline: 2px solid var(--nd-ink);
  outline-offset: 2px;
}

/* A thumbnail now, not a picture. In a row its job is to tell the two
   apart at a glance; the gallery above is where either is looked at. */
.nd-addons__shot {
  flex: 0 0 auto;
  display: block;
  width: 3rem;
  overflow: hidden;
  border-radius: 0.5rem;
  background-color: var(--nd-surface);
  aspect-ratio: 1;
}

/* Cover, not contain.
 *
 * The source shots are square with the object centred and a band of
 * white above and below it. Contained in a 16:9 frame they pillarbox --
 * a small square photograph marooned in a wide grey box, which is worse
 * than the tall frame it replaced. Cover crops that white instead, so
 * the product fills the width and the frame gets shorter without the
 * object getting smaller.
 */
.nd-addons__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.nd-addons__name {
  display: block;
  margin: 0;
  font-size: 1.3rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--nd-ink);
}

.nd-addons__text {
  flex: 1 1 auto;
  min-width: 0;
}

/* What it does, in one line. The name says which object it is; this says
   why anyone would tick it, which is the question the row is asking. */
.nd-addons__note {
  display: block;
  margin-top: 0.1rem;
  font-size: 1.1rem;
  font-weight: 500;
  line-height: 1.3;
  color: var(--nd-ink-2, #333333);
}

/* The delta and the tick, at the end of the row. Vertically centred by
   the row itself now -- there is no column to push them to the foot of,
   so the `margin-top: auto` this comment used to describe is gone. */
/* The price alone now, pushed to the end of the row -- the tick left
   this group and went to sit with the picture. */
.nd-addons__pick {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-left: auto;
}

.nd-addons__box {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border: 1px solid var(--nd-hairline-strong, rgba(0, 0, 0, 0.28));
  border-radius: 999px;
  color: transparent;
  transition: background-color var(--nd-speed) ease, border-color var(--nd-speed) ease;
}

.nd-addons__box svg {
  width: 1.1rem;
  height: 1.1rem;
}

.nd-addons__check:checked + .nd-addons__card .nd-addons__box {
  border-color: var(--nd-ink);
  background-color: var(--nd-ink);
  color: var(--nd-paper);
}

.nd-addons__delta {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-family: var(--font-heading-family);
  font-size: 1.2rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--nd-ink);
}

.nd-addons__delta s {
  font-size: 1.1rem;
  font-weight: 400;
  color: var(--nd-ink-2);
}

@media (prefers-reduced-motion: reduce) {
  .nd-addons__card,
  .nd-addons__box {
    transition: none;
  }
}


/* Mid-request. The row is not clickable and says so, rather than
   flickering between two states while the cart catches up. */
.nd-addons__cell.is-busy {
  opacity: 0.55;
  pointer-events: none;
}


/* The pack multiplier on an add-on card.
   Ahead of the price and set apart from it, so "x3 + $30" reads as three
   at ten rather than as one number someone has mistyped. */
.nd-addons__mult {
  font-weight: 700;
  color: var(--nd-ink-2);
}

.nd-addons__mult[hidden] {
  display: none;
}

.nd-addons__delta s[hidden] {
  display: none;
}

/* Already included ------------------------------------------------------
 *
 * The chosen pack gives this away, so the card trades its price for the
 * fact. It keeps its normal border rather than the selected one: it is
 * not something you chose, it is something you are getting -- and it is
 * not dimmed either, because it is the best thing on the row.
 */
.nd-addons__cell.is-gift .nd-addons__opt {
  cursor: default;
}

/* Marked in the accent, at the same weight a chosen card gets.
 *
 * It is included, so it should carry as much of the eye as something you
 * ticked -- but in the accent rather than the ink, because the black
 * border means "you chose this" and this one you did not. It reads as
 * the same treatment in the colour of the tier that granted it: the Best
 * Value cap directly above is the same terracotta, so the border ties
 * the free thing to the tier that gives it.
 *
 * A border and nothing else. A tint fill behind it as well would put
 * three accented surfaces in one column -- the cap, the card, the text
 * inside it -- and the row stops reading as a quiet list with one gift
 * in it. */
.nd-addons__cell.is-gift .nd-addons__card {
  border-color: var(--nd-select);
  box-shadow: inset 0 0 0 1px var(--nd-select);
}

.nd-addons__cell.is-gift .nd-addons__box,
.nd-addons__cell.is-gift .nd-addons__mult,
.nd-addons__cell.is-gift [data-nd-delta],
.nd-addons__cell.is-gift [data-nd-was] {
  display: none;
}

.nd-addons__free {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-heading-family);
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--nd-select-deep);
}

.nd-addons__free[hidden] {
  display: none;
}

.nd-addons__free svg {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
}


/* The kit, as four pictures ---------------------------------------------
 *
 * Four across on every width. The items are the argument for the product
 * -- a case with a stand, a keyboard, a mouse and a phone holder in it --
 * and four things shown at once is the whole point; a scroller would hide
 * a quarter of the argument behind a swipe nobody makes.
 */
/* The same micro-label the Quantity and Add-ons rows carry -- 1.1rem at
   600, 0.12em, uppercase, secondary ink. Not a new voice, just a name for
   the row, which four pictures on their own do not give. */
.nd-kit__label {
  /* The title owns no space under itself, so this label was sitting on
     its baseline. The gap belongs here rather than on the title, which
     other blocks also follow. */
  margin: 1.4rem 0 0.6rem;
  font-family: var(--font-heading-family);
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--nd-ink-2);
}

.nd-kit {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.8rem;
  /* The label owns the gap above now. */
  margin: 0 0 1.4rem;
  padding: 0;
  list-style: none;
}

.nd-kit__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  text-align: center;
}

/* Contain on a soft ground, not cover. These are objects photographed on
   white; cropped to a square they lose the shape that identifies them,
   which at this size is all there is to go on. */
.nd-kit__shot {
  display: block;
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--nd-hairline);
  border-radius: var(--nd-radius-sm);
  background-color: var(--nd-surface);
  /* Wider than tall, like the objects in them. A square frame gave a
     keyboard and a mouse a band of empty ground above and below, which
     is height the row was spending on nothing. */
  aspect-ratio: 4 / 3;
}

.nd-kit__img {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0.4rem;
  object-fit: contain;
}

/* Small, and it has to be: four names across 390px is 82px each. The
   picture carries the recognition and the word only confirms it. */
.nd-kit__name {
  font-size: 1rem;
  line-height: 1.2;
  color: var(--nd-ink-2);
  text-wrap: balance;
}


/* ======================================================================
 * Rhythm and separators, after IM8's product information column
 * ======================================================================
 *
 * Appended at the end of the file on purpose: this stylesheet has
 * comments left unclosed further up, and a rule written inside one of
 * them parses as nothing. Everything below is checked as live CSS.
 *
 * Measured from the reference: separators are about 7% black; the reviews
 * row is framed by two of them; each labelled selection group opens with
 * one. On desktop the column is edged by a vertical hairline and the
 * horizontal lines thin out towards the right, which stops them reading as
 * table rules and keeps the eye moving down rather than across.
 *
 * Built as background images rather than borders, so a line can fade.
 */

.nd-product__info {
  --nd-sep-ink: rgba(0, 0, 0, 0.09);
  /* A phone column is narrow enough that a fade reads as a mistake. */
  --nd-sep: linear-gradient(to right, var(--nd-sep-ink), var(--nd-sep-ink));
}

@media screen and (min-width: 750px) {
  /* Two equal halves, and the information fills its half.
   *
   * The column was capped at 460px inside a 708px track, so on a wide
   * screen a quarter of the right half was empty and the buy box read as a
   * narrow strip beside the picture. The reference splits the section down
   * the middle and runs its information to the edge of the grid; this does
   * the same. The picture gives up its slightly larger share to make the
   * halves equal -- 744px to 718px at full width, smaller without being
   * small. */
  .nd-product__grid {
    --nd-col-gap: clamp(3.2rem, 4.4vw, 4.8rem);
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--nd-col-gap);
  }

  .nd-product__info {
    position: relative;
    max-width: none;
    --nd-sep: linear-gradient(to right, var(--nd-sep-ink) 0%, var(--nd-sep-ink) 62%, rgba(0, 0, 0, 0) 100%);
  }

  /* The column's edge: a hairline in the middle of the gutter between the
     gallery and the information, fading out as the column runs down so it
     frames the buying decision without boxing in the accordions. */
  .nd-product__info::before {
    display: none;
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--nd-col-gap, 4.8rem) / -2);
    width: 1px;
    background-image: linear-gradient(to bottom, var(--nd-sep-ink) 0%, var(--nd-sep-ink) 70%, rgba(0, 0, 0, 0) 100%);
    pointer-events: none;
  }
}

/* ---- the reviews row, between two lines ---- */

.nd-product__info .nd-rating {
  margin: 0 0 1rem;
  padding: 0.7rem 0;
  background-image: var(--nd-sep), var(--nd-sep);
  background-repeat: no-repeat;
  background-size: 100% 0px, 100% 1px;
  background-position: left top, left bottom;
  /* Up from 12px. The reviews are the first thing in the column and the
     reference sets them at body size; at 12 they read as a footnote. */
  font-size: 1.35rem;
  line-height: 1.3;
  color: var(--nd-ink);
}

.nd-product__info .nd-rating__score {
  font-weight: 700;
}

.nd-product__info .nd-rating .nd-star {
  width: 1.4rem;
  height: 1.4rem;
}

/* ---- name and specification: a step smaller, a step closer ---- */

/* 28px on a phone and 40px on a desktop, bold, was the loudest thing on the
   page above the price. The reference sets its name large but not heavy;
   ours keeps its weight and gives up size instead. */
.nd-product__info .nd-title {
  font-size: clamp(1.8rem, 4.4vw, 3.4rem);
  line-height: 1.1;
}

.nd-product__info .nd-intro {
  margin: 0.6rem 0 1rem;
}

/* The panel's type and icons are untouched; only the air inside it comes
   down, the same way it did everywhere else in the column. */

/* ---- one line before each labelled group ---- */

/* 12px each side of the line. The reference gives its separators more
   air than this; the brief here was tighter, and 12 is where a line still
   reads as dividing two groups rather than underlining the one above. */
.nd-product__info .nd-tiers,
.nd-product__info .nd-addons {
  margin-top: 1.2rem;
  padding-top: 1.2rem;
  background-image: var(--nd-sep);
  background-repeat: no-repeat;
  background-size: 100% 1px;
  background-position: left top;
}

/* Add-ons lose their outer box. With a line opening the group, a card
   around it was a second edge saying the same thing -- the reference runs
   each group open under its label, and the rows inside keep their own
   cards, which is where the edge is useful. */
.nd-product__info .nd-addons {
  padding-right: 0;
  padding-bottom: 0;
  padding-left: 0;
  border: 0;
  border-radius: 0;
  background-color: transparent;
}

/* Group labels in full ink and a weight heavier -- they head a decision,
   and at the secondary grey they sat below the options they introduce. */
.nd-product__info .nd-tiers__label,
.nd-product__info .nd-addons__label {
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--nd-ink);
}

/* Floated so the legend is ordinary content. A fieldset draws its legend
   straddling its own top edge, which put the group's separator line behind
   the label -- running out to the right of "1 DeskMate Pro" -- and left the
   padding meant for above the label sitting under it instead. */
.nd-product__info .nd-tiers__legend {
  float: left;
  width: 100%;
  margin-bottom: 0.8rem;
}

.nd-product__info .nd-tiers__grid {
  clear: both;
}

.nd-product__info .nd-addons__label {
  margin-bottom: 0.8rem;
}

/* ---- the purchase group, pulled together ---- */

.nd-product__info .nd-atc {
  margin-top: 1.2rem;
}

.nd-product__info .nd-trust {
  margin-top: 0.8rem;
}

.nd-product__info .nd-promise {
  margin-top: 0.8rem;
}


/* ======================================================================
 * Desktop only: a narrower product section, and the benefits as pills
 * ====================================================================== */

@media screen and (min-width: 990px) {
  /* The section is capped and centred, rather than the column capped on its
     own. Capping just the information brought back the empty strip on its
     right; capping the pair keeps both halves full and makes the rows
     inside them shorter -- the pack rows had most of their width as air
     between the name and the price. 134rem is where a 1440 laptop already
     sits, so only wider screens change: 718px to 640px a half. */
  .nd-product__grid {
    max-width: 100%;
    margin-right: auto;
    margin-left: auto;
  }

  /* The benefits as outlined check pills, after the reference: no panel,
     no per-claim illustration, one mark repeated. Wraps onto a second row
     rather than shrinking the words to force one. */
}


/* ======================================================================
 * Desktop only: room to breathe, one grid for every row, readable sizes
 * ======================================================================
 *
 * The column was set for a phone and then widened. Measured at 1920:
 * 12px either side of each section line and 8px between add to cart, the
 * trust pills and the promise; a 9px tag, a 10px saving and 10.5px trust
 * labels inside 320px pills; and the add-on rows starting their circle,
 * picture and name about 10px left of the pack rows directly above them.
 * None of those is wrong on its own. Together they are what "not clean"
 * looks like.
 */

@media screen and (min-width: 990px) {

  /* ---- the benefit pills carry the same icons as the phone ---- */

  .nd-product__info .nd-benefits__item::before {
    content: none;
    display: none;
  }

  .nd-product__info .nd-benefits__item .nd-benefits__icon {
    position: static;
    display: block;
    flex: 0 0 auto;
    width: 1.8rem;
    height: 1.8rem;
  }

  /* ---- vertical rhythm: sections separate, parts of a section stay close ---- */

  .nd-product__info .nd-rating {
    margin-bottom: 1.6rem;
    padding: 1rem 0;
  }

  .nd-product__info .nd-intro {
    margin: 0.8rem 0 1.6rem;
  }

  .nd-product__info .nd-tiers,
  .nd-product__info .nd-addons {
    margin-top: 2.4rem;
    padding-top: 2.4rem;
  }

  .nd-product__info .nd-tiers__legend,
  .nd-product__info .nd-addons__label {
    margin-bottom: 1.2rem;
  }

  .nd-product__info .nd-tiers__label,
  .nd-product__info .nd-addons__label {
    font-size: 1.2rem;
  }

  .nd-product__info .nd-tiers__value {
    font-size: 1.4rem;
  }

  .nd-product__info .nd-atc {
    margin-top: 2.4rem;
  }

  .nd-product__info .nd-trust {
    gap: 1rem;
    margin-top: 1.2rem;
  }

  .nd-product__info .nd-promise {
    margin-top: 1.2rem;
  }

  .nd-product__info .nd-promise + .nd-acc {
    margin-top: 2.8rem;
  }

  /* ---- one grid for pack rows and add-on rows ----
   *
   * Both kinds of row now share every horizontal measure: 20px in from the
   * edge, a 22px circle, 14px, a 52px picture, 14px, then the words, and
   * the price 20px in from the right. The circles, pictures, names and
   * prices of all five rows fall on the same four vertical lines. */

  .nd-product__info .nd-tiers--vertical .nd-tiers__grid {
    gap: 1.4rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier {
    column-gap: 1.4rem;
    padding: 1.4rem 2rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier--capped {
    padding-top: 1.2rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__radio {
    width: 2.2rem;
    height: 2.2rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__radio::after {
    inset: 0.45rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__thumb {
    width: 3rem;
    border-radius: 0.7rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__body {
    column-gap: 1.4rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__main {
    gap: 0.45rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__count {
    font-size: 2.2rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__unit-word {
    font-size: 1.45rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__save {
    padding: 0.3rem 0.8rem;
    font-size: 1.15rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__total {
    font-size: 2rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__was {
    font-size: 1.35rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__cap {
    right: 2rem;
    padding: 0.3rem 0.8rem;
    font-size: 1rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__benefit {
    margin-top: 1rem;
    padding: 0.3rem 1rem 0.3rem 0.3rem;
    font-size: 1.25rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__gift-img {
    width: 2.4rem;
    height: 2.4rem;
  }

  .nd-product__info .nd-addons__grid {
    gap: 0.8rem;
  }

  .nd-product__info .nd-addons__card {
    gap: 1.4rem;
    padding: 1rem 2rem;
  }

  .nd-product__info .nd-addons__box {
    width: 1.6rem;
    height: 1.6rem;
  }

  .nd-product__info .nd-addons__box svg {
    width: 1.3rem;
    height: 1.3rem;
  }

  .nd-product__info .nd-addons__shot {
    width: 3rem;
    border-radius: 0.7rem;
  }

  .nd-product__info .nd-addons__name {
    font-size: 1.55rem;
  }

  .nd-product__info .nd-addons__note {
    margin-top: 0.2rem;
    font-size: 1.3rem;
  }

  .nd-product__info .nd-addons__delta {
    font-size: 1.55rem;
  }

  .nd-product__info .nd-addons__delta s {
    font-size: 1.3rem;
  }

  /* ---- add to cart ---- */

  /* 49px to 57px and a step up in type: the one button on the page is the
     one place the column should not feel economical. */
  .nd-product__info .nd-atc__btn {
    padding: 2rem 2.4rem;
    font-size: 1.7rem;
  }

  /* ---- trust pills: readable, with ink marks ---- */

  /* The smallest type on the page sat in the widest boxes. The marks were
     the default grey accent, which read as disabled next to the ink
     button above them. */
  .nd-product__info .nd-trust__item {
    gap: 0.7rem;
    padding: 0.95rem 1.2rem;
  }

  .nd-product__info .nd-trust__label {
    font-size: 1.3rem;
  }

  .nd-product__info .nd-trust__icon {
    width: 1.7rem;
    height: 1.7rem;
  }

  .nd-product__info .nd-trust__icon .nd-icon__ink,
  .nd-product__info .nd-trust__icon .nd-icon__accent {
    fill: var(--nd-ink);
  }

  .nd-product__info .nd-trust__icon .nd-icon__stroke,
  .nd-product__info .nd-trust__icon .nd-icon__accentstroke {
    stroke: var(--nd-ink);
  }

  /* ---- the return promise, at the column's new scale ---- */

  .nd-product__info .nd-promise {
    gap: 1.8rem;
    padding: 1.8rem 2.2rem;
  }

  .nd-product__info .nd-promise__badge {
    width: 5rem;
    height: 5rem;
  }

  .nd-product__info .nd-promise__icon {
    width: 2.2rem;
    height: 2.2rem;
  }

  .nd-product__info .nd-promise__heading {
    font-size: 1.7rem;
  }

  .nd-product__info .nd-promise__text {
    margin-top: 0.4rem;
    font-size: 1.4rem;
    line-height: 1.5;
  }
}


/* ======================================================================
 * Spacing that survives any block being removed
 * ======================================================================
 *
 * Every gap in the column used to be made by two neighbours between them:
 * the name had no margin under it and the specification line brought 6px
 * above and 10px below. Remove the specification and the name sat directly
 * on the benefits panel -- measured at 0px. Other removals only worked by
 * luck of which neighbour happened to carry the margin.
 *
 * Now each block owns the space ABOVE itself, set by what kind of block it
 * is, and nothing below. Whatever is removed or hidden, the next block
 * still arrives with its own correct gap, and the first block in the
 * column takes none. Margins only ever run one way, so none collapse.
 *
 * The steps are chosen so grouping reads without thinking: a pair is
 * closer than a group, a group closer than a section. The one real
 * ambiguity the old values had -- add to cart sat as close to the add-ons
 * as a section's own parts sit to each other, so the button read as part
 * of the add-ons -- is what the "buy" step fixes.
 *
 * This set is last in the file and replaces the margins set on these
 * blocks anywhere above it.
 */

.nd-product__info {
  --nd-gap-pair: 0.6rem;     /* a name and its specification             */
  --nd-gap-block: 1.2rem;    /* blocks in the header                     */
  --nd-gap-section: 1.2rem;  /* each side of a section's line            */
  --nd-gap-label: 1rem;      /* a group label to its options             */
  --nd-gap-buy: 1.6rem;      /* into add to cart                         */
  --nd-gap-support: 0.8rem;  /* what sits under add to cart              */
  --nd-gap-details: 2.4rem;  /* into the accordions                      */
  --nd-gap-stack: 0.6rem;    /* accordion to accordion                   */
}
.nd-product__info {
    background-color: #fff;
    border-radius: 25px;
    padding: 0 25px 25px 25px;
}
@media screen and (min-width: 990px) {
  .nd-product__info {
    --nd-gap-pair: 0.8rem;
    --nd-gap-block: 1.6rem;
    --nd-gap-section: 2.4rem;
    --nd-gap-label: 1.2rem;
    --nd-gap-buy: 2.4rem;
    --nd-gap-support: 1.2rem;
    --nd-gap-details: 2.8rem;
    --nd-gap-stack: 0.6rem;
  }
}

/* Default: a header block. */
.nd-product__info > .nd-eyebrow,
.nd-product__info > .nd-rating,
.nd-product__info > .nd-title,
.nd-product__info > .nd-pills,
.nd-product__info > .nd-intro,
.nd-product__info > .nd-kit,
.nd-product__info > .nd-benefits,
.nd-product__info > .nd-quotes {
  margin-top: 1rem;
  margin-bottom: 0;
}

/* Pairs: the specification belongs to the name above it, and the name to
   its eyebrow. Only when they are actually adjacent -- remove the name and
   the specification falls back to the header step. */
.nd-product__info > .nd-eyebrow + .nd-title,
.nd-product__info > .nd-title + .nd-intro,
.nd-product__info > .nd-title + .nd-kit {
  margin-top: var(--nd-gap-pair);
}

/* Sections: space, the line, space. */
.nd-product__info > .nd-tiers,
.nd-product__info > .nd-addons {
  margin-top: 1rem;
  margin-bottom: 0;
  padding-top: 1rem;
}

.nd-product__info .nd-tiers__legend,
.nd-product__info .nd-addons__label {
  margin-bottom: var(--nd-gap-label);
}

/* The purchase group. */
.nd-product__info > .nd-atc {
  margin-top: var(--nd-gap-buy);
  margin-bottom: 0;
}

.nd-product__info > .nd-delivery,
.nd-product__info > .nd-trust,
.nd-product__info > .nd-promise {
  margin-top: var(--nd-gap-support);
  margin-bottom: 0;
}

/* The accordions: a clear break into them, then a stack. Written at three
   classes' weight to outrank the older ".nd-product__info .nd-promise +
   .nd-acc" rule without reference to which block comes before. */
.nd-product__info > :not(.nd-acc) + .nd-acc {
  margin-top: var(--nd-gap-details);
  margin-bottom: 0;
}

.nd-product__info > .nd-acc + .nd-acc {
  margin-top: var(--nd-gap-stack);
  margin-bottom: 0;
}

/* Whatever is first takes no space above it. Written last so it wins over
   every role above at the same weight. */
.nd-product__info > :first-child,
.nd-product__info > :first-child.nd-acc {
  margin-top: 0;
}

/* ======================================================================
 * Type: the smallest words were on the things that matter most
 * ======================================================================
 *
 * Measured on a phone: trust labels 10.5px, the pack tags 9px, the savings
 * 10px, and add to cart set at 15px beside prices at 17 and 18 -- the
 * button's own words were smaller than the numbers it confirms. Raised to
 * a floor that reads at arm's length. Desktop values set in PP-21 are
 * already above these and stay.
 */

@media screen and (max-width: 989px) {
  .nd-product__info .nd-trust__label {
    font-size: 1.2rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__cap {
    font-size: 1rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__save {
    font-size: 1.1rem;
  }

  .nd-product__info .nd-atc__btn {
    font-size: 1.6rem;
  }
  .nd-product__media {
    position: relative;
    top: 0;
  }
}

/* Desktop: the rating and the specification a step up, so they do not
   read as footnotes beside a 34px name in a 638px column. */
@media screen and (min-width: 990px) {
  .nd-product__info .nd-rating {
    font-size: 1.4rem;
  }

  .nd-product__info .nd-intro {
    font-size: 1.5rem;
  }
}


/* ---- dispatch countdown ----
 *
 * Under add to cart and deliberately quiet: small, centred, one line, the
 * time in bold and nothing else loud. The buy box already has the pills and
 * the return promise below it; this is a fact, not another panel. A small
 * green dot is the only mark -- the colour of the return promise, and the
 * convention for "live / available now".
 */
.nd-product__info > .nd-dispatch {
  margin-top: var(--nd-gap-support, 0.8rem);
  margin-bottom: 0;
}

.nd-dispatch {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  font-size: 1.25rem;
  line-height: 1.35;
  color: var(--nd-ink);
}

.nd-dispatch[hidden] {
  display: none;
}

.nd-dispatch strong {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.nd-dispatch__dot {
  flex: 0 0 auto;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background-color: #2f8a57;
  box-shadow: 0 0 0 3px rgba(47, 138, 87, 0.16);
}

@media screen and (min-width: 990px) {
  .nd-dispatch {
    font-size: 1.35rem;
  }
}


/* ---- customer photo strip ----
 *
 * One row of customers under the trust pills, scrolling endlessly and
 * slowly. Photos only, no names or marks; a single small line above says
 * who they are. The row is the width of add to cart, and its edges fade so
 * photos arrive and leave rather than being cut off at the column's side.
 *
 * Slow on purpose -- about two and a half seconds per photo -- and it
 * pauses under a pointer or a finger. Movement this close to the button
 * should read as ambient, not as something asking to be watched.
 */
.nd-product__info > .nd-community {
  margin-top: var(--nd-gap-support, 0.8rem);
  margin-bottom: 0;
}

.nd-community__label {
  margin: 0 0 0.8rem;
  font-size: 1.25rem;
  font-weight: 500;
  line-height: 1.3;
  text-align: center;
  color: var(--nd-ink);
}

.nd-community__viewport {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 9%, rgba(0, 0, 0, 1) 91%, rgba(0, 0, 0, 0));
  mask-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 9%, rgba(0, 0, 0, 1) 91%, rgba(0, 0, 0, 0));
}

.nd-community__track {
  --nd-community-gap: 0.8rem;
  display: flex;
  gap: var(--nd-community-gap);
  width: max-content;
  margin: 0;
  padding: 0;
  list-style: none;
  animation: nd-community-scroll calc(var(--nd-community-count, 12) * 2.5s) linear infinite;
}

/* One pass is the track's half plus half a gap, so the slide ends exactly
   where the second pass began and the loop has no visible jump. */
@keyframes nd-community-scroll {
  to {
    transform: translateX(calc(-50% - var(--nd-community-gap) / 2));
  }
}

.nd-community__viewport:hover .nd-community__track,
.nd-community__viewport:active .nd-community__track {
  animation-play-state: paused;
}

.nd-community__item {
  flex: 0 0 auto;
  width: 6rem;
  height: 6rem;
  overflow: hidden;
  border-radius: 1rem;
  background-color: var(--nd-surface);
}

.nd-community__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media screen and (min-width: 990px) {
  .nd-community__item {
    width: 6.8rem;
    height: 6.8rem;
  }
}

/* No motion: a still row that can be swiped, without the repeated copies. */
@media (prefers-reduced-motion: reduce) {
  .nd-community__viewport {
    overflow-x: auto;
    scrollbar-width: none;
  }

  .nd-community__track {
    animation: none;
  }

  .nd-community__item[data-repeat] {
    display: none;
  }
}

/* ==========================================================================
   Bundle & Save, refined
   --------------------------------------------------------------------------
   The same rows, the same order, the same information. Each part is set a
   little quieter so the choice reads before the decoration does:

   - The thumbnail is just the picture: no frame, no grey tile behind it.
   - The radio is a ring with no fill until it is chosen.
   - "1 pack" is one line at one size -- the numeral heavier, the word
     lighter -- rather than a large figure beside small type.
   - The saving is the same soft pill on every row. It turned solid on the
     chosen row, which put two filled marks on it next to the badge.
   - The old price is a step smaller and greyer, so the price leads.
   - The badges are a size down with finer tracking.
   - The free gift is a line of text with its picture, not a second pill.
   - Hairline borders; the chosen row gets a 1.5px ink edge.

   Appended last, and scoped through .nd-product__info, so it outranks the
   stacked-layout rules above at both widths without !important.
   ========================================================================== */

.nd-product__info .nd-tiers--vertical .nd-tiers__grid {
  gap: 1.6rem;
}

.nd-product__info .nd-tiers--vertical .nd-tier {
  border: 1px solid rgba(17, 17, 17, 0.1);
  border-radius: 1.4rem;
  background-color: #ffffff;
  outline: 0;
  box-shadow: none;
  transition: border-color var(--nd-speed) ease, box-shadow var(--nd-speed) ease;
}

@media (hover: hover) {
  .nd-product__info .nd-tiers--vertical .nd-tier:hover {
    border-color: rgba(17, 17, 17, 0.24);
  }
}

.nd-product__info .nd-tiers--vertical .nd-tier.is-selected,
.nd-product__info .nd-tiers--vertical .nd-tier:has(.nd-tier__input:checked) {
  border-color: var(--nd-ink);
  outline: 0;
  box-shadow: 0 0 0 0.5px var(--nd-ink);
}

.nd-product__info .nd-tiers--vertical .nd-tier:has(.nd-tier__input:focus-visible) {
  outline: 2px solid var(--nd-ink);
  outline-offset: 2px;
}

/* Radio */
.nd-product__info .nd-tiers--vertical .nd-tier__radio {
  width: 2rem;
  height: 2rem;
  border: 1.5px solid rgba(17, 17, 17, 0.22);
  background-color: transparent;
}

.nd-product__info .nd-tiers--vertical .nd-tier__radio::after {
  inset: 0.4rem;
}

.nd-product__info .nd-tiers--vertical .nd-tier.is-selected .nd-tier__radio,
.nd-product__info .nd-tiers--vertical .nd-tier:has(.nd-tier__input:checked) .nd-tier__radio {
  border-color: var(--nd-ink);
}

/* Thumbnail: the picture on its own */
.nd-product__info .nd-tiers--vertical .nd-tier__thumb {
  border: 0;
  border-radius: 0;
  background-color: transparent;
}

/* "1 pack" */
.nd-product__info .nd-tiers--vertical .nd-tier__name {
  align-items: baseline;
  gap: 0.35rem;
}

.nd-product__info .nd-tiers--vertical .nd-tier__count,
.nd-product__info .nd-tiers--vertical .nd-tier__unit-word {
  font-family: var(--nd-font-heading);
  font-size: 1.5rem;
  line-height: 1.2;
  letter-spacing: 0;
  color: var(--nd-ink);
}

.nd-product__info .nd-tiers--vertical .nd-tier__count {
  font-weight: 700;
}

.nd-product__info .nd-tiers--vertical .nd-tier__unit-word {
  font-weight: 500;
}

/* The saving: one style on every row */
.nd-product__info .nd-tiers--vertical .nd-tier__save,
.nd-product__info .nd-tiers--vertical .nd-tier.is-selected .nd-tier__save,
.nd-product__info .nd-tiers--vertical .nd-tier:has(.nd-tier__input:checked) .nd-tier__save {
  padding: 0.25rem 0.8rem;
  border-radius: var(--nd-radius-pill);
  background-color: var(--nd-save-soft);
  color: var(--nd-save);
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.25;
}

/* Price */
.nd-product__info .nd-tiers--vertical .nd-tier__price {
  gap: 0.2rem;
}

.nd-product__info .nd-tiers--vertical .nd-tier__total {
  font-family: var(--nd-font-heading);
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: -0.005em;
}

.nd-product__info .nd-tiers--vertical .nd-tier__was {
  font-size: 1.15rem;
  color: rgba(17, 17, 17, 0.45);
  text-decoration-color: rgba(17, 17, 17, 0.45);
  text-decoration-thickness: 1px;
}

/* Badges. Medium, not 600: Gotham has no semibold, so 600 renders as Bold
   and the tags read as the loudest type in the group. */
.nd-product__info .nd-tiers--vertical .nd-tier__cap {
  padding: 0.3rem 0.9rem;
  font-family: var(--nd-font);
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  line-height: 1.3;
}

/* The free gift: a line with its picture */
.nd-product__info .nd-tiers--vertical .nd-tier__benefit,
.nd-product__info .nd-tiers--vertical .nd-tier__benefit--long-only,
.nd-product__info .nd-tiers--vertical .nd-tier__benefit:not(:has(.nd-tier__gift-img)) {
  gap: 0.7rem;
  margin-top: 0.8rem;
  padding: 0;
  border-radius: 0;
  background-color: transparent;
  color: var(--nd-save);
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: 0;
}

.nd-product__info .nd-tiers--vertical .nd-tier__gift-img {
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 0.4rem;
  background-color: transparent;
}

@media screen and (min-width: 990px) {
  .nd-product__info .nd-tiers--vertical .nd-tiers__grid {
    gap: 1.2rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__radio {
    width: 2.2rem;
    height: 2.2rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__radio::after {
    inset: 0.45rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__count,
  .nd-product__info .nd-tiers--vertical .nd-tier__unit-word {
    font-size: 1.4rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__save,
  .nd-product__info .nd-tiers--vertical .nd-tier.is-selected .nd-tier__save,
  .nd-product__info .nd-tiers--vertical .nd-tier:has(.nd-tier__input:checked) .nd-tier__save {
    padding: 0.3rem 0.9rem;
    font-size: 1.15rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__total {
    font-size: 1.6rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__was {
    font-size: 1.2rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__cap {
    font-size: 1rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__benefit,
  .nd-product__info .nd-tiers--vertical .nd-tier__benefit--long-only,
  .nd-product__info .nd-tiers--vertical .nd-tier__benefit:not(:has(.nd-tier__gift-img)) {
    margin-top: 0rem;
    font-size: 1.3rem;
  }

  .nd-product__info .nd-tiers--vertical .nd-tier__gift-img {
    width: 2.4rem;
    height: 2.4rem;
  }
}

/* ==========================================================================
   Products that are not packs
   --------------------------------------------------------------------------
   A colour or a size is chosen by its own name in the same rows the packs
   use, and those products get the sticky bar without pack pills: the
   picture, the name over the live price, and the button.
   ========================================================================== */

.nd-product__info .nd-tiers--vertical .nd-tier__count--option {
  font-weight: 600;
  font-variant-numeric: normal;
  white-space: normal;
}

.nd-sticky--simple .nd-sticky__buy {
  gap: 1.2rem;
}

.nd-sticky--simple .nd-sticky__thumb--simple {
  flex: 0 0 auto;
  width: 4.4rem;
  height: 4.4rem;
  border: 0;
  background-color: transparent;
  box-shadow: none;
}

.nd-sticky--simple .nd-sticky__thumb-img {
  object-fit: contain;
}

.nd-sticky--simple .nd-sticky__price {
  flex: 1 1 auto;
  min-width: 0;
  gap: 0.2rem;
  font-size: 1.6rem;
}

.nd-sticky--simple .nd-sticky__name {
  overflow: hidden;
  font-family: var(--nd-font);
  font-size: 1.2rem;
  font-weight: 500;
  line-height: 1.2;
  text-overflow: ellipsis;
  color: var(--nd-ink-2);
}

.nd-sticky--simple .nd-sticky__submit {
  flex: 0 0 auto;
  min-width: 14rem;
}

@media screen and (min-width: 990px) {
  .nd-sticky--simple .nd-sticky__inner {
    max-width: 72rem;
  }
}

/* ==========================================================================
   Colours and sizes, bought one to three at a time
   ========================================================================== */

/* The choice, on top ------------------------------------------------------ */
.nd-product__info .nd-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.8rem;
  margin: 0.2rem 0 0;
}

.nd-product__info .nd-swatch {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--nd-font);
  color: var(--nd-ink);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* Colours: a photo tile with the name under it. */
.nd-product__info .nd-tiers--visual .nd-swatch {
  width: 6.8rem;
}

.nd-product__info .nd-swatch__thumb {
  display: block;
  width: 6.8rem;
  height: 6.8rem;
  padding: 0.5rem;
  border: 1px solid rgba(17, 17, 17, 0.1);
  border-radius: 1.2rem;
  background-color: #ffffff;
  transition: border-color var(--nd-speed) ease, box-shadow var(--nd-speed) ease;
}

.nd-product__info .nd-swatch__thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.nd-product__info .nd-swatch__name {
  max-width: 100%;
  font-size: 1.15rem;
  font-weight: 500;
  line-height: 1.25;
  text-align: center;
  color: rgba(17, 17, 17, 0.62);
}

.nd-product__info .nd-swatch.is-on .nd-swatch__thumb {
  border-color: var(--nd-ink);
  box-shadow: 0 0 0 0.5px var(--nd-ink);
}

.nd-product__info .nd-swatch.is-on .nd-swatch__name {
  color: var(--nd-ink);
  font-weight: 600;
}

/* Sizes: a chip with the size on it. */
.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-swatch {
  flex-direction: row;
  min-height: 4.4rem;
  padding: 0 1.8rem;
  border: 1px solid rgba(17, 17, 17, 0.14);
  border-radius: var(--nd-radius-pill);
  background-color: #ffffff;
}

.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-swatch__name {
  font-size: 1.4rem;
  color: var(--nd-ink);
}

.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-swatch.is-on {
  border-color: var(--nd-ink);
  box-shadow: 0 0 0 0.5px var(--nd-ink);
}

.nd-product__info .nd-swatch[disabled] {
  cursor: not-allowed;
}

.nd-product__info .nd-swatch[disabled] .nd-swatch__thumb,
.nd-product__info .nd-swatch[disabled] .nd-swatch__name {
  opacity: 0.45;
}

.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-swatch[disabled] .nd-swatch__name {
  text-decoration: line-through;
}

.nd-product__info .nd-swatch__note {
  font-size: 1rem;
  font-weight: 500;
  color: rgba(17, 17, 17, 0.5);
}

.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-swatch__note {
  display: none;
}

.nd-product__info .nd-swatch:focus-visible {
  outline: 2px solid var(--nd-ink);
  outline-offset: 3px;
  border-radius: 1.2rem;
}

/* The heading over the quantities, the same voice as the legend. */
.nd-product__info .nd-tiers__sublabel {
  margin: 2rem 0 1rem;
  font-family: var(--font-heading-family);
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--nd-ink);
}

/* The rows ---------------------------------------------------------------- */
.nd-product__info .nd-tiers--options .nd-tiers__grid {
  gap: 1rem;
}

.nd-product__info .nd-tiers--options .nd-tier {
  grid-template-columns: auto auto minmax(0, 1fr);
}

/* One to three thumbnails of the chosen items, overlapped like a hand of
   cards, so the row shows what is being bought, not just how many. */
.nd-product__info .nd-tier__stack {
  display: flex;
  align-items: center;
  width: 7.2rem;
  height: 4.8rem;
}

.nd-product__info .nd-tier__stack-item {
  display: block;
  width: 4.4rem;
  height: 4.4rem;
  flex: 0 0 auto;
  padding: 0.3rem;
  border: 1px solid rgba(17, 17, 17, 0.08);
  border-radius: 0.9rem;
  background-color: #ffffff;
}

.nd-product__info .nd-tier__stack-item + .nd-tier__stack-item {
  margin-left: -2.6rem;
}

.nd-product__info .nd-tier__stack-item:nth-child(2) {
  transform: translateX(0.7rem);
}

.nd-product__info .nd-tier__stack-item:nth-child(3) {
  transform: translateX(1.4rem);
}

.nd-product__info .nd-tier__stack-item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* The per-item choice opens under the chosen row, joined to it. */
.nd-product__info .nd-units {
  display: none;
}

.nd-product__info .nd-tier-wrap.is-open .nd-units {
  display: grid;
  gap: 1rem;
  margin-top: -1.2rem;
  padding: 2rem 1.6rem 1.4rem;
  border: 1px solid var(--nd-ink);
  border-top: 0;
  border-radius: 0 0 1.4rem 1.4rem;
  box-shadow: 0 0 0 0.5px var(--nd-ink);
  clip-path: inset(0 -2px -2px -2px);
  background-color: #fafaf8;
}

.nd-product__info .nd-tier-wrap.is-open .nd-tier {
  position: relative;
  z-index: 1;
}

.nd-product__info .nd-unit {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.nd-product__info .nd-unit__label {
  flex: 0 0 auto;
  font-size: 1.2rem;
  font-weight: 600;
  color: rgba(17, 17, 17, 0.7);
}

.nd-product__info .nd-unit__choices {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.6rem;
}

.nd-product__info .nd-unit__choice {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 3.6rem;
  height: 3.6rem;
  padding: 0 1rem;
  border: 1px solid rgba(17, 17, 17, 0.14);
  border-radius: var(--nd-radius-pill);
  background-color: #ffffff;
  font-family: var(--nd-font);
  font-size: 1.2rem;
  font-weight: 500;
  color: var(--nd-ink);
  cursor: pointer;
}

.nd-product__info .nd-unit__choice--visual {
  width: 3.6rem;
  padding: 0.25rem;
  border-radius: 0.9rem;
}

.nd-product__info .nd-unit__choice--visual img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.nd-product__info .nd-unit__choice.is-on {
  border-color: var(--nd-ink);
  box-shadow: 0 0 0 0.5px var(--nd-ink);
}

.nd-product__info .nd-unit__choice[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}

@media screen and (min-width: 990px) {
  .nd-product__info .nd-tiers--visual .nd-swatch,
  .nd-product__info .nd-swatch__thumb {
    width: 7.6rem;
  }

  .nd-product__info .nd-swatch__thumb {
    height: 7.6rem;
  }
}

/* ==========================================================================
   Price under the title, for products with nothing to choose
   ========================================================================== */
.nd-product__info .nd-price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 1rem;
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.nd-product__info .nd-price__now {
  font-family: var(--nd-font-heading);
  font-size: 2.4rem;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--nd-ink);
}

.nd-product__info .nd-price__was {
  font-size: 1.5rem;
  line-height: 1.1;
  color: rgba(17, 17, 17, 0.45);
  text-decoration-color: rgba(17, 17, 17, 0.45);
  text-decoration-thickness: 1px;
}

.nd-product__info .nd-price__save {
  align-self: center;
  padding: 0.3rem 0.9rem;
  border-radius: var(--nd-radius-pill);
  background-color: var(--nd-save-soft);
  color: var(--nd-save);
  font-size: 1.2rem;
  font-weight: 600;
  line-height: 1.25;
}

@media screen and (min-width: 990px) {
  .nd-product__info .nd-price__now {
    font-size: 2.8rem;
  }

  .nd-product__info .nd-price__was {
    font-size: 1.7rem;
  }
}

/* The legend floats (it sits on the separator line), so the row of choices
   has to clear it and take the full width -- otherwise it squeezed into a
   one-tile column beside the legend. */
.nd-product__info .nd-tiers--options .nd-swatches {
  clear: both;
  width: 100%;
  padding-top: 0.2rem;
}

.nd-product__info .nd-tiers--options .nd-tiers__sublabel {
  clear: both;
}

/* One width for every row, so the names line up whether a row shows one
   thumbnail or three. */
.nd-product__info .nd-tier__stack {
  width: 8.8rem;
  height: 4.6rem;
}

.nd-product__info .nd-tier__stack-item + .nd-tier__stack-item {
  margin-left: -2.2rem;
}

.nd-product__info .nd-tier__stack-item:nth-child(2),
.nd-product__info .nd-tier__stack-item:nth-child(3) {
  transform: none;
}

/* Each chosen colour shows enough of itself to read. */
.nd-product__info .nd-tier__stack-item {
  width: 4rem;
  height: 4rem;
}

.nd-product__info .nd-tier__stack-item + .nd-tier__stack-item {
  margin-left: -1.6rem;
  box-shadow: -2px 0 0 #ffffff;
}

/* The open row and its picker read as one card. */
.nd-product__info .nd-tier-wrap.is-open .nd-tier {
  border-bottom-left-radius: 0.6rem;
  border-bottom-right-radius: 0.6rem;
}

.nd-product__info .nd-title + .nd-price {
  margin-top: 0.6rem;
}

/* ==========================================================================
   Bundle & Save, premium pass
   --------------------------------------------------------------------------
   Both layouts share one language, taken from a well-made configurator:
   - options rest on a soft warm ground with no outline; the chosen one
     lifts onto white with an ink edge and a long, faint shadow
   - a round check in the corner (side by side) or at the start of the row
     (stacked) says "chosen" instead of a radio dot
   - the badge is a small tag hung on the top edge
   - the price leads; the old price, the per-item price and the saving sit
     under it in descending weight
   Scoped to .nd-product__info and the layout class so it outranks every
   earlier tier rule without !important.
   ========================================================================== */

.nd-product__info .nd-tiers {
  --tier-ground: #f5f4f1;
  --tier-ground-hover: #efede9;
  --tier-lift: 0 14px 32px -16px rgba(17, 17, 17, 0.3), 0 2px 6px -2px rgba(17, 17, 17, 0.06);
}

/* ---- shared: the check ---- */
.nd-product__info .nd-tier__check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  flex: 0 0 auto;
  border: 1.5px solid rgba(17, 17, 17, 0.2);
  border-radius: 50%;
  background-color: #ffffff;
  color: #ffffff;
  transition: background-color var(--nd-speed) ease, border-color var(--nd-speed) ease;
}

.nd-product__info .nd-tier__check svg {
  width: 1rem;
  height: 1rem;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity var(--nd-speed) ease, transform var(--nd-speed) ease;
}

.nd-product__info .nd-tier.is-selected .nd-tier__check,
.nd-product__info .nd-tier:has(.nd-tier__input:checked) .nd-tier__check {
  border-color: var(--nd-ink);
  background-color: var(--nd-ink);
}

.nd-product__info .nd-tier.is-selected .nd-tier__check svg,
.nd-product__info .nd-tier:has(.nd-tier__input:checked) .nd-tier__check svg {
  opacity: 1;
  transform: none;
}

.nd-product__info .nd-tiers .nd-tier__radio {
  display: none;
}

.nd-product__info .nd-tier__each {
  font-size: 1.15rem;
  font-weight: 500;
  line-height: 1.3;
  color: rgba(17, 17, 17, 0.55);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---- shared: resting and chosen ---- */
.nd-product__info .nd-tiers--horizontal .nd-tier,
.nd-product__info .nd-tiers--vertical .nd-tier {
  border: 1.5px solid transparent;
  background-color: var(--tier-ground);
  box-shadow: none;
  outline: 0;
  transition: background-color var(--nd-speed) ease, border-color var(--nd-speed) ease, box-shadow 0.3s ease, transform 0.2s ease;
}

@media (hover: hover) {
  .nd-product__info .nd-tiers--horizontal .nd-tier:hover,
  .nd-product__info .nd-tiers--vertical .nd-tier:hover {
    border-color: transparent;
    background-color: var(--tier-ground-hover);
  }
}

.nd-product__info .nd-tiers--horizontal .nd-tier.is-selected,
.nd-product__info .nd-tiers--horizontal .nd-tier:has(.nd-tier__input:checked),
.nd-product__info .nd-tiers--vertical .nd-tier.is-selected,
.nd-product__info .nd-tiers--vertical .nd-tier:has(.nd-tier__input:checked) {
  border-color: var(--nd-ink);
  background-color: #ffffff;
  box-shadow: var(--tier-lift);
}

.nd-product__info .nd-tiers--horizontal .nd-tier:has(.nd-tier__input:focus-visible),
.nd-product__info .nd-tiers--vertical .nd-tier:has(.nd-tier__input:focus-visible) {
  outline: 2px solid var(--nd-ink);
  outline-offset: 3px;
}

/* ---- shared: the badge, hung on the top edge ---- */
.nd-product__info .nd-tiers--horizontal .nd-tier__cap,
.nd-product__info .nd-tiers--vertical .nd-tier__cap {
  position: absolute;
  top: 0;
  z-index: 2;
  display: block;
  padding: 0.35rem 0.9rem;
  border-radius: var(--nd-radius-pill) ;
  background-color: var(--nd-accent, #c1502e);
  color: #ffffff;
  font-family: var(--nd-font);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  line-height: 1.2;
  text-transform: uppercase;
  white-space: nowrap;
}

.nd-product__info .nd-tiers--horizontal .nd-tier--gold .nd-tier__cap,
.nd-product__info .nd-tiers--vertical .nd-tier--gold .nd-tier__cap {
  background-color: var(--nd-accent, #c1502e);
  color: #ffffff;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__cap:empty,
.nd-product__info .nd-tiers--vertical .nd-tier__cap:empty {
  display: none;
}

/* The saving, the same soft mark everywhere. */
.nd-product__info .nd-tiers--horizontal .nd-tier__save,
.nd-product__info .nd-tiers--horizontal .nd-tier.is-selected .nd-tier__save,
.nd-product__info .nd-tiers--horizontal .nd-tier:has(.nd-tier__input:checked) .nd-tier__save {
  padding: 0.3rem 0.8rem;
  border-radius: var(--nd-radius-pill);
  background-color: var(--nd-save-soft);
  color: var(--nd-save);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.25;
}

/* ==========================================================================
   Side by side
   ========================================================================== */
.nd-product__info .nd-tiers--horizontal .nd-tiers__grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.8rem;
  padding-top: 0.9rem;
}
fieldset.nd-tiers.nd-tiers--solo > .nd-tiers__grid[hidden] {
  display: none !important;
}
.nd-product__info .nd-tiers--horizontal .nd-tier-wrap {
  display: contents;
}

.nd-product__info .nd-tiers--horizontal .nd-tier {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 1.8rem 0.6rem 1.4rem;
  border-radius: 1.8rem;
  overflow: visible;
  text-align: center;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__cap {
  left: 50%;
  right: auto;
  transform: translate(-50%, -50%);
}

.nd-product__info .nd-tiers--horizontal .nd-tier__check {
  position: absolute;
  top: 0.9rem;
  right: 0.9rem;
  width: 2rem;
  height: 2rem;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__thumb {
  width: 76%;
  margin: 0.4rem auto 1rem;
  border: 0;
  border-radius: 0;
  background-color: transparent;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__stack {
  justify-content: center;
  width: 100%;
  height: 5.4rem;
  margin: 0.4rem 0 1rem;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__stack-item {
  width: 4.6rem;
  height: 4.6rem;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__stack-item + .nd-tier__stack-item {
  margin-left: -2.8rem;
}

/* The words: one centred column, in the order they are read. */
.nd-product__info .nd-tiers--horizontal .nd-tier__body,
.nd-product__info .nd-tiers--horizontal .nd-tier__thumb + .nd-tier__body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  align-items: center;
  gap: 0;
  width: 100%;
  padding: 0;
  text-align: center;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__main,
.nd-product__info .nd-tiers--horizontal .nd-tier__price {
  display: contents;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__name {
  order: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  line-height: 1;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__count {
  font-family: var(--nd-font-heading);
  font-size: 2.6rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--nd-ink);
}

.nd-product__info .nd-tiers--horizontal .nd-tier__unit-word {
  font-family: var(--nd-font);
  font-size: 1.15rem;
  font-weight: 500;
  line-height: 1.2;
  color: rgba(17, 17, 17, 0.6);
}

.nd-product__info .nd-tiers--horizontal .nd-tier__total {
  order: 2;
  margin-top: 1.1rem;
  font-family: var(--nd-font-heading);
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--nd-ink);
}

.nd-product__info .nd-tiers--horizontal .nd-tier__was {
  order: 3;
  margin-top: 0.2rem;
  font-size: 1.15rem;
  line-height: 1.2;
  color: rgba(17, 17, 17, 0.42);
}

.nd-product__info .nd-tiers--horizontal .nd-tier__each {
  order: 4;
  margin-top: 0.2rem;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__save {
  order: 5;
  align-self: center;
  margin-top: auto;
  transform: translateY(0.9rem);
}

.nd-product__info .nd-tiers--horizontal .nd-tier__price + * ,
.nd-product__info .nd-tiers--horizontal .nd-tier__save:not([hidden]) {
  margin-top: 1rem;
  transform: none;
}

/* The gift, a line under the tile's words. */
.nd-product__info .nd-tiers--horizontal .nd-tier__benefit {
  display: block;
  margin: 0.8rem 0 0;
  padding: 0;
  border-radius: 0;
  background: none;
  color: var(--nd-save);
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__benefit--long-only {
  display: none;
}

/* The per-item picker opens full width under the row of tiles. */
.nd-product__info .nd-tiers--horizontal .nd-units,
.nd-product__info .nd-tiers--horizontal .nd-tier-wrap.is-open .nd-units {
  grid-column: 1 / -1;
  order: 10;
}

.nd-product__info .nd-tiers--horizontal .nd-tier-wrap:not(.is-open) .nd-units {
  display: none;
}

.nd-product__info .nd-tiers--horizontal .nd-tier-wrap.is-open .nd-units {
  display: grid;
  gap: 1rem;
  margin-top: 0.2rem;
  padding: 1.4rem 1.6rem;
  border: 0;
  border-radius: 1.6rem;
  box-shadow: none;
  clip-path: none;
  background-color: var(--tier-ground);
}

.nd-product__info .nd-tiers--horizontal .nd-tier-wrap.is-open .nd-tier {
  border-bottom-left-radius: 1.8rem;
  border-bottom-right-radius: 1.8rem;
}

@media screen and (max-width: 420px) {
  .nd-product__info .nd-tiers--horizontal .nd-tier__cap {
    padding: 0.3rem 0.7rem;
    font-size: 0.8rem;
    letter-spacing: 0.06em;
  }

  .nd-product__info .nd-tiers--horizontal .nd-tier__check {
    top: 0.7rem;
    right: 0.7rem;
    width: 1.8rem;
    height: 1.8rem;
  }

  .nd-product__info .nd-tiers--horizontal .nd-tier__count {
    font-size: 2.3rem;
  }
}

@media screen and (min-width: 990px) {
  .nd-product__info .nd-tiers--horizontal .nd-tiers__grid {
    gap: 1.2rem;
  }

  .nd-product__info .nd-tiers--horizontal .nd-tier {
    padding: 2.2rem 1rem 1.8rem;
    border-radius: 2rem;
  }

  .nd-product__info .nd-tiers--horizontal .nd-tier__count {
    font-size: 3rem;
  }

  .nd-product__info .nd-tiers--horizontal .nd-tier__total {
    font-size: 2rem;
  }

  .nd-product__info .nd-tiers--horizontal .nd-tier__was,
  .nd-product__info .nd-tiers--horizontal .nd-tier__each {
    font-size: 1.25rem;
  }

  .nd-product__info .nd-tiers--horizontal .nd-tier__cap {
    font-size: 0.95rem;
  }
}

/* ==========================================================================
   Stacked
   ========================================================================== */
.nd-product__info .nd-tiers--vertical .nd-tiers__grid {
  padding-top: 0.6rem;
}

.nd-product__info .nd-tiers--vertical .nd-tier {
  border-radius: 1.8rem;
}

.nd-product__info .nd-tiers--vertical .nd-tier__cap {
  right: 1.2rem;
  left: auto;
  transform: translateY(-50%);
}

.nd-product__info .nd-tiers--vertical .nd-tier__price {
  gap: 0.15rem;
}

.nd-product__info .nd-tiers--vertical .nd-tier__was {
  color: rgba(17, 17, 17, 0.42);
}

/* Colour and size stacks: the chosen row and its picker are one white card. */
.nd-product__info .nd-tiers--vertical .nd-tier-wrap.is-open .nd-tier {
  border-bottom-color: transparent;
  border-bottom-left-radius: 0.6rem;
  border-bottom-right-radius: 0.6rem;
  box-shadow: none;
}

.nd-product__info .nd-tiers--vertical .nd-tier-wrap.is-open .nd-units {
  margin-top: -1.4rem;
  padding: 2rem 1.6rem 1.4rem;
  border: 1.5px solid var(--nd-ink);
  border-top: 0;
  border-radius: 0 0 1.8rem 1.8rem;
  box-shadow: var(--tier-lift);
  clip-path: inset(0 -40px -40px -40px);
  background-color: #ffffff;
}

.nd-product__info .nd-tiers--vertical .nd-tier-wrap.is-open .nd-units::before {
  content: "";
  display: block;
  height: 1px;
  margin: -0.6rem 0 0.4rem;
  background-color: rgba(17, 17, 17, 0.08);
}

.nd-product__info .nd-tier__stack-item {
  border: 0;
  background-color: #ffffff;
  box-shadow: 0 1px 3px rgba(17, 17, 17, 0.12);
}

/* ==========================================================================
   Colour tiles: a light wash of each colour behind its photo
   ========================================================================== */
.nd-product__info .nd-swatch[style*="--swatch-tint"] .nd-swatch__thumb {
  border-color: transparent;
  background-color: var(--swatch-tint);
}

.nd-product__info .nd-swatch.is-on[style*="--swatch-tint"] .nd-swatch__thumb {
  border-color: var(--nd-ink);
}

/* ---- premium pass: refinements ---- */

/* Side by side: the saving sits on one line across all three tiles,
   whatever is above it, and the empty circles go -- only the chosen tile
   carries its check. */
.nd-product__info .nd-tiers--horizontal .nd-tier__save,
.nd-product__info .nd-tiers--horizontal .nd-tier__price + *,
.nd-product__info .nd-tiers--horizontal .nd-tier__save:not([hidden]) {
  margin-top: auto;
  transform: none;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__price::after {
  content: "";
  order: 4;
  flex: 0 0 1.1rem;
  height: 1.1rem;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__benefit {
  order: 6;
  margin-top: 0.7rem;
}

.nd-product__info .nd-tiers--horizontal .nd-tier:not(.is-selected):not(:has(.nd-tier__input:checked)) .nd-tier__check {
  opacity: 0;
}

.nd-product__info .nd-tiers--horizontal .nd-tier__stack-item + .nd-tier__stack-item {
  margin-left: -2.2rem;
}

/* Stacked: the per-item price under the old price, not above the price. */
.nd-product__info .nd-tiers--vertical .nd-tier__total { order: 1; }
.nd-product__info .nd-tiers--vertical .nd-tier__price__detail { order: 2; }
.nd-product__info .nd-tiers--vertical .nd-tier__each { order: 3; }

/* Keyboard focus as a soft ring, not a second hard edge. */
.nd-product__info .nd-tiers--horizontal .nd-tier:has(.nd-tier__input:focus-visible),
.nd-product__info .nd-tiers--vertical .nd-tier:has(.nd-tier__input:focus-visible) {
  outline: 3px solid rgba(17, 17, 17, 0.18);
  outline-offset: 2px;
}

/* Colour tiles: the product photos sit on white, so the wash only showed
   as a frame. Multiplied, the white takes the tint and the product keeps
   its own colour. */
.nd-product__info .nd-swatch[style*="--swatch-tint"] .nd-swatch__thumb img {
  mix-blend-mode: multiply;
}

/* A gift line goes above the saving, so the savings still line up. */
.nd-product__info .nd-tiers--horizontal .nd-tier__benefit { order: 5; margin-top: 0.6rem; }
.nd-product__info .nd-tiers--horizontal .nd-tier__price::after { order: 6; }
.nd-product__info .nd-tiers--horizontal .nd-tier__save { order: 7; }

@media screen and (min-width: 990px) {
  /* Wide tiles do not need wide pictures: a capped thumbnail keeps the
     three tiles compact and the prices near the top of the fold. */
  .nd-product__info .nd-tiers--horizontal .nd-tier__thumb {
    width: 62%;
    max-width: 13rem;
    margin-bottom: 0.8rem;
  }
}

/* The gift line lives outside the words' wrapper, so the wrapper is
   dissolved too: every piece becomes a direct item of the tile and the
   order above puts the gift before the saving. */
.nd-product__info .nd-tiers--horizontal .nd-tier__body,
.nd-product__info .nd-tiers--horizontal .nd-tier__thumb + .nd-tier__body {
  display: contents;
}

/* ==========================================================================
   Bundle & Save: outlined at rest, tinted when chosen; one radius family
   --------------------------------------------------------------------------
   Every option is a white card with the same hairline the add-on cards use.
   The chosen one keeps its card and gains an ink edge and the warm stone
   ground -- no lift onto white, no shadow.

   Radii follow the buy box: 12px for cards and panels (benefits, the
   return promise, the accordion, the gallery), 8px for small controls
   (the button, colour tiles, per-item choices). The add-on cards move from
   8px to 12px so the two groups of cards that sit together match.
   ========================================================================== */

.nd-product__info .nd-tiers--horizontal .nd-tier,
.nd-product__info .nd-tiers--vertical .nd-tier {
  border: 1px solid var(--nd-hairline, #e2e6e1);
  border-radius: 1.2rem;
  background-color: #ffffff;
  box-shadow: none;
}

@media (hover: hover) {
  .nd-product__info .nd-tiers--horizontal .nd-tier:hover,
  .nd-product__info .nd-tiers--vertical .nd-tier:hover {
    border-color: rgba(17, 17, 17, 0.28);
    background-color: #ffffff;
  }
}

.nd-product__info .nd-tiers--horizontal .nd-tier.is-selected,
.nd-product__info .nd-tiers--horizontal .nd-tier:has(.nd-tier__input:checked),
.nd-product__info .nd-tiers--vertical .nd-tier.is-selected,
.nd-product__info .nd-tiers--vertical .nd-tier:has(.nd-tier__input:checked) {
  border-color: var(--nd-ink);
  background-color: var(--tier-ground, #f5f4f1);
  box-shadow: inset 0 0 0 0.5px var(--nd-ink);
}

/* Side by side: the per-item picker is a panel of its own under the tiles. */
.nd-product__info .nd-tiers--horizontal .nd-tier-wrap.is-open .nd-tier {
  border-radius: 1.2rem;
}

.nd-product__info .nd-tiers--horizontal .nd-tier-wrap.is-open .nd-units {
  border: 1px solid var(--nd-hairline, #e2e6e1);
  border-radius: 1.2rem;
  background-color: #ffffff;
}

/* Stacked: the chosen row and its picker are one tinted card. */
.nd-product__info .nd-tiers--vertical .nd-tier-wrap.is-open .nd-tier {
  border-bottom-color: transparent;
  border-bottom-left-radius: 0.4rem;
  border-bottom-right-radius: 0.4rem;
  box-shadow: inset 0.5px 0.5px 0 0 var(--nd-ink), inset -0.5px 0 0 0 var(--nd-ink);
}

.nd-product__info .nd-tiers--vertical .nd-tier-wrap.is-open .nd-units {
  border: 1px solid var(--nd-ink);
  border-top: 0;
  border-radius: 0 0 1.2rem 1.2rem;
  box-shadow: inset 0.5px -0.5px 0 0 var(--nd-ink), inset -0.5px 0 0 0 var(--nd-ink);
  clip-path: none;
  background-color: var(--tier-ground, #f5f4f1);
}

.nd-product__info .nd-tiers--vertical .nd-tier-wrap.is-open .nd-units::before {
  background-color: rgba(17, 17, 17, 0.1);
}

/* Small controls at 8px, like the button. */
.nd-product__info .nd-swatch__thumb {
  border-radius: 0.8rem;
}

.nd-product__info .nd-unit__choice--visual,
.nd-product__info .nd-tier__stack-item {
  border-radius: 0.6rem;
}

.nd-product__info .nd-swatch:focus-visible {
  border-radius: 0.8rem;
}

/* The add-on cards join the card radius. */
.nd-product__info .nd-addons__card {
  border-radius: 1.2rem;
}

/* The sticky button on one line on a phone. */
@media screen and (max-width: 749px) {
  .template-product-nomadesk-triview .nd-atc__sep,.template-product-nomadesk-triview .nd-atc__price  {
    display: none !important;
  }
  .template-product-deskmate-pro .nd-atc__sep,.template-product-deskmate-pro .nd-atc__price  {
    display: none;
  }
  .nd-media__stage {
    align-items: flex-start !important;
    transition: height .22s ease;
  }

  .nd-media__slide {
    aspect-ratio: auto !important;
    height: 100% !important;
    min-height: 0 !important;
    align-self: flex-start !important;
  }

  .nd-media__slide picture,
  .nd-media__picture {
    display: block;
    width: 100%;
    height: 100% !important;
  }

  .nd-media__slide .nd-media__img,
  .nd-media__slide .nd-media__video,
  .nd-media__slide .nd-media__embed {
    width: 100%;
    height: 100% !important;
    object-fit: contain !important;
  }
  .nd-product__info {
    padding: 0 12px 20px 12px;
    border-radius: 15px;
  }
  .nd-product__info .nd-rating {
    font-size: 1.2rem;
  }
  .nd-sticky__submit {
    padding: 0 1.2rem;
    font-size: 1.2rem;
    letter-spacing: 0.04em;
    white-space: nowrap;
  }
  .nd-product__info .nd-tiers--vertical .nd-tier__save,
  .nd-product__info .nd-tiers--vertical .nd-tier.is-selected .nd-tier__save,
  .nd-product__info .nd-tiers--vertical .nd-tier:has(.nd-tier__input:checked) .nd-tier__save,
  .nd-product__info .nd-tiers--horizontal .nd-tier__cap, .nd-product__info .nd-tiers--vertical .nd-tier__cap {
    border-radius: 0.3rem;
  }
  
}

/* The joined card is only for rows that have a per-item picker. "1 wallet"
   has none, so when it is chosen it keeps its full border and corners --
   the open-row rule was removing its bottom edge. */
.nd-product__info .nd-tiers--vertical .nd-tier-wrap.is-open:not(:has(.nd-units)) .nd-tier {
  border-bottom-color: var(--nd-ink);
  border-bottom-left-radius: 1.2rem;
  border-bottom-right-radius: 1.2rem;
  box-shadow: inset 0 0 0 0.5px var(--nd-ink);
}


/* ----------------------------------------------- stacked tiers: gift line
   Gift line: full row. The free-gift pill used to start under the pack name
   (column 3), leaving the thumbnail column empty on the left and pushing the
   long line ("Free 3x Precision MousePads + 3x Keyboard Lifts") onto two
   lines on phones. It now runs across the whole foot of the tile on one line.
   The text shrinks a little only when the tile is too narrow to fit it at
   full size (container units measure the pill's own width). */
.nd-product__info .nd-tiers--vertical .nd-tier__benefit,
.nd-product__info .nd-tiers--vertical .nd-tier__thumb ~ .nd-tier__benefit {
  grid-column: 1 / -1 !important;
  justify-self: stretch !important;
  justify-content: flex-start;
  max-width: none;
  white-space: nowrap;
  container-type: inline-size;
}

.nd-product__info .nd-tiers--vertical .nd-tier__benefit-long {
  font-size: min(1em, calc((100cqi - 2.5rem) / 25.5));
}


/* -------------------------------------------- desktop: centred header
   Desktop: centre the top of the product info -- rating, eyebrow,
   heading (with its tagline) and the price line. Boxes (tiers, add-ons,
   accordions) already span the column, so they are left as they are.
   Applies to every product page built on the nd-product section. */
@media screen and (min-width: 990px) {
  .nd-product__info .nd-rating,
  .nd-product__info .ndp,
  .nd-product__info .nd-price {
    justify-content: center !important;
    text-align: center;
  }

  .nd-product__info .nd__head {
    align-items: center !important;
    text-align: center !important;
  }

  .nd-product__info .nd__h1-row {
    justify-content: center !important;
  }

  .nd-product__info .nd__h1,
  .nd-product__info .nd__eyebrow {
    text-align: center !important;
  }
}


/* DeskMate Pro only. Most popular badge: black outline on white instead
   of the solid orange, so "Best value" is the one coloured badge. The
   padding drops 1px to make room for the border, keeping the same size. */
.template-product-deskmate-pro .nd-tier--capped:not(.nd-tier--gold) .nd-tier__cap {
  background: #fff !important;
  color: #14181c !important;
  border: 1px solid #14181c !important;
  padding: 2.5px 8px !important;
}

/* The same badge on the sticky bar's "Buy 2" pill, in both its selected and
   unselected states, so it matches the tile above. The padding drops 1px for
   the border, keeping the pill the same size. */
.template-product-deskmate-pro .nd-sticky__tier[data-nd-sticky-tier="1"] .nd-sticky__badge {
  background: #fff !important;
  color: #14181c !important;
  border: 1px solid #14181c !important;
  padding: 0.05rem 0.5rem !important;
}


/* Optional sentence under a tier's free-gift line (block settings
   "Sentence under tile 1/2/3"). Theme body font, small and muted so the
   gift line stays the thing that reads first. */
.nd-product__info .nd-tier__note {
  grid-column: 1 / -1;
  display: block;
  margin-top: 0.5rem;
  font-family: var(--font-body-family);
  font-size: 1.2rem;
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: 0;
  text-transform: none;
  text-align: left;
  color: rgba(20, 24, 28, 0.72);
}


/* ------------------------------------------------------------------------
   Size chips share the label line, centred.

   A <legend> is not a flex item, so it can't be centred on the same row as
   the chips. The label is repeated inside .nd-tiers__choice (aria-hidden)
   and the legend is hidden from view but kept for screen readers. The
   fieldset keeps its full width so its separator line still spans the
   column. Scoped to text-chip rows, so colour swatches are untouched.
   ------------------------------------------------------------------------ */
.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-tiers__legend {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-tiers__choice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  column-gap: 1.2rem;
  row-gap: 0.8rem;
}

.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-swatches {
  clear: none;
  width: auto;
  margin: 0;
  padding-top: 0;
  justify-content: center;
}

/* The repeated label only shows on these rows. */
.nd-tiers__label--inline {
  display: none;
}

.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-tiers__label--inline {
  display: inline-block;
}

/* Anything after the choice row starts a fresh line again. */
.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-tiers__sublabel,
.nd-product__info .nd-tiers--options:not(.nd-tiers--visual) .nd-tiers__grid {
  clear: both;
}
