/* ==========================================================================
   Bongeeo storefront
   Header, navigation, hero, product cards, cart, checkout and the home page
   sections. Depends on design-system.css and components.css.
   ========================================================================== */

/* ==========================================================================
   Announcement bar
   ========================================================================== */

.announcement-bar {
  position: relative;
  z-index: calc(var(--z-header) + 1);
  padding-block: var(--space-2);
  background: var(--brand-800);
  color: var(--text-inverse);
  font-size: var(--text-sm);
  text-align: center;
}

.announcement-bar__inner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  flex-wrap: wrap;

  /* Keeps the message clear of the dismiss button in the corner. */
  padding-inline: var(--space-8);
}

.announcement-bar a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  font-weight: 600;
}

.announcement-bar__close {
  position: absolute;
  inset-inline-end: var(--space-2);
  inset-block: 0;
  display: grid;
  place-items: center;
  width: 40px;
  border: 0;
  background: none;
  color: inherit;
  opacity: 0.75;
  cursor: pointer;
}

.announcement-bar__close:hover { opacity: 1; }

/* ==========================================================================
   Header
   ========================================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  transition: box-shadow var(--duration-base) var(--ease-out);
}

/* Shadow appears only once the page has scrolled, so the header lifts off. */
.site-header[data-scrolled="true"] {
  box-shadow: var(--shadow-md);
}

/* One row holds the brand, the navigation, the search and the account controls.
   Everything is a flex item so the navigation takes the slack and the controls
   stay pinned to the end of the row. */
.header__bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--header-height);
}

.header__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  color: var(--ink-900);
}

.header__brand:hover { color: var(--brand-700); }

/* Clipped rather than allowed to set the row width: on a 320px phone the
   wordmark is the one item that would otherwise push the controls off-screen. */
.header__brand { overflow: hidden; }
.header__brand img { height: 34px; width: auto; max-width: 100%; }

.header__search {
  display: none;
  position: relative;

  /* Allowed to give up width before anything else on the row does. */
  flex: 0 1 300px;
  min-width: 0;
  margin-inline-end: var(--space-2);
}

.header__search .input {
  padding-inline-start: 2.75rem;
  background: var(--surface-sunken);
  border-color: transparent;
}

.header__search .input:focus {
  background: var(--surface);
  border-color: var(--brand-500);
}

.header__search-icon {
  position: absolute;
  inset-inline-start: var(--space-4);
  inset-block-start: 50%;
  transform: translateY(-50%);
  color: var(--ink-400);
  pointer-events: none;
}

.header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-1);

  /* Fixed by default: the buttons inside cannot shrink, so letting this box
     shrink only pushes them out of it. It becomes shrinkable at the width where
     the search field appears, because then there is something inside that can
     actually give up space. */
  flex-shrink: 0;

  /* Pins the controls to the end whether or not the navigation is rendered. */
  margin-inline-start: auto;
}

.header__action {
  position: relative;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: var(--radius-md);
  background: none;
  color: var(--ink-700);
  font-size: 1.2rem;
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-out);
}

.header__action:hover {
  background: var(--ink-100);
  color: var(--brand-700);
}

.header__action-badge {
  position: absolute;
  inset-block-start: 4px;
  inset-inline-end: 4px;
  min-width: 18px;
  height: 18px;
  padding-inline: 4px;
  display: grid;
  place-items: center;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  color: #fff;
  background: var(--danger-600);
  border: 2px solid var(--surface);
  border-radius: var(--radius-pill);
}

/* The drawer toggle and the inline navigation are two views of the same thing,
   so they swap at exactly the same width — never both, never neither. */
.header__action--nav-toggle { display: grid; }
.header__action--desktop-only { display: none; }

@media (min-width: 1280px) {
  .header__action--nav-toggle { display: none; }
  .header__action--desktop-only { display: grid; }
}

/* The inline field appears from 1440px up; below that the button opens the same
   search drawer the phone layout uses. It settles at 189px and stays there --
   the container caps at 1320px, so no extra viewport width ever reaches it. */
@media (min-width: 1440px) {
  .header__search { display: block; }
  .header__action--search { display: none; }

  .header__actions {
    flex-shrink: 1;
    min-width: 0;
  }
}

/* The container caps at 1320px, so beyond that width nothing gains room: the
   field simply takes whatever the brand, navigation and controls leave. */

/* Small phones: the logo plus five 44px targets is more than 360px of width.
   Tightening both keeps the row on one line down to 320px without dropping
   any control below the 40px touch-target floor. */
@media (max-width: 480px) {
  .header__bar {
    gap: var(--space-1);
  }

  .header__brand {
    font-size: var(--text-lg);
  }

  .header__brand img {
    height: 28px;
  }

  .header__action {
    width: 40px;
    height: 40px;
    font-size: 1.1rem;
  }
}

/* 320-380px: the drawer toggle, the wordmark and four controls still have to
   share the row, and 40px targets no longer leave room for the logo. 36px is
   the floor — still above the 24px WCAG 2.2 minimum target size. */
@media (max-width: 380px) {
  .header__bar,
  .header__actions {
    gap: 2px;
  }

  .header__brand img {
    height: 24px;
  }

  .header__action {
    width: 34px;
    height: 34px;
    font-size: 1rem;
  }
}

/* ---- Primary navigation ---------------------------------------------- */

/* Inline from 1280px up. Below that the row cannot hold the brand, eight links
   and four controls at a legible size, so navigation moves wholesale into the
   drawer rather than being trimmed down link by link.

   The measured natural width of the list is 731px; with a 142px brand, 220px of
   controls and the gaps, 1280px is the first step where the container clears it. */
.primary-nav {
  display: none;

  /* Never the item that gives up width — the search field absorbs the slack
     instead. Shrinking this one clips links out of the middle of the row. */
  flex: 0 0 auto;
}

@media (min-width: 1280px) {
  .primary-nav { display: block; }
}

.primary-nav__list {
  display: flex;
  align-items: center;

  /* No wrapping: this is one row of a single-row header now. The links are
     sized to fit the narrowest width at which the bar is shown at all. */
  flex-wrap: nowrap;
  gap: 0;
  list-style: none;
  margin: 0;
  padding: 0;
}

.primary-nav__item { position: static; }

.primary-nav__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-4) var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-700);
  white-space: nowrap;
  border-bottom: 2.5px solid transparent;
  transition: all var(--duration-fast) var(--ease-out);
}


.primary-nav__item:hover .primary-nav__link,
.primary-nav__link[aria-current="page"] {
  color: var(--brand-700);
  border-bottom-color: var(--brand-600);
}

.primary-nav__link--highlight { color: var(--danger-600); }

/* ---- Mega menu -------------------------------------------------------- */

.mega-menu {
  position: absolute;
  inset-inline: 0;
  inset-block-start: 100%;
  z-index: var(--z-dropdown);
  background: var(--surface);
  border-block-start: 1px solid var(--border);
  box-shadow: var(--shadow-lg);

  /* Hidden by opacity rather than display so the reveal can be animated. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transition:
    opacity var(--duration-base) var(--ease-out),
    visibility var(--duration-base),
    transform var(--duration-base) var(--ease-out);
}

.primary-nav__item:hover .mega-menu,
.primary-nav__item:focus-within .mega-menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.mega-menu__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-10);
  padding-block: var(--space-8);

  /* A category tree deeper than the viewport must scroll inside the panel
     rather than run off the bottom of the screen. */
  max-height: calc(100svh - var(--header-offset));
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* The featured rail only earns its 300px once there is room for it. */
@media (min-width: 1200px) {
  .mega-menu__inner {
    grid-template-columns: minmax(0, 1fr) 300px;
  }
}

@media (max-width: 1199px) {
  .mega-menu__featured {
    display: none;
  }
}

.mega-menu__columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-6);
}

.mega-menu__heading {
  margin-block-end: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-900);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}

.mega-menu__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  list-style: none;
  margin: 0;
  padding: 0;
}

.mega-menu__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-1) 0;
  font-size: var(--text-base);
  color: var(--text-secondary);
}

