/* Hallmark · genre: editorial · design-system: design.md · designed-as-app
 * macrostructures: Bento Grid (home) · Portfolio Grid (archive) · Photographic
 *   (product) · Long Document (selection)
 * theme: custom (tuned) · vibe: "indigo dye, hand-blocked cotton, warm paper"
 * paper: oklch(96% 0.014 75) · accent: oklch(38% 0.125 264)
 * display: Fraunces 700 roman · body: Switzer 400
 * axes: light / roman-serif / cool · nav: N12 · footer: Ft6
 * enrichment: none · studied: no · context: user-provided
 *
 * Pre-emit critique: P5 H4 E4 S5 R4 V4
 * ==========================================================================
   Maruti Collection — brand layer

   Register: warm sand paper, warm near-black ink, ONE cool accent — indigo,
   the dye of Indian hand-block printing. The photography carries the page;
   this layer stays out of its way.

   Every colour and face resolves to tokens.css. Nothing here inlines a hex.

   Kept separate from style.css on purpose: style.css is functional (selection
   flow, price suppression, size charts), this file is purely visual, so the
   look can be iterated without touching behaviour.
   ========================================================================== */

/* ---- Bridge to tokens.css ----------------------------------------------
   The --m-* names are referenced ~400 times across this file and style.css.
   Rather than rewrite every call site in one pass (and risk missing one on a
   live catalogue), the names are kept and re-pointed at the token layer. Every
   value now resolves to a single OKLCH definition in tokens.css; nothing in
   this file inlines a hex or a font stack any more.

   Note the rule swap: the old --m-rule was the LIGHTER hairline (#DED5C6) and
   --m-rule-2 the darker (#CBBFAB). tokens.css names them the other way round,
   so they cross over here. Mapping them naively would have thickened every
   divider on the site.
   --------------------------------------------------------------------- */

:root {
  /* Surface */
  --m-paper:      var(--color-paper);
  --m-paper-2:    var(--color-paper-2);
  --m-paper-3:    var(--color-paper-3);
  --m-rule:       var(--color-rule-2);   /* lighter of the two — see above */
  --m-rule-2:     var(--color-rule);     /* darker */

  /* Ink */
  --m-ink:        var(--color-ink);
  --m-ink-2:      var(--color-ink-2);
  --m-muted:      var(--color-muted);

  /* The one accent — now indigo, the block-print dye */
  --m-accent:     var(--color-accent);
  --m-accent-deep:var(--color-accent-2);
  --m-accent-tint:var(--color-accent-tint);

  /* Type */
  --m-display: var(--font-display);
  --m-body:    var(--font-body);

  /* Rhythm */
  --m-gap:     clamp(0.75rem, 2vw, 1.5rem);
  --m-section: clamp(2.5rem, 6vw, 5rem);

  --m-ease: var(--ease-out);
}

/* ---- Base -------------------------------------------------------------- */

body {
  background: var(--color-paper);
  color: var(--color-ink-2);
  font-family: var(--font-body);
  /* Was 300. Marcellus/Jost tolerated a light body; Switzer 300 is genuinely
     thin at 16px, and the system needs a 300-unit gap to a 700 display. */
  font-weight: var(--weight-body);
  letter-spacing: 0;
}

/* Fraunces is variable. WONK 1 engages the alternate, slightly canted forms
   and SOFT 30 takes the hardest corners off — together they read as type that
   was cut by hand rather than interpolated, which is the same quality the
   block printing has. Roman only; italic headings are banned. */
h1, h2, h3, h4,
.site-title,
.ct-title {
  font-family: var(--font-display);
  color: var(--color-ink);
  font-weight: var(--weight-display);
  font-style: normal;
  font-variation-settings: "SOFT" 30, "WONK" 1;
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
  min-width: 0;
}

a { color: var(--m-ink); }
a:hover { color: var(--m-accent-deep); }

::selection { background: var(--m-accent-tint); color: var(--m-ink); }

*:focus-visible {
  outline: 2px solid var(--m-accent);
  outline-offset: 3px;
}

/* ---- Header: quiet, centred wordmark ---------------------------------- */

.site-title,
.site-title a {
  font-family: var(--m-display);
  font-weight: var(--weight-display);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--m-ink);
}

/* Blocksy paints the header on an UNCLASSED row div
   (header.ct-header > div[data-device] > div[data-row]), so a selector aimed
   at [class*="ct-header"] never reached it and the header stayed white above
   a sand page. Target the row itself. */
header [class*="ct-header"],
.ct-header,
.ct-header > [data-device] > [data-row] {
  background-color: var(--m-paper);
}
.ct-header > [data-device] > [data-row="middle"] {
  border-bottom: 1px solid var(--m-rule);
}

.menu-item > a,
.ct-menu-link {
  font-family: var(--m-body);
  font-size: 0.8125rem;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--m-ink-2);
}
.menu-item > a:hover,
.ct-menu-link:hover { color: var(--m-accent-deep); }

/* ---- Kill the empty page-title band ----------------------------------- */
/* Blocksy ships a tall hero strip containing only the page title. On a
   catalogue that is ~300px of nothing above the products. Replaced with a
   compact centred title. */

.hero-section {
  background: transparent !important;
  padding-block: var(--m-section) 0 !important;
  min-height: 0 !important;
}
/* Blocksy puts min-height:250px + 50px padding on .entry-header itself,
   which is where the empty band actually comes from. */
.hero-section .entry-header,
header.entry-header {
  min-height: 0 !important;
  padding-block: 0 !important;
  display: block !important;
}
.hero-section .page-title,
.entry-header .page-title {
  font-family: var(--m-display);
  font-size: clamp(1.75rem, 3.5vw, 2.75rem);
  font-weight: var(--weight-display);
  letter-spacing: 0.02em;
  text-align: center;
}
.hero-section .ct-breadcrumbs,
.ct-breadcrumbs {
  font-family: var(--m-body);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--m-muted);
}

/* ---- Shop toolbar: result count + sort -------------------------------- */

.woocommerce-result-count,
.woocommerce-ordering select {
  font-family: var(--m-body);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--m-muted);
}
.woocommerce-ordering select {
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--m-rule-2);
  border-radius: 0;
  padding: 0.4rem 1.5rem 0.4rem 0;
  color: var(--m-ink-2);
}

/* ---- The product grid ------------------------------------------------- */

ul.products {
  display: grid !important;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: calc(var(--m-gap) * 1.5) var(--m-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 48rem) {
  ul.products { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 90rem) {
  ul.products { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

ul.products li.product {
  margin: 0 !important;
  width: auto !important;
  float: none !important;
  padding: 0;
  text-align: left;
  background: transparent;
  border: 0;
}

/* Image: tall portrait, edge to edge. The card IS the photograph.
   Blocksy sets aspect-ratio: 4/3 on the img and owns the box via
   .ct-media-container, so the ratio has to be forced on the container
   and the img told to fill it. */
ul.products li.product .ct-media-container,
ul.products li.product figure {
  /* 2:3 is the NATIVE ratio of the photography (0.659-0.684).
     Forcing 3/4 cropped the feet off and jammed the head to the top edge.
     Exposed as a variable so the Portfolio Grid section below can vary it per
     tile — that variation is what stops the archive reading as a spreadsheet.
     The !important is Blocksy's fault: it sets aspect-ratio inline on the img. */
  aspect-ratio: var(--tile-ratio, 2 / 3) !important;
  display: block !important;
  overflow: hidden;
  border-radius: 2px;
  background: var(--m-paper-2);
  margin: 0;
}
ul.products li.product .ct-media-container img,
ul.products li.product figure img,
ul.products li.product img.wp-post-image {
  aspect-ratio: auto !important;
  width: 100% !important;
  height: 100% !important;
  object-fit: cover !important;
  object-position: center;
  display: block;
  transition: transform 700ms var(--m-ease), opacity 500ms var(--m-ease);
}
@media (hover: hover) and (pointer: fine) {
  ul.products li.product:hover img { transform: scale(1.02); }
}

/* Product title — sans, quiet, two lines maximum */
ul.products li.product .woocommerce-loop-product__title,
ul.products li.product h2,
ul.products li.product .ct-woo-card-title {
  font-family: var(--m-body) !important;
  font-size: 0.9375rem !important;
  font-weight: 400 !important;
  line-height: 1.45;
  letter-spacing: 0.01em;
  color: var(--m-ink) !important;
  margin: 0.875rem 0 0 !important;
  padding: 0 !important;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Category label above the title — very quiet */
ul.products li.product .ct-woo-card-extra,
ul.products li.product .posted_in {
  font-size: 0.625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--m-accent);
  order: -1;                 /* category reads ABOVE the title */
}
/* Card becomes a column so `order` can reposition the category label. */
ul.products li.product {
  display: flex;
  flex-direction: column;
}
ul.products li.product .ct-media-container,
ul.products li.product figure { order: -2; }

/* No filled button on the card — the references never do this. */
ul.products li.product .button,
ul.products li.product .added_to_cart {
  display: none !important;
}

/* ---- Size chips: what sits where price normally would -----------------
   These are now the control that builds a selection, not a read-only list, so
   they have to look pressable. The old treatment (plain text separated by
   hairline pipes) actively said "label" -- fine when it was decoration, wrong
   once tapping one adds a piece to the enquiry.

   Deliberately still quiet: an outline, not a filled button. Eleven cards each
   carrying four filled buttons would turn the grid into a control panel and
   bury the photography, which is the thing doing the selling. */

.maruti-card-sizes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3xs);
  margin-block-start: 0.5rem;
}

/* Visually hidden, but read out before the chips. */
.maruti-card-sizes__label {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* Selectors are deliberately .maruti-card-sizes .maruti-size rather than bare
   .maruti-size: Blocksy styles buttons globally with padding 0.9em/1.6em, which
   out-specifies a single class and inflated each chip to 61x43px -- a segmented
   control loud enough to compete with the photograph. */
.maruti-card-sizes .maruti-size {
  appearance: none;
  -webkit-appearance: none;
  font-family: var(--m-body);
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.08em;
  line-height: 1;
  color: var(--m-muted);
  background: transparent;
  border: 1px solid var(--m-rule);
  border-radius: 1px;
  box-shadow: none;
  /* 1.75rem == 28px: quiet on the card, still a comfortable thumb target
     given the 5px gap between chips. */
  min-width: 1.75rem;
  height: 1.75rem;
  min-height: 0;
  padding: 0 var(--space-2xs);
  margin: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  cursor: pointer;
  transition:
    border-color 220ms var(--m-ease),
    background-color 220ms var(--m-ease),
    color 220ms var(--m-ease);
}

@media (hover: hover) and (pointer: fine) {
  .maruti-card-sizes .maruti-size:hover {
    border-color: var(--m-ink-2);
    color: var(--m-ink);
    background: transparent;
  }
}

.maruti-card-sizes .maruti-size.is-selected {
  background: var(--m-accent);
  border-color: var(--m-accent);
  color: var(--color-accent-ink);
}
@media (hover: hover) and (pointer: fine) {
  .maruti-card-sizes .maruti-size.is-selected:hover {
    background: var(--m-accent-deep);
    border-color: var(--m-accent-deep);
    color: var(--color-accent-ink);
  }
}

.maruti-card-sizes .maruti-size.is-busy { opacity: 0.55; cursor: progress; }

/* A size with no matching variation: shown for completeness, not offered. */
.maruti-card-sizes .maruti-size.is-inert {
  border-style: dashed;
  color: var(--m-rule-2);
  cursor: default;
}

/* Confirmation, in the flow rather than as a toast. */
.maruti-card-sizes__hint {
  font-family: var(--m-body);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--m-accent-deep);
  margin-inline-start: 0.125rem;
}

@media (prefers-reduced-motion: reduce) {
  .maruti-card-sizes .maruti-size { transition: none; }
}

/* ---- Badges: hairline, never loud ------------------------------------- */

ul.products li.product .onsale,
.ct-woo-badge,
span.onsale {
  background: var(--m-paper) !important;
  color: var(--m-ink) !important;
  border: 1px solid var(--m-rule-2);
  border-radius: 0 !important;
  font-family: var(--m-body) !important;
  font-size: 0.5625rem !important;
  font-weight: 400 !important;
  letter-spacing: 0.16em !important;
  text-transform: uppercase;
  padding: var(--space-3xs) var(--space-2xs) !important;
  min-height: 0 !important;
  min-width: 0 !important;
  line-height: 1 !important;
}

/* ---- Buttons: outline first, one filled action ------------------------ */

/* The size chips are excluded here for the same reason the copy button is:
   this rule is the site's ONE button treatment (0.9rem/1.6rem padding, uppercase
   tracking) and it out-specifies a two-class selector, so anything that is a
   <button> but not a button-shaped control has to opt out by name. */
.button,
button:not(.maruti-copy-btn):not([data-maruti-copy]):not(.maruti-size),
input[type="submit"],
.ct-button {
  font-family: var(--m-body);
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-radius: 2px;
  padding: var(--space-sm) var(--space-md);
  transition: background-color 250ms var(--m-ease), color 250ms var(--m-ease),
              border-color 250ms var(--m-ease);
}

.single_add_to_cart_button,
.button.alt {
  background: var(--m-ink) !important;
  color: var(--m-paper) !important;
  border: 1px solid var(--m-ink) !important;
}
.single_add_to_cart_button:hover,
.button.alt:hover {
  background: var(--m-accent-deep) !important;
  border-color: var(--m-accent-deep) !important;
}

/* Secondary actions read as outlines */
.woocommerce-cart .button:not(.alt):not(.maruti-wa-btn) {
  background: transparent !important;
  color: var(--m-ink) !important;
  border: 1px solid var(--m-rule-2) !important;
}

/* ---- Single product -------------------------------------------------- */

.product_title {
  font-family: var(--m-display);
  font-size: clamp(1.5rem, 3vw, 2.25rem);
  font-weight: var(--weight-display);
  letter-spacing: 0.01em;
}
.woocommerce-product-details__short-description { color: var(--m-ink-2); }

table.variations th,
table.variations label {
  font-family: var(--m-body);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--m-muted);
  font-weight: 400;
}
table.variations select {
  border: 1px solid var(--m-rule-2);
  border-radius: 2px;
  background: var(--m-paper);
  padding: var(--space-xs) var(--space-sm);
  font-family: var(--m-body);
  color: var(--m-ink);
}
.stock.in-stock,
.stock.out-of-stock {
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--m-muted);
}
.stock.out-of-stock { color: var(--m-accent-deep); }

