/* ==========================================================================
   IRONBEASTS - SINGLE PRODUCT  (product.css)
   Loaded only on a product page.
   ========================================================================== */

.product-page {
  padding-block: var(--sp-5) clamp(3rem, 6vw, 6rem);
}

/* ---- Layout --------------------------------------------------------------
   Gallery left, details right. Below 900px the gallery comes first, which is
   the source order, so nothing has to be reordered. */
.product-layout {
  display: grid;
  /* The gallery takes slightly less than half, which shrinks the images and
     gives the description the room it wants. The `sizes` on each image match
     these proportions. */
  grid-template-columns: minmax(0, 0.88fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: start;
}

/* The details stay put while the gallery scrolls past, until the whole layout
   ends and related products push it away.

   Sticky on the summary rather than a scrollbar on the gallery: a nested scroll
   container swallows the wheel while the pointer is over it, and the page stops
   responding until you move off. This keeps one scrollbar on the page and gets
   the same result.

   `align-items: start` on .product-layout is what makes it possible -- a grid
   item stretched to the row's height has nothing to stick within. */
@media (min-width: 900px) {
  .product-summary {
    /* Holds still while the gallery scrolls past it. It deliberately does not
       cap its own height: a max-height with overflow turned this column into a
       scrolling box of its own, so the page had room everywhere and the
       details had none. If the summary is taller than the screen it simply
       scrolls with the page, which is what a reader expects. */
    position: sticky;
    top: calc(var(--header-h) + var(--sp-4));
  }
}

@media (max-width: 899px) {
  .product-layout {
    /* minmax(0, 1fr), not 1fr. A bare 1fr means minmax(auto, 1fr), and an auto
       minimum refuses to shrink below the item's content -- which for a
       horizontally scrolling carousel is several screens wide, so the column
       blows past the viewport and the image is cut off. */
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Wraps the carousel and its strip so the layout grid treats them as one item.
   min-width: 0 for the same reason the columns use minmax(0, ...): a grid item
   defaults to refusing to shrink below its content, and the carousel's content
   is deliberately wider than the screen. */
.product-media {
  min-width: 0;
}

/* ---- Gallery -------------------------------------------------------------
   A two-column grid. Items marked full span both columns, which produces
   one / pair / one, with the last image always alone. The pattern is worked
   out in PHP so the markup carries it -- nothing here depends on counting
   with :nth-child, which cannot express "and always the last one". */
.product-gallery {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-3);
}

.product-gallery__item {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  aspect-ratio: 5 / 6;
  overflow: hidden;
  background: var(--surface-2);
  cursor: zoom-in;
}

.product-gallery__item.is-full {
  grid-column: 1 / -1;
}

.product-gallery__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.product-gallery__placeholder {
  display: block;
}

/* The strip belongs to the mobile carousel below; on desktop the grid is the
   gallery and these would be a second, redundant control. */
.product-gallery__strip {
  display: none;
}

/* ---- Gallery on small screens --------------------------------------------
   The grid becomes one image per screen, swiped horizontally. scroll-snap
   does the swiping natively -- no carousel library, no touch handlers -- and
   the strip below jumps between them for anyone who would rather tap. */
@media (max-width: 899px) {
  .product-gallery {
    display: flex;
    gap: 0;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* Momentum scrolling should not drag the page behind it. */
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }

  .product-gallery::-webkit-scrollbar {
    display: none;
  }

  /* Every image is one screen wide here, so the desktop full/half distinction
     stops applying. */
  .product-gallery__item,
  .product-gallery__item.is-full {
    flex: 0 0 100%;
    grid-column: auto;
    scroll-snap-align: center;
    cursor: pointer;
  }

  .product-gallery__strip {
    display: flex;
    gap: var(--sp-2);
    margin-top: var(--sp-2);
    overflow-x: auto;
    scrollbar-width: none;
  }

  .product-gallery__strip::-webkit-scrollbar {
    display: none;
  }

  .product-gallery__thumb {
    flex: 0 0 64px;
    aspect-ratio: 5 / 6;
    padding: 0;
    border: 1px solid transparent;
    background: var(--surface-2);
    cursor: pointer;
    opacity: .5;
    transition: opacity var(--t), border-color var(--t);
  }

  .product-gallery__thumb.is-current {
    opacity: 1;
    border-color: var(--heading);
  }

  /* contain, not cover: whatever shape the photograph is, the whole of it is
     visible. cover would crop anything that is not exactly 5:6, which is the
     thing being fixed here. */
  .product-gallery__thumb-img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
  }

}

/* ---- Lightbox ------------------------------------------------------------
   A <dialog>, so the backdrop, Escape and the focus trap are the browser's
   rather than a library's. */
.product-lightbox {
  width: 100vw;
  max-width: 100vw;
  /* vh first as the fallback: a browser without dvh drops that declaration
     rather than the rule, and without a height at all the image renders at its
     natural size and overflows the screen. */
  height: 100vh;
  height: 100dvh;
  max-height: 100vh;
  max-height: 100dvh;
  padding: 0;
  border: 0;
  /* Transparent, so the black comes from ::backdrop below. The dialog covers
     the viewport, so anything painted here would hide the backdrop entirely. */
  background: transparent;
  overflow: hidden;
}

/* Only when open -- a dialog is display:none until then, and setting flex
   unconditionally would leave it on the page permanently. The padding keeps the
   image clear of the close and step controls. */
.product-lightbox[open] {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: clamp(1rem, 5vw, 4.5rem);
}

/* min-height: 0 is what makes this work. A flex item will not shrink below its
   content by default, so without it a tall image pushes the counter off the
   screen instead of scaling down. */
.product-lightbox__stage {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.product-lightbox__count {
  flex: 0 0 auto;
  margin: 0;
  font-family: var(--font-display);
  line-height: var(--lh-snug);
  font-weight: 600;
  font-size: var(--fs-13);
  letter-spacing: var(--ls-6);
  color: var(--on-dark-text);
  /* Tabular figures, so the counter does not shift as the number changes. */
  font-variant-numeric: tabular-nums;
}

.product-lightbox::backdrop {
  background: var(--shade-6);
}

/* Bounded rather than sized: the image is never told how big to be, only how
   big it may get. object-fit could crop if the box it is given turns out taller
   or wider than expected; a max-width and max-height pair cannot. */
.product-lightbox__img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  display: block;
}

/* The theme's own icon-button idiom -- .icon-btn in the header: a round hit
   area with nothing drawn behind it, and a soft fill only on hover. Inverted
   for the black overlay, so the icon is white and the fill is a lift rather
   than a shade. Solid squares here read as pasted on from another design. */
.product-lightbox__close,
.product-lightbox__nav {
  position: absolute;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--on-dark-text);
  cursor: pointer;
  transition: background-color var(--t), color var(--t);
}