.mega-menu__link:hover { color: var(--brand-700); }

.mega-menu__count {
  font-size: var(--text-xs);
  color: var(--ink-400);
}

.mega-menu__featured {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding-inline-start: var(--space-8);
  border-inline-start: 1px solid var(--border);
}

/* ---- Mobile navigation drawer ---------------------------------------- */

.mobile-nav__section + .mobile-nav__section {
  margin-block-start: var(--space-5);
  padding-block-start: var(--space-5);
  border-block-start: 1px solid var(--border);
}

.mobile-nav__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--ink-800);
}

.mobile-nav__sublink {
  display: block;
  padding: var(--space-2) 0 var(--space-2) var(--space-5);
  font-size: var(--text-base);
  font-weight: 400;
  color: var(--text-secondary);
}

/* ---- Search suggestions ------------------------------------------------ */

.search-suggestions {
  position: absolute;
  inset-inline: 0;
  inset-block-start: calc(100% + var(--space-2));
  z-index: var(--z-dropdown);
  max-height: 480px;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
}

.search-suggestions[hidden] { display: none; }

.search-suggestions__group + .search-suggestions__group {
  border-block-start: 1px solid var(--border);
}

.search-suggestions__label {
  padding: var(--space-3) var(--space-4) var(--space-2);
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--text-muted);
}

.search-suggestion {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  color: var(--ink-800);
  transition: background-color var(--duration-fast) var(--ease-out);
}

.search-suggestion:hover,
.search-suggestion[data-active="true"] {
  background: var(--surface-sunken);
  color: var(--ink-900);
}

.search-suggestion img {
  width: 44px;
  height: 44px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  background: var(--ink-100);
}

/* ==========================================================================
   Hero

   A stage with depth rather than a flat banner. Inside one perspective
   container the photograph, the aurora wash, the drifting orbs and the frosted
   copy panel each sit at a different Z, so pointer movement separates them —
   the image tilts away from the pointer while the copy leans toward it, which
   is what reads as three-dimensional rather than as a picture that moves.

   Three custom properties drive all of it, written by the carousel script:
     --hero-x, --hero-y   pointer position, -1..1 from the centre
     --hero-lift          0 at rest, 1 while the pointer is over the hero
   They default to 0 here, so the hero is correct before the script runs, on
   touch devices (where the script never binds them) and with scripting off.

   Nothing in the 3D chain may set opacity, filter or overflow: each of those
   silently collapses `preserve-3d` to `flat`, which would flatten the stage
   mid-transition and pop it back at the end. The slides therefore move between
   states on transform alone.
   ========================================================================== */

.hero {
  --hero-x: 0;
  --hero-y: 0;
  --hero-lift: 0;
  --hero-interval: 6500ms;
  --hero-tint: #0b1220;

  position: relative;
  overflow: hidden;

  /* Keeps the blurred orbs and the blend modes below from reaching the header. */
  isolation: isolate;

  background: var(--ink-900);
  perspective: 1500px;
  perspective-origin: 50% 42%;
}

.hero__slides {
  display: flex;

  /* The slides are children of a transformed track, so the track has to pass
     the perspective through instead of flattening them into its own plane. */
  transform-style: preserve-3d;
  transition: transform 850ms var(--ease-in-out);
}

/* Slides change in three dimensions: the outgoing one turns and falls back
   into the scene while the incoming one swings forward to meet the viewer. */
