/* =============================================================================
   93 · COHERENCE — the pass that makes one site out of eight templates
   -----------------------------------------------------------------------------
   Loads LAST, after every other part file, so it carries its own media queries.
   It adds no new language. Every value in here is lifted from something the
   home page or the states pass already ships; the job is only to stop the same
   object speaking two dialects on two templates.

   WHAT WAS INCOHERENT (each fix is numbered to match)

   1  THE PRODUCT CARD WAS TYPOGRAPHICALLY INVERTED between the shop grid and
      the home carousel. Shop: name Fraunces / price Inter / a round action on
      the ledger's right. Home: name INTER / price FRAUNCES / the ledger's right
      half empty. The Bible's rule — "Product names are set in Fraunces, not
      Inter" — and the home's OWN featured card (Fraunces 300) both side with
      the shop. So the carousel comes across.

   2  THE RELATED SHELF on a PDP inherited `.products-grid`'s bare 5-column
      default while WooCommerce only ever returns four related products. One
      empty 250x430 cell, every product page, top right. Its header was a bare
      bold "Related Products" with no kicker and no counterweight.

   3  THE SHOP'S EMPTY STATE was never redesigned with the others: a 3rem colour
      EMOJI and a 24px semibold h3, sitting one click away from the search empty
      state, which is fully composed. It now speaks the states language.

   4  THE CART BODY sat on its own left axis — 202px, because the block adds a
      100.8px inner padding on top of the container's 101px — and set the
      product name in accent green Inter. Every other page hangs off 101px, and
      no other product name on the site is green.

   5  THE BLOG ARCHIVE's kicker line ran half uppercase-tracked ("TEA") and half
      mixed-case untracked ("June 23, 2026 · matthew"), and its "Read More" was
      16px body copy where the home's equivalent is a 0.72rem tracked rule-link.
      Three posts in a two-up grid also left one dead cell.

   6  THE PDP spoke in weights nothing else uses — Fraunces 800 for the title,
      Inter 800 at 32px in ACCENT GREEN for the price, a green mixed-case tab
      row — which put four accents on the one view the Bible allows two.
   ========================================================================== */


/* =============================================================================
   1 · ONE PRODUCT CARD, EVERYWHERE
   -----------------------------------------------------------------------------
   Values copied verbatim from 50-product-card.css so the two objects are the
   same object. The shop grid, search, the wishlist, the brand and tag archives
   and the PDP's related shelf already render `.product-card`; only the home
   carousel had its own. It now matches it line for line.
   ========================================================================== */

/* --- the name: the display voice, not the body voice --------------------- */
.products-section .product-carousel-name {
  font-family: var(--font-heading);
  font-optical-sizing: auto;
  font-size: clamp(1.06rem, 1.15vw, 1.22rem);
  font-weight: 400;
  line-height: 1.24;
  letter-spacing: -0.021em;
  color: var(--color-dark);
  /* em, never rem: the size clamps at the breakpoints below and a rem floor
     would leak a clipped third line out from under the two-line clamp. */
  min-height: calc(2 * 1.24em);
}

/* --- the ledger: hairline, price left, one round action right ------------- */
.products-section .product-carousel-price {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  flex-wrap: nowrap;
  gap: var(--space-2);
  min-width: 0;
  padding-top: clamp(11px, 1.15vw, 15px);
}

.products-section .product-carousel-price .price-current {
  font-family: var(--font-primary);
  font-size: 0.8rem;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-light);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 0.4s var(--pc-ease);
}

.products-section .product-carousel-price .price-original {
  flex: none;
  font-family: var(--font-primary);
  font-size: 0.74rem;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
  text-decoration: line-through;
}

/* The disc the shop card carries and the carousel card did not, which is why
   every ledger on the home page ended in a dead right half. It is decoration
   here — the whole plate is already one link — so it is not focusable and
   takes no pointer events; it exists to close the line. */
.products-section .product-carousel-price::after {
  content: "\2192";
  flex: none;
  margin-left: auto;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  background: transparent;
  font-family: var(--font-primary);
  font-size: 0.85rem;
  line-height: 1;
  letter-spacing: 0;
  color: var(--color-text-light);
  pointer-events: none;
  transition:
    color 0.3s var(--pc-ease),
    border-color 0.3s var(--pc-ease),
    background-color 0.3s var(--pc-ease);
}

.products-section .product-carousel-card:hover .product-carousel-price::after,
.products-section .product-carousel-card:focus-visible .product-carousel-price::after {
  border-color: var(--paper);
  background: var(--paper);
  color: var(--night);
}

/* The carousel tinted the whole name accent on hover. The shop card holds the
   name and lets the ledger carry the state, so this one does too. */