.product-lightbox__close:hover,
.product-lightbox__nav:hover,
.product-lightbox__close:focus-visible,
.product-lightbox__nav:focus-visible {
  background-color: var(--on-dark-fill-2);
  color: var(--ib-white);
}

.product-lightbox__close {
  top: var(--sp-4);
  right: var(--sp-4);
}

.product-lightbox__nav {
  top: 50%;
  transform: translateY(-50%);
}

.product-lightbox__nav--prev { left: var(--sp-4); }
.product-lightbox__nav--next { right: var(--sp-4); }

.product-lightbox__close svg,
.product-lightbox__nav svg {
  width: 22px;
  height: 22px;
}

/* ---- Summary -------------------------------------------------------------- */
/* One rhythm for the whole column. Every block was carrying its own margin --
   16px under the title, 32px under the price, 56px before the description --
   which is what made the spacing read as empty and cramped at the same time:
   no two gaps agreed, so nothing grouped. A single gap here means the blocks
   separate evenly and each reads as its own thing.

   It is deliberately tight. The title and the price are one group and want to
   sit close; the blocks that need real separation get it from the rule and the
   padding below, not from this. */
.product-summary {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

/* The page's h1, so it is the largest thing on it. The display face, the
   uppercase and the tight leading all come from the h1 rule in base.css --
   only the size belongs to the product page. It was borrowing .section-title,
   which is sized for a heading that introduces a section, not for the subject
   of the page. */
/* The one heading on the site that is not a label.
 
   `h1`-`h4` are set in capitals with the tracking opened up, which was right
   for a condensed face: narrow letters need the air. On a wide one the same
   treatment turns a product name into a long loose band of capitals, and this
   is the largest heading on the site, so it shows there first.
 
   A product name is a name. It is set the way a name is written, and told
   apart from the text around it by size and weight rather than by shouting.
 
/* Three declarations, and nothing else.
 
   The capitals, the tracking that goes with them and the colour all come from
   the `h1`-`h4` rule in base.css, which is where they belong -- so this says
   only what a product name does differently from every other heading:
 
   - one step down the ramp. It was the largest thing on the site after the
     hero, and size alone was carrying the whole hierarchy;
   - medium weight rather than the heading's 600. Long names set in capitals
     get heavy quickly, and the extra weight was reading as a banner;
   - leading a shade looser than the heading default, because these wrap to
     two lines far more often than a section title does. */
.product-summary__title {
  font-size: var(--fs-fluid-lg);
  font-weight: 500;
  line-height: var(--lh-snug);
}

/* Second in the hierarchy after the title. This was borrowing .card__price,
   which is 14px because it sits under a thumbnail. */
.product-summary__price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-3);
  font-family: var(--font-display);
  line-height: var(--lh-snug);
  font-size: var(--fs-16);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--heading);
}