.hero__slide {
  position: relative;
  flex: 0 0 100%;
  min-height: clamp(460px, 62svh, 700px);
  display: grid;
  align-items: center;
  transform-style: preserve-3d;
  transition: transform 850ms var(--ease-in-out);

  /* Carries the banner's own colour. It is what shows through the wedge the
     rotation opens beside a turning slide, and it is the whole background on a
     phone, where the picture becomes a card and the scrim is dropped. */
  background: var(--ink-900);
  background: linear-gradient(165deg, color-mix(in srgb, var(--hero-tint) 62%, #0b1220), #0b1220 78%);
}

.hero__slide[data-side="before"] {
  transform: translateZ(-240px) rotateY(20deg) scale(0.94);
}

.hero__slide[data-side="after"] {
  transform: translateZ(-240px) rotateY(-20deg) scale(0.94);
}

.hero__slide[data-state="active"] {
  transform: none;
}

/* The copy sits inside the shared container, which would otherwise flatten
   everything below it out of the stage's 3D context. */
.hero__slide > .container {
  transform-style: preserve-3d;
}

/* A phone held sideways is barely 400px tall. Letting the slide size to its
   own copy there beats forcing a minimum the viewport cannot show. */
@media (max-height: 520px) and (orientation: landscape) {
  .hero__slide {
    min-height: 0;
  }

  .hero__content {
    padding-block: var(--space-8);
  }

  .hero__trust {
    display: none;
  }
}

/* ---- Depth layers -------------------------------------------------------- */

.hero__stage {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;

  /* Purely scenery — every click belongs to the copy in front of it. */
  pointer-events: none;
}

/* The photographic plane. Inset past the slide edges so tilting it never drags
   a corner into view, and pushed back in Z so the copy floats above it. */
.hero__plane {
  position: absolute;
  inset: -7%;
  transform-style: preserve-3d;
  transform:
    translate3d(calc(var(--hero-x) * -24px), calc(var(--hero-y) * -18px), -60px)
    rotateX(calc(var(--hero-y) * -3.4deg))
    rotateY(calc(var(--hero-x) * 4.6deg))
    scale(calc(1 + var(--hero-lift) * 0.03));
  will-change: transform;
}

.hero__media {
  position: absolute;
  inset: 0;
}

.hero__media img,
.hero__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform-origin: 50% 45%;
}

/* A slow drift across the frame, running only on the slide being read so the
   off-screen ones cost nothing. */
.hero__slide[data-state="active"] .hero__media img,
.hero__slide[data-state="active"] .hero__media video {
  animation: hero-drift 24s var(--ease-in-out) infinite alternate;
}

@keyframes hero-drift {
  from {
    transform: scale(1.05) translate3d(-1.2%, 0.6%, 0);
  }
  to {
    transform: scale(1.16) translate3d(1.6%, -1.6%, 0);
  }
}

/* A gradient rather than a flat overlay keeps the image visible on the side
   opposite the copy while guaranteeing text contrast. The banner's own colour
   is mixed into the dark end so every slide keeps its own mood; the plain
   version above it is what browsers without `color-mix` fall back to, and it
   has to stay legible on its own.

   The first layer earns its place separately. Every hero asset is a photograph
   composited onto a flat field of the banner's own colour, and the join is a
   hard vertical edge about three-quarters of the way across. Fading that same
   colour back in over the trailing edge dissolves the join — the shade matches
   because --hero-tint is the field colour — and reads as a vignette on any
   asset that has no seam to hide. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to left, var(--hero-tint) 0%, var(--hero-tint) 15%, transparent 40%),
    linear-gradient(
      100deg,
      rgba(6, 11, 22, 0.92) 0%,
      rgba(6, 11, 22, 0.68) 38%,
      rgba(6, 11, 22, 0.12) 60%,
      transparent 72%
    ),
    linear-gradient(to top, rgba(6, 11, 22, 0.45) 0%, transparent 38%);
  background:
    linear-gradient(to left, var(--hero-tint) 0%, var(--hero-tint) 15%, transparent 40%),
    linear-gradient(
      100deg,
      color-mix(in srgb, var(--hero-tint) 50%, rgba(6, 11, 22, 0.94)) 0%,
      color-mix(in srgb, var(--hero-tint) 32%, rgba(6, 11, 22, 0.72)) 38%,
      rgba(6, 11, 22, 0.12) 60%,
      transparent 72%
    ),
    linear-gradient(to top, rgba(6, 11, 22, 0.45) 0%, transparent 38%);
}

.hero__slide[data-align="right"] .hero__scrim {
  background:
    linear-gradient(to left, var(--hero-tint) 0%, var(--hero-tint) 15%, transparent 40%),
    linear-gradient(
      260deg,
      rgba(6, 11, 22, 0.92) 0%,
      rgba(6, 11, 22, 0.68) 38%,
      rgba(6, 11, 22, 0.12) 60%,
      transparent 72%
    ),
    linear-gradient(to top, rgba(6, 11, 22, 0.45) 0%, transparent 38%);
  background:
    linear-gradient(to left, var(--hero-tint) 0%, var(--hero-tint) 15%, transparent 40%),
    linear-gradient(
      260deg,
      color-mix(in srgb, var(--hero-tint) 50%, rgba(6, 11, 22, 0.94)) 0%,
      color-mix(in srgb, var(--hero-tint) 32%, rgba(6, 11, 22, 0.72)) 38%,
      rgba(6, 11, 22, 0.12) 60%,
      transparent 72%
    ),
    linear-gradient(to top, rgba(6, 11, 22, 0.45) 0%, transparent 38%);
}

/* A slow colour wash over the photograph. `screen` lifts the brand hues out of
   the dark end of the picture without washing the garment colours out. */
.hero__aurora {
  position: absolute;
  inset: -20%;
  background:
    radial-gradient(38% 46% at 22% 30%, rgba(31, 162, 133, 0.45), transparent 70%),
    radial-gradient(34% 40% at 76% 68%, rgba(240, 160, 75, 0.38), transparent 70%),
    radial-gradient(46% 52% at 58% 12%, rgba(116, 216, 190, 0.24), transparent 72%);
  mix-blend-mode: screen;
  opacity: 0.42;
  filter: blur(10px);
  mask-image: linear-gradient(to right, #000 0%, #000 34%, transparent 62%);
  -webkit-mask-image: linear-gradient(to right, #000 0%, #000 34%, transparent 62%);
  animation: hero-aurora 26s var(--ease-in-out) infinite alternate;
}

@keyframes hero-aurora {
  from {
    transform: translate3d(calc(var(--hero-x) * -14px - 2%), calc(var(--hero-y) * -10px - 1%), -30px) scale(1);
  }
  to {
    transform: translate3d(calc(var(--hero-x) * -14px + 3%), calc(var(--hero-y) * -10px + 2%), -30px) scale(1.12);
  }
}

/* Two soft spheres between the picture and the copy. They are what the eye
   reads the parallax from: they travel further and faster than anything else,
   so the pointer offset is folded into their float keyframes rather than being
   set separately — an animated transform would override a static one. */
.hero__orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(48px);
  opacity: 0.34;
  will-change: transform;
}

.hero__orb--one {
  width: clamp(220px, 26vw, 420px);
  aspect-ratio: 1;
  inset-block-start: -10%;
  inset-inline-end: 58%;
  background: radial-gradient(circle at 35% 35%, rgba(64, 189, 159, 0.9), rgba(15, 105, 89, 0) 70%);
  transform: translate3d(calc(var(--hero-x) * 46px), calc(var(--hero-y) * 34px), 40px);
  animation: hero-float-a 18s var(--ease-in-out) infinite alternate;
}

.hero__orb--two {
  width: clamp(180px, 22vw, 360px);
  aspect-ratio: 1;
  inset-block-end: -14%;
  inset-inline-end: 72%;
  background: radial-gradient(circle at 60% 40%, rgba(240, 160, 75, 0.85), rgba(194, 101, 32, 0) 70%);
  transform: translate3d(calc(var(--hero-x) * -38px), calc(var(--hero-y) * -28px), 70px);
  animation: hero-float-b 22s var(--ease-in-out) infinite alternate;
}

@keyframes hero-float-a {
  from {
    transform: translate3d(calc(var(--hero-x) * 46px), calc(var(--hero-y) * 34px - 18px), 40px) scale(1);
  }
  to {
    transform: translate3d(calc(var(--hero-x) * 46px + 34px), calc(var(--hero-y) * 34px + 26px), 40px) scale(1.14);
  }
}

@keyframes hero-float-b {
  from {
    transform: translate3d(calc(var(--hero-x) * -38px), calc(var(--hero-y) * -28px + 20px), 70px) scale(1.08);
  }
  to {
    transform: translate3d(calc(var(--hero-x) * -38px - 30px), calc(var(--hero-y) * -28px - 24px), 70px) scale(0.94);
  }
}

/* Film grain. It costs one inline SVG and stops the large flat gradients above
   from banding on wide screens. */
.hero__grain {
  position: absolute;
  inset: 0;
  opacity: 0.055;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* One highlight raked across the whole carousel and tracking the pointer, so
   the stage reads as a surface catching light rather than stacked rectangles. */
.hero__glare {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: radial-gradient(
    62% 78% at calc(50% + var(--hero-x) * 22%) calc(38% + var(--hero-y) * 22%),
    rgba(255, 255, 255, 0.08),
    transparent 62%
  );
  opacity: calc(0.08 + var(--hero-lift) * 0.24);
  transition: opacity var(--duration-slow) var(--ease-out);
}

/* ---- Copy ---------------------------------------------------------------- */

.hero__content {
  position: relative;
  z-index: 2;
  max-width: 640px;
  padding-block: var(--space-12);
  color: var(--text-inverse);

  /* Leans toward the pointer while the picture leans away from it: the opposed
     directions are what give the two layers their apparent distance. The
     separation is done in X and Y rather than by standing the copy forward in
     Z — a positive translateZ is magnified about the hero's perspective origin,
     which pushes the copy outward far enough to eat the container's gutter on a
     tablet, and buys nothing here because the copy is never rotated. */
  transform: translate3d(calc(var(--hero-x) * 14px), calc(var(--hero-y) * 10px), 0);
  will-change: transform;
}

.hero__slide[data-align="right"] .hero__content { margin-inline-start: auto; }
.hero__slide[data-align="center"] .hero__content { margin-inline: auto; text-align: center; }

/* A frosted pane behind the copy. It is a sibling rather than a background so
   it can be blurred and lit without touching the text rendering above it. */
.hero__panel {
  position: absolute;
  inset: calc(var(--space-5) * -1) calc(var(--space-8) * -1);
  border-radius: var(--radius-2xl);
  background: linear-gradient(
    145deg,
    rgba(255, 255, 255, 0.14),
    rgba(255, 255, 255, 0.03) 45%,
    rgba(255, 255, 255, 0.08)
  );
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow:
    0 40px 80px rgba(3, 8, 18, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.22);
  backdrop-filter: blur(14px) saturate(1.15);
  -webkit-backdrop-filter: blur(14px) saturate(1.15);
  opacity: calc(0.45 + var(--hero-lift) * 0.4);
  transition: opacity var(--duration-slow) var(--ease-out);

  /* Dissolved rather than cut off at the edge nearest the photograph. A hard
     rounded edge landing in the middle of the picture reads as a slab pasted
     over it; fading it out reads as light falling across the copy. */
  mask-image: linear-gradient(to right, #000 52%, transparent 100%);
  -webkit-mask-image: linear-gradient(to right, #000 52%, transparent 100%);
}

.hero__slide[data-align="right"] .hero__panel {
  mask-image: linear-gradient(to left, #000 52%, transparent 100%);
  -webkit-mask-image: linear-gradient(to left, #000 52%, transparent 100%);
}

.hero__slide[data-align="center"] .hero__panel {
  mask-image: linear-gradient(to right, transparent 0%, #000 22%, #000 78%, transparent 100%);
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 22%, #000 78%, transparent 100%);
}

/* Its own perspective, so the reveal below turns in depth without depending on
   the stage's 3D chain surviving the opacity it animates. */
.hero__copy {
  position: relative;
  perspective: 900px;
}

/* Each element rises out of the depth of the stage in turn, and only on the
   slide being shown — so every rotation replays the reveal. */
.hero__reveal {
  animation: hero-rise 800ms var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 85ms + 120ms);
}

.hero__slide:not([data-state="active"]) .hero__reveal {
  animation: none;
  opacity: 0;
}

@keyframes hero-rise {
  from {
    opacity: 0;
    transform: translate3d(0, 28px, -90px) rotateX(10deg);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.hero__offer {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-block-end: var(--space-4);
  padding: 0.45rem 1rem;
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-900);
  background: linear-gradient(135deg, var(--accent-400), var(--accent-500));
  border-radius: var(--radius-pill);
  box-shadow:
    0 10px 24px rgba(224, 127, 44, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.4);
}

.hero__offer i {
  animation: hero-spark 2.6s var(--ease-in-out) infinite;
}

@keyframes hero-spark {
  0%, 72%, 100% { opacity: 1; transform: scale(1); }
  80% { opacity: 0.55; transform: scale(1.22); }
  88% { opacity: 1; transform: scale(0.96); }
}

.hero__title {
  font-family: var(--font-display);
  font-size: var(--text-4xl);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: var(--tracking-tight);
  color: inherit;
  text-shadow: 0 2px 24px rgba(3, 8, 18, 0.45);
}

.hero__subtitle {
  margin-block-start: var(--space-3);
  font-size: var(--text-lg);
  font-weight: 500;
  color: rgba(255, 255, 255, 0.94);
}

.hero__description {
  margin-block-start: var(--space-3);
  font-size: var(--text-md);
  color: rgba(255, 255, 255, 0.8);
  max-width: 52ch;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-block-start: var(--space-6);
}

.hero__cta {
  position: relative;
  overflow: hidden;
  box-shadow: 0 14px 30px rgba(3, 8, 18, 0.35);
  transition:
    transform var(--duration-base) var(--ease-spring),
    box-shadow var(--duration-base) var(--ease-out),
    background-color var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out);
}

.hero__cta:hover:not(:disabled) {
  transform: translateY(-3px) scale(1.02);
  box-shadow: 0 22px 44px rgba(3, 8, 18, 0.45);
}

.hero__cta i {
  transition: transform var(--duration-base) var(--ease-out);
}

.hero__cta:hover i {
  transform: translateX(4px);
}

/* A highlight that sweeps the button on hover. */
.hero__cta::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: -60%;
  width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.42), transparent);
  transform: skewX(-18deg);
  transition: inset-inline-start 620ms var(--ease-out);
  pointer-events: none;
}

.hero__cta:hover::after {
  inset-inline-start: 120%;
}

.hero__cta--ghost {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.4);
  background: rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.hero__cta--ghost:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.18);
  border-color: rgba(255, 255, 255, 0.65);
  color: #fff;
}

.hero__trust {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-block-start: var(--space-8);
  padding-block-start: var(--space-5);
  border-block-start: 1px solid rgba(255, 255, 255, 0.18);
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.88);
}

/* Glass chips rather than plain text: they read at a glance and give the row
   the same material as the pane behind it. */
.hero__trust-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.4rem 0.8rem;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius-pill);
  transition:
    transform var(--duration-base) var(--ease-out),
    background-color var(--duration-base) var(--ease-out);
}