.products-section .product-carousel-card:hover .product-carousel-name {
  color: var(--color-dark);
}
.products-section .product-carousel-card:hover .product-carousel-price .price-current {
  color: var(--color-dark);
}


/* =============================================================================
   2 · THE RELATED SHELF — four cards, four columns, one section header
   ========================================================================== */

/* Four related products in a five-column track is the "dead pocket at bottom
   right" the Bible names three times. WooCommerce returns four; the track is
   four. The whole ladder is restated because this file loads after main.css's
   breakpoints and would otherwise pin every width to the desktop count. */
.single-product-page .sp-related .products-grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 28px;
}

.single-product-page .sp-related .section-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: clamp(20px, 3vw, 60px);
  padding-bottom: clamp(16px, 1.6vw, 22px);
  margin-bottom: clamp(30px, 3.2vw, 44px);
  border-bottom: 1px solid var(--color-border);
}

/* The three-part lockup every other section on the site opens with: kicker
   with its 34x1 rule, display line, and something holding the right half. */
.single-product-page .sp-related .section-header::before {
  content: "ALSO ON THIS SHELF";
  grid-column: 1;
  grid-row: 1;
  margin-bottom: clamp(14px, 1.4vw, 20px);
  font-family: var(--font-primary);
  font-size: 0.7rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  /* The signature 34x1px rule, drawn as a background so the header needs no
     extra element it does not have. */
  padding-left: 50px;
  background-image: linear-gradient(var(--color-border-strong), var(--color-border-strong));
  background-repeat: no-repeat;
  background-size: 34px 1px;
  background-position: 0 0.62em;
}

.single-product-page .sp-related .section-header::after {
  content: "FOUR PIECES";
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  margin-bottom: clamp(14px, 1.4vw, 20px);
  font-family: var(--font-primary);
  font-size: 0.7rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-text-muted);
}