/* The dates the sale above runs between. A footnote to the price, so it takes
   the muted colour and the small size the theme uses for anything qualifying a
   number rather than stating one -- the same treatment as `.includes_tax`.

   No rule of its own: it belongs to the price block, and the sibling selector
   below draws the one line that closes that block. */
.product-summary__promo {
  margin: var(--sp-1) 0 0;
  font-size: var(--fs-12);
  letter-spacing: var(--ls-1);
  line-height: var(--lh-heading);
  color: var(--text-muted);
}

/* On sale, WooCommerce marks the old price <del> and the new one <ins>. The
   old one steps back to body size, body weight and the muted grey; the new one
   keeps the heading colour and the display face, so the two separate on weight
   and size rather than on colour. <ins> also drops the underline a browser
   gives it by default. */
.product-summary__price del {
  font-family: var(--font-body);
  font-size: var(--fs-16);
  font-weight: 400;
  color: var(--text-muted);
}

.product-summary__price ins {
  text-decoration: none;
  color: var(--heading);
}

.product-summary__soldout {
  font-family: var(--font-display);
  line-height: var(--lh-snug);
  font-weight: 600;
  letter-spacing: var(--ls-5);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Held to a readable measure rather than the full column: past about 60
   characters the eye loses the start of the next line. */
/* The short description. The line under it belongs to the first panel below
   (.product-care), so every panel title sits the same distance from its own
   line. The reading measure is held on the text inside, not on this box. */
.product-summary__description {
  margin-top: var(--sp-5);
  padding-bottom: var(--sp-2);
  color: var(--text-muted);
}

/* ---- Alternatives ---------------------------------------------------------
   The same garment in another colour. Reads as a set of options with one
   already chosen, which is why the current product is in the row rather than
   implied by its absence. */
.product-alternatives {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

/* "3 VERSIONS" and "SIZE" name the same kind of thing -- a set of options
   below them -- so they are one rule. They had drifted apart: one dark and
   tracked at .1em, the other grey at .12em, which is half of why the column
   read as unrelated parts. */
.product-alternatives__label,
.product-variations__label,
.product-care__label {
  display: block;
  font-family: var(--font-display);
  line-height: var(--lh-snug);
  font-weight: 600;
  font-size: var(--fs-12);
  letter-spacing: var(--ls-6);
  text-transform: uppercase;
  color: var(--text-muted);
}

.product-alternatives__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The same 5:6 the cards use, so a garment reads as a garment rather than as a
   square crop of one. Fixed, so the row does not reflow as images arrive. */
.product-alternatives__item {
  width: 60px;
  aspect-ratio: 5 / 6;
  border: 1px solid var(--border);
  transition: border-color var(--t);
}

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

.product-alternatives__item a {
  display: block;
  height: 100%;
}

.product-alternatives__item:hover {
  border-color: var(--heading);
}

/* The one you are looking at: a heavier border, and it is not a link. */
.product-alternatives__item.is-current {
  border-color: var(--heading);
  border-width: 2px;
}

/* Quiet by design: it should register without competing with the button it
   sits under. */

/* A soft ground rather than an outline. Tried without it, and the lock and the
   card logos came loose -- they belong to the button above them, and the fill
   is what says so without drawing another rectangle. */
.trust {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--surface-2);
  text-align: center;
}

/* Said the way the product page says SIZE and 3 VERSIONS, because it is the same kind
   of thing: a label over what sits under it. It was the one line in the column
   set as a sentence. */
.trust__secure {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  font-size: var(--fs-11);
  letter-spacing: var(--ls-5);
  text-transform: uppercase;
  color: var(--text-muted);
}

.trust__secure svg {
  width: 14px;
  height: 14px;
  flex: none;
}

/* One plate behind the whole run, exactly as the footer draws it.
 
   These marks are somebody else's artwork and several of them carry their own
   white background, so laid straight onto a panel they came out as a row of
   pale patches. A single white ground puts every one of them on the surface
   it was designed for, and the row reads as one object rather than eight
   mismatched ones. */
.trust__marks {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: .55rem;
  margin: 0;
  padding: .45rem .7rem;
  list-style: none;
  background: var(--ib-white);
  border-radius: 6px;
}

.trust__marks li {
  display: flex;
  align-items: center;
  height: 20px;
}

/* Full colour. Greyed back, they read as switched off -- and next to a button that is itself
   greyed until a size is chosen, the whole block looked like something had
   failed to load. */
.trust__marks img {
  display: block;
  height: 20px;
  width: auto;
  max-width: none;
}

/* A rule between sections, the way the reference stores draw it: the title and
   price are one block, what you choose is another, and what you read about it
   is a third. The rhythm gap alone was not enough to group them.

   The first is a sibling selector rather than a list of every block that might
   follow the price, so the line lands on the colourways where a garment has
   them and on the buy controls where it does not -- one line either way, with
   nothing to take back off again.

   The promotion dates are part of the price block, not a section after it, so
   they are stepped over and the line falls on whatever follows them. Still one
   line, still nothing to take back off. */
.product-summary__price + *:not(.product-summary__promo),
.product-summary__promo + * {
  border-top: 1px solid var(--border);
  padding-top: var(--sp-5);
}

/* Space between paragraphs, but none trailing the last one -- hence p + p
   rather than a bottom margin that then has to be taken off again. */
/* Past about 60 characters the eye loses the start of the next line. */
.product-summary__description p {
  max-width: 58ch;
  margin-block: 0;
}

.product-summary__description p + p {
  margin-block-start: var(--sp-4);
}

/* ---- Stock-Off notice ------------------------------------------------------
   Under the panels, on a product in the stock-off category: no exchanges or
   returns. The title takes the panel titles' size so it reads as one more of
   them, in wine red. */
.product-stockoff {
  font-weight: 700;
  line-height: var(--lh-heading);
  color: var(--ib-wine);
}

.product-stockoff__title {
  margin: 0;
  font-size: var(--fs-13);
  font-weight: inherit;
  letter-spacing: var(--ls-6);
  color: inherit;
}

.product-stockoff__text {
  margin: var(--sp-1) 0 0;
  font-size: var(--fs-14);
}

/* ---- Materials & care -----------------------------------------------------
   A <details>, so it works with no script. The marker is drawn here rather
   than left to the browser, because the default triangle differs on every one
   of them. */
/* Each panel draws the line above it, and the panels sit in one block with
   no gap (.product-panels), so the heading's own height is the only space
   around a title: every one sits midway between its two lines. */
.product-care {
  border-top: 1px solid var(--border);
}

.product-care__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  min-height: var(--control-h);
  padding-block: var(--sp-2);
  cursor: pointer;
  list-style: none;
}