.hero__trust-item:hover {
  transform: translateY(-2px);
  background: rgba(255, 255, 255, 0.16);
}

.hero__trust-item i { color: var(--brand-300); font-size: 1.05rem; }

/* ---- Carousel chrome ----------------------------------------------------- */

.hero__controls {
  position: absolute;
  inset-block-end: var(--space-6);
  inset-inline-start: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  z-index: 4;
}

/* The visible mark is a 4px bar, but a 4px-tall tap target is unusable on a
   phone. Vertical padding grows the hit area to 24px without changing what is
   drawn: the bar and its progress fill are both inset children. */
.hero__dot {
  position: relative;
  width: 32px;
  height: 24px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transition: width var(--duration-base) var(--ease-out);
}

.hero__dot::before {
  content: "";
  position: absolute;
  inset: 10px 0;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.35);
}

/* The active dot's fill doubles as the autoplay progress bar. */
.hero__dot-fill {
  position: absolute;
  inset-block: 10px;
  inset-inline-start: 0;
  width: 0;
  border-radius: var(--radius-pill);
  background: #fff;
  box-shadow: 0 0 12px rgba(255, 255, 255, 0.6);
}

.hero__dot[aria-current="true"] {
  width: 56px;
}

.hero__dot[aria-current="true"] .hero__dot-fill {
  animation: hero-progress var(--hero-interval) linear forwards;
}

/* Paused autoplay pauses the bar with it, so the two never disagree. */
.hero[data-paused="true"] .hero__dot-fill {
  animation-play-state: paused;
}

@keyframes hero-progress {
  from { width: 0; }
  to { width: 100%; }
}

.hero__arrow {
  position: absolute;
  inset-block-start: 50%;
  z-index: 4;
  display: none;
  place-items: center;
  width: 52px;
  height: 52px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: #fff;
  font-size: 1.2rem;
  cursor: pointer;
  box-shadow: 0 12px 28px rgba(3, 8, 18, 0.32);
  opacity: calc(0.5 + var(--hero-lift) * 0.5);
  transition:
    background-color var(--duration-fast) var(--ease-out),
    transform var(--duration-base) var(--ease-spring),
    opacity var(--duration-base) var(--ease-out);
}

.hero__arrow:hover {
  background: rgba(255, 255, 255, 0.3);
  opacity: 1;
}

/* Angled to face the middle of the stage, which is where the slides turn. */
.hero__arrow--prev {
  inset-inline-start: var(--space-5);
  transform: translateY(-50%) perspective(600px) rotateY(14deg);
}

.hero__arrow--next {
  inset-inline-end: var(--space-5);
  transform: translateY(-50%) perspective(600px) rotateY(-14deg);
}

.hero__arrow--prev:hover { transform: translateY(-50%) perspective(600px) rotateY(14deg) scale(1.08); }
.hero__arrow--next:hover { transform: translateY(-50%) perspective(600px) rotateY(-14deg) scale(1.08); }

@media (min-width: 900px) {
  .hero__arrow { display: grid; }
}

/* ---- Phones -------------------------------------------------------------- */

/* Phones stack the slide instead of overlaying it: the picture leads, then the
   offer, heading, copy, buttons and trust row follow one under the other. On a
   narrow screen the overlay put text across the garment and cropped the model
   to a strip; below the image there is room for both. The depth goes with it —
   there is no pointer to drive the parallax, and the blurred layers are the
   most expensive thing on the page for the least gain at this size. What stays
   is the picture as a raised card, which still reads as depth. */
@media (max-width: 767px) {
  .hero {
    perspective: none;
  }

  .hero__slide {
    display: flex;
    flex-direction: column;

    /* Height comes from the picture plus the copy, not from a viewport minimum. */
    min-height: 0;
  }

  .hero__slide[data-side="before"],
  .hero__slide[data-side="after"] {
    transform: scale(0.94);
  }

  /* Taken out of the absolute layer so it occupies real space at the top. */
  .hero__stage {
    position: relative;
    inset: auto;
    width: 100%;
  }

  .hero__plane {
    position: relative;
    inset: auto;
    transform: none;
    margin: var(--space-4) var(--space-4) 0;
    border-radius: var(--radius-xl);
    overflow: hidden;
    box-shadow: 0 24px 48px rgba(3, 8, 18, 0.5);
  }

  .hero__media {
    position: relative;
    inset: auto;
  }

  /* Natural height, so the whole picture is shown rather than a cropped strip
     of it — the mobile renditions are portrait and fill the width exactly.
     `contain` only matters once the cap below bites: it keeps a short screen
     from being filled edge to edge by the image, leaving the copy below
     reachable without a scroll, and does it without distorting anything. */
  .hero__media img,
  .hero__media video {
    height: auto;
    max-height: 54svh;
    object-fit: contain;
  }

  /* The gradient existed to hold text legible over the photograph. Nothing is
     printed over it now, so it only greyed the picture out. */
  .hero__scrim,
  .hero__aurora,
  .hero__orb,
  .hero__grain,
  .hero__glare,
  .hero__panel {
    display: none;
  }

  .hero__content {
    max-width: none;
    padding-block: var(--space-6) var(--space-8);
    transform: none;

    /* Clears the slide dots, which stay pinned to the bottom of the carousel. */
    padding-block-end: var(--space-12);
  }

  .hero__copy {
    perspective: none;
  }

  /* Alignment is a desktop concern: the copy owns the full width here. */
  .hero__slide[data-align="right"] .hero__content,
  .hero__slide[data-align="center"] .hero__content {
    margin-inline: 0;
    text-align: start;
  }

  .hero__title {
    font-size: var(--text-3xl);
  }

  .hero__trust {
    gap: var(--space-2);
    margin-block-start: var(--space-6);
  }
}