.single-product-page .sp-related .section-title {
  grid-column: 1 / -1;
  grid-row: 2;
  margin: 0;
  font-family: var(--font-heading);
  font-optical-sizing: auto;
  font-weight: 300;
  font-size: clamp(1.9rem, 2.5vw, 2.7rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
  color: var(--ink);
  text-wrap: balance;
}


/* =============================================================================
   3 · THE SHOP'S EMPTY STATE — the same voice as the search's
   -----------------------------------------------------------------------------
   archive-product.php writes this block with inline styles, so the emoji and
   its margin can only be reached with `!important`. Nothing else in this file
   needs it.
   ========================================================================== */

.shop-page .shop-empty {
  max-width: 46rem;
  padding: clamp(48px, 6vw, 96px) 0 clamp(56px, 7vw, 112px);
  text-align: left;
}

/* The 3rem colour emoji: the only piece of full-colour raster UI on a site
   built entirely of hairlines. Its glyph is zeroed and the node becomes the
   kicker the other states open with. */
.shop-page .shop-empty > div:first-child {
  font-size: 0 !important;
  line-height: 0 !important;
  margin: 0 0 clamp(18px, 1.9vw, 28px) !important;
  display: flex;
  align-items: center;
  gap: 16px;
}

.shop-page .shop-empty > div:first-child::before {
  content: "";
  flex: 0 0 auto;
  display: block;
  width: 34px;
  height: 1px;
  background: var(--color-accent);
}

.shop-page .shop-empty > div:first-child::after {
  content: "NOTHING ON THIS SHELF";
  font-family: var(--font-primary);
  font-size: 0.7rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.shop-page .shop-empty h3 {
  margin: 0 0 clamp(20px, 2.1vw, 32px);
  font-family: var(--font-heading);
  font-optical-sizing: auto;
  font-weight: 300;
  font-size: clamp(2.5rem, 4.4vw, 4.1rem);
  line-height: 0.94;
  letter-spacing: -0.045em;
  color: var(--ink);
  text-wrap: balance;
  max-width: 16ch;
}

.shop-page .shop-empty p {
  margin: 0 0 clamp(28px, 3vw, 44px);
  max-width: 46ch;
  font-size: clamp(0.95rem, 0.4vw + 0.86rem, 1.06rem);
  line-height: 1.68;
  color: var(--color-text-light);
}

.shop-page .shop-empty .btn {
  margin-top: 0 !important;
}


/* =============================================================================
   4 · THE CART — back onto the page's left axis, product name in Fraunces
   ========================================================================== */

/* .wp-block-woocommerce-cart already sits inside the container, then adds
   100.8px of its own horizontal padding on top of it, which put the one table
   a customer reads most 101px right of every other element on the site. */
.woocommerce-cart .wp-block-woocommerce-cart,
.woocommerce-cart .wp-block-woocommerce-cart.alignwide {
  padding-left: 0;
  padding-right: 0;
  max-width: none;
}

/* No other product name on the site is green, and none is set in the body
   sans. This is the same object as the card title.

   The chain is long on purpose. The cart page is a normal WP page, so the
   block lands inside `.page-article .post-content`, and main.css paints every
   prose link there `--color-accent` at weight 500 through a (0,5,1) selector.
   That rule is right for a paragraph link and wrong for a line item, so this
   one matches its weight exactly and wins on order. */
.woocommerce-cart .post-content .wc-block-cart-items .wc-block-cart-item__wrap a.wc-block-components-product-name,
.woocommerce-cart .wc-block-cart .wc-block-cart-items .wc-block-cart-item__wrap a.wc-block-components-product-name {
  font-family: var(--font-heading);
  font-optical-sizing: auto;
  font-size: 1.02rem;
  font-weight: 400;
  line-height: 1.24;
  letter-spacing: -0.021em;
  color: var(--color-dark);
  text-decoration: none;
  transition: color 0.35s ease;
}
.woocommerce-cart .post-content .wc-block-cart-items .wc-block-cart-item__wrap a.wc-block-components-product-name:hover,
.woocommerce-cart .post-content .wc-block-cart-items .wc-block-cart-item__wrap a.wc-block-components-product-name:focus-visible {
  color: var(--color-accent);
}


/* =============================================================================
   5 · THE BLOG ARCHIVE — one kicker line, one "more" affordance, no dead cell
   ========================================================================== */

/* The category chip was already 0.7rem uppercase tracked; the date and author
   beside it were 12.5px sentence case with no tracking. One line, one voice. */
.blog-page .blog-card-meta {
  font-family: var(--font-primary);
  font-size: 0.7rem;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
}

/* Lifted from .products-head-link on the home page — the site's one "go on"
   affordance: 0.72rem, tracked, uppercase, seated on a hairline. */
.blog-page .blog-read-more {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: 8px;
  border-bottom: 1px solid var(--color-border-strong);
  font-family: var(--font-primary);
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-text-light);
  transition: color 0.4s ease, border-color 0.4s ease;
}
.blog-page .blog-read-more svg {
  width: 15px;
  height: 15px;
  transition: transform 0.45s cubic-bezier(0.2, 0.75, 0.2, 1);
}
.blog-page .blog-read-more:hover,
.blog-page .blog-read-more:focus-visible {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}
.blog-page .blog-read-more:hover svg,
.blog-page .blog-read-more:focus-visible svg { transform: translateX(6px); }


/* Three posts in a two-up grid left the second row half empty — a 452x490
   dead cell, which is the rejection the Bible quotes three times. An odd last
   card takes the whole row and turns on its side, so the field is resolved on
   all four edges instead of trailing off. */
@media (min-width: 769px) {
  .blog-page .blog-grid-full > .blog-card:last-child:nth-child(odd) {
    grid-column: 1 / -1;
    display: grid;
    /* Three children — plate, body, footer. The plate holds both rows of
       column one; the body and its "read more" stack in column two, so the
       link stays with the copy it belongs to. */
    grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr);
    grid-template-rows: 1fr auto;
    align-items: center;
    column-gap: clamp(28px, 3.4vw, 56px);
    row-gap: clamp(16px, 1.6vw, 24px);
  }
  .blog-page .blog-grid-full > .blog-card:last-child:nth-child(odd) .blog-card-image {
    grid-column: 1;
    grid-row: 1 / -1;
  }
  .blog-page .blog-grid-full > .blog-card:last-child:nth-child(odd) .blog-card-body {
    grid-column: 2;
    grid-row: 1;
  }
  .blog-page .blog-grid-full > .blog-card:last-child:nth-child(odd) .blog-card-footer {
    grid-column: 2;
    grid-row: 2;
    align-self: start;
  }
  /* The card's plate carries `aspect-ratio: 16/9`. Left alone, a min-height
     would drive the WIDTH off that ratio and blow the plate 155px past its
     grid column, straight over the copy. The ratio has to be released first. */
  .blog-page .blog-grid-full > .blog-card:last-child:nth-child(odd) .blog-card-image {
    aspect-ratio: auto;
    width: 100%;
    max-width: 100%;
    height: 100%;
    min-height: 320px;
    overflow: hidden;
  }
  .blog-page .blog-grid-full > .blog-card:last-child:nth-child(odd) .blog-card-image a,
  .blog-page .blog-grid-full > .blog-card:last-child:nth-child(odd) .blog-card-image img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  /* A card twice as wide earns one step up in the display line, the way the
     home journal's lead entry does. */
  .blog-page .blog-grid-full > .blog-card:last-child:nth-child(odd) .blog-card-title {
    font-size: clamp(1.45rem, 1.9vw, 1.95rem);
    line-height: 1.14;
    letter-spacing: -0.028em;
  }
  .blog-page .blog-grid-full > .blog-card:last-child:nth-child(odd) .blog-card-excerpt {
    max-width: 46ch;
  }
}