/* Tabs / meta quieter */
.woocommerce-tabs ul.tabs li a,
.product_meta {
  font-family: var(--m-body);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ---- Size chart in the brand register -------------------------------- */

.maruti-sizechart caption {
  font-family: var(--m-display);
  font-size: 1rem;
  font-weight: var(--weight-display);
  color: var(--m-ink);
}
.maruti-sizechart th,
.maruti-sizechart td { border-bottom-color: var(--m-rule); }
.maruti-sizechart thead th { color: var(--m-muted); letter-spacing: 0.12em; }

/* ---- Selection page + enquiry block ---------------------------------- */

.maruti-enquiry {
  background: var(--m-paper-2);
  border: 1px solid var(--m-rule);
  border-radius: 2px;
}
.maruti-enquiry__title {
  font-family: var(--m-display);
  font-size: 1.125rem;
  font-weight: var(--weight-display);
  color: var(--m-ink);
}
.maruti-enquiry__note { color: var(--m-muted); }
.maruti-copy-btn {
  font-family: var(--m-body);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-color: var(--m-rule-2);
  border-radius: 2px;
  color: var(--m-ink);
}

table.cart th {
  font-family: var(--m-body);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--m-muted);
  font-weight: 400;
  border-bottom: 1px solid var(--m-rule);
}
table.cart td { border-bottom: 1px solid var(--m-rule); }

/* ---- Footer ---------------------------------------------------------- */

footer [class*="ct-footer"],
.site-footer {
  background: var(--m-paper-2);
  border-top: 1px solid var(--m-rule);
}
.site-footer,
footer [class*="ct-footer"] {
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--m-muted);
}

/* ---- One reveal primitive, respecting reduced motion ----------------- */

@media (prefers-reduced-motion: no-preference) {
  ul.products li.product {
    opacity: 0;
    transform: translateY(12px);
    animation: m-rise 620ms var(--m-ease) forwards;
  }
  /* CSS-only stagger — no PHP needed to stamp an index. */
  ul.products li.product:nth-child(1)  { animation-delay:   0ms; }
  ul.products li.product:nth-child(2)  { animation-delay:  55ms; }
  ul.products li.product:nth-child(3)  { animation-delay: 110ms; }
  ul.products li.product:nth-child(4)  { animation-delay: 165ms; }
  ul.products li.product:nth-child(5)  { animation-delay: 220ms; }
  ul.products li.product:nth-child(6)  { animation-delay: 275ms; }
  ul.products li.product:nth-child(7)  { animation-delay: 330ms; }
  ul.products li.product:nth-child(8)  { animation-delay: 385ms; }
  ul.products li.product:nth-child(n+9){ animation-delay: 440ms; }
  @keyframes m-rise { to { opacity: 1; transform: none; } }
}

/* ---- Footer credit: not a WordPress advert -------------------------- */
.site-footer .ct-footer-copyright a[href*="blocksy"],
.site-footer [class*="copyright"] a[href*="creativethemes"] { display: none; }


/* ---- Chrome corrections ------------------------------------------------ */

/* Blocksy's active menu item falls back to a link blue (#1559ED), which is
   the only saturated blue on the site and reads as an unstyled default.
   Class selectors lose here: Blocksy's rule is
   nav[class*="menu"] li[class*="current-menu-"] > .ct-menu-link (0,3,2)
   against 0,2,1 for anything sensible written by hand. Rather than escalate
   into an !important war, set the CUSTOM PROPERTIES its rule already reads. */
:root {
  --theme-link-hover-color: var(--m-accent-deep);
  --theme-link-active-color: var(--m-accent-deep);
}

/* The copyright row carried a paper-2 fill that stopped short of the viewport
   edge, so it read as a stray bar under the footer rather than part of it. */
.ct-footer-copyright {
  background-color: transparent;
}
footer .ct-footer-copyright,
footer [data-row] {
  background-color: var(--m-paper);
}


/* ==========================================================================
   N12 · ANNOUNCEMENT BANNER + STICKY BAR

   The banner sits in normal flow at the very top and simply scrolls away; the
   Blocksy header below it is sticky and docks to the top once it has gone.
   That is the retract behaviour without a scroll listener and without
   animating a layout offset, which motion.md bans.

   Fill is tint+ink rather than the archetype default gradient — a gradient
   band would be the loudest thing on a page whose job is to show cloth.
   ========================================================================== */

.mn-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  padding: var(--space-2xs) var(--space-md);
  background: var(--color-accent-tint);
  color: var(--color-accent-2);
  border-bottom: var(--rule-hair) solid var(--color-rule);
}
.mn-banner[hidden] { display: none; }

.mn-banner__text {
  margin: 0;
  font-size: var(--text-sm);
  letter-spacing: 0.02em;
  text-align: center;
}
.mn-banner__link {
  color: var(--color-accent-2);
  text-decoration: underline;
  text-decoration-thickness: var(--rule-hair);
  text-underline-offset: 0.25em;
  white-space: nowrap;   /* clickable text never wraps to two lines */
}
.mn-banner__x {
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-input);
  color: var(--color-accent-2);
  font-size: var(--text-md);
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .mn-banner__x:hover { background: var(--color-paper-3); }
}

/*
   Dock the header once the banner has scrolled past.

   The ID is not decoration. Blocksy ships `#header { position: relative }`,
   and an ID out-specifies a bare class, so the earlier `.ct-header` version of
   this rule never reached the header at all -- the only element it did reach
   was `div#offcanvas.ct-panel.ct-header`, the mobile menu panel, which is
   display:none. The header scrolled away on every page while this rule sat
   here looking like it worked, and the comment above claimed otherwise.

   z-index 50 rather than 20 to match Blocksy's own value and the note in
   tokens.css, which sets --z-float: 100 specifically to clear a header at 50.

   The sticky element is #header itself, not the row inside it. A sticky row
   would be trapped in its containing block, and #header is 70px tall, so it
   would have nowhere to travel. #header's own containing block is
   #main-container, which spans the document.
*/
header#header.ct-header {
  position: sticky;
  top: 0;
  z-index: 50;
}

/* ==========================================================================
   THE PHONE NUMBER IN THE HEADER

   Spliced into Blocksy's end column by functions.php section 20. Because the
   header is sticky (rule above), the number rides down the whole page.

   Brass rather than ink: it is the last thing in the row, and --color-accent
   scores 8.82:1 on the ground, so colouring it costs no legibility. Tabular
   figures because a phone number set in proportional digits gets uneven gaps
   between its groups.

   ---- WHAT COMPETES FOR THE ROW, AND WHAT GIVES WAY -----------------------

   Blocksy's header row is a two-column CSS grid, not a flex line. The end
   column takes exactly the width its contents need and the wordmark column
   gets whatever is left, so adding anything to the end column takes room
   directly from the wordmark. When the wordmark runs short it wraps rather
   than overflows: "MARUTI COLLECTION" splits across two lines and the header
   doubles in height on every page of the site. That is the failure every
   threshold below was measured against, on the real markup.

   Three facts drive the numbers:

   1. Blocksy gives every header item `margin: var(--margin, 0 10px)`. Those
      20px were on their own enough to wrap the wordmark at 360px, so this
      link waives the margin and separates itself with its own padding.

   2. The wordmark needs 218px to stay on one line (208 plus the logo's own
      10px margin), and the end column needs the link plus 44 for the
      hamburger plus a 10px gap between them.

   3. Blocksy steps the wordmark from 17px to 25px at exactly 481px, where it
      becomes 313px and needs 323. So the row gets TIGHTER as the viewport
      crosses 481, and a ladder built only on "wider is roomier" breaks there.
      Hence the dip in the middle of the ladder, which is not an oversight.

   What gives way is the glyph, not the digits. The glyph is decoration; the
   number is the content, and the client asked for a number you can read. So
   the narrow rungs drop the phone glyph and keep the digits, which buys 44px
   of end column and moves the digits down from a 448px screen to a 400px one.

   ---- THE LADDER, WITH THE MEASUREMENT BEHIND EACH RUNG -------------------

   Row width available (viewport less Blocksy's percentage gutter), measured:
   317 at 360, 352 at 400, 422 at 480, 423 at 481, 458 at 520, 528 at 600.

     state                      end col   row needed   band
     glyph alone                    98          316    360-399
     digits, no glyph              126          344    400-479
     glyph alone (25px mark)        98          421    480-519
     digits, no glyph (25px)       126          449    520-599
     glyph + digits + "+91" (25px) 196          519    600 and up

   Below 360 not even the glyph fits, so the link is removed rather than
   allowed to wrap the wordmark: a two-line brand mark on every page is a
   worse outcome than no call button on a 320px screen, which still has the
   floating WhatsApp button. At 360 exactly the fit is 317 against 316, so a
   wordmark one pixel wider than this one would wrap there.

   Wherever the digits are hidden the aria-label still carries the whole
   number, so a screen reader loses nothing at any width.
   ========================================================================== */