/* A reader who asked for less motion keeps the layout and the depth but gets
   none of the drift: the global reduce rule already collapses transition and
   animation durations, and these stop the ambient loops and the pointer
   parallax from having anything to say. */
@media (prefers-reduced-motion: reduce) {
  .hero__plane,
  .hero__aurora,
  .hero__orb,
  .hero__content,
  .hero__glare,
  .hero__media img,
  .hero__media video {
    transform: none;
    animation: none;
  }

  .hero__offer i {
    animation: none;
  }

  .hero__dot[aria-current="true"] .hero__dot-fill {
    animation: none;
    width: 100%;
  }
}

/* ==========================================================================
   Product card
   ========================================================================== */

.product-card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition:
    border-color var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out);
}

.product-card:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-lg);
  transform: translateY(-4px);
}

.product-card__media {
  position: relative;

  /* A fixed ratio stops the grid reflowing as images load. */
  aspect-ratio: 3 / 4;
  overflow: hidden;

  /* A soft, slightly warm ground rather than flat grey. The catalogue's
     photography is shot on pale backdrops, so the tile reads as part of the
     picture instead of as a box around it. */
  background:
    radial-gradient(120% 90% at 50% 0%, #fff, transparent 70%),
    linear-gradient(170deg, var(--ink-50), var(--ink-100));
}

/* The link and the <picture> both have to fill the tile, or the percentage
   height below resolves against an auto-height parent and silently becomes
   `auto` — which is what made `object-fit` a no-op. Photographs then rendered
   at their natural height: the taller ones overflowed and lost heads and feet
   to the crop, the wider ones left a white band under the picture. */
.product-card__media > a {
  position: absolute;
  inset: 0;
  display: block;
}

/* Both renditions -- the primary and the hover swap -- are wrapped in <picture>
   so each can offer a WebP candidate list. Overlaying them rather than letting
   them stack keeps the second one out of the tile's layout. */
.product-card__media picture {
  position: absolute;
  inset: 0;
  display: block;
}

/* `contain`, not `cover`. The catalogue ships photographs in four different
   ratios (0.55 to 0.80) against one fixed tile, so cropping to fill would cut
   part of the garment out of most of them. Fitting the whole frame guarantees
   every product is shown complete — which is the entire job of the picture on
   a shopping grid — and the ground above fills whatever margin is left. */
.product-card__image {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  transition: opacity var(--duration-slow) var(--ease-out),
    transform var(--duration-slow) var(--ease-out);
}

.product-card__image--secondary {
  position: absolute;
  inset: 0;
  opacity: 0;
}

.product-card:hover .product-card__image--primary { transform: scale(1.05); }
.product-card:hover .product-card__image--secondary { opacity: 1; transform: scale(1.05); }

.product-card__badges {
  position: absolute;
  inset-block-start: var(--space-3);
  inset-inline-start: var(--space-3);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

.product-card__wishlist {
  position: absolute;
  inset-block-start: var(--space-3);
  inset-inline-end: var(--space-3);
  z-index: 2;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.94);
  color: var(--ink-600);
  font-size: 1rem;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: all var(--duration-fast) var(--ease-out);
}

.product-card__wishlist:hover { color: var(--danger-600); transform: scale(1.08); }
.product-card__wishlist[aria-pressed="true"] { color: var(--danger-600); }

/* Secondary actions slide up on hover on pointer devices, and are always
   visible on touch where there is no hover. */
.product-card__actions {
  position: absolute;
  inset-inline: var(--space-3);
  inset-block-end: var(--space-3);
  z-index: 2;
  display: flex;
  gap: var(--space-2);
  opacity: 0;
  transform: translateY(10px);
  transition:
    opacity var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out);
}

.product-card:hover .product-card__actions,
.product-card:focus-within .product-card__actions {
  opacity: 1;
  transform: translateY(0);
}

@media (hover: none) {
  .product-card__actions { opacity: 1; transform: none; }
}

.product-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: var(--space-2);
  padding: var(--space-4);
}

.product-card__brand {
  font-size: var(--text-2xs);
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--text-muted);
}

.product-card__title {
  font-size: var(--text-base);
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink-800);
}

.product-card__title a { color: inherit; }
.product-card__title a:hover { color: var(--brand-700); }

.product-card__price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block-start: auto;
  padding-block-start: var(--space-2);
}

.price {
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}

.price--sale { color: var(--danger-600); }

.price--was {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-muted);
  text-decoration: line-through;
}

/* The saving is the number that closes the sale, so it gets a chip of its own
   rather than being one more run of small text beside the price. */
.price--save {
  padding: 0.1rem 0.4rem;
  font-size: var(--text-xs);
  font-weight: 650;
  color: var(--success-700);
  background: var(--success-50);
  border-radius: var(--radius-xs);
}

.product-card__stock {
  font-size: var(--text-xs);
  font-weight: 600;
}

.product-card__stock--low { color: var(--warning-700); }
.product-card__stock--out { color: var(--danger-600); }
.product-card__stock--in { color: var(--success-700); }

/* Flash sale claimed-stock bar. */
.stock-meter {
  height: 6px;
  background: var(--ink-200);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.stock-meter__fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent-500), var(--danger-500));
  border-radius: var(--radius-pill);
  transition: width var(--duration-slow) var(--ease-out);
}

/* ==========================================================================
   Countdown timer
   ========================================================================== */

.countdown {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.countdown__unit {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 46px;
  padding: var(--space-2);
  background: var(--ink-900);
  color: #fff;
  border-radius: var(--radius-sm);
}

.countdown__value {
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.countdown__label {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  opacity: 0.7;
}

.countdown--inverse .countdown__unit {
  background: rgba(255, 255, 255, 0.16);
  backdrop-filter: blur(4px);
}

/* ==========================================================================
   Category and brand tiles
   ========================================================================== */

.category-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 640px) {
  .category-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
  .category-grid {
    gap: var(--space-5);
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }
}

/* A collection is picked by look, so the artwork leads and the label reads
   underneath it. The tile used to be a 62px circle floating in a mostly empty
   card, which gave the largest element on the row the least to say. */
.category-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  transition:
    border-color var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out);
}

.category-tile:hover {
  border-color: var(--accent, var(--brand-500));
  box-shadow: var(--shadow-lg);
  transform: translateY(-4px);
}

/* 4:3, and the collection artwork is now drawn at 4:3 to match, so the tile shows
   the whole composition rather than a band cropped out of a portrait frame. The
   art carries no caption either -- the tile prints the name and count below. */
.category-tile__figure {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 50% 0%, #fff, transparent 70%),
    linear-gradient(170deg, var(--ink-50), var(--ink-100));
}

/* `contents` keeps the <img> as the figure's grid item: the <picture> exists only
   to offer a WebP candidate and should not become a box of its own. */
.category-tile__figure picture {
  display: contents;
}

/* `contain`, not `cover`, for the same reason the product card uses it: a tile
   has to take whatever picture it is given. The generated artwork and the cropped
   photography are both 4:3 and fill this box exactly, so nothing changes for them
   -- but a portrait or panoramic image set later through the admin is shown whole
   instead of having its middle band cropped out, and the tile's own soft ground
   fills whatever margin is left rather than a hard letterbox. */
.category-tile__image {
  /* Pinned to the figure rather than sized with percentages. `height: 100%` does
     not resolve against a grid area whose height comes from `aspect-ratio`, so the
     image was laid out at its own intrinsic ratio and overflowed -- a 3:4 picture
     got a 205x308 box inside a 205x154 frame and `overflow: hidden` cut the
     bottom off. `cover` hid that; `contain` made it obvious. Absolute inset gives
     the image the frame's exact box, which is what object-fit needs to work
     against. The figure is already `position: relative`. */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  transition: transform var(--duration-slow) var(--ease-out);
}