/* A real heading, so these sections show up in the document outline the way
   "You may also like" does. The display face, the weight, the uppercase and
   the leading all come from the h1-h4 rule in base.css; what is set here is
   what a section title needs on top of that, and the margin a browser gives
   every heading by default. */
.product-care__title {
  margin: 0;
  font-size: var(--fs-13);
  letter-spacing: var(--ls-6);
}

/* Safari draws its own triangle unless this is said in its own dialect. */
.product-care__head::-webkit-details-marker {
  display: none;
}

/* A plus that becomes a minus, drawn from two gradients rather than from a
   glyph so it cannot shift with the font. */
.product-care__head::after {
  content: "";
  flex: none;
  width: 11px;
  height: 11px;
  background:
    linear-gradient(var(--text-muted), var(--text-muted)) center/100% 1px no-repeat,
    linear-gradient(var(--text-muted), var(--text-muted)) center/1px 100% no-repeat;
}

/* Open: the upright stroke goes and the flat one is left. It used to rotate as
   well, which turned that flat stroke back upright -- an open panel drew a
   vertical bar instead of a minus. */
.product-care[open] .product-care__head::after {
  background: linear-gradient(var(--text-muted), var(--text-muted)) center/100% 1px no-repeat;
}

/* No inner rule: with the section already ruled top and bottom, a third line
   between the heading and its contents is one too many. */