.mn-tel {
  /* The base state is the smallest rung: glyph alone, 44x44. Every rule below
     is a grant made only once the row has been measured to afford it, so an
     untested width degrades to something that fits rather than to a wrapped
     wordmark. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3xs);
  min-width: 2.75rem;
  min-height: 2.75rem;
  border-radius: var(--radius-input);
  color: var(--color-accent);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 500;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  /* A number broken across two lines is not a number. */
  white-space: nowrap;
  transition: color var(--dur-micro) var(--ease-out);
}

/* Fact 1. The 20px this waives is what makes a 360px phone work at all. */
[data-device="mobile"] .mn-tel { --margin: 0; }

.mn-tel__icon {
  flex: none;
  width: 1.0625rem;
  height: 1.0625rem;
  fill: currentColor;
}

.mn-tel__num {
  /* Hidden by default, granted per band below. */
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  line-height: 1;
}

.mn-tel__cc { display: none; }

/*
   400-479 and 520-599: the digits, paid for by the glyph. Two ranges rather
   than one because of fact 3 -- between 480 and 519 the wordmark has already
   stepped up to 25px but the viewport has not yet grown enough to pay for it,
   so those widths fall through to the base rung.
*/
@media (min-width: 25rem) and (max-width: 29.9375rem),
       (min-width: 32.5rem) and (max-width: 37.4375rem) {
  .mn-tel {
    min-width: 0;
    justify-content: flex-start;
    padding-inline: var(--space-3xs);
  }
  .mn-tel__icon { display: none; }
  .mn-tel__num {
    position: static;
    width: auto; height: auto;
    overflow: visible;
    clip-path: none;
    font-size: var(--text-xs);
  }
}

/* 600 and up, desktop included: the whole thing at its proper size. */
@media (min-width: 37.5rem) {
  .mn-tel {
    min-width: 0;
    justify-content: flex-start;
    padding-inline: var(--space-2xs);
  }
  .mn-tel__icon { display: block; }
  .mn-tel__num {
    position: static;
    width: auto; height: auto;
    overflow: visible;
    clip-path: none;
    font-size: var(--text-sm);
  }
  .mn-tel__cc { display: inline; }
}

/* Below 360: gone, so the wordmark keeps its single line. */
@media (max-width: 22.4375rem) {
  [data-device="mobile"] .mn-tel { display: none; }
}

@media (hover: hover) and (pointer: fine) {
  .mn-tel:hover { color: var(--color-accent-2); }
}

.mn-tel:active { color: var(--color-accent-2); }

/* The global *:focus-visible rule supplies the ring; this stops it animating. */
.mn-tel:focus-visible { transition: none; }

/*
   Fallback position only. functions.php appends the link in a .mn-tel-loose
   wrapper if Blocksy's end column ever stops being findable; centring it keeps
   that state presentable rather than broken.
*/
.mn-tel-loose {
  display: flex;
  justify-content: center;
  padding-block: var(--space-3xs);
}


/* ==========================================================================
   ARCHIVE HEAD — Portfolio Grid
   A short tagline and a filter bar, then straight into the garments. No
   display type, no photograph behind text, no scrim.
   ========================================================================== */

.mn-head {
  /* Rendered at blocksy:content:top, which is outside Blocksy inner container,
     so it needs its own gutter or it bleeds to the viewport edge. */
  max-width: 84rem;
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 4vw, 3rem);
  padding-block: var(--space-xl) var(--space-lg);
  border-bottom: var(--rule-hair) solid var(--color-rule);
  /* No bottom margin: Blocksy .ct-container already contributes 60px of top
     padding below this, and the two stacked to a 118px dead band. */
  margin-bottom: 0;
}

/* Trim Blocksy container padding on the archives so the grid starts closer to
   the filter row. Scoped to these two templates only. */
.post-type-archive-product .ct-container,
.tax-product_cat .ct-container {
  padding-top: var(--space-lg);
}
.mn-head__line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
}
/* Set small on purpose. Portfolio Grid leads with a tagline, not a display
   heading — the photographs are the headline. */
.mn-head__title {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-md);
  letter-spacing: 0;
  margin: 0;
}
.mn-head__count {
  margin: 0;
  font-size: var(--text-sm);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-muted);
}

/* ==========================================================================
   PORTFOLIO GRID — the archive
   Mostly regular, with deliberate size variation. The lead piece spans two
   columns; ratios cycle so tile bottoms do not line up into a table edge.
   design.md caps this at one double-width tile per row.
   ========================================================================== */

/* The archive H1 now lives in .mn-head, so Blocksy own title strip is
   redundant here. Hidden only on these archives; pages keep theirs. */
.post-type-archive-product .hero-section,
.tax-product_cat .hero-section {
  display: none !important;
}

ul.products {
  /* Ragged bottoms are the point — labels sit at different heights. */
  align-items: start;
}

@media (min-width: 48rem) {
  /*
   * Ratio variation only — no column spans.
   *
   * A span-2 lead tile was tried and removed: two columns wide with a 4/3 box
   * put a 1.333 frame around a 0.667 photograph and cropped the model head
   * off, which is precisely the fault the image-size work existed to fix.
   * Portfolio Grid also calls for SUBTLE variation, and a double-width
   * double-height tile is not subtle.
   *
   * These three ratios all sit close to the native 0.667, so the widest of
   * them trims about 5% off the top and bottom of the background and nothing
   * else. The cycles are out of step with the 3- and 4-column rhythms, so no
   * two rows break in the same place.
   */
  ul.products li.product:nth-child(3n + 1) { --tile-ratio: 3 / 4; }   /* 0.750 */
  ul.products li.product:nth-child(4n + 3) { --tile-ratio: 5 / 7; }   /* 0.714 */
}


/* ==========================================================================
   Ft6 · LETTER CLOSE
   Set roman, not italic — italic display is banned. The letter quality comes
   from the wording and the signature line.
   ========================================================================== */

.mn-close {
  /* width:100% matters — the parent is a flex row, so without it the div
     shrink-wraps to the 54ch body and gets centred by the parent. */
  width: 100%;
  max-width: 84rem;
  margin-inline: auto;
  text-align: start;
  padding: var(--space-2xl) clamp(1.25rem, 4vw, 3rem) var(--space-lg);
  border-top: var(--rule-hair) solid var(--color-rule);
}
.mn-close__body { max-width: 54ch; }
.mn-close__body {
  font-size: var(--text-base);
  line-height: 1.65;
  color: var(--color-ink-2);
  margin: 0 0 var(--space-sm);
}
.mn-close__sign {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-style: normal;
  font-size: var(--text-md);
  color: var(--color-ink);
  margin: 0;
}


/* ==========================================================================
   ONE ORCHESTRATED ENTRANCE — front page
   The archive has its own single sequence (the product grid, above). This is
   the home page one. Nothing reveals on scroll; the page settles once.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  /* Bands rise once, in order. The frames inside a wall do NOT stagger
     individually — 22 frames each fading in on their own is the
     animate-everything tic, and it makes a dense page feel unsettled. */
  .mn > section {
    opacity: 0;
    transform: translateY(12px);
    animation: mn-rise var(--dur-long) var(--ease-out) forwards;
  }
  .mn > section:nth-child(1) { animation-delay:   0ms; }
  .mn > section:nth-child(2) { animation-delay:  90ms; }
  .mn > section:nth-child(3) { animation-delay: 180ms; }
  .mn > section:nth-child(4) { animation-delay: 270ms; }
  .mn > section:nth-child(n+5) { animation-delay: 360ms; }
  @keyframes mn-rise { to { opacity: 1; transform: none; } }
}

/* ---- Responsive floor ------------------------------------------------ */
/* clip, not hidden: overflow hidden on the root kills position:sticky. */
html, body { overflow-x: clip; }


/* ---- Wordmark at narrow widths ---------------------------------------
   At 320px the header wordmark wrapped to three lines. A two-word brand may
   take two lines; three reads as a layout fault. Step it down under 30rem. */
@media (max-width: 30rem) {
  .site-title,
  .site-title a {
    font-size: 1.0625rem;
    letter-spacing: 0.04em;
    line-height: 1.25;
  }
}


/* ==========================================================================
   INTERACTIVE STATES + REDUCED MOTION
   Slop-test gate 26 wants default / hover / focus-visible / active / disabled
   present in code for every control, and gate 27 wants every transform and
   animation to have a reduced-motion fallback. Collected here so the coverage
   is auditable in one place rather than scattered through the file.
   ========================================================================== */

/* ---- Active (pressed) ------------------------------------------------- */

/* The selector list used to end in a comma with no opening brace, which made
   this whole rule invalid — so the three controls below had no pressed state at
   all, silently, for as long as the file has existed. Found by counting braces
   while validating an unrelated change. */
.mn-btn:active,
.maruti-card-sizes .maruti-size:active,
.mn-banner__x:active {
  transform: translateY(1px);
}
.mn-link:active { color: var(--color-accent-2); }

/* ---- Disabled --------------------------------------------------------- */
/* Three channels, never opacity alone: dimmed, cursor, and the real
   attribute (or aria-disabled) so assistive tech agrees with the pixels. */

.mn-btn[aria-disabled="true"],
.mn-btn:disabled,
.maruti-card-sizes .maruti-size:disabled,
.mn-banner__x:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

/* ---- Focus ------------------------------------------------------------ */
/* Never transitioned — a keyboard user needs the ring on the first frame.
   The global *:focus-visible rule supplies the ring; this only guarantees
   nothing animates it. */

.mn-btn:focus-visible,
.mn-link:focus-visible,
.maruti-card-sizes .maruti-size:focus-visible,
.mn-banner__x:focus-visible,
.mn-tile:focus-visible,
.mn-frame:focus-visible {
  transition: none;
}

/* ---- Reduced motion --------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .mn-tile__media img,
  .mn-btn,
  .mn-link,
  .mn-banner__x,
  .maruti-card-sizes .maruti-size,
  ul.products li.product img {
    transition-duration: 150ms;
  }
  /* Kill spatial motion outright; opacity may still cross-fade. */
  .mn-tile:hover .mn-tile__media img,
  .mn-btn:active,
  .maruti-card-sizes .maruti-size:active,
  .mn-banner__x:active,
  ul.products li.product:hover img {
    transform: none;
  }
  .mn > section,
  ul.products li.product {
    animation: none;
    opacity: 1;
    transform: none;
  }
}


/* ==========================================================================
   FRONT PAGE — Photographic, full bleed, on the dark ground

   Bands of cloth alternating with narrow bands of type. Gutters are 2px, so
   the garments read as one continuous surface rather than as a grid of cards.
   The two previous passes lost on politeness: generous whitespace, tidy
   gutters, one feature image. Here the density is the argument.

   Nothing is centred over a photograph. All display type sits in its own dark
   band where it competes with nothing.
   ========================================================================== */