.category-tile:hover .category-tile__image {
  transform: scale(1.06);
}

/* The accent colour washes up from the foot of the picture, which ties the
   tile to its collection and keeps the label legible over any artwork. */
.category-tile__figure::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--accent, var(--brand-700)) 55%, transparent) 0%,
    transparent 52%
  );
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-out);
}

.category-tile:hover .category-tile__figure::after {
  opacity: 1;
}

.category-tile__icon {
  display: grid;
  place-items: center;
  width: 62px;
  height: 62px;
  font-size: 1.6rem;
  color: var(--accent, var(--brand-600));
  background: color-mix(in srgb, var(--accent, var(--brand-600)) 12%, transparent);
  border-radius: 50%;
}

/* Two rows: the name across the top, then the count and the hover cue sharing a
   line. The cue used to be a third row, which reserved its height permanently and
   left every tile with a band of empty white under the label. */
.category-tile__body {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: var(--space-1) var(--space-3);
  flex: 1;
  padding: var(--space-4);
}

.category-tile__name {
  grid-column: 1 / -1;
}

.category-tile__name {
  font-size: var(--text-base);
  font-weight: 650;
  color: var(--ink-800);
}

.category-tile__count {
  font-size: var(--text-xs);
  color: var(--text-muted);
}

/* Sits beside the count and fades in on hover, so revealing it costs no layout. */
.category-tile__cue {
  display: flex;
  align-items: center;
  justify-self: end;
  gap: var(--space-2);
  font-size: var(--text-xs);
  font-weight: 650;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--accent, var(--brand-700));
  opacity: 0;
  transform: translateX(-6px);
  transition:
    opacity var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out);
}

.category-tile:hover .category-tile__cue,
.category-tile:focus-visible .category-tile__cue {
  opacity: 1;
  transform: none;
}

@media (hover: none) {
  .category-tile__cue { opacity: 1; transform: none; }
}

/* Explicit column counts rather than auto-fit: with eight brands, auto-fit packs
   as many as will fit and drops the remainder onto a short orphan row. Four and
   two divide the set evenly at every breakpoint. */
.brand-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 700px) {
  .brand-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.brand-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-5) var(--space-4);
  min-height: 116px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition:
    border-color var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out);
}

.brand-tile:hover {
  border-color: var(--brand-400);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

.brand-tile:focus-visible {
  outline: 3px solid var(--brand-500);
  outline-offset: 2px;
}

/* Fixed height so wordmarks of different proportions sit on one baseline
   across the row instead of each tile centring its own logo differently. */
.brand-tile__mark {
  display: grid;
  place-items: center;
  width: 100%;
  height: 42px;
}

.brand-tile img {
  max-width: 100%;
  max-height: 42px;
  width: auto;

  /* Held slightly back rather than fully desaturated: at grayscale(1) and 65%
     these wordmarks read as disabled instead of as brands. */
  opacity: 0.9;
  transition: opacity var(--duration-base) var(--ease-out);
}

.brand-tile:hover img {
  opacity: 1;
}

.brand-tile__fallback {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--ink-600);
  text-align: center;
}

.brand-tile__meta {
  font-size: var(--text-xs);
  color: var(--text-muted);
  letter-spacing: var(--tracking-wide);
}

/* ==========================================================================
   USP strip and trust badges
   ========================================================================== */

/* Two USPs side by side need roughly 420px; below that the icon plus the
   padding leaves too little room for the label and it spills. */
.usp-strip {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 420px) {
  .usp-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 900px) {
  .usp-strip { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.usp {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.usp__icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  font-size: 1.35rem;
  color: var(--brand-700);
  background: var(--brand-50);
  border-radius: var(--radius-md);
}

/* The text column must be allowed to shrink; a flex item defaults to its
   min-content width, which a long label would push past the tile. */
.usp > span {
  min-width: 0;
}

.usp__title {
  font-size: var(--text-base);
  font-weight: 650;
  color: var(--ink-800);
}

.usp__text {
  font-size: var(--text-xs);
  color: var(--text-muted);
}

/* Six badges: two, three and six all divide it evenly, so no row is left short. */
.trust-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 700px) {
  .trust-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 1200px) {
  .trust-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* An explicit flex column, not the .stack utility: .stack only sets a margin
   between siblings, so inline children (an icon and two spans) run together on
   one line instead of stacking. */
.trust-badge {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  height: 100%;
  padding: var(--space-6) var(--space-4);
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  transition:
    border-color var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out);
}

.trust-badge:hover {
  border-color: var(--brand-300, var(--brand-400));
  box-shadow: var(--shadow-sm);
}

.trust-badge__icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  margin-block-end: var(--space-1);
  font-size: 1.35rem;
  color: var(--brand-700);
  background: var(--brand-50);
  border-radius: 50%;
}

.trust-badge__title {
  font-size: var(--text-base);
  font-weight: 650;
  line-height: 1.3;
  color: var(--ink-800);
  text-wrap: balance;
}

.trust-badge__text {
  font-size: var(--text-xs);
  line-height: var(--leading-normal);
  color: var(--text-muted);
  text-wrap: pretty;
}

/* ==========================================================================
   Testimonials
   ========================================================================== */

.testimonial {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  height: 100%;
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.testimonial__quote {
  flex: 1;
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  color: var(--text-secondary);
}

.testimonial__author {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--border);
}

.testimonial__avatar {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--brand-700);
  background: var(--brand-100);
  border-radius: 50%;
  object-fit: cover;
}

.testimonial__name {
  font-size: var(--text-base);
  font-weight: 650;
  color: var(--ink-800);
}

.testimonial__meta {
  font-size: var(--text-xs);
  color: var(--text-muted);
}

/* Rating summary shown above the testimonial rail. */
.rating-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6);
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.rating-summary__score {
  font-family: var(--font-display);
  font-size: var(--text-4xl);
  font-weight: 700;
  line-height: 1;
  color: var(--ink-900);
}

.rating-bars {
  flex: 1;
  min-width: 220px;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.rating-bar {
  display: grid;
  grid-template-columns: 48px 1fr 42px;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.rating-bar__track {
  height: 7px;
  background: var(--ink-200);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.rating-bar__fill {
  height: 100%;
  background: var(--accent-500);
  border-radius: var(--radius-pill);
}

/* ==========================================================================
   Newsletter
   ========================================================================== */

.newsletter {
  position: relative;
  overflow: hidden;
  padding: clamp(2.5rem, 5vw, 4rem);
  background: linear-gradient(135deg, var(--brand-800), var(--brand-600));
  border-radius: var(--radius-xl);
  color: var(--text-inverse);
  text-align: center;
}

/* Decorative wash; kept subtle so it never competes with the form. */
.newsletter::before {
  content: "";
  position: absolute;
  inset-block-start: -40%;
  inset-inline-end: -10%;
  width: 460px;
  height: 460px;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.13), transparent 68%);
  pointer-events: none;
}

.newsletter__inner {
  position: relative;
  max-width: 620px;
  margin-inline: auto;
}

.newsletter__title {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  color: inherit;
}

.newsletter__text {
  margin-block-start: var(--space-3);
  color: rgba(255, 255, 255, 0.84);
}

.newsletter__form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-block-start: var(--space-6);
}

.newsletter__form .input {
  flex: 1;
  min-width: 220px;
  border-color: transparent;
}

.newsletter__note {
  margin-block-start: var(--space-4);
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.66);
}

/* ==========================================================================
   Shop layout, filters and toolbar
   ========================================================================== */

.shop-layout {
  display: grid;
  gap: var(--space-8);
  align-items: start;
}

@media (min-width: 1024px) {
  .shop-layout { grid-template-columns: 272px 1fr; }
}