/* One size for everything inside a panel, and smaller than the description
   above. Set nowhere, the body inherited the page's 16px, so Materials & Care
   read at the same size as the description while the size guide beside it ran
   at 11 to 13 -- two panels with the same heading and nothing else in common. */
.product-care__body {
  padding-bottom: var(--sp-4);
  font-size: var(--fs-14);
  color: var(--text-muted);
}

.product-care__row + .product-care__row {
  margin-top: var(--sp-4);
}

/* Scoped to a care row, not to everything in a panel body. Left broad, it also
   caught the size guide's legend and note -- and beat them, since an element
   selector outweighs their class -- so their own margins were quietly ignored
   and the legend needed !important to get 5px back. */
.product-care__row p {
  margin-block: 0;
}

.product-care__row p + p {
  margin-block-start: var(--sp-3);
}

/* The full description, in the first panel. Paragraphs spaced like the care
   rows, at the same reading measure as the short description above. */
.product-description__body p {
  max-width: 58ch;
  margin-block: 0;
}

.product-description__body p + p {
  margin-block-start: var(--sp-3);
}

/* The guide reuses the care panel's shell, so two sections that open cannot
   look like two different kinds of thing. Only its contents are its own. */
.product-size-guide__body {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
}

.product-size-guide__diagram {
  flex: 0 0 120px;
}

.product-size-guide__diagram img {
  display: block;
  width: 100%;
  height: auto;
}

.product-size-guide__figures {
  flex: 1;
  min-width: 0;
}

/* Scrolls rather than squashing: a guide covering seven sizes is wider than a
   phone, and a squashed column of numbers is unreadable. */
.product-size-guide__table {
  display: block;
  width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

.product-size-guide__table th,
.product-size-guide__table td {
  padding: var(--sp-2) var(--sp-3);
  text-align: center;
  white-space: nowrap;
}

/* Lines between the columns and between the rows, and none around the outside.
   Written as adjacent siblings so the edges never get one to take back off. */
.product-size-guide__table th + th,
.product-size-guide__table th + td,
.product-size-guide__table td + td,
.product-size-guide__table td + th {
  border-left: 1px solid var(--border);
}

.product-size-guide__table tbody th,
.product-size-guide__table tbody td {
  border-top: 1px solid var(--border);
}

.product-size-guide__table th {
  font-family: var(--font-display);
  line-height: var(--lh-snug);
  font-weight: 600;
  letter-spacing: var(--ls-2);
  text-transform: uppercase;
  color: var(--text);
}

/* The same blue the L and A are drawn in on the diagram beside it, so the
   letters in the legend and the letters on the garment read as the same thing.
   That is the whole reason this colour is not the muted grey. */
.product-size-guide__legend {
  display: flex;
  flex-direction: column;
  margin-top: 5px;
  /* A paragraph carries a browser default underneath it; this is what takes
     it off. */
  margin-bottom: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-11);
  /* Snug rather than the body's leading: two lines that belong together, and
     at this size the default left them looking like separate notes. */
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-2);
  text-transform: uppercase;
  color: var(--ib-measure);
}

.product-size-guide__note {
  margin-block: var(--sp-3) 0;
  line-height: var(--lh-heading);
  color: var(--text-muted);
}

@media (max-width: 480px) {
  .product-size-guide__body {
    flex-direction: column;
  }

  /* A width, because flex: none on its own falls back to the file's natural
     size -- the diagram came out at 300px on a phone against 120px on a
     desktop, bigger on the smaller screen. */
  .product-size-guide__diagram {
    flex: none;
    width: 140px;
    align-self: center;
  }
}

/* ---- Variation controls --------------------------------------------------
   The chips are what a shopper touches; the select beside each row is what is
   submitted, and what WooCommerce reads. The <noscript> block in the template
   swaps their visibility, so with no script the dropdowns are the control. */
/* Laid out like the swatch block above it -- label, then the options, one gap
   between. It used to carry a bottom margin as well, which doubled up with the
   form's own gap once the form became a flex column. */
.product-variations__row {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

/* One option is not a choice: the value is still submitted, but nothing is
   drawn for it. Hiding the row rather than the control means the <noscript>
   swap below cannot bring it back. */
.product-variations__row--fixed {
  display: none;
}

.product-variations__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.product-variations__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--control-h);
  /* So M and L are not half the width of 3XL. */
  min-width: var(--control-h);
  justify-content: center;
  padding-inline: 1rem;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-display);
  line-height: var(--lh-snug);
  font-weight: 600;
  font-size: var(--fs-13);
  letter-spacing: var(--ls-3);
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color var(--t), background-color var(--t), color var(--t);
}

