/* Reset + primitives shared by every page. */

*, *::before, *::after { box-sizing: border-box; }
/* overflow-x must be clipped on BOTH html and body. Setting it on body alone
   did not hold: the rotated ribbon still let the page scroll 24-39px sideways
   at every width, because the viewport takes its overflow from html. */
html {
  -webkit-text-size-adjust: 100%;
  overflow-x: hidden;
  /* MEASURED: the reference scrolls smoothly. The reduced-motion block in
     tokens.css forces this back to auto for anyone who asked for less motion. */
  scroll-behavior: smooth;
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  overflow-x: hidden;
}
img, svg, video { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
ul, ol { margin: 0; padding: 0; list-style: none; }

/* MEASURED off the reference: Tenor Sans, weight 500, letter-spacing NORMAL,
   uppercase, line-height 1.2 - and section headings are BLACK, not maroon.
   The maroon is reserved for category rows, prices and links. */
h1, h2, h3, h4, h5 {
  font-family: var(--font-head);
  font-weight: 500;
  color: #000;
  text-transform: uppercase;
  letter-spacing: var(--tracking-head);
  margin: 0;
  line-height: 1.2;
}
h1 { font-size: clamp(2rem, 4vw, 3.5rem); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: 20px; }
p  { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

/* Layout ------------------------------------------------------------------ */
.wrap { width: 100%; padding-inline: var(--gutter); margin-inline: auto; }
.wrap--narrow { max-width: 820px; }
.wrap--mid { max-width: 1200px; }
/* MEASURED: reference sections have no vertical padding; they are separated by
   a 100px bottom margin. Two stacked sections therefore sit 100px apart, not
   240px as padding-on-both-sides produced. */
.section { padding-block: 0; margin-bottom: var(--section-gap); }
.section--tight { padding-block: 0; margin-bottom: var(--section-gap); }
.section:last-child { margin-bottom: 0; }

.grid { display: grid; gap: 24px; }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 991px) {
  .grid--3, .grid--4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 575px) {
  .grid--2, .grid--3, .grid--4 { grid-template-columns: 1fr; }
}

/* Type helpers ------------------------------------------------------------ */
/* MEASURED: "Expert Services Crafted" is Nunito 16px weight 500,
   letter-spacing normal, maroon - not an 11px tracked micro-label. */
.eyebrow {
  font-size: 16px;
  font-weight: 500;
  letter-spacing: normal;
  text-transform: uppercase;
  color: var(--brand);
}
.lede { color: var(--ink-muted); text-transform: uppercase; letter-spacing: 0.04em; font-size: 12px; }
.center { text-align: center; }

/* Links and buttons ------------------------------------------------------- */
/* The reference's primary CTA is an underlined uppercase text link, not a pill. */
/* MEASURED: "Shop Now" is Nunito 14px, weight 700, letter-spacing normal. */
.link-cta {
  position: relative;
  display: inline-block;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: normal;
  text-transform: uppercase;
  color: var(--brand);
  /* MEASURED: 2px maroon at rest, with 2px of padding above it. */
  border-bottom: 2px solid var(--brand);
  padding-bottom: 2px;
  transition: color var(--dur) var(--ease);
}
/* MEASURED: on hover a BLACK 2px rule wipes across the maroon one, right to
   left, over 0.3s on cubic-bezier(.785,.135,.15,.86). Every link on the
   reference does this; opacity 0.6 was a stand-in. */
.link-cta::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 2px;
  background: #000;
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.link-cta:hover::after,
.link-cta:focus-visible::after { transform: scaleX(1); }
/* MEASURED: "Discover More" is 13px on a phone, 14px on desktop. */
@media (max-width: 575px) { .link-cta { font-size: 13px; } }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  /* MEASURED: the reference "Add to cart" is a BLACK button with bone text,
     Nunito 14px weight 500, square corners - not a maroon pill. Re-measured on
     the product page: both it and Buy Now are 45px tall, not 50. */
  min-height: 45px;
  padding: 10px 34px;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: normal;
  text-transform: uppercase;
  background: #000;
  color: #f0efea;
  border-radius: var(--radius);
  transition: color var(--dur) var(--ease);
}
.btn--ghost { background: transparent; color: #000; border: 1px solid #000; }
.btn--ghost:hover { color: #f0efea; }
.btn--block { width: 100%; }

/* The hover wipe ------------------------------------------------------------
   MEASURED: every button on the reference fills from the RIGHT edge leftwards
   over 0.4s ease-in-out - it does not swap its background colour. The fill is
   a pseudo-element at z-index -1, so it paints over the button's own ground
   but under its label; `isolation: isolate` keeps that -1 inside the button.

   Reference geometry: inset 0 with left:100%/width:0 at rest, left:0/width:100%
   on hover. scaleX from a right origin is the same wipe and does not relayout.

   Applied here to every control that gets one on the reference: .btn, the
   product-card icon column, the card's add-to-cart row and the carousel
   arrows. */
.btn-hv,
.btn,
.prod-card__act,
.prod-card__add,
.swiper-nav {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.btn-hv::after,
.btn::after,
.prod-card__act::after,
.prod-card__add::after,
.swiper-nav::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--brand);
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 0.4s ease-in-out;
  z-index: -1;
}
.btn-hv:hover::after,
.btn-hv:focus-visible::after,
.btn:hover::after,
.btn:focus-visible::after,
.prod-card__act:hover::after,
.prod-card__act:focus-visible::after,
.prod-card__add:hover::after,
.prod-card__add:focus-visible::after,
.swiper-nav:hover::after,
.swiper-nav:focus-visible::after { transform: scaleX(1); }

/* A wipe that animates a transform is motion; honour the preference. */
@media (prefers-reduced-motion: reduce) {
  .btn-hv::after,
  .btn::after,
  .prod-card__act::after,
  .prod-card__add::after,
  .swiper-nav::after,
  .link-cta::after { transition: none; }
}

/* Accessibility ----------------------------------------------------------- */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 8px; top: -60px; z-index: 999;
  background: var(--brand); color: #fff; padding: 10px 18px;
  transition: top 0.2s;
}
.skip-link:focus { top: 8px; }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* Scroll reveal -------------------------------------------------------------
   MEASURED off the reference, which DOES have one - an earlier note in
   tokens.css said it did not. That was wrong, and the reason is worth
   recording: the animation runs once with animation-fill-mode: none, and the
   pre-reveal class is stripped when it fires. Sampling below-fold elements
   after they have already revealed therefore shows opacity 1 and no
   transform, which is exactly what the reference is meant to end up as.

   The real mechanism:
     before   .elementor-invisible { visibility: hidden }   - no transform, so
              nothing is displaced and the page height never changes
     on enter that class is dropped and `animated fadeInUp` added
     keyframes 0%   opacity 0, translate3d(0, 100%, 0)
               100% opacity 1, transform none
     timing   1.25s, ease, one iteration

   translate3d(0, 100%, 0) is 100% of the ELEMENT'S OWN height, so a 750px
   section starts a full section-height low. That long travel is what makes the
   reference's reveal read the way it does; a 20-30px nudge is not the same
   movement.
   -------------------------------------------------------------------------- */
@keyframes fadeInUp {
  from { opacity: 0; transform: translate3d(0, 100%, 0); }
  to   { opacity: 1; transform: none; }
}

/* Scoped to .js so the sections are never hidden if the script fails. */
.js [data-reveal] { visibility: hidden; }
.js [data-reveal].is-revealed {
  visibility: visible;
  animation: fadeInUp 1.25s ease;
}

/* A long slide is exactly what a reduced-motion reader asked not to have.
   They still get the content, just without the travel. */
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { visibility: visible; }
  .js [data-reveal].is-revealed { animation: none; }
}