.filter-sidebar {
  display: none;
  position: sticky;

  /* Sticks below the whole header — the brand row *and* the primary nav —
     rather than disappearing under the nav. */
  top: calc(var(--header-offset) + var(--space-4));
  max-height: calc(100svh - var(--header-offset) - var(--space-8));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-inline-end: var(--space-2);
}

@media (min-width: 1024px) {
  .filter-sidebar { display: block; }
}

.filter-group { border-block-end: 1px solid var(--border); }

.filter-group__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) 0;
  font-size: var(--text-base);
  font-weight: 650;
  color: var(--ink-800);
  background: none;
  border: 0;
  cursor: pointer;
}

.filter-group__body {
  padding-block-end: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-height: 260px;
  overflow-y: auto;
}

.filter-option {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-base);
  color: var(--text-secondary);
  cursor: pointer;
}

.filter-option:hover { color: var(--ink-800); }

.filter-option__count {
  margin-inline-start: auto;
  font-size: var(--text-xs);
  color: var(--ink-400);
}

/* Colour swatch filters. */
.swatch-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.swatch {
  position: relative;

  /* The colour filter renders each swatch as a <span>, which is an inline box:
     width and height do not apply to one, so every swatch in the shop sidebar
     collapsed to the 4px its two borders occupied and the filter was unusable.
     The product page draws its swatches as <button>, which is inline-block
     already, which is why the fault only ever showed on the listing. */
  display: inline-block;
  width: 34px;
  height: 34px;
  border: 2px solid var(--border);
  border-radius: 50%;
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-out);
}

.swatch:hover { transform: scale(1.1); }

.swatch[aria-pressed="true"] {
  border-color: var(--brand-600);
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--brand-600);
}

.swatch[data-unavailable="true"] {
  opacity: 0.35;
  cursor: not-allowed;
}

.swatch[data-unavailable="true"]::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top right,
    transparent 46%,
    var(--danger-500) 46%,
    var(--danger-500) 54%,
    transparent 54%
  );
  border-radius: 50%;
}

.shop-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-4);
  border-block-end: 1px solid var(--border);
  margin-block-end: var(--space-6);
}

.view-toggle {
  display: none;
  gap: var(--space-1);
  padding: 3px;
  background: var(--ink-100);
  border-radius: var(--radius-md);
}

@media (min-width: 640px) { .view-toggle { display: flex; } }

.view-toggle__btn {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink-500);
  cursor: pointer;
}

.view-toggle__btn[aria-pressed="true"] {
  background: var(--surface);
  color: var(--brand-700);
  box-shadow: var(--shadow-xs);
}

.active-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block-end: var(--space-5);
}

/* ==========================================================================
   Product detail
   ========================================================================== */

.product-detail {
  display: grid;

  /* `minmax(0, …)` everywhere below: a grid or flex item defaults to its
     min-content width, so the thumbnail strip would widen the whole column
     instead of scrolling inside itself. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-8);
}

@media (min-width: 1024px) {
  .product-detail { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: var(--space-12); }
}

.gallery {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
}

/* The thumbnail column is only reserved when there are thumbnails to put in
   it. A product with a single photograph renders no strip, and the column was
   still there — so the one image it does have landed in the 84px track and was
   shown at thumbnail size beside an empty half-page. */
@media (min-width: 640px) {
  .gallery:has(.gallery__thumbs) { grid-template-columns: 84px minmax(0, 1fr); }
}

.gallery__thumbs {
  display: flex;
  gap: var(--space-2);
  order: 2;
  min-width: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

@media (min-width: 640px) {
  .gallery__thumbs {
    order: 0;
    flex-direction: column;
    max-height: 560px;
    overflow-y: auto;
    overflow-x: hidden;
  }
}

.gallery__thumb {
  flex-shrink: 0;
  width: 72px;
  aspect-ratio: 3 / 4;
  padding: 0;
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--ink-100);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-out);
}

.gallery__thumb[aria-current="true"] { border-color: var(--brand-600); }
.gallery__thumb img { width: 100%; height: 100%; object-fit: contain; }

.gallery__main {
  position: relative;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 50% 0%, #fff, transparent 70%),
    linear-gradient(170deg, var(--ink-50), var(--ink-100));
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);

  /* Signals that the image responds to pointer movement for zoom. */
  cursor: zoom-in;
}

/* `contain` for the same reason as the grid tile: this is the one place the
   customer inspects the garment before buying it, so no part of the frame may
   be cropped away to fill the box. Zoom below still magnifies to 2x. */
.gallery__main img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: transform var(--duration-slow) var(--ease-out);
}

.gallery__main[data-zoomed="true"] img {
  transform: scale(2);
  transform-origin: var(--zoom-x, 50%) var(--zoom-y, 50%);
}

.variant-group + .variant-group { margin-block-start: var(--space-5); }

.variant-group__label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-block-end: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 650;
  color: var(--ink-700);
}

.variant-group__selected { font-weight: 400; color: var(--text-muted); }

.variant-options { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.variant-option {
  min-width: 48px;
  padding: 0.6rem 0.9rem;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-700);
  background: var(--surface);
  border: 1.5px solid var(--border-strong);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-out);
}

.variant-option:hover:not([data-unavailable="true"]) { border-color: var(--brand-500); }

.variant-option[aria-pressed="true"] {
  color: var(--brand-800);
  background: var(--brand-50);
  border-color: var(--brand-600);
}

.variant-option[data-unavailable="true"] {
  opacity: 0.42;
  cursor: not-allowed;
  text-decoration: line-through;
}

.buy-box {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-5);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

/* The buy-box action rows stay on one line while they fit. A button's label is
   `nowrap`, so a flex item cannot shrink below it — on a narrow phone the row
   must be allowed to wrap rather than squash the quantity stepper and push the
   page sideways. */
.buy-box__row {
  flex-wrap: nowrap;
}

.buy-box__row .quantity {
  flex-shrink: 0;
}

@media (max-width: 420px) {
  .buy-box__row {
    flex-wrap: wrap;
  }

  /* Once wrapped, the primary action takes the full width of its own line. */
  .buy-box__row > .btn--lg {
    flex: 1 0 100%;
  }
}

.trust-list {
  display: grid;
  gap: var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.trust-list li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.trust-list i {
  flex-shrink: 0;
  margin-block-start: 2px;
  color: var(--brand-600);
}

/* ==========================================================================
   Cart and checkout
   ========================================================================== */

.checkout-layout {
  display: grid;
  gap: var(--space-8);
  align-items: start;
}

@media (min-width: 1024px) {
  .checkout-layout { grid-template-columns: minmax(0, 1fr) 380px; gap: var(--space-10); }
}

.order-summary {
  position: sticky;
  top: calc(var(--header-offset) + var(--space-4));
  padding: var(--space-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.summary-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-2);
  font-size: var(--text-base);
  color: var(--text-secondary);
}

.summary-row--total {
  margin-block-start: var(--space-3);
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--border);
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--ink-900);
}

.summary-row--discount { color: var(--success-700); font-weight: 600; }

.cart-line {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: var(--space-4);
  padding-block: var(--space-5);
  border-block-end: 1px solid var(--border);
}