/* =============================================================================
   6 · THE PRODUCT PAGE — the same weights the rest of the site is set in
   -----------------------------------------------------------------------------
   Nothing here is restructured; only the voice. The PDP is still the one
   template with no page-header lockup — that needs markup, and it is flagged
   in the report rather than faked here.
   ========================================================================== */

/* Fraunces 800 is a weight nothing else on the site uses. The display voice is
   300 everywhere it matters: the hero, the outro, the page headers, the empty
   states, the home's own featured card. */
.single-product-page .product-title {
  font-family: var(--font-heading);
  font-optical-sizing: auto;
  font-weight: 300;
  font-size: clamp(2.1rem, 2.7vw, 3rem);
  line-height: 1.04;
  letter-spacing: -0.038em;
  color: var(--ink);
  text-wrap: balance;
  margin-bottom: clamp(14px, 1.4vw, 20px);
}

/* Inter 800 at 32px in accent green. It was the loudest object on the site and
   the only coloured price in the catalogue; the card ledgers, the cart totals
   and the order summary all set figures in the neutral. The accent on this
   view now belongs to "In Stock" alone. */
.single-product-page .product-price,
.single-product-page .product-price .price-current,
.single-product-page .product-price .woocommerce-Price-amount {
  font-family: var(--font-heading);
  font-optical-sizing: auto;
  font-weight: 300;
  font-size: clamp(1.9rem, 2.3vw, 2.5rem);
  line-height: 1.1;
  letter-spacing: -0.022em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.single-product-page .product-price .price-original,
.single-product-page .product-price del {
  font-family: var(--font-primary);
  font-size: 0.8rem;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
  text-decoration: line-through;
}

/* The tab row is chrome. It gets the chrome voice — the same one the home
   carousel's filter tabs and the shop toolbar are set in — instead of a second
   green. */
.single-product-page .product-tabs .tab-btn {
  font-family: var(--font-primary);
  font-size: 0.72rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: none;
  transition: color 0.4s ease, border-color 0.4s ease;
}
.single-product-page .product-tabs .tab-btn:hover {
  color: var(--color-text-light);
  background: none;
}
.single-product-page .product-tabs .tab-btn.active {
  color: var(--color-dark);
  background: none;
  border-bottom-color: var(--color-accent);
}

/* The one page that opens with no header lockup at least opens on the site's
   own HUD voice rather than 13px body copy. The bar is a SIBLING of
   .single-product-page, not a child of it — hence the separate scope. */
.sp-breadcrumb-bar .breadcrumb,
.single-product-page .breadcrumb {
  padding-top: clamp(26px, 2.6vw, 40px);
  font-family: var(--font-primary);
  font-size: 0.68rem;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.sp-breadcrumb-bar .breadcrumb a,
.single-product-page .breadcrumb a { color: var(--color-text-muted); }
.sp-breadcrumb-bar .breadcrumb a:hover,
.single-product-page .breadcrumb a:hover { color: var(--color-accent); }


/* =============================================================================
   NARROW WIDTHS
   Part files load after every media query in main.css, so each step has to be
   restated here or the desktop value would hold all the way down to 320px.
   ========================================================================== */

@media (max-width: 1200px) {
  .single-product-page .sp-related .products-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 24px;
  }
}

@media (max-width: 1024px) {
  .single-product-page .sp-related .products-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 768px) {
  .single-product-page .sp-related .products-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 20px;
  }
  .single-product-page .sp-related .section-header {
    grid-template-columns: minmax(0, 1fr);
  }
  .single-product-page .sp-related .section-header::after { display: none; }
  .single-product-page .sp-related .section-title { grid-row: 2; }
}

@media (max-width: 600px) {
  .products-section .product-carousel-price::after {
    width: 30px;
    height: 30px;
    font-size: 0.8rem;
  }
  .shop-page .shop-empty { padding-top: clamp(36px, 9vw, 56px); }
}

@media (max-width: 480px) {
  /* 36-carousel drops the name to 0.9rem here; the shop card holds 1.06rem at
     the same width in a narrower track, so the card keeps one size. */
  .products-section .product-carousel-name {
    font-size: 1.02rem;
    min-height: calc(2 * 1.24em);
  }
}

@media (prefers-reduced-motion: reduce) {
  .products-section .product-carousel-price::after,
  .blog-page .blog-read-more,
  .blog-page .blog-read-more svg { transition-duration: 0.01ms; }
}