.mn {
  /* No container. front-page.php sits inside Blocksy <main> without an inner
     wrapper, so bands reach the viewport edge by default — which is what a
     full-bleed layout wants. Text bands supply their own inner measure. */
  background: var(--color-paper);
}

/* ---- The walls -------------------------------------------------------- */

.mn-wall {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--wall-gap);
  background: var(--color-paper);
}
@media (min-width: 48rem) {
  .mn-wall { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 75rem) {
  .mn-wall { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Three then six. Both divide the frame count exactly (front-page.php trims
   the list to a multiple of six), so neither breakpoint ends on a ragged row. */
.mn-wall--detail { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (min-width: 48rem) {
  .mn-wall--detail { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/*
 * The detail band crops SQUARE.
 *
 * It shows the same front-facing photograph as the wall above — that is the
 * only frame in each set known to face front — so the crop is what keeps it
 * from reading as the wall repeated. Square against the wall's full-length 2:3
 * is a different composition at a glance: closer, print-forward, six across
 * instead of four.
 *
 * object-position is inherited deliberately, NOT overridden to something lower
 * to "show more cloth". A square window over a 2:3 frame already shows two
 * thirds of the height; pushing it down the body puts the cut through the
 * model's face, and the rule here is that a crop eats the floor, never the
 * face.
 */
.mn-wall--detail .mn-frame__media { aspect-ratio: 1 / 1; }

.mn-frame {
  position: relative;
  display: block;
  text-decoration: none;
  color: var(--color-ink);
  background: var(--color-paper-2);
  min-width: 0;
}
.mn-frame__media {
  display: block;
  overflow: hidden;
  aspect-ratio: 2 / 3;
  background: var(--color-paper-2);
  border-radius: var(--radius-card);
}
.mn-frame__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Top-biased. These shoots frame head-to-ankle, so any crop must eat the
     floor, never the face. */
  object-position: center 12%;
  display: block;
  transition: opacity var(--dur-short) var(--ease-out);
}

/* Caption sits UNDER the frame on the ground, never over the photograph. */
.mn-frame__cap {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-2xs) var(--space-sm);
}
.mn-frame__name {
  font-size: var(--text-sm);
  line-height: 1.3;
  color: var(--color-ink-2);
}
.mn-frame__colour {
  flex: none;
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-accent);
}

/* Uncaptioned detail frames still need an accessible name. */
.mn-frame__sr {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

@media (hover: hover) and (pointer: fine) {
  /* One signal, not four. The frame lifts its neighbours out of the way by
     dimming itself slightly — no scale, no shadow, no border. */
  .mn-frame:hover .mn-frame__media img { opacity: 0.82; }
  .mn-frame:hover .mn-frame__name { color: var(--color-ink); }
}

/* ---- Band 2 · the statement ------------------------------------------- */

.mn-say {
  padding: var(--space-3xl) clamp(1.25rem, 5vw, 4rem) var(--space-2xl);
  border-block: var(--rule-hair) solid var(--color-rule-2);
}
.mn-say__inner {
  max-width: 84rem;
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-lg);
  align-items: end;
}
@media (min-width: 60rem) {
  .mn-say__inner { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: var(--space-2xl); }
}

/* 22 characters over two lines — comfortably inside the display bracket. */
.mn-say__mark {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-display);
  font-variation-settings: "SOFT" 30, "WONK" 1;
  line-height: 0.95;
  letter-spacing: -0.035em;
  color: var(--color-ink);
  margin: 0;
  overflow-wrap: anywhere;
  min-width: 0;
}
.mn-say__side { min-width: 0; }
.mn-say__body {
  max-width: 46ch;
  font-size: var(--text-md);
  line-height: 1.55;
  color: var(--color-ink-2);
  margin: 0 0 var(--space-lg);
}
.mn-say__act { margin: 0; }

/* ---- Band 4 · doors --------------------------------------------------- */

/*
 * One door per category, and the row has to fit the screen.
 *
 * Two columns over three categories was wrong twice over. Each door came out
 * 952px wide, so at 4/5 the photograph alone was 1189px and the whole door
 * 1279px — taller than the window, so you could never see one door at once,
 * let alone compare them. And three items in a two-column grid left the fourth
 * cell empty, which reads as a missing tile rather than a deliberate end.
 *
 * Three columns fixes both: 638px wide gives an 798px photograph, the band is
 * one row, and there is no hole. Measured at 1920x1080 the whole band now sits
 * inside the viewport.
 */
.mn-doors {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--wall-gap);
}
@media (min-width: 48rem) {
  .mn-doors { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* An odd last door spans the pair rather than leaving a gap beside it. */
  .mn-doors .mn-door:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/*
 * The column count follows the number of categories, so the row is always full.
 *
 * A fixed count cannot work here: the shop had three categories, then Kurta
 * Sets turned out to be a single kurti and was folded into Kurtis, leaving two.
 * A hardcoded three-column grid would have left an empty cell — the same hole
 * that made the old two-column grid look broken with three doors.
 *
 * :has() counts the children in CSS, so adding or retiring a category needs no
 * edit here. Capped at four: beyond that the doors get too narrow to read as
 * photographs, and a second row is the better answer.
 */
@media (min-width: 64rem) {
  /*
   * Widest a 4/5 door can be and still leave its caption on screen.
   * 14rem covers the caption plus breathing room above and below the band.
   */
  .mn-doors {
    --door-max: calc((100svh - 14rem) * 4 / 5);
    grid-template-columns: repeat(2, minmax(0, 1fr));
    /*
     * Bound the BAND, not the door.
     *
     * Capping the door's height instead looked like the obvious fix and was
     * wrong: aspect-ratio then shrank the door's WIDTH to keep 4/5, leaving
     * 242px of bare panel beside every photograph — the same failure as the
     * letterboxed product cover, in the other axis. Capping the door's width
     * while the grid track stayed wide would have done the same thing.
     *
     * Bounding the band shrinks the tracks themselves, so each photograph still
     * fills its door edge to edge and the 2px wall gap is preserved. On a short
     * window the band simply narrows and centres.
     */
    max-width: calc(var(--door-max) * 2 + var(--wall-gap));
    margin-inline: auto;
  }
  .mn-doors:has(.mn-door:nth-child(3)) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    max-width: calc(var(--door-max) * 3 + var(--wall-gap) * 2);
  }
  .mn-doors:has(.mn-door:nth-child(4)) {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    max-width: calc(var(--door-max) * 4 + var(--wall-gap) * 3);
  }
  /* Full rows above, so the odd-last span from the tablet rule must not apply. */
  .mn-doors .mn-door:last-child:nth-child(odd) { grid-column: auto; }
}
.mn-door {
  position: relative;
  display: block;
  text-decoration: none;
  color: var(--color-ink);
  background: var(--color-paper-2);
}
.mn-door__media {
  display: block;
  overflow: hidden;
  border-radius: var(--radius-card);
  /* Portrait, not landscape. The previous pass used 3/2 with a 22% offset and
     cut the model face off — a landscape box over a 2:3 source has to eat
     roughly half the frame height, and at 22% it takes it off the top. */
  aspect-ratio: 4 / 5;
  /*
   * No max-height here. It seems like the natural way to stop a door growing
   * taller than the window, but with aspect-ratio set it caps the height and
   * then shrinks the WIDTH to keep the ratio — so the photograph sat in a
   * 710px column inside a 952px door with bare panel beside it. The height is
   * bounded by .mn-doors' max-width instead, which shrinks the track so the
   * photograph keeps filling it.
   */
}
.mn-door__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 10%;
  display: block;
  transition: opacity var(--dur-short) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .mn-door:hover .mn-door__media img { opacity: 0.86; }
}
.mn-door__cap {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md) var(--space-lg);
}
.mn-door__name {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-lg);
  color: var(--color-ink);
}
.mn-door__count {
  flex: none;
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-muted);
}

/* ---- Band 5 · terms --------------------------------------------------- */
/* Padding here is deliberately unlike band 2 — sections that share one
   padding value read as a template. */

.mn-terms {
  padding: var(--space-2xl) clamp(1.25rem, 5vw, 4rem) var(--space-3xl);
  border-top: var(--rule-hair) solid var(--color-rule-2);
}
.mn-terms__inner {
  max-width: 84rem;
  margin-inline: auto;
}
.mn-terms__title {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-2xl);
  line-height: 1.08;
  letter-spacing: -0.025em;
  margin: 0 0 var(--space-sm);
  color: var(--color-ink);
}
.mn-terms__body {
  max-width: 58ch;
  font-size: var(--text-base);
  line-height: 1.65;
  color: var(--color-ink-2);
  margin: 0 0 var(--space-md);
}
.mn-terms__act { margin: 0; }

/* ---- CTA voice, per design.md ---------------------------------------- */
/* On the dark ground the primary fill is BRASS with ground-coloured text —
   an ink fill would disappear into the page. */

.mn-btn {
  display: inline-block;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent-ink);
  background: var(--color-accent);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-input);
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--dur-short) var(--ease-out);
}
.mn-btn:hover { background: var(--color-accent-2); color: var(--color-accent-ink); }

.mn-link {
  font-size: var(--text-sm);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-ink);
  text-decoration: none;
  border-bottom: var(--rule-hair) solid var(--color-rule);
  padding-bottom: 0.2rem;
  white-space: nowrap;
  transition: border-color var(--dur-short) var(--ease-out), color var(--dur-short) var(--ease-out);
}
/* Colour only. Thickening the border from 1px to 2px on hover reflowed
   everything below the link by a pixel, which is a twitch under the cursor for
   no gain — the colour change is already the whole signal. */
.mn-link:hover {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}


/* ==========================================================================
   THE ARCHIVE ON THE DARK GROUND
   Same density argument as the home walls: tight gutters, top-biased crops,
   captions on the ground rather than over the cloth.
   ========================================================================== */

ul.products {
  gap: var(--wall-gap) !important;
}
@media (min-width: 75rem) {
  ul.products { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Uniform 2:3 across the archive. The ratio cycle from the light pass is
   dropped: with 2px gutters the frames tile into a continuous surface, and
   ragged bottoms break that surface instead of enlivening it. */
ul.products li.product .ct-media-container,
ul.products li.product figure {
  --tile-ratio: 2 / 3;
  border-radius: var(--radius-card);
  background: var(--color-paper-2);
}
ul.products li.product:nth-child(3n + 1),
ul.products li.product:nth-child(4n + 3) {
  --tile-ratio: 2 / 3;
}

/* Crop eats the floor, never the face. */
ul.products li.product .ct-media-container img,
ul.products li.product figure img,
ul.products li.product img.wp-post-image {
  object-position: center 12% !important;
}

/* Card text sits inside the gutter rhythm. */
ul.products li.product .woocommerce-loop-product__title,
ul.products li.product h2,
ul.products li.product .ct-woo-card-title {
  margin: var(--space-2xs) var(--space-2xs) 0 !important;
  color: var(--color-ink) !important;
}
ul.products li.product .ct-woo-card-extra,
ul.products li.product .posted_in {
  padding-inline: var(--space-2xs);
  color: var(--color-accent);
}
.maruti-card-sizes { padding-inline: var(--space-2xs); }

/* On dark, the hover signal is a dim rather than a scale — a scaling tile
   inside a 2px gutter grid collides with its neighbours. */
@media (hover: hover) and (pointer: fine) {
  ul.products li.product:hover img { transform: none; opacity: 0.82; }
}

/* Archive head, on dark */
.mn-head {
  padding-inline: clamp(1.25rem, 5vw, 4rem);
  border-bottom-color: var(--color-rule-2);
}


/* ---- The brass tile that closes the wall ------------------------------
   Twelfth cell of a 12-cell wall. Carries the accent at the exact point the
   eye finishes the grid, and it is the only filled brass area on the page
   besides the button — well inside the 5% accent budget. */

.mn-frame--cta {
  background: var(--color-accent);
  border-radius: var(--radius-card);
  display: grid;
  place-items: center;
  aspect-ratio: 2 / 3;
  transition: background-color var(--dur-short) var(--ease-out);
}
.mn-frame__cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-md);
  text-align: center;
}
.mn-frame__ctaLine {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent-ink);
  line-height: 1.35;
}
.mn-frame__ctaArrow {
  font-size: var(--text-lg);
  line-height: 1;
  color: var(--color-accent-ink);
  transition: transform var(--dur-short) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .mn-frame--cta:hover { background: var(--color-accent-2); }
  .mn-frame--cta:hover .mn-frame__ctaArrow { transform: translateX(4px); }
}
@media (prefers-reduced-motion: reduce) {
  .mn-frame--cta:hover .mn-frame__ctaArrow { transform: none; }
  .mn-frame--cta, .mn-frame__ctaArrow { transition-duration: 150ms; }
}