.cart-line__image {
  width: 88px;
  aspect-ratio: 3 / 4;
  object-fit: contain;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background:
    radial-gradient(120% 90% at 50% 0%, #fff, transparent 70%),
    linear-gradient(170deg, var(--ink-50), var(--ink-100));
}

/* Free-delivery progress meter shown in the cart. */
.progress-meter {
  height: 8px;
  background: var(--ink-200);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.progress-meter__fill {
  height: 100%;
  background: linear-gradient(90deg, var(--brand-500), var(--brand-600));
  border-radius: var(--radius-pill);
  transition: width var(--duration-slow) var(--ease-out);
}

/* ---- Checkout stepper -------------------------------------------------- */

.stepper {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-block-end: var(--space-8);
}

.stepper__step {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-muted);
}

.stepper__marker {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  font-size: var(--text-xs);
  border: 1.5px solid var(--border-strong);
  border-radius: 50%;
  background: var(--surface);
}

.stepper__step[data-state="current"] { color: var(--brand-700); }

.stepper__step[data-state="current"] .stepper__marker {
  background: var(--brand-600);
  border-color: var(--brand-600);
  color: #fff;
}

.stepper__step[data-state="done"] { color: var(--success-700); }

.stepper__step[data-state="done"] .stepper__marker {
  background: var(--success-500);
  border-color: var(--success-500);
  color: #fff;
}

.stepper__divider {
  flex: 1;
  height: 1.5px;
  background: var(--border);
  min-width: 16px;
}

/* Three labelled steps do not fit across a phone. Only the step the customer
   is actually on keeps its label; the others stay as numbered markers, which
   is enough to convey position. */
@media (max-width: 639px) {
  .stepper {
    margin-block-end: var(--space-6);
  }

  .stepper__step > :not(.stepper__marker) {
    display: none;
  }

  .stepper__step[data-state="current"] > :not(.stepper__marker) {
    display: inline;
  }
}

/* ---- Payment method picker ---------------------------------------------- */

.payment-option {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4);
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-out);
}

.payment-option:hover { border-color: var(--brand-400); }

.payment-option:has(input:checked) {
  border-color: var(--brand-600);
  background: var(--brand-50);
}

.payment-option__logo {
  width: 46px;
  height: 32px;
  object-fit: contain;
  flex-shrink: 0;
}

/* ==========================================================================
   Order tracking timeline
   ========================================================================== */

.timeline { position: relative; padding-inline-start: var(--space-8); }

.timeline::before {
  content: "";
  position: absolute;
  inset-block: 12px;
  inset-inline-start: 11px;
  width: 2px;
  background: var(--border);
}

.timeline__item {
  position: relative;
  padding-block-end: var(--space-6);
}

.timeline__item:last-child { padding-block-end: 0; }

.timeline__marker {
  position: absolute;
  inset-inline-start: calc(var(--space-8) * -1);
  inset-block-start: 0;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  font-size: 11px;
  color: var(--ink-400);
  background: var(--surface);
  border: 2px solid var(--border);
  border-radius: 50%;
}

.timeline__item[data-state="done"] .timeline__marker {
  color: #fff;
  background: var(--success-500);
  border-color: var(--success-500);
}

.timeline__item[data-state="current"] .timeline__marker {
  color: #fff;
  background: var(--brand-600);
  border-color: var(--brand-600);

  /* A soft pulse draws the eye to where the order actually is. */
  box-shadow: 0 0 0 4px rgba(18, 131, 108, 0.16);
}

.timeline__title { font-weight: 650; color: var(--ink-800); }
.timeline__item[data-state="pending"] .timeline__title { color: var(--text-muted); }

/* ==========================================================================
   Footer
   ========================================================================== */

.site-footer {
  margin-block-start: auto;
  background: var(--ink-900);
  color: rgba(255, 255, 255, 0.72);
}

.footer__main {
  display: grid;
  gap: var(--space-8);
  padding-block: var(--space-16);
  grid-template-columns: 1fr;
}

@media (min-width: 700px) {
  .footer__main { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
  .footer__main { grid-template-columns: 1.6fr repeat(3, minmax(0, 1fr)); gap: var(--space-10); }
}

.footer__heading {
  margin-block-end: var(--space-4);
  font-size: var(--text-sm);
  font-weight: 700;
  color: #fff;
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}

.footer__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer__links a {
  color: rgba(255, 255, 255, 0.72);
  font-size: var(--text-base);
}

.footer__links a:hover { color: #fff; }

.footer__contact {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  font-size: var(--text-base);
}

.footer__contact-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.footer__contact-item i {
  margin-block-start: 3px;
  color: var(--brand-400);
}

.footer__social {
  display: flex;
  gap: var(--space-2);
  margin-block-start: var(--space-5);
}

.footer__social a {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  font-size: 1.05rem;
  color: rgba(255, 255, 255, 0.8);
  background: rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-md);
  transition: all var(--duration-fast) var(--ease-out);
}

.footer__social a:hover {
  color: #fff;
  background: var(--brand-600);
  transform: translateY(-2px);
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-6);
  border-block-start: 1px solid rgba(255, 255, 255, 0.1);
  font-size: var(--text-sm);
}

.payment-methods {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.payment-methods img,
.payment-methods span {
  display: grid;
  place-items: center;
  height: 30px;
  min-width: 48px;
  padding-inline: var(--space-2);
  font-size: 10px;
  font-weight: 700;
  color: var(--ink-700);
  background: #fff;
  border-radius: var(--radius-xs);
}

/* ==========================================================================
   Floating actions
   ========================================================================== */

.floating-actions {
  position: fixed;
  inset-block-end: calc(var(--space-5) + var(--safe-bottom));
  inset-inline-end: var(--space-5);
  z-index: var(--z-sticky);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  transition: inset-block-end var(--duration-base) var(--ease-out);
}

/* On a product page the sticky buy bar occupies the bottom of the screen, so
   the floating buttons step up over it instead of covering "Add to bag". */
body:has(.sticky-buy-bar[data-visible="true"]) .floating-actions {
  inset-block-end: calc(var(--space-4) + var(--buy-bar-height) + var(--safe-bottom));
}

.floating-btn {
  display: grid;
  place-items: center;
  width: 50px;
  height: 50px;
  border: 0;
  border-radius: 50%;
  font-size: 1.25rem;
  color: #fff;
  cursor: pointer;
  box-shadow: var(--shadow-lg);
  transition: transform var(--duration-fast) var(--ease-out);
}

.floating-btn:hover { transform: scale(1.08); color: #fff; }

.floating-btn--whatsapp { background: #25d366; }
.floating-btn--call { background: var(--brand-600); }
.floating-btn--top { background: var(--ink-800); }

.floating-btn--top[hidden] { display: none; }

/* Mobile sticky add-to-cart bar on the product page. */
.sticky-buy-bar {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  padding-block-end: calc(var(--space-3) + var(--safe-bottom));
  background: var(--surface);
  border-block-start: 1px solid var(--border);
  box-shadow: 0 -4px 16px rgba(15, 23, 42, 0.08);
  transform: translateY(100%);
  transition: transform var(--duration-base) var(--ease-out);
}

/* Long product names must not squash the thumbnail or push "Add" off screen;
   the name column between them already carries min-width: 0 so it truncates. */
.sticky-buy-bar > img,
.sticky-buy-bar .btn {
  flex-shrink: 0;
}

.sticky-buy-bar[data-visible="true"] { transform: translateY(0); }

@media (min-width: 1024px) {
  .sticky-buy-bar { display: none; }
}

/* ==========================================================================
   Account dashboard
   ========================================================================== */

.account-layout {
  display: grid;
  gap: var(--space-8);
  align-items: start;
}

@media (min-width: 900px) {
  .account-layout { grid-template-columns: 250px 1fr; }
}

.account-nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.account-nav__link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--ink-700);
  border-radius: var(--radius-md);
  transition: all var(--duration-fast) var(--ease-out);
}

.account-nav__link:hover { background: var(--ink-100); color: var(--ink-900); }

.account-nav__link[aria-current="page"] {
  background: var(--brand-50);
  color: var(--brand-800);
  font-weight: 650;
}

.account-nav__badge {
  margin-inline-start: auto;
  padding: 1px 7px;
  font-size: var(--text-2xs);
  font-weight: 700;
  background: var(--ink-200);
  border-radius: var(--radius-pill);
}

.stat-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.stat-card__label {
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--text-muted);
}

.stat-card__value {
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   Recent purchase toast (social proof)
   ========================================================================== */

.purchase-toast {
  position: fixed;
  inset-block-end: var(--space-5);
  inset-inline-start: var(--space-5);
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: 330px;
  padding: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  animation: toast-in var(--duration-base) var(--ease-spring);
}

.purchase-toast[hidden] { display: none; }

.purchase-toast img {
  width: 48px;
  height: 48px;
  object-fit: contain;
  border-radius: var(--radius-sm);
  background: var(--ink-100);
}

@media (max-width: 640px) {
  .purchase-toast { display: none; }
}