.product-variations__chip:hover {
  border-color: var(--heading);
}

.product-variations__chip.is-on {
  background-color: var(--heading);
  border-color: var(--heading);
  color: var(--bg);
}

/* Struck through rather than merely dimmed: a combination that does not exist
   should read as ruled out, not as low contrast. */
.product-variations__chip.is-unavailable {
  opacity: .4;
  cursor: not-allowed;
  text-decoration: line-through;
}

.product-variations__dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--swatch, var(--surface-2));
  box-shadow: 0 0 0 1px var(--border);
}

/* Visually hidden, never removed -- WooCommerce's script reads these. */
.product-variations__native {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* An attribute with no chips built for it falls back to its dropdown. */
.product-variations__native.is-shown {
  position: static;
  width: auto;
  height: auto;
  clip: auto;
  clip-path: none;
  overflow: visible;
  white-space: normal;
}

/* `background-color`, not the `background` shorthand: the shorthand resets
   background-image and would erase the arrow base.css draws on a select. */
.product-variations__native select {
  padding-block: .55rem;
  padding-left: .9rem;
  border: 1px solid var(--border);
  background-color: var(--surface);
  font-family: inherit;
  font-size: var(--fs-field);
  color: var(--text);
}

/* ---- Add to cart ---------------------------------------------------------- */
/* A row, so the button fills it. A row is the default direction, so nothing
   here has to be turned back on below. */
.product-page form.cart,
.product-page .woocommerce-variation-add-to-cart {
  display: flex;
  align-items: stretch;
  gap: var(--sp-3);
}

/* The quantity field is hidden on a product page, but WooCommerce still prints
   the div around it -- and an empty flex item still takes a gap, which would
   indent the button by it. A hidden form control submits its value whether or
   not its wrapper is drawn, so the 1 still posts. */
.product-page form.cart .quantity {
  display: none;
}

/* Except on a variable product, where the form also holds the attribute rows
   and those stack above the buy controls -- which are a row of their own. */
.product-page form.cart.variations_form {
  flex-direction: column;
  gap: var(--sp-4);
}

.product-page form.cart button[type="submit"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 1 1 auto;
  min-height: var(--control-h);
  padding: .8rem 1.75rem;
  border: 1px solid var(--heading);
  background: var(--heading);
  color: var(--bg);
  font-family: var(--font-display);
  line-height: var(--lh-snug);
  font-weight: 600;
  font-size: var(--fs-14);
  letter-spacing: var(--ls-4);
  text-transform: uppercase;
  text-shadow: none;
  cursor: pointer;
  transition: background-color var(--t), border-color var(--t), color var(--t);
}

/* Deepens rather than emptying out. Going to the page colour on hover read as
   the button switching off at the moment of the click. */
.product-page form.cart button[type="submit"]:hover,
.product-page form.cart button[type="submit"]:focus-visible {
  background-color: var(--btn-hover);
  border-color: var(--btn-hover-border);
  color: var(--btn-hover-fg);
}

/* A variable product's button ships disabled until a variation is chosen, and
   WooCommerce repaints the disabled state its own purple through
   `button.button.alt:disabled[disabled]` -- (0,4,1). Setting only opacity here
   left that background showing, which is why the purple came back on variable
   products and nowhere else. The fill is restated for every disabled form the
   button can take, hover included, since WooCommerce styles those too. */
.product-page form.cart button[type="submit"]:disabled,
.product-page form.cart button[type="submit"][disabled],
.product-page form.cart button[type="submit"].disabled,
.product-page form.cart button[type="submit"]:disabled:hover,
.product-page form.cart button[type="submit"][disabled]:hover,
.product-page form.cart button[type="submit"].disabled:hover {
  background-color: var(--heading);
  border-color: var(--heading);
  color: var(--bg);
  opacity: .45;
  cursor: not-allowed;
}

/* Never drawn here: product.js puts the chosen size's price in the summary's
   own price line instead, so the page never shows two prices. */
.product-page .woocommerce-variation-price {
  display: none;
}

.product-page .woocommerce-variation-availability {
  flex: 1 1 100%;
  font-size: var(--fs-14);
  color: var(--text-muted);
}

.product-page .stock.out-of-stock {
  color: var(--text-muted);
  font-size: var(--fs-14);
}

/* ---- Related --------------------------------------------------------------- */
.product-related {
  padding-block: clamp(3rem, 6vw, 5rem) 0;
}