/* ---- Archive: full bleed, and start the cloth higher ------------------
   The grid ran inside Blocksy container padding while the home walls reach the
   viewport edge, so the two pages disagreed. Zeroing the container inline
   padding on these two templates lets the archive tile edge to edge; the head
   and the toolbar carry their own gutter so the type stays off the edge. */

.post-type-archive-product .ct-container,
.tax-product_cat .ct-container {
  padding-top: var(--space-sm);
  padding-inline: 0;
  max-width: none;
}

.mn-head {
  padding-block: var(--space-lg) var(--space-md);
}

.post-type-archive-product .woocommerce-notices-wrapper,
.tax-product_cat .woocommerce-notices-wrapper,
.post-type-archive-product .woocommerce-result-count,
.tax-product_cat .woocommerce-result-count,
.post-type-archive-product .woocommerce-ordering,
.tax-product_cat .woocommerce-ordering {
  padding-inline: clamp(1.25rem, 5vw, 4rem);
}

/* The brass tile label is a centred block, not an inline button, so wrapping
   is not the broken-affordance failure gate 49 targets — but two lines in a
   small tile still reads loose. Tighten the tracking under 30rem. */
@media (max-width: 30rem) {
  .mn-frame__ctaLine { letter-spacing: 0.06em; font-size: var(--text-xs); }
  .mn-frame__cta { padding: var(--space-2xs); }
}


/* ==========================================================================
   PRODUCT PAGE — Photographic

   Two columns on desktop: a stack of every frame in the shoot on the left, a
   sticky panel of annotation on the right. One image fills the reading width at
   a time, so scrolling the page IS browsing the shoot.
   ========================================================================== */

/* Kill the empty band Blocksy puts above the breadcrumb. On this template it
   was ~250px of nothing before anything appeared. */
.single-product .hero-section {
  padding-block: var(--space-lg) 0 !important;
}
.single-product .ct-breadcrumbs {
  padding-inline: clamp(1.25rem, 5vw, 4rem);
}

/*
 * The grid goes on .product-entry-wrapper, NOT on div.product.
 *
 * Blocksy nests the gallery and the summary together inside that wrapper, so
 * div.product has only two children — the wrapper and the tabs. Gridding
 * div.product therefore put the whole wrapper in column one and the tabs in
 * column two, which stacked the summary underneath 9,600px of photographs and
 * left the tabs sitting where the product title should be.
 */
.single-product div.product {
  display: block;
}
.single-product .product-entry-wrapper {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-lg);
  align-items: start;
  /* Blocksy constrains this wrapper; the stack wants the full content width. */
  max-width: none;
  width: 100%;
}
@media (min-width: 60rem) {
  .single-product .product-entry-wrapper {
    /*
     * The gallery column is sized to its CONTENT (auto), not to a fraction.
     * Once the cover became height-driven it stopped filling a 1.55fr track,
     * which left ~200px of dead ground between the frame and the annotation.
     * auto lets the gallery take exactly rail + cover, and the annotation
     * absorbs the slack.
     */
    grid-template-columns: auto minmax(0, 1fr);
    gap: clamp(1.5rem, 4vw, 4rem);
  }
}
/* Tabs run full width beneath both columns. */
.single-product .woocommerce-tabs {
  margin-top: var(--space-2xl);
  padding-inline: clamp(1.25rem, 5vw, 4rem);
}

/* ---- The annotation panel -------------------------------------------- */

.single-product div.product .summary,
.single-product div.product .entry-summary {
  min-width: 0;
  padding-inline: clamp(1.25rem, 4vw, 2rem);
  padding-block: var(--space-md) var(--space-2xl);
}
@media (min-width: 60rem) {
  .single-product div.product .summary,
  .single-product div.product .entry-summary {
    position: sticky;
    top: var(--space-xl);
    padding-inline-end: clamp(1.25rem, 5vw, 4rem);
  }
}

.single-product .product_title {
  font-size: var(--text-2xl) !important;
  line-height: 1.08;
  letter-spacing: -0.025em;
  margin: 0 0 var(--space-2xs) !important;
}
.single-product .woocommerce-product-details__short-description {
  font-size: var(--text-base);
  line-height: 1.6;
  color: var(--color-ink-2);
  max-width: 46ch;
  margin: 0 0 var(--space-lg);
}

/* ---- Size picker ----------------------------------------------------- */

.mp-pick {
  border-top: var(--rule-hair) solid var(--color-rule);
  padding-top: var(--space-md);
}
.mp-pick__colour {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent);
}
/* The chips are bigger here than on a card — this is the decision point, and
   there is room for it. */
.mp-pick__sizes { gap: var(--space-2xs); }
.mp-pick__sizes .maruti-size {
  min-width: 2.75rem;
  height: 2.75rem;
  font-size: var(--text-sm);
}
.mp-pick__note {
  margin: var(--space-sm) 0 var(--space-lg);
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--color-muted);
  max-width: 40ch;
}
.mp-pick__act { margin: 0; }

/* ---- Meta, set as small print ---------------------------------------- */

.single-product .product_meta {
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: var(--rule-hair) solid var(--color-rule-2);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-muted);
}
.single-product .product_meta a { color: var(--color-ink-2); }
.single-product .product_meta a:hover { color: var(--color-accent); }

/* Related pieces reuse the wall treatment. */
.single-product .related > h2,
.single-product .up-sells > h2 {
  font-size: var(--text-lg);
  padding-inline: clamp(1.25rem, 5vw, 4rem);
  margin-bottom: var(--space-md);
}


/* ==========================================================================
   SELECTION PAGE — Long Document

   A numbered list of what was picked, then the message that will be sent.
   Cart furniture that does not apply to an enquiry is removed in functions.php
   (quantity stepper, update button) and the rest is set as a document.
   ========================================================================== */

.woocommerce-cart .hero-section {
  padding-block: var(--space-lg) 0 !important;
}
/* Left-aligned, in the measure. A centred display heading is banned by the
   system and reads wrong on a page that is essentially a letter. */
.woocommerce-cart .page-title,
.woocommerce-cart .entry-header .page-title {
  text-align: start !important;
  font-size: var(--text-2xl) !important;
  letter-spacing: -0.02em !important;
  max-width: 62ch;
  margin-inline: 0 !important;
}
.woocommerce-cart .hero-section .entry-header,
.woocommerce-cart .entry-header {
  text-align: start !important;
}

/* Table head carries no information once quantity is gone. */
.woocommerce-cart table.shop_table thead { display: none; }

.woocommerce-cart table.shop_table {
  border: 0;
  border-collapse: collapse;
  width: 100%;
  counter-reset: sel;
}
.woocommerce-cart table.shop_table td {
  border: 0;
  border-bottom: var(--rule-hair) solid var(--color-rule-2);
  padding: var(--space-md) var(--space-2xs);
  vertical-align: middle;
  background: transparent;
}

/* Number each line — this is a list someone is about to send. */
.woocommerce-cart table.shop_table tr.woocommerce-cart-form__cart-item td.product-thumbnail {
  position: relative;
  width: 6rem;
}
.woocommerce-cart tr.woocommerce-cart-form__cart-item td.product-thumbnail::before {
  counter-increment: sel;
  content: counter(sel);
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-sm);
  color: var(--color-accent);
}
.woocommerce-cart td.product-thumbnail img {
  width: 4.5rem;
  height: auto;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  object-position: center 12%;
  margin-inline-start: var(--space-md);
  display: block;
}
.woocommerce-cart td.product-name a {
  font-size: var(--text-base);
  color: var(--color-ink);
  text-decoration: none;
}
.woocommerce-cart td.product-name a:hover { color: var(--color-accent); }

.mc-size {
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent);
}

/* Remove-line button: quiet until hovered. */
.woocommerce-cart a.remove {
  color: var(--color-muted) !important;
  font-size: var(--text-md) !important;
  background: transparent !important;
  transition: color var(--dur-micro) var(--ease-out);
}
.woocommerce-cart a.remove:hover {
  color: var(--color-ink) !important;
  background: transparent !important;
}

/* Cart furniture that does not apply to an enquiry. */
.woocommerce-cart .actions .button[name="update_cart"],
.woocommerce-cart .actions .coupon,
.woocommerce-cart .cart-collaterals,
.woocommerce-cart .wc-proceed-to-checkout {
  display: none !important;
}
.woocommerce-cart td.actions { border-bottom: 0; padding: 0; }

/* ---- The enquiry block ----------------------------------------------- */

.maruti-enquiry {
  margin-top: var(--space-xl);
  padding: var(--space-lg);
  background: var(--color-paper-2);
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-card);
}
.maruti-enquiry__title {
  font-size: var(--text-lg) !important;
  margin: 0 0 var(--space-2xs) !important;
}
.maruti-enquiry p {
  color: var(--color-ink-2);
  max-width: 58ch;
}

/*
 * The send button is BRASS, not WhatsApp green.
 *
 * Green is the platform colour and instantly recognisable, which is a real
 * argument — but a saturated #25D366 slab is the loudest thing on a dark indigo
 * page and it broke the one-accent rule the whole system rests on. The glyph
 * and the label both say WhatsApp, so recognition is not carried by the fill.
 */
.maruti-wa-btn {
  background: var(--color-accent) !important;
  color: var(--color-accent-ink) !important;
  border: 0 !important;
  border-radius: var(--radius-input) !important;
  font-family: var(--font-body) !important;
  font-size: var(--text-sm) !important;
  letter-spacing: 0.1em !important;
  text-transform: uppercase !important;
  padding: var(--space-sm) var(--space-md) !important;
  white-space: nowrap;
}
.maruti-wa-btn:hover { background: var(--color-accent-2) !important; }
.maruti-wa-btn svg,
.maruti-wa-btn img { filter: none; }

.maruti-copy-btn {
  background: transparent !important;
  color: var(--color-ink-2) !important;
  border: var(--rule-hair) solid var(--color-rule) !important;
  border-radius: var(--radius-input) !important;
  font-size: var(--text-sm) !important;
  letter-spacing: 0.1em !important;
  text-transform: uppercase !important;
  padding: var(--space-sm) var(--space-md) !important;
  white-space: nowrap;
}
.maruti-copy-btn:hover {
  color: var(--color-ink) !important;
  border-color: var(--color-ink-2) !important;
}

/* Empty state: an invitation, not a dead end. */
.woocommerce-cart .cart-empty,
.woocommerce-cart .wc-empty-cart-message {
  font-size: var(--text-md);
  color: var(--color-ink-2);
  max-width: 52ch;
  padding-block: var(--space-lg);
}

/* ---- WooCommerce notices, on the dark ground -------------------------
 * The empty-selection message arrived as a light grey slab with light text
 * inside it — around 1.5:1, effectively unreadable. The rule above set the
 * colour and never the background, so Blocksy's default kept painting.
 *
 * Blocksy's woocommerce.min.css does this:
 *     :is(.woocommerce-info, …) { background: var(--background-color); }
 * and its global.css sets --background-color: #F0F1F3.
 *
 * So the fix is its custom property, not a specificity fight — the fourth time
 * on this build that the answer has been Blocksy's own variable rather than
 * !important. One extra element on the selector beats global.css without
 * needing to out-shout anything.
 */
body .woocommerce-info,
body .woocommerce-message,
body .woocommerce-thankyou-order-received {
  --background-color: var(--color-paper-2);
  --theme-text-color: var(--color-ink);
  border: var(--rule-hair) solid var(--color-rule-2);
}

/* ---- Blocksy's summary dividers --------------------------------------
 * Blocksy separates the product summary rows with <span class="ct-product-divider">
 * carrying border-top: 1px #E1E8ED — a light hairline, invisible in Blocksy's
 * own light theme and a bright scratch across this one.
 *
 * They also stack. Removing the price and add-to-cart rows from the summary
 * leaves their dividers behind, so two land adjacent and draw an empty banded
 * gap between the selection link and the SKU. Hide the duplicate rather than
 * the element: a single divider there is doing a real job.
 */
.ct-product-divider {
  border-top-color: var(--color-rule-2);
}
.ct-product-divider + .ct-product-divider {
  display: none;
}


/* ---- Footer, on the dark ground --------------------------------------
   The footer ELEMENT carries a light Blocksy default (#F2F5F7). The earlier
   rule only painted [data-row] descendants and .ct-footer-copyright, so the
   result was a dark bar sitting inside a light band — an inversion, not a fix.
   Paint the element, and let the rows inherit. */

footer.ct-footer,
footer[class*="ct-footer"],
.site-footer {
  background-color: var(--color-paper);
}
footer.ct-footer [data-row],
footer.ct-footer .ct-container,
.ct-footer-copyright {
  background-color: transparent;
}
.ct-footer-copyright,
.ct-footer-copyright a {
  color: var(--color-muted);
}
.ct-footer-copyright a:hover { color: var(--color-accent); }

/* ---- Trim the dead band above these two templates -------------------
   Blocksy reserves a title strip on every template. On the product and
   selection pages the H1 is supplied inside the content, so the strip is just
   empty ground — it was ~250px before anything appeared. */

.single-product .hero-section,
.woocommerce-cart .hero-section,
.single-product .entry-header,
.woocommerce-cart .entry-header {
  padding-block: 0 !important;
  min-height: 0 !important;
}
.single-product .ct-container,
.woocommerce-cart .ct-container {
  padding-top: var(--space-md);
}


/* ---- Footer + top gap, at the specificity Blocksy actually uses -------
   Blocksy paints the footer through an ID selector (#footer), which outranks
   any class selector, so footer.ct-footer never took. Matching the ID and
   loading later wins it. The container on these templates is
   .ct-container-full, not .ct-container — the earlier rule missed it. */

footer#footer,
#footer.ct-footer {
  background-color: var(--color-paper);
}
footer#footer [data-row],
footer#footer .ct-container,
footer#footer .ct-container-full,
footer#footer .ct-footer-copyright {
  background-color: transparent;
}
footer#footer .ct-footer-copyright,
footer#footer .ct-footer-copyright a {
  color: var(--color-muted);
}
footer#footer .ct-footer-copyright a:hover { color: var(--color-accent); }

.single-product .ct-container-full,
.woocommerce-cart .ct-container-full {
  padding-top: var(--space-md);
}




/* ==========================================================================
   PRODUCT GALLERY — cover + thumbnail rail

   Replaces the vertical stack of every frame. The stack showed the whole shoot
   but made the page a long scroll before anything else could be read; this puts
   the cover and the whole shoot in one screen, which is how a catalogue page
   works. Corners are softened per --radius-card.
   ========================================================================== */

/*
 * Vertical thumbnail rail beside the cover.
 *
 * A rail UNDER the cover was tried first and capped the cover to 72vh so both
 * would fit one screen. Two problems: the chrome above still pushed the rail
 * off the fold, and capping the height of a 2:3 frame shrinks its width to
 * ~380px in an 866px column, so the cover stopped being a cover. Putting the
 * rail alongside keeps the frame full size and both are visible at once, which
 * is how a catalogue page actually works.
 */
.mp-gallery {
  min-width: 0;
  display: grid;
  gap: var(--space-2xs);
  padding-inline: clamp(1.25rem, 4vw, 2.5rem);
  /* The thumbnail's fixed edge. Absolute on purpose — see .mp-gallery__btn. */
  --thumb-w: 4rem;
}
@media (min-width: 48rem) {
  .mp-gallery {
    /*
     * Wide enough for the thumbnail, its right-hand padding, and the scrollbar
     * gutter that is now permanently reserved. Sizing the column to the
     * thumbnail alone would push the reserved gutter into the thumbnail.
     */
    grid-template-columns: calc(var(--thumb-w) + 1.25rem) minmax(0, 1fr);
    gap: var(--space-2xs);
    align-items: start;
  }
  /* Rail first in the visual order, cover second. */
  .mp-gallery__thumbs { grid-column: 1; grid-row: 1; }
  .mp-gallery__cover  { grid-column: 2; grid-row: 1; }
}

/*
 * The frame takes the PHOTOGRAPH's ratio, not a hardcoded 2:3.
 *
 * Not every frame in this catalogue is 2:3 — a good number are 4:5 (1000x1250).
 * Forcing 2:3 on a 4:5 photograph left the image sitting in a box 132px taller
 * than itself, and because the box is painted --color-paper-2 the surplus read
 * as a slab of dead panel under the garment. Every product page for a 4:5 shoot
 * had it.
 *
 * maruti_pdp_gallery() writes the cover's real ratio into --cover-ar, and
 * js/gallery.js updates it on every swap, so the frame always fits its own
 * contents. The 2/3 fallback only applies if the attachment metadata is missing.
 *
 * Keeping aspect-ratio rather than dropping to height:auto matters: it reserves
 * the correct space before the image decodes, so the summary beside it does not
 * jump on load.
 */
.mp-gallery__cover {
  margin: 0;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--color-paper-2);
  aspect-ratio: var(--cover-ar, 2 / 3);
  /* Mobile: full column width, ratio sets the height. */
  width: 100%;
}

/*
 * The cover image had NO rule of its own, which is how the gap survived: an
 * unstyled img sizes itself from its own intrinsic ratio and ignores the box.
 * Filling the frame is now explicit. Because the frame carries the image's own
 * ratio, `cover` crops nothing — it just absorbs sub-pixel rounding that would
 * otherwise show as a hairline of panel along one edge.
 */
.mp-gallery__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/*
 * Desktop: drive the cover from HEIGHT, not width.
 *
 * width:100% on a 2:3 frame in an ~800px column makes the cover ~1200px tall,
 * so a full-length garment ran off the bottom of every screen and the customer
 * had to scroll to see the piece they clicked. Setting the height instead and
 * letting aspect-ratio derive the width means the whole frame fits the viewport
 * with nothing cropped and no letterboxing — the box itself gets narrower.
 *
 * 20rem is the chrome above it: announcement banner, header, breadcrumb and
 * padding. svh rather than vh so mobile browser toolbars do not push it under.
 */
@media (min-width: 48rem) {
  .mp-gallery {
    /* One height for the pair, so the rail can never tower over the cover. */
    /* 18rem covers the measured chrome above the frame (banner + header +
       breadcrumb + padding = 276px) with a little slack; 17rem overflowed by
       4px at 1366x768. The 26rem floor stops a short laptop shrinking the cover
       to a postage stamp — below that it is better to give up a little scroll
       than to stop showing the garment. */
    --cover-h: clamp(26rem, calc(100svh - 18rem), 52rem);
  }
  .mp-gallery__cover {
    height: var(--cover-h);
    width: auto;
    max-width: 100%;
    /* start, not center: centring left a gap between the rail and the frame
       once the cover became narrower than its column. */
    justify-self: start;
  }
}

/* ---- Thumbnail rail --------------------------------------------------- */

.mp-gallery__thumbs {
  list-style: none;
  margin: 0;
  padding: 0 0 var(--space-2xs);
  display: grid;
  /* Mobile: a horizontal rail under the cover. */
  grid-auto-flow: column;
  grid-auto-columns: 3.75rem;
  gap: var(--space-2xs);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
}
.mp-gallery__thumb { scroll-snap-align: start; min-width: 0; }

@media (min-width: 48rem) {
  .mp-gallery__thumbs {
    /* Desktop: a vertical rail, bounded to the cover height so an eleven-frame
       shoot scrolls inside its own box instead of running past the fold.
       max-height:100% did not work here — the grid row has no definite height
       for the percentage to resolve against. */
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-auto-rows: max-content;
    overflow-x: visible;
    overflow-y: auto;
    height: var(--cover-h);
    max-height: var(--cover-h);
    /*
     * NO scroll snapping on the vertical rail.
     *
     * `scroll-snap-type: y proximity` here made the rail visibly dance. One
     * wheel tick moved it 100px, then snap dragged it back a pixel at a time —
     * 99, 98, 97 … 89 over about 170ms — before lurching to the end and
     * chaining to the page. Traced with a scroll listener: eleven backwards
     * scroll events per tick.
     *
     * Snapping earns its keep on the mobile rail below, where a thumb swipes
     * the strip and wants to land on a frame. A mouse wheel over a 72px column
     * does not: the pointer is not the thing being aligned, so snap only fights
     * the gesture. Removed on desktop, kept on mobile.
     */
    scroll-snap-type: none;
    padding: 0 var(--space-2xs) 0 0;
    scrollbar-width: thin;
    /*
     * Reserve the scrollbar's width whether or not it is showing.
     *
     * Half of the fix below — see .mp-gallery__btn. Without this the rail's
     * inner width changes the moment a scrollbar appears, and anything sized
     * from that width changes with it.
     */
    scrollbar-gutter: stable;
  }
}

.mp-gallery__btn {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: var(--color-paper-2);
  border-radius: var(--radius-card);
  overflow: hidden;
  cursor: pointer;
  aspect-ratio: 2 / 3;
  /* Reserve the ring at rest so selecting a thumb does not shift the rail. */
  box-shadow: inset 0 0 0 2px transparent;
  transition:
    box-shadow var(--dur-short) var(--ease-out),
    opacity var(--dur-short) var(--ease-out);
  opacity: 0.6;
}
/*
 * Absolute fill rather than width/height 100%.
 *
 * Percentage sizing on the thumbnail image resolved against an indefinite box
 * inside the aspect-ratio button and came out 16x64 in a 64x96 cell — the
 * thumbnails rendered as slivers floating in dark boxes. Anchoring to the
 * button inset is unambiguous and does not depend on how the ratio resolves.
 */
.mp-gallery__btn { position: relative; }
.mp-gallery__btn img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 12%;
  display: block;
}
.mp-gallery__btn.is-on {
  opacity: 1;
  box-shadow: inset 0 0 0 2px var(--color-accent);
}
@media (hover: hover) and (pointer: fine) {
  .mp-gallery__btn:hover { opacity: 1; }
}
.mp-gallery__btn:active { transform: translateY(1px); }
.mp-gallery__btn:disabled { opacity: 0.35; cursor: not-allowed; }
.mp-gallery__btn:focus-visible { transition: none; }

/*
 * DESKTOP: the thumbnail is sized in absolute units, never from the rail.
 *
 * This is the fix for the rail flickering its thumbnails small/big/small/big
 * without ever settling. The cause was a genuine circular dependency:
 *
 *   thumb height  <- thumb width (aspect-ratio 2/3)
 *   thumb width   <- rail inner width (width: 100%)
 *   rail inner w. <- is a scrollbar showing?
 *   scrollbar     <- does the content overflow? i.e. total thumb height
 *
 * With seven frames at 64px the column needs 720px; with a scrollbar stealing
 * width it needs only 636px. So at any rail height between those two, showing
 * the scrollbar made the content fit (so hide it) and hiding it made the
 * content overflow (so show it). Bistable, and the browser flips every frame.
 *
 * Measured: thumb width alternating 54, 64, 54, 64 … on consecutive frames at
 * viewport heights of 940 to 1000px, and rock steady at 911 and 1080 — which is
 * exactly why it depends on how tall the window happens to be.
 *
 * Fixing the width alone (scrollbar-gutter, above) would be enough today, but
 * pinning the height too means no future change to the rail's width can bring
 * the loop back. The thumbnail's size is now nobody else's business.
 */
@media (min-width: 48rem) {
  .mp-gallery__btn {
    width: var(--thumb-w);
    height: calc(var(--thumb-w) * 1.5);
    /* Explicitly off: with both dimensions given, a ratio could only fight. */
    aspect-ratio: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .mp-gallery__btn { transition-duration: 150ms; }
  .mp-gallery__btn:active { transform: none; }
}

/* On a phone the rail sits under the cover and the annotation follows, so the
   title and size chips are reachable without scrolling a whole shoot. */
@media (max-width: 60rem) {
  .mp-gallery__thumbs { grid-auto-columns: 3.75rem; }
  .single-product .product-entry-wrapper { gap: var(--space-md); }
  .single-product div.product .summary,
  .single-product div.product .entry-summary {
    padding-block: var(--space-sm) var(--space-xl);
  }
}


/* ---- Rail scrollbar --------------------------------------------------
   The default track renders as a bright bar right beside the thumbnails on a
   dark page. Tint it into the ground so it reads as an affordance, not a
   divider. */

.mp-gallery__thumbs {
  scrollbar-color: var(--color-rule) transparent;
}
.mp-gallery__thumbs::-webkit-scrollbar { width: 6px; }
.mp-gallery__thumbs::-webkit-scrollbar-track { background: transparent; }
.mp-gallery__thumbs::-webkit-scrollbar-thumb {
  background: var(--color-rule);
  border-radius: var(--radius-pill);
}
.mp-gallery__thumbs::-webkit-scrollbar-thumb:hover { background: var(--color-muted); }


/* ==========================================================================
   PAGINATION

   Blocksy paints the current page as white text on the accent. On this palette
   the accent is brass at 85% lightness, so white-on-brass measured about
   1.3:1 — the number telling you which page you are on was the least readable
   thing on the site. Brass fill takes GROUND-coloured text, which is what
   --color-accent-ink exists for.
   ========================================================================== */

.ct-pagination .page-numbers,
.woocommerce-pagination .page-numbers {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  letter-spacing: 0.08em;
  color: var(--color-ink-2);
  background: transparent;
  /* --color-rule, not rule-2: at 25% lightness the subtler hairline was
     invisible on the ground, so "1" read as bare text beside a boxed "Prev"
     and the control looked half-styled. */
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-input);
  /* 44px floor — pagination is a primary control on a 32-piece catalogue. */
  min-width: 2.75rem;
  min-height: 2.75rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--space-2xs);
  text-decoration: none;
  transition:
    border-color var(--dur-short) var(--ease-out),
    color var(--dur-short) var(--ease-out);
}

.ct-pagination .page-numbers.current,
.woocommerce-pagination .page-numbers.current {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-accent-ink);
}

@media (hover: hover) and (pointer: fine) {
  .ct-pagination a.page-numbers:hover,
  .woocommerce-pagination a.page-numbers:hover {
    color: var(--color-ink);
    border-color: var(--color-ink-2);
  }
}
.ct-pagination a.page-numbers:active,
.woocommerce-pagination a.page-numbers:active { transform: translateY(1px); }
.ct-pagination a.page-numbers:focus-visible,
.woocommerce-pagination a.page-numbers:focus-visible { transition: none; }

.ct-pagination,
.woocommerce-pagination {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  align-items: center;
  justify-content: center;
  padding: var(--space-2xl) clamp(1.25rem, 5vw, 4rem) var(--space-xl);
}

@media (prefers-reduced-motion: reduce) {
  .ct-pagination .page-numbers,
  .woocommerce-pagination .page-numbers { transition-duration: 150ms; }
  .ct-pagination a.page-numbers:active,
  .woocommerce-pagination a.page-numbers:active { transform: none; }
}


/* ==========================================================================
   MOBILE MENU TRIGGER

   Blocksy renders the hamburger as an 18x18 box. That is the primary
   navigation control for the visitors most likely to be on a phone, and it is
   less than half the 44px minimum target. Padding rather than a bigger glyph:
   the icon keeps its size, the tappable area grows around it.
   ========================================================================== */

.ct-header-trigger {
  min-width: 2.75rem;
  min-height: 2.75rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Pull the extra area back out of the layout so the header does not grow. */
  margin-inline: calc(var(--space-2xs) * -1);
}
.ct-header-trigger:focus-visible { outline-offset: 0; }


/* Blocksy styles the current page through [data-pagination="simple"]
   .page-numbers.current — an attribute selector, so 0,2,1 against my 0,2,0 —
   and pulls the colour from --theme-text-active-color, defaulting to #fff.
   Match the specificity AND set the variable, so it holds whichever way
   Blocksy resolves it. Same trap as the active nav link earlier. */

:root {
  --theme-text-active-color: var(--color-accent-ink);
}

[data-pagination] .page-numbers.current,
[data-pagination] .ct-pagination .page-numbers.current,
.ct-pagination .page-numbers.current {
  background-color: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-accent-ink);
}


/* Offcanvas menu links were 31px tall. Fixing the hamburger to 44px only to
   drop the visitor onto six under-sized targets solves half a problem. */
#offcanvas .menu-item > a,
#offcanvas .ct-menu-link {
  min-height: 2.75rem;
  display: flex;
  align-items: center;
}


/* ==========================================================================
   ENQUIRY FIELDS — the qualifier and the note

   Sits above the send buttons because it changes what they do. Deliberately
   quiet: this is a form on a page whose job is to hand over a list, not a
   checkout that needs to look important.
   ========================================================================== */

.mq {
  margin: var(--space-md) 0 var(--space-lg);
  padding-top: var(--space-md);
  border-top: var(--rule-hair) solid var(--color-rule-2);
  display: grid;
  gap: var(--space-md);
  max-width: 46rem;
}

.mq-legend,
.mq-label {
  display: block;
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-muted);
  margin: 0 0 var(--space-2xs);
  padding: 0;
}
.mq-optional {
  text-transform: none;
  letter-spacing: 0.04em;
  color: var(--color-rule);
  margin-inline-start: var(--space-2xs);
}

.mq-who {
  border: 0;
  padding: 0;
  margin: 0;
  min-width: 0;
}
.mq-who__opts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

/* ---- Retail / wholesale ---------------------------------------------
   A visually-hidden native radio driving a styled label. The input stays in
   normal document flow with zero size — NOT position:absolute;top:0, which
   makes the page jump to the top of the section on every click. */

.mq-opt { position: relative; display: inline-flex; }
.mq-opt input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}
.mq-opt__chip {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 var(--space-md);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  color: var(--color-ink-2);
  background: transparent;
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-input);
  cursor: pointer;
  white-space: nowrap;
  transition:
    border-color var(--dur-short) var(--ease-out),
    background-color var(--dur-short) var(--ease-out),
    color var(--dur-short) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .mq-opt:hover .mq-opt__chip { border-color: var(--color-ink-2); color: var(--color-ink); }
}
.mq-opt input:checked + .mq-opt__chip {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-accent-ink);
}
/* Focus ring on the label, driven by the hidden input. Never transitioned —
   a keyboard user needs it on the first frame. */
.mq-opt input:focus-visible + .mq-opt__chip {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
  transition: none;
}
.mq-opt input:active + .mq-opt__chip { transform: translateY(1px); }
.mq-opt input:disabled + .mq-opt__chip {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

/* ---- Text inputs ----------------------------------------------------
   Border-width is constant across every state; state changes go to
   background, outline and border-COLOUR only, so nothing shifts on focus. */

.mq-input {
  width: 100%;
  min-height: 2.75rem;
  padding: var(--space-2xs) var(--space-xs);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: 1.5;
  color: var(--color-ink);
  background: var(--color-paper-2);
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-input);
  /* Reserved at rest so activating does not change the geometry. */
  outline: 2px solid transparent;
  outline-offset: 1px;
  transition:
    border-color var(--dur-short) var(--ease-out),
    background-color var(--dur-short) var(--ease-out);
}
.mq-input::placeholder { color: var(--color-muted); opacity: 1; }
@media (hover: hover) and (pointer: fine) {
  .mq-input:hover { border-color: var(--color-ink-2); }
}
.mq-input:focus-visible {
  outline: 2px solid var(--color-focus);
  border-color: var(--color-ink-2);
  background: var(--color-paper-3);
  transition: none;
}
.mq-input:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  background: var(--color-paper);
}
.mq-input[aria-invalid="true"] { border-color: var(--color-accent); }

.mq-textarea {
  resize: vertical;
  min-height: 5.5rem;
}

/* Helper row keeps its height whether or not it has anything to say, so an
   appearing message never pushes the send buttons down the page. */
.mq-help {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-sm);
  align-items: baseline;
  margin: var(--space-2xs) 0 0;
  min-height: 1lh;
  font-size: var(--text-xs);
  color: var(--color-muted);
}
.mq-count { font-variant-numeric: tabular-nums; color: var(--color-rule); }
.mq-help__text { color: var(--color-muted); }

.mq-qty[hidden] { display: none; }
.mq-qty .mq-input { max-width: 12rem; }

@media (prefers-reduced-motion: reduce) {
  .mq-opt__chip,
  .mq-input { transition-duration: 150ms; }
  .mq-opt input:active + .mq-opt__chip { transform: none; }
}


/* ==========================================================================
   FORM FIELDS, SITE-WIDE

   Blocksy styles inputs through
     select, textarea, input:is([type="text"], [type="tel"], …)
   and :is() carries the specificity of its most specific argument, so that
   selector is 0,1,1 — it beat .mq-input (0,1,0) and left the quantity field
   rendering rgb(58,79,102) text on a transparent background: dark slate on a
   dark panel, effectively invisible. The textarea escaped only by accident.

   Setting the custom properties Blocksy already reads fixes it once for every
   field on the site — the search box included — rather than per component.
   The explicit rules below are belt-and-braces at matching specificity.
   ========================================================================== */

:root {
  --theme-form-text-initial-color: var(--color-ink);
  --theme-form-text-focus-color: var(--color-ink);
  --theme-form-field-background-initial-color: var(--color-paper-2);
  --theme-form-field-background-focus-color: var(--color-paper-3);
  --theme-form-field-border-initial-color: var(--color-rule);
  --theme-form-field-border-focus-color: var(--color-ink-2);
  --theme-form-placeholder-color: var(--color-muted);
}

.mq input.mq-input,
.mq textarea.mq-input {
  color: var(--color-ink);
  background-color: var(--color-paper-2);
  border-color: var(--color-rule);
}
.mq input.mq-input:focus-visible,
.mq textarea.mq-input:focus-visible {
  background-color: var(--color-paper-3);
  border-color: var(--color-ink-2);
}


/* ==========================================================================
   FABRIC AND CARE

   The fabric line sits in the summary panel where a price would normally go —
   pure cotton is a reason to buy, so it should not be behind a tab. The care
   detail IS behind a tab: someone deciding wants photographs and a size, and
   only reaches for washing instructions once they have decided.
   ========================================================================== */

.mp-fabric {
  margin: var(--space-2xs) 0 0;
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-muted);
}

.mp-care { max-width: 62ch; }
.mp-care__fabric {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-md);
  color: var(--color-ink);
  margin: 0 0 var(--space-sm);
}
.mp-care__list {
  list-style: none;
  margin: 0 0 var(--space-md);
  padding: 0;
  display: grid;
  gap: var(--space-2xs);
}
.mp-care__list li {
  position: relative;
  padding-inline-start: var(--space-md);
  font-size: var(--text-base);
  line-height: 1.6;
  color: var(--color-ink-2);
}
/* Brass marker rather than a bullet glyph — one accent, no icon library. */
.mp-care__list li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.62em;
  width: 0.375rem;
  height: var(--rule-hair);
  background: var(--color-accent);
}
/* --color-muted read 5.46:1 here, which passes but sat too faint to be read
   comfortably at this size against the photographs above it. ink-2 takes the
   same line to 9.94:1 and it is still visibly quieter than the care points. */
.mp-care__note {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-ink-2);
}

/* ---- Woo tabs on the dark ground -------------------------------------
   Three tabs now (Description · Fabric & care · Additional information), so
   they need to read as a real control rather than inherit light-theme styling. */

.woocommerce-tabs ul.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  margin: 0 0 var(--space-lg);
  padding: 0 0 var(--space-2xs);
  border-bottom: var(--rule-hair) solid var(--color-rule-2);
  list-style: none;
}
.woocommerce-tabs ul.tabs::before,
.woocommerce-tabs ul.tabs::after { display: none; }
.woocommerce-tabs ul.tabs li {
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
}
.woocommerce-tabs ul.tabs li a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 var(--space-sm);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transition: color var(--dur-short) var(--ease-out), border-color var(--dur-short) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .woocommerce-tabs ul.tabs li a:hover { color: var(--color-ink); }
}
.woocommerce-tabs ul.tabs li.active a {
  color: var(--color-ink);
  border-bottom-color: var(--color-accent);
}
.woocommerce-tabs ul.tabs li a:focus-visible { transition: none; }

.woocommerce-tabs .panel > h2 {
  font-size: var(--text-lg);
  margin: 0 0 var(--space-sm);
}

@media (prefers-reduced-motion: reduce) {
  .woocommerce-tabs ul.tabs li a { transition-duration: 150ms; }
}


/* ==========================================================================
   THE WHATSAPP BUTTON, FIXED BOTTOM RIGHT

   WhatsApp green with the official glyph and a pulsing ring, matched to the
   client's reference implementation. This is the one element allowed out of the
   indigo-and-brass palette: the floating chat bubble is a convention people
   scan for, and an accent-coloured one made them look twice. The page CTA on
   the selection screen stays brass — that one is part of the layout, not a
   universal affordance.

   The ring is a ::before rather than an extra <span>. Being out of flow it
   paints before the glyph, so the glyph stays crisp instead of being veiled by
   a 75%-opacity overlay every cycle — which is what happens in the reference,
   where the ring element sits after the icon in the markup.
   ========================================================================== */

.mw {
  position: fixed;
  z-index: var(--z-float);
  /* Clear of the iOS home indicator; env() falls back to 0 elsewhere. */
  inset-block-end: calc(var(--space-2xs) + env(safe-area-inset-bottom, 0px));
  inset-inline-end: var(--space-2xs);

  display: grid;
  place-items: center;
  width: 4rem;
  height: 4rem;
  border-radius: var(--radius-pill);

  background: var(--color-wa);
  color: var(--color-wa-ink);
  box-shadow: var(--shadow-float);
  text-decoration: none;

  transition:
    background-color var(--dur-short) var(--ease-out),
    transform var(--dur-short) var(--ease-out);
}

/* ---- The pulse ------------------------------------------------------
   Tailwind's `animate-ping`, written out: hold, then scale to double while
   fading, forever. The ring is pinned to the button box and scales outward
   past it, so nothing clips it and nothing reflows. */
@keyframes maruti-ping {
  75%,
  100% {
    transform: scale(2);
    opacity: 0;
  }
}

.mw::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--color-wa);
  opacity: 0.75;
  /* Never intercept the tap — the halo is decoration, the button is the target. */
  pointer-events: none;
  animation: maruti-ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
}

.mw__icon {
  width: 2rem;
  height: 2rem;
  fill: currentColor;
  display: block;
}

@media (hover: hover) and (pointer: fine) {
  .mw:hover {
    background: var(--color-wa-hover);
    transform: scale(1.1);
  }
}

.mw:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 3px;
  /* Never transitioned — a keyboard user needs the ring on the first frame. */
  transition: none;
}

.mw:active {
  transform: scale(1.04);
  background: var(--color-wa-hover);
}

/*
 * Small screens: 56px rather than 64px. At 375px a 64px circle with a halo
 * pulsing to 128px covers a real share of the photograph underneath, and 56px
 * is still comfortably past the 44px touch-target floor.
 */
@media (max-width: 30rem) {
  .mw {
    width: 3.5rem;
    height: 3.5rem;
  }
  .mw__icon { width: 1.75rem; height: 1.75rem; }
}

/*
 * Reduced motion: no pulse at all, and no scale on hover.
 *
 * A ring that expands and fades once per second, on every page, forever, is
 * exactly the kind of unrequested motion this setting exists to stop. The
 * button keeps its colour change so hover still reads.
 */
@media (prefers-reduced-motion: reduce) {
  .mw { transition-duration: 150ms; }
  .mw::before { animation: none; display: none; }
  .mw:hover,
  .mw:active { transform: none; }
}

/* Printing a floating chat button wastes ink and means nothing on paper. */
@media print {
  .mw { display: none; }
}


/* ==========================================================================
   ARRIVAL — blocks and images coming in

   The only motion on the site beyond hover states, and it is deliberately one
   idea repeated: things rise a little and fade up, once, as you reach them.
   Nothing bounces, nothing scales, nothing slides sideways. On a page that is
   almost entirely photographs, the restraint is what makes it read as a gallery
   turning pages rather than a template showing off.

   Every selector here is gated on a class that js/reveal.js adds. CSS on its
   own never hides anything — with JavaScript off, or a browser without
   IntersectionObserver, or a reduced-motion preference, these rules match
   nothing and the page renders exactly as it did before.
   ========================================================================== */

.mr {
  opacity: 0;
  /*
   * 14px, and only ever upward. Larger reads as sliding, and a horizontal
   * component would fight the 2px gutter between wall tiles by momentarily
   * opening a gap where the design wants none.
   */
  transform: translate3d(0, 14px, 0);

  /*
   * Stagger comes from --mr-i, set per element by the script and capped there.
   * 55ms is enough to read as a sequence across a row without the last tile
   * feeling late.
   */
  transition:
    opacity var(--dur-long) var(--ease-out),
    transform var(--dur-long) var(--ease-out);
  transition-delay: calc(var(--mr-i, 0) * 55ms);

  /*
   * Promote once, up front. Without this the first frame of a twelve-tile row
   * is where the browser decides to rasterise twelve large photographs, and the
   * transition drops frames exactly when it is most visible.
   */
  will-change: opacity, transform;
}

.mr.is-in {
  opacity: 1;
  transform: none;
  /* Stop holding the compositor layers once each block has arrived. */
  will-change: auto;
}

/* ---- Images fading up from the panel ---------------------------------
   The frames are painted --color-paper-2, so an image that has not decoded
   reads as an empty panel rather than as a gap. Fading from that panel to the
   photograph is a shorter distance than fading from nothing, which is why this
   is a plain opacity change with no movement — the frame is already in place
   and only its contents are arriving. */

.mr-img {
  opacity: 0;
  transition: opacity var(--dur-short) var(--ease-out);
}

.mr-img.is-loaded {
  opacity: 1;
}

/* ---- Reduced motion ---------------------------------------------------
   The script exits before adding any class, so these rules should never have
   anything to match. They exist because a preference can change after load —
   and because a stylesheet that depends on a script having behaved is not a
   guarantee. */

@media (prefers-reduced-motion: reduce) {
  .mr,
  .mr-img {
    opacity: 1;
    transform: none;
    transition: none;
    will-change: auto;
  }
}

/* Nothing arrives on paper. */
@media print {
  .mr,
  .mr-img {
    opacity: 1;
    transform: none;
    transition: none;
  }
}


/* ==========================================================================
   THE ADDITIONAL INFORMATION TABLE

   Blocksy stripes it for a light theme:

     .woocommerce-product-attributes tr:nth-child(even) th,
     .woocommerce-product-attributes tr:nth-child(even) td
       { background: rgba(242, 244, 247, .5) }

   Over this ground that half-transparent white resolved to #7C7E85, and the
   inherited light text on top of it measured 2.02:1 — the Colour row was
   effectively unreadable while the Size row above it sat at 9.94:1. A striped
   table where alternate rows cannot be read is worse than no striping.

   The stripe is kept, because it does a real job on a two-column spec table;
   it just runs dark instead of light. Same selector shape as Blocksy's so the
   specificity matches, and design.css loads after woocommerce.min.css, so this
   wins without !important.
   ========================================================================== */

.woocommerce-product-attributes tr:nth-child(even) th,
.woocommerce-product-attributes tr:nth-child(even) td,
.shop_attributes tr:nth-child(even) th,
.shop_attributes tr:nth-child(even) td {
  /* ink-2 on paper-2 is 9.39:1 — the striped row now reads as well as the
     unstriped one. */
  background: var(--color-paper-2);
}

.woocommerce-product-attributes th,
.woocommerce-product-attributes td,
.shop_attributes th,
.shop_attributes td {
  color: var(--color-ink-2);
  border-color: var(--color-rule-2);
}

/* The label column is the quieter of the two: it repeats down the table while
   the values are what somebody is actually reading. */
.woocommerce-product-attributes th,
.shop_attributes th {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-muted);
  font-weight: 400;
}

.woocommerce-product-attributes td p,
.shop_attributes td p {
  margin: 0;
}
