/* ==========================================================================
   Components — nav, product card, grids, gallery, filters, cart, modal
   ========================================================================== */

/* --------------------------------------------------------------------------
   Navigation
   -------------------------------------------------------------------------- */

.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 900;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  gap: var(--s-5);
  padding-inline: var(--gutter);
  background: rgba(11, 11, 13, 0.72);
  backdrop-filter: blur(18px) saturate(140%);
  border-bottom: 1px solid transparent;
  transition: background var(--med), border-color var(--med);
}
.nav.is-scrolled {
  background: rgba(11, 11, 13, 0.94);
  border-bottom-color: var(--line);
}

.nav__brand {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  flex-shrink: 0;
}
.nav__brand span { color: var(--accent); }

.nav__links {
  display: flex;
  align-items: center;
  gap: var(--s-5);
  margin-left: auto;
}

.nav__link {
  position: relative;
  font-family: var(--f-display);
  font-weight: 500;
  font-size: var(--t-sm);
  letter-spacing: var(--track-nav);
  text-transform: uppercase;
  color: var(--ink-2);
  padding-block: 6px;
  transition: color var(--fast);
}
.nav__link:hover { color: var(--ink); }
.nav__link.is-active { color: var(--ink); }
.nav__link.is-active::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--accent);
}

/* Shop dropdown — the only nested menu, and it is built from real categories */
.nav__shop { position: relative; }
.nav__menu {
  /* Same guard as .scrim and the rest: hidden by an animation, so it must stop
     taking taps by a property that does not animate. */
  pointer-events: none;
  position: absolute;
  top: calc(100% + 14px);
  left: 50%;
  translate: -50% 0;
  /* 460px is wider than a 390px phone, so the menu pushed the page sideways and
     the whole site scrolled horizontally. Cap it to the viewport. */
  min-width: min(460px, calc(100vw - 2 * var(--gutter)));
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 2px;
  padding: var(--s-3);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
  opacity: 0;
  visibility: hidden;
  translate: -50% -6px;
  transition: opacity var(--fast), translate var(--fast), visibility 0s linear var(--fast);
}
/* Desktop only. On a phone .nav__shop sits inside the nav panel and the menu is
   folded open within it, so this rule has no job there - but it is specific
   enough to beat the mobile rule, and `:focus-within` fires as soon as anything
   inside the panel takes focus. That would assert `visible` and `auto` on the
   menu while its parent panel is closed, putting the range links back over the
   page. Fenced off rather than fought with specificity. */
@media (min-width: 861px) {
  .nav__shop:hover .nav__menu,
  .nav__shop:focus-within .nav__menu {
    pointer-events: auto;
    opacity: 1;
    visibility: visible;
    translate: -50% 0;
    transition: opacity var(--fast), translate var(--fast), visibility 0s;
  }
}

.nav__menu-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  padding: 11px 13px;
  border-radius: var(--radius);
  transition: background var(--fast);
}
.nav__menu-item:hover { background: var(--surface-3); }
.nav__menu-item b {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: var(--t-sm);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.nav__menu-item i {
  font-family: var(--f-mono);
  font-style: normal;
  font-size: var(--t-3xs);
  color: var(--ink-3);
}

.nav__cart {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 8px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font-family: var(--f-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  transition: border-color var(--fast), background var(--fast);
}
.nav__cart:hover { border-color: var(--ink); background: rgba(255, 255, 255, 0.04); }
.nav__cart-count {
  min-width: 19px;
  height: 19px;
  padding-inline: 5px;
  display: grid;
  place-items: center;
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: 10px;
  font-weight: 600;
  font-size: 10px;
}
.nav__cart-count[data-empty='true'] { background: var(--surface-3); color: var(--ink-3); }

.nav__burger {
  display: none;
  width: 40px; height: 40px;
  margin-left: auto;
  position: relative;
}
.nav__burger span {
  position: absolute;
  left: 9px; right: 9px;
  height: 2px;
  background: var(--ink);
  transition: transform var(--med), opacity var(--fast);
}
.nav__burger span:nth-child(1) { top: 14px; }
.nav__burger span:nth-child(2) { top: 20px; }
.nav__burger span:nth-child(3) { top: 26px; }
.nav__burger.is-open span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav__burger.is-open span:nth-child(2) { opacity: 0; }
.nav__burger.is-open span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* --------------------------------------------------------------------------
   Product card
   -------------------------------------------------------------------------- */

.grid {
  display: grid;
  /* Two across on a phone.
     auto-fill with a 240px floor collapsed to ONE column at 390px, so each card
     was ~350px wide — while `sizes` still told the browser 46vw (179px), and it
     duly downloaded the 400px file for a 350px slot. At 3x pixel density that is
     a third of the resolution needed, which is why the grid looked soft on a
     phone and sharp on a desktop. Two columns makes 46vw true again. */
  grid-template-columns: repeat(auto-fill, minmax(min(240px, 46%), 1fr));
  gap: clamp(var(--s-4), 2.2vw, var(--s-6)) clamp(var(--s-3), 1.6vw, var(--s-5));
}
.grid--wide { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }

:root {
  /* The halo, written once. Any rule that sets box-shadow on a frame must
     include these, or it silently erases the glow — which is exactly what the
     photo-tier well did, leaving every on-model card without one. */
  --halo-ring: 0 0 0 1px color-mix(in srgb, var(--halo, var(--accent)) 85%, transparent);
  --halo-glow: 0 0 30px -6px color-mix(in srgb, var(--halo, var(--accent)) 65%, transparent);
}

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  cursor: pointer;
}

/* The well, in two states.
   A photograph fills it edge to edge and keeps its own frame — it is a picture,
   not a garment floating in space. A cut-out sits on graphite with a grounding
   shadow. Square, because the garments are roughly square once cropped to their
   content and a 4:5 well letterboxed them down to a third of the card. */
.card__well {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background:
    radial-gradient(118% 86% at 50% 4%, var(--well-from) 0%, var(--well-to) 62%, var(--ground) 100%);
  transition: transform var(--med), box-shadow var(--med);
}
/* A photograph fills its card. The vignette lets a studio backdrop meet the
   dark page instead of ending at a hard bright edge; no pixel is recoloured. */
.card__well[data-tier='studio'],
.card__well[data-tier='photo'] {
  background: var(--surface);
  /* Keeps its inner hairline AND the halo. Setting only the hairline here beat
     the halo rule on specificity and blanked the glow on every photo card. */
  box-shadow: inset 0 0 0 1px var(--line), var(--halo-ring), var(--halo-glow);
}
.card__well[data-tier='studio'] .card__img,
.card__well[data-tier='photo'] .card__img {
  inset: 0; width: 100%; height: 100%;
  object-fit: cover;
  filter: none;
}
.card__well[data-tier='studio']::after,
.card__well[data-tier='photo']::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background: radial-gradient(124% 108% at 50% 42%, transparent 54%, rgba(9, 9, 11, 0.58) 100%);
}
@media (hover: hover) {
  .card:hover .card__well {
    transform: translateY(-3px);
    box-shadow: 0 18px 44px var(--well-shadow), var(--halo-ring), var(--halo-glow);
  }
}

.card__img {
  position: absolute;
  inset: 3%;
  width: 94%;
  height: 94%;
  object-fit: contain;
  transition: opacity var(--med);
}
/* Cut-outs get a grounding shadow so they sit in the well rather than float. */
.card__well[data-tier='cutout'] .card__img {
  filter: drop-shadow(0 14px 22px rgba(0, 0, 0, 0.45));
}
.card__img--back { opacity: 0; }

/* The hover image can be a different kind from the lead one — a cut-out back
   behind an on-model front. Each image carries its own treatment so neither is
   forced into the other's framing. */
.card__img[data-tier='studio'],
.card__img[data-tier='photo'] { inset: 0; width: 100%; height: 100%; object-fit: cover; filter: none; }
.card__img[data-tier='cutout'] {
  inset: 3%; width: 94%; height: 94%; object-fit: contain;
  filter: drop-shadow(0 14px 22px rgba(0, 0, 0, 0.45));
}
/* Unprocessed source: the manufacturer's own render, on its own white ground.

   This used to be inset as a small plate, on the reasoning that a white square
   should not take over a dark card. That was right while it was a rare
   fallback. It is now the lead image on every beanie and every rebuilt baby
   tee, and a row of small white plates floating on dark cards looks like a
   loading error rather than a decision.

   So the white becomes the card: the well takes the same ground as the image,
   the product is centred in it, and it reads as a product photographed on
   white - which is what it is - while keeping the hairline and the halo that
   every other card has. */
.card__well[data-tier='raw'] .card__img,
.card__img[data-tier='raw'] {
  inset: 4%; width: 92%; height: 92%; object-fit: contain;
}

/* Desktop only: cross-fade to the back print. Never the sole route to it —
   the product page always shows both. */
@media (hover: hover) and (pointer: fine) {
  .card:hover .card__img--front { opacity: 0; }
  .card:hover .card__img--back  { opacity: 1; }
  .card:hover .card__view { opacity: 1; }
}

.card__view {
  position: absolute;
  right: 10px; bottom: 10px;
  font-family: var(--f-mono);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  padding: 4px 7px;
  border-radius: 2px;
  background: rgba(11, 11, 13, 0.76);
  color: var(--ink-2);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity var(--fast);
}
.card__view { z-index: 2; }

/* In the item, above the type. No longer an overlay: on a chest print the
   floating badge landed squarely on the artwork. */
.card__badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
  margin-bottom: 6px;
}

.badge {
  font-family: var(--f-mono);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  padding: 4px 8px;
  border-radius: 2px;
}
.badge--new { background: var(--accent); color: var(--accent-ink); }
.badge--drop { background: var(--ink); color: var(--ground); }

.card__body {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding-top: var(--s-3);
  min-width: 0;
}
.card__eyebrow {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.card__name {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: var(--t-md);
  line-height: 1.14;
  letter-spacing: 0.015em;
  text-transform: uppercase;
}
.card__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin-top: 2px;
}
.card__price {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.card__price small { color: var(--ink-3); font-size: 0.85em; }

.card__swatches { display: flex; gap: 4px; 
  /* They are buttons now - a reset, a target big enough for a thumb,
     and a visible state for the one being shown. */
  cursor: pointer;
  padding: 0;
  border: 1px solid rgba(255,255,255,0.22);
  appearance: none;
}
.card__swatch {
  width: 11px; height: 11px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}
.card__swatch-more {
  font-family: var(--f-mono);
  font-size: 9px;
  color: var(--ink-3);
  align-self: center;
}

/* Options button. Never a fake one-click add: these products need a size. */
.card__options {
  position: absolute;
  left: 10px; right: 10px; bottom: 10px;
  padding: 11px;
  background: rgba(244, 244, 241, 0.95);
  color: var(--ink-on-light);
  border-radius: var(--radius);
  font-family: var(--f-display);
  font-weight: 600;
  font-size: var(--t-2xs);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-align: center;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--fast), transform var(--fast);
  z-index: 2;
}
@media (hover: hover) and (pointer: fine) {
  .card:hover .card__options { opacity: 1; transform: none; }
}
/* Touch devices get no hover, so the whole card opens the product and the
   button is not rendered at all — a control that never appears is better than
   one that appears and cannot be reached. */
@media (hover: none) {
  .card__options { display: none; }
}

/* --------------------------------------------------------------------------
   Horizontal rail (New Drops on mobile, related products)
   -------------------------------------------------------------------------- */

.rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(210px, 1fr);
  gap: var(--s-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--gutter);
  padding-bottom: var(--s-3);
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  scrollbar-width: none;
}
.rail::-webkit-scrollbar { display: none; }
.rail > * { scroll-snap-align: start; }

/* --------------------------------------------------------------------------
   Category tiles
   -------------------------------------------------------------------------- */

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s-4);
}
.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 340px;
  padding: var(--s-5);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface);
  isolation: isolate;
}
.tile__art {
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(100% 74% at 50% 8%, var(--well-from) 0%, var(--well-to) 70%, var(--ground) 100%);
}
.tile__img {
  position: absolute;
  inset: 4% 4% 30% 4%;
  width: 92%;
  height: 66%;
  object-fit: contain;
  z-index: -1;
  filter: drop-shadow(0 16px 26px rgba(0, 0, 0, 0.5));
  transition: transform var(--med);
}
@media (hover: hover) {
  .tile:hover .tile__img { transform: scale(1.045); }
}
/* The label sits over the artwork, so it needs a real scrim under it — deep
   and opaque where the type is, gone by the time it reaches the product. */
.tile::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 68%;
  background: linear-gradient(to top,
    rgba(11, 11, 13, 0.97) 0%,
    rgba(11, 11, 13, 0.92) 32%,
    rgba(11, 11, 13, 0.55) 62%,
    transparent 100%);
  z-index: -1;
}
.tile__name {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--t-lg);
  line-height: 1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.tile__meta {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-top: 7px;
}
.tile__name { text-wrap: balance; }
@media (hover: hover) {
  .tile:hover .tile__meta { color: var(--accent); }
}

/* A tile is a way in to a set of products, not a product. It carries a photo,
   a name and a price, which is exactly what a product card carries - so people
   tapped one expecting the item and got a grid of everything. This says which
   it is before they tap, and the meta line now leads with the action rather
   than with a price. */
.tile__kind {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 6px;
}
.tile:focus-visible .tile__kind { color: var(--accent); }
@media (hover: hover) {
  .tile:hover .tile__kind { color: var(--accent); }
}
/* The price stays, quietly: useful once you know it is a set, misleading when
   it is the loudest thing on the tile. */
.tile__from {
  display: block;
  margin-top: 3px;
  color: var(--ink-3);
  letter-spacing: 0.08em;
}

/* --------------------------------------------------------------------------
   Filters
   -------------------------------------------------------------------------- */

.filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  padding-block: var(--s-4);
  border-block: 1px solid var(--line);
  margin-bottom: var(--s-6);
}
/* The chip groups. On a wide screen they sit in the bar as they always have;
   the toggle that reveals them on a phone is hidden and this wrapper is
   transparent to the layout, so nothing about the desktop bar changes. */
.filters__groups {
  display: contents;
}
.filters__toggle { display: none; }

.filters__group {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
  min-width: 0;
  max-width: 100%;
}
.filters__label {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-3);
  margin-right: 2px;
}
.chip {
  font-family: var(--f-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.05em;
  /* 34px tall was under the 44px a thumb needs, and the filter chips sit in
     tight rows — the easiest thing on the page to mis-tap. The height is set on
     the control rather than added as padding so the text stays where it is and
     only the hit area grows. */
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  padding: 7px 13px;
  border: 1px solid var(--line);
  border-radius: 100px;
  color: var(--ink-2);
  white-space: nowrap;
  transition: border-color var(--fast), color var(--fast), background var(--fast);
  /* A filter value is catalog text and can be arbitrarily long. Cap it here so
     one unusual option can never widen the page. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chip:hover { border-color: var(--line-strong); color: var(--ink); }
.chip.is-active {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}
.chip__count { color: var(--ink-3); margin-left: 5px; }
.chip.is-active .chip__count { color: var(--accent-deep); }

.filters__sort {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.filters__sort select {
  font-family: var(--f-mono);
  font-size: var(--t-2xs);
  padding: 7px 11px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
}
.filters__clear {
  font-family: var(--f-mono);
  font-size: var(--t-2xs);
  color: var(--ink-3);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.filters__clear:hover { color: var(--danger); }

.load-more {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
  padding-top: var(--s-7);
}
.load-more__done {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-3);
}

.result-count {
  font-family: var(--f-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: var(--s-4);
}

/* --------------------------------------------------------------------------
   Drawer shell — shared by cart and product
   -------------------------------------------------------------------------- */

.scrim {
  position: fixed;
  inset: 0;
  z-index: 950;
  /* Stop taking taps the moment it closes. `visibility` here is deliberately
     delayed until the fade ends, and a transition can also be paused or dropped
     outright - a phone that locks or switches apps mid-animation leaves this
     sheet visible and covering the page. `pointer-events` does not transition,
     so it flips instantly and the layer stops intercepting taps whatever the
     animation does. Same defect as the loading screen, which swallowed every
     tap on the shop; see .loader.is-hidden in layout.css. */
  pointer-events: none;
  background: rgba(5, 5, 6, 0.72);
  backdrop-filter: blur(3px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--med), visibility 0s linear var(--med);
}
.scrim.is-open {
  pointer-events: auto;
  opacity: 1;
  visibility: visible;
  transition: opacity var(--med), visibility 0s;
}

.drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  z-index: 960;
  width: min(430px, 100vw);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 1px solid var(--line);
  transform: translateX(100%);
  transition: transform var(--med);
}
.drawer.is-open { transform: none; }

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-5);
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.drawer__title {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: var(--t-lg);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.drawer__close {
  width: 34px; height: 34px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink-2);
  font-size: 18px;
  line-height: 1;
  transition: border-color var(--fast), color var(--fast);
}
.drawer__close:hover { border-color: var(--ink); color: var(--ink); }
.drawer__body { flex: 1; overflow-y: auto; padding: var(--s-5); }
.drawer__foot {
  flex-shrink: 0;
  padding: var(--s-5);
  border-top: 1px solid var(--line);
  background: var(--surface-2);
}

/* --------------------------------------------------------------------------
   Cart
   -------------------------------------------------------------------------- */

.cart-line {
  display: grid;
  grid-template-columns: 74px minmax(0, 1fr);
  gap: var(--s-4);
  padding-bottom: var(--s-4);
  margin-bottom: var(--s-4);
  border-bottom: 1px solid var(--line);
}
.cart-line:last-child { border-bottom: 0; margin-bottom: 0; }
.cart-line__well {
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  overflow: hidden;
  position: relative;
  background: radial-gradient(110% 84% at 50% 6%, var(--well-from), var(--well-to));
}
.cart-line__well[data-tier='studio'],
.cart-line__well[data-tier='photo'] { background: var(--surface); }
.cart-line__well[data-tier='studio'] img,
.cart-line__well[data-tier='photo'] img { inset: 0; width: 100%; height: 100%; object-fit: cover; }
.cart-line__well img {
  position: absolute; inset: 8%;
  width: 84%; height: 84%;
  object-fit: contain;
}
.cart-line__name {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: var(--t-sm);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.15;
}
.cart-line__variant {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  color: var(--ink-3);
  margin-top: 4px;
}
.cart-line__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin-top: var(--s-3);
}
.cart-line__price {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}
.cart-line__remove {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  color: var(--ink-3);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cart-line__remove:hover { color: var(--danger); }
.cart-line.is-invalid { background: var(--danger-soft); border-radius: var(--radius); padding: var(--s-3); }
.cart-line__error {
  font-size: var(--t-xs);
  color: var(--danger);
  margin-top: var(--s-2);
  line-height: 1.4;
}

.qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.qty button {
  width: 28px; height: 28px;
  display: grid; place-items: center;
  color: var(--ink-2);
  font-size: 15px;
  line-height: 1;
  transition: background var(--fast), color var(--fast);
}
.qty button:hover:not([disabled]) { background: var(--surface-3); color: var(--ink); }
.qty button[disabled] { opacity: 0.3; cursor: not-allowed; }
.qty output {
  min-width: 30px;
  text-align: center;
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}

.totals { display: grid; gap: var(--s-2); margin-bottom: var(--s-4); }
.totals__row {
  display: flex;
  justify-content: space-between;
  font-size: var(--t-xs);
  color: var(--ink-2);
}
.totals__row--total {
  font-family: var(--f-display);
  font-size: var(--t-lg);
  font-weight: 600;
  color: var(--ink);
  padding-top: var(--s-3);
  margin-top: var(--s-1);
  border-top: 1px solid var(--line);
  letter-spacing: 0.02em;
}
.totals__row span:last-child { font-family: var(--f-mono); font-variant-numeric: tabular-nums; }
/* A saving the customer has actually earned. Named, so it reads as this set
   rather than as an unexplained credit, and coloured so it is not mistaken for
   another charge. */
.totals__row--save { color: var(--accent, var(--ink)); align-items: flex-start; }
.totals__row--save small {
  display: block;
  font-size: var(--t-3xs);
  color: var(--ink-3);
  letter-spacing: 0.02em;
}
.totals__note {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  color: var(--ink-3);
  text-align: center;
  margin-top: var(--s-3);
  line-height: 1.5;
}
.cart-alert {
  padding: var(--s-3) var(--s-4);
  background: var(--danger-soft);
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  color: var(--danger);
  font-size: var(--t-xs);
  line-height: 1.45;
  margin-bottom: var(--s-4);
}

/* --------------------------------------------------------------------------
   Product page (overlay)
   -------------------------------------------------------------------------- */

.product {
  position: fixed;
  inset: 0;
  z-index: 970;
  /* Stop taking taps the moment it closes. `visibility` here is deliberately
     delayed until the fade ends, and a transition can also be paused or dropped
     outright - a phone that locks or switches apps mid-animation leaves this
     sheet visible and covering the page. `pointer-events` does not transition,
     so it flips instantly and the layer stops intercepting taps whatever the
     animation does. Same defect as the loading screen, which swallowed every
     tap on the shop; see .loader.is-hidden in layout.css. */
  pointer-events: none;
  overflow-y: auto;
  background: var(--ground);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--med), visibility 0s linear var(--med);
}
.product.is-open {
  pointer-events: auto;
  opacity: 1;
  visibility: visible;
  transition: opacity var(--med), visibility 0s;
}

.product__bar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-4) var(--gutter);
  background: rgba(11, 11, 13, 0.92);
  backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--line);
}
.product__crumbs {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.product__crumbs b { color: var(--ink-2); font-weight: 500; }
.product__bar-actions { display: flex; align-items: center; gap: var(--s-3); flex-shrink: 0; }

.product__layout {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(var(--s-5), 4vw, var(--s-8));
  align-items: start;
  padding: var(--s-6) var(--gutter) var(--s-9);
  max-width: var(--page-max);
  margin-inline: auto;
}

/* --- gallery --- */
.gallery { min-width: 0; position: sticky; top: 92px; }
.gallery__stage {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background:
    radial-gradient(112% 84% at 50% 4%, var(--well-from) 0%, var(--well-to) 64%, var(--ground) 100%);
}
.gallery__stage[data-tier='studio'],
.gallery__stage[data-tier='photo'] {
  background: var(--surface);
  /* Same trap as the card well: this beat the halo rule on specificity. */
  box-shadow: inset 0 0 0 1px var(--line), var(--halo-ring), var(--halo-glow);
}
.gallery__slide[data-tier='studio'],
.gallery__slide[data-tier='photo'] { padding: 0; }
.gallery__slide[data-tier='photo'] img { width: 100%; height: 100%; object-fit: cover; }
.gallery__slide img { cursor: zoom-in; }

/* Zoom affordance. A customer buying a graphic tee has to be able to look at
   the graphic; the control says so rather than relying on them guessing. */
.gallery__zoom {
  position: absolute;
  right: 12px; bottom: 12px;
  z-index: 3;
  display: flex; align-items: center; gap: 7px;
  padding: 8px 13px;
  border-radius: 100px;
  background: rgba(11, 11, 13, 0.78);
  backdrop-filter: blur(8px);
  border: 1px solid var(--line-strong);
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink);
  transition: border-color var(--fast), background var(--fast);
}
.gallery__zoom:hover { border-color: var(--accent); color: var(--accent); }
.gallery__zoom span {
  width: 11px; height: 11px;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  position: relative;
}
.gallery__zoom span::after {
  content: '';
  position: absolute;
  right: -4px; bottom: -4px;
  width: 5px; height: 1.5px;
  background: currentColor;
  transform: rotate(45deg);
}

/* --------------------------------------------------------------------------
   Lightbox
   -------------------------------------------------------------------------- */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 990;
  /* Stop taking taps the moment it closes. `visibility` here is deliberately
     delayed until the fade ends, and a transition can also be paused or dropped
     outright - a phone that locks or switches apps mid-animation leaves this
     sheet visible and covering the page. `pointer-events` does not transition,
     so it flips instantly and the layer stops intercepting taps whatever the
     animation does. Same defect as the loading screen, which swallowed every
     tap on the shop; see .loader.is-hidden in layout.css. */
  pointer-events: none;
  display: grid;
  place-items: center;
  background: rgba(6, 6, 8, 0.96);
  padding: 4vmin;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--med), visibility 0s linear var(--med);
}
.lightbox.is-open {
  pointer-events: auto;
  opacity: 1;
  visibility: visible;
  transition: opacity var(--med), visibility 0s;
}
.lightbox__stage {
  max-width: 100%;
  max-height: 92vh;
  overflow: auto;
  display: grid;
  place-items: center;
}
.lightbox img {
  max-width: 100%;
  max-height: 92vh;
  object-fit: contain;
  border-radius: 4px;
  cursor: zoom-in;
  transition: transform var(--med);
}
.lightbox.is-zoomed img {
  transform: scale(2.4);
  cursor: zoom-out;
}
.lightbox__close {
  position: absolute;
  top: 18px; right: 18px;
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: rgba(11, 11, 13, 0.7);
  color: var(--ink);
  font-size: 21px; line-height: 1;
}
.lightbox__close:hover { border-color: var(--ink); }
.lightbox__hint {
  position: absolute;
  left: 0; right: 0; bottom: 16px;
  text-align: center;
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: 0.06em;
  color: var(--ink-3);
}
@media (hover: none) {
  /* Touch has pinch-zoom, which beats anything reimplemented here. */
  .lightbox__hint::before { content: 'Pinch to zoom · '; }
  .lightbox.is-zoomed img { transform: none; }
}
.gallery__track {
  display: flex;
  height: 100%;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}
.gallery__track::-webkit-scrollbar { display: none; }
.gallery__slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  display: grid;
  place-items: center;
  padding: 5%;
}
.gallery__slide img {
  max-width: 100%; max-height: 100%;
  object-fit: contain;
}
.gallery__stage[data-tier='cutout'] .gallery__slide img {
  filter: drop-shadow(0 20px 32px rgba(0, 0, 0, 0.5));
}
.gallery__dots {
  position: absolute;
  left: 0; right: 0; bottom: 12px;
  display: flex;
  justify-content: center;
  gap: 6px;
}
.gallery__dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.3);
  transition: background var(--fast), width var(--fast);
}
.gallery__dot.is-active { background: var(--accent); width: 18px; border-radius: 3px; }


.gallery__thumbs {
  display: flex;
  gap: var(--s-2);
  margin-top: var(--s-3);
  overflow-x: auto;
  padding-bottom: 4px;
  min-width: 0;
  scrollbar-width: thin;
}
.gallery__thumb {
  flex: 0 0 62px;
  aspect-ratio: 1;
  border-radius: var(--radius);
  overflow: hidden;
  position: relative;
  background: radial-gradient(110% 84% at 50% 6%, var(--well-from), var(--well-to));
  box-shadow: inset 0 0 0 1px transparent, var(--halo-ring), var(--halo-glow);
  transition: box-shadow var(--fast), opacity var(--fast);
  opacity: 0.6;
}
.gallery__thumb[data-tier='photo'] { background: var(--surface); }
.gallery__thumb[data-tier='photo'] img { inset: 0; width: 100%; height: 100%; object-fit: cover; }
.gallery__thumb img { position: absolute; inset: 9%; width: 82%; height: 82%; object-fit: contain; }
.gallery__thumb.is-active {
  opacity: 1;
  box-shadow: inset 0 0 0 1.5px var(--accent), var(--halo-ring), var(--halo-glow);
}
.gallery__thumb:hover { opacity: 1; }

/* --- detail column --- */
.detail { min-width: 0; display: flex; flex-direction: column; gap: var(--s-5); }
.detail__family {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent);
}
.detail__name {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: clamp(1.6rem, 4vw, var(--t-2xl));
  line-height: 1;
  letter-spacing: 0.005em;
  text-transform: uppercase;
  margin-top: var(--s-2);
  text-wrap: balance;
}
.detail__price {
  font-family: var(--f-mono);
  font-size: var(--t-lg);
  font-variant-numeric: tabular-nums;
  margin-top: var(--s-3);
}
.detail__price small { font-size: var(--t-2xs); color: var(--ink-3); margin-left: 7px; }

.opt { display: flex; flex-direction: column; gap: var(--s-3); }
.opt__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
}
.opt__name {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-3);
}
.opt__value { font-size: var(--t-xs); color: var(--ink); }

.swatches { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.swatch {
  width: 32px; height: 32px;
  border-radius: 50%;
  position: relative;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
  transition: transform var(--fast);
}
.swatch::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1.5px solid transparent;
  transition: border-color var(--fast);
}
.swatch:hover { transform: scale(1.07); }
.swatch.is-active::after { border-color: var(--accent); }

.sizes { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.size {
  min-width: 46px;
  /* Same reason as the chips: 40px, and on the path to buying. */
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 10px 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-family: var(--f-mono);
  font-size: var(--t-2xs);
  text-align: center;
  color: var(--ink-2);
  transition: border-color var(--fast), color var(--fast), background var(--fast);
}
.size:hover { border-color: var(--line-strong); color: var(--ink); }
.size.is-active {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}
.size[disabled] { opacity: 0.28; cursor: not-allowed; text-decoration: line-through; }

.detail__actions { display: flex; gap: var(--s-3); align-items: stretch; }
.detail__actions .btn { flex: 1; }

.detail__note {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  color: var(--ink-3);
  line-height: 1.6;
}

/* accordion — real spec copy from the blueprint, never invented */
.acc { border-top: 1px solid var(--line); }
.acc__item { border-bottom: 1px solid var(--line); }
.acc__btn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-4) 0;
  font-family: var(--f-display);
  font-weight: 500;
  font-size: var(--t-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: left;
  color: var(--ink);
}
.acc__btn::after { content: '+'; color: var(--ink-3); font-size: 17px; }
.acc__item.is-open .acc__btn::after { content: '\2212'; }
.acc__panel {
  display: none;
  padding-bottom: var(--s-4);
  font-size: var(--t-xs);
  line-height: 1.65;
  color: var(--ink-2);
  white-space: pre-line;
}
.acc__item.is-open .acc__panel { display: block; }
.acc__panel dl { display: grid; grid-template-columns: auto 1fr; gap: 5px var(--s-4); }
.acc__panel dt { color: var(--ink-3); font-family: var(--f-mono); font-size: var(--t-3xs); text-transform: uppercase; letter-spacing: 0.08em; }
.acc__panel dd { color: var(--ink); }

.size-table { width: 100%; border-collapse: collapse; font-family: var(--f-mono); font-size: var(--t-3xs); }
.size-table th, .size-table td { padding: 6px 9px; text-align: left; border-bottom: 1px solid var(--line); }
.size-table th { color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em; }

/* --------------------------------------------------------------------------
   Toast
   -------------------------------------------------------------------------- */

.toast {
  /* Same guard as .scrim and the rest: hidden by an animation, so it must stop
     taking taps by a property that does not animate. */
  pointer-events: none;
  position: fixed;
  left: 50%;
  bottom: 26px;
  translate: -50% 0;
  z-index: 990;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 13px 20px;
  background: var(--surface-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font-size: var(--t-xs);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
  opacity: 0;
  visibility: hidden;
  translate: -50% 12px;
  transition: opacity var(--med), translate var(--med), visibility 0s linear var(--med);
  max-width: calc(100vw - 32px);
}
.toast.is-open {
  pointer-events: auto;
  opacity: 1;
  visibility: visible;
  translate: -50% 0;
  transition: opacity var(--med), translate var(--med), visibility 0s;
}
.toast::before {
  content: '';
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}
.toast[data-kind='error']::before { background: var(--danger); }

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 1080px) {
  .product__layout { grid-template-columns: 1fr; }
  .gallery { position: static; }
}

@media (max-width: 860px) {
  .nav__links {
    position: fixed;
    inset: var(--nav-h) 0 auto 0;
    /* The worst of them: this panel is pinned across the full width under the
       nav bar and holds SHOP, COLLECTIONS and THE ARTIST. Left live while its
       close animation runs, a tap meant for the first row of products lands on
       a nav link instead - which is exactly the reported "clicking an item
       takes you to the collection instead of the product". Phones only, since
       the panel only exists below 860px, and intermittent, since it needs the
       menu to have been opened. */
    pointer-events: none;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--s-4) var(--gutter) var(--s-6);
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    transform: translateY(-12px);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--med), transform var(--med), visibility 0s linear var(--med);
    max-height: calc(100vh - var(--nav-h));
    overflow-y: auto;
  }
  .nav__links.is-open {
    pointer-events: auto;
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity var(--med), transform var(--med), visibility 0s;
  }
  .nav__link { padding: 14px 0; font-size: var(--t-lg); border-bottom: 1px solid var(--line); }
  .nav__link.is-active::after { display: none; }
  .nav__link.is-active { color: var(--accent); }
  .nav__burger { display: block; }
  .nav__cart { margin-left: 0; }
  .nav__shop { width: 100%; }
  .nav__menu {
    position: static;
    translate: none;
    opacity: 1;
    /* `inherit`, NOT `visible`/`auto`. On a phone this dropdown is folded open
       inside .nav__links, and that panel is what hides the menu - by going
       `visibility: hidden` and `pointer-events: none` when closed. Both of
       those are inherited properties, so a child that asserts `visible` or
       `auto` overrides its own hidden parent and stays live.

       That is what it did: five range links - ADULT, KIDS, BABY, ART PRINTS,
       EVERYTHING - sat in a fixed, invisible strip across the top of every
       phone screen, swallowing taps meant for the products underneath and
       sending the customer to a whole range instead of the item they touched.
       Phones only; the desktop layout never folds the menu open, which is why
       the site behaved perfectly in desktop mode on the same handset.

       Inheriting means the menu is exactly as visible, and exactly as
       tappable, as the panel containing it. */
    visibility: inherit;
    pointer-events: inherit;
    min-width: 0;
    background: none;
    border: 0;
    box-shadow: none;
    padding: 0 0 var(--s-3);
    /* One column. In two, each item's count sits hard against the next item's
       name and reads as though it belongs to it. */
    grid-template-columns: 1fr;
  }
  .nav__menu-item {
    padding: 11px 0 11px var(--s-4);
    border-left: 1px solid var(--line);
  }
  .nav__menu-item b { font-size: var(--t-md); }
  .nav__menu-item:hover { background: none; }
}

/* --- filters on a phone ---------------------------------------------------
   Three chip groups run to about 535px on a 390px screen, which put the first
   product below the fold: a customer opening the shop saw a screenful of
   filters and no products. They fold behind a button, so the grid starts where
   the range name ends. */
@media (max-width: 860px) {
  .filters {
    flex-direction: column;
    align-items: stretch;
    gap: var(--s-3);
  }
  .filters__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    align-self: flex-start;
    padding: 10px 16px;
    border: 1px solid var(--line-strong, var(--line));
    border-radius: 999px;
    background: none;
    color: var(--ink);
    font-family: var(--f-mono);
    font-size: var(--t-2xs);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    cursor: pointer;
  }
  .filters__groups {
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: var(--s-3);
  }
  .filters.is-open .filters__groups { display: flex; }
  .filters__group { align-items: flex-start; }
}

@media (max-width: 640px) {
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4) var(--s-3); }
  .grid--wide { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .card__name { font-size: var(--t-sm); }
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tile { min-height: 210px; padding: var(--s-4); }
  .tile__name { font-size: var(--t-md); }
  .filters__sort { margin-left: 0; width: 100%; justify-content: space-between; }
  .drawer { width: 100vw; }
  .gallery__thumb { flex-basis: 54px; }
}

@media (max-width: 400px) {
  .grid { gap: var(--s-3) var(--s-2); }
}

/* --- the halo -------------------------------------------------------------
   Each card glows in its own artwork's colour, sampled from the print file.

   Per card rather than site-wide, and the reason is what this shop is: a wall of
   different original characters. One colour over the whole page would say the
   opposite — that it is a single brand statement — and it would fight whichever
   designs did not share that hue. A neutral accent carries the interface; the
   colour on the page comes from the art.

   Visible at rest, not only on hover. A halo that appears when you point at it
   is a hover effect; this is meant to be the thing you see across the grid. */
/* Each card glows in its own artwork's colour, set on .card by card.js and
   falling back to the section accent. The fallback lives HERE, on the parent —
   declaring it on .card__well would re-set the variable at the level that
   consumes it and the per-product colour would never arrive. */
.card { --halo: var(--accent); }

/* The glow goes on the WELL ITSELF, not on a pseudo-element inside it.
   `.card__well` sets overflow:hidden to crop the product image, and that clips
   any child — so the outer glow drawn on ::before was cut off at the frame and
   only a 1px inset line survived, which on a white garment is invisible. An
   element's own box-shadow is not clipped by its own overflow, so moving it
   here is the whole fix. */
.card__well {
  position: relative;
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--halo) 70%, transparent),
    0 0 24px -8px color-mix(in srgb, var(--halo) 55%, transparent);
  transition: box-shadow var(--med), transform var(--med);
}
.card:focus-visible .card__well {
  box-shadow:
    0 0 0 2px color-mix(in srgb, var(--halo) 95%, transparent),
    0 0 38px -6px color-mix(in srgb, var(--halo) 80%, transparent),
    0 18px 44px var(--well-shadow);
}

/* The same lift, but only where a pointer can hover. On a touch screen a
   hover style is applied by the first tap and swallows it - the customer taps a
   product, sees it light up, and has to tap again to open it. */
@media (hover: hover) {
  .tile:hover,
  .plate:hover,
  .editorial__frame:hover,
  .gallery__thumb:hover {
    box-shadow:
      0 0 0 2px color-mix(in srgb, var(--halo) 95%, transparent),
      0 0 38px -6px color-mix(in srgb, var(--halo) 80%, transparent),
      0 18px 44px var(--well-shadow);
  }
}

@media (hover: hover) {
  .card:hover .card__well {
    box-shadow:
      0 0 0 2px color-mix(in srgb, var(--halo) 95%, transparent),
      0 0 38px -6px color-mix(in srgb, var(--halo) 80%, transparent),
      0 18px 44px var(--well-shadow);
  }
}
/* A tint of the same colour under the image, so the glow reads as coming off
   the artwork rather than being drawn around it. */
.card__well[data-tier='cutout'],
.card__well[data-tier='studio'] {
  background:
    radial-gradient(118% 86% at 50% 8%,
      color-mix(in srgb, var(--halo) 20%, var(--well-from)) 0%,
      var(--well-to) 62%, var(--ground) 100%);
}



/* --- the halo, everywhere -------------------------------------------------
   Every frame that holds an image carries the same glow in the same colour, so
   the page reads as one system rather than a grid of lit tiles among dark ones.

   The glow is a box-shadow on each element ITSELF, never on a pseudo-element
   inside it. All of these crop their image with overflow:hidden, and an element
   clips its own children — which is exactly how the first attempt ended up
   invisible. An element's own shadow is not clipped by its own overflow. */
.tile,
.plate,
.editorial__frame,
.stage-size__room,
.gallery__stage,
.gallery__thumb,
.artist-portrait__frame,
.cart-line__well,
.outfit__shot,
.hero__art,
.lightbox__stage,
.editorial,
.card__well {
  --halo: var(--accent);
  position: relative;
  box-shadow: var(--halo-ring), var(--halo-glow);
  transition: box-shadow var(--med);
}
.card:focus-visible .card__well {
  box-shadow:
    0 0 0 2px color-mix(in srgb, var(--halo) 100%, transparent),
    0 0 44px -4px color-mix(in srgb, var(--halo) 90%, transparent),
    0 18px 44px var(--well-shadow);
}
@media (hover: hover) {
  .card:hover .card__well {
    box-shadow:
      0 0 0 2px color-mix(in srgb, var(--halo) 100%, transparent),
      0 0 44px -4px color-mix(in srgb, var(--halo) 90%, transparent),
      0 18px 44px var(--well-shadow);
  }
}
/* WHY 'studio' IS DRAWN LIKE A PHOTOGRAPH.
   The build stopped masking flats out of their backdrop — every halo and torn
   white sleeve came from an imperfect mask — so a studio image now arrives WITH
   its own white ground and is a complete picture, not a floating object. This
   stylesheet predates that tier and knew only photo, cutout and raw, so studio
   fell through to the default well: a pale mockup inset on dark graphite, which
   reads exactly as a bad cut-out on a black background. Filling the well the way
   a photograph does puts the backdrop against the frame and the ring disappears. */

/* The image itself sits on a tint of the same colour, so the glow reads as
   light coming off the artwork rather than a ring drawn around it. */
.card__well[data-tier='cutout'],
.card__well[data-tier='studio'],
.card__well[data-tier='raw'] {
  background:
    radial-gradient(118% 86% at 50% 8%,
      color-mix(in srgb, var(--halo) 24%, var(--well-from)) 0%,
      var(--well-to) 62%, var(--ground) 100%);
}

/* The page itself is inside the halo.
   Asked for the whole page to carry it: a hairline at the top of the viewport
   and a soft wash at the edges, fixed so it stays as you scroll. Set on ::before
   of the body so no layout is affected and nothing can intercept a click. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9;
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--accent) 26%, transparent),
    inset 0 0 90px -30px color-mix(in srgb, var(--accent) 45%, transparent);
  transition: box-shadow var(--accent-fade, 420ms);
}

/* The accent transitions between sections rather than snapping. */
:root { transition: none; }
.btn, .nav__link, .tile::after, .plate::after, .card__well::before,
.editorial__frame::after, .gallery__stage::after {
  transition-duration: var(--accent-fade, 420ms);
}

/* --- hover lights the item up ---------------------------------------------
   The category tiles turned their meta line the accent colour on hover and
   nothing else on the page did, so the rest of the grid felt inert by
   comparison. Every item that can be opened now does the same — and does it in
   its OWN colour, the one sampled from its artwork, so hovering a card tells you
   which design you are pointing at rather than repeating one site-wide accent.

   Text only. The halo already answers the pointer; recolouring the artwork or
   the frame as well would be three things happening at once for one gesture. */
.card:focus-visible .card__name,
.card:focus-visible .card__price {
  color: var(--halo);
}
@media (hover: hover) {
  .card:hover .card__name,
  .card:hover .card__price {
    color: var(--halo);
  }
}
.card:focus-visible .card__eyebrow {
  color: color-mix(in srgb, var(--halo) 70%, var(--ink-3));
}
@media (hover: hover) {
  .card:hover .card__eyebrow {
    color: color-mix(in srgb, var(--halo) 70%, var(--ink-3));
  }
}

.tile:focus-visible .tile__name,
.tile:focus-visible .tile__meta {
  color: var(--halo);
}
@media (hover: hover) {
  .tile:hover .tile__name,
  .tile:hover .tile__meta {
    color: var(--halo);
  }
}

.plate:hover .plate__tag,
.plate:focus-visible .plate__tag {
  color: var(--halo);
}

.outfit__open:hover .outfit__name,
.outfit__open:focus-visible .outfit__name {
  color: var(--halo);
}

.art__family:hover b,
.art__family:focus-visible b {
  color: var(--accent);
}

.cart-line:hover .cart-line__name {
  color: var(--halo);
}

/* The transition belongs on the resting state or the colour snaps back the
   instant the pointer leaves. */
.card__name, .card__price, .card__eyebrow,
.tile__name, .tile__meta, .plate__tag,
.outfit__name, .art__family b, .cart-line__name {
  transition: color var(--fast);
}


/* Posters and canvas: the print at its real size, in place of the photograph.
   The room is a fixed square so every size is judged against the same space —
   that is what makes 11x9 read as smaller than 36x24 rather than merely wider. */
.stage-size { margin-bottom: var(--s-4); }
.stage-size__room {
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  padding: var(--s-5);
  border-radius: var(--radius);
  /* Deliberately darker than the product well. A white sheet needs somewhere
     dark to sit or its edges read as part of the page. */
  background:
    radial-gradient(120% 90% at 50% 0%, #26262c 0%, #17171b 60%, #0f0f12 100%);
  box-shadow: inset 0 0 0 1px var(--line);
}
.stage-size__sheet {
  display: grid;
  place-items: center;
  background: #fff;
  /* The artwork's own background is white and so is the paper, so without an
     edge the print dissolves into the sheet and the sheet into the well — you
     cannot see where any of them stop. A hairline plus a lifted shadow gives
     the paper a defined edge against both. */
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.55), 0 14px 34px rgba(0, 0, 0, 0.62);
  max-width: 100%;
  max-height: 100%;
  transition: width var(--med), height var(--med);
}
.stage-size__sheet img { display: block; height: auto; max-height: 100%; }
.stage-size__note {
  margin-top: var(--s-3);
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-3);
}
.stage-size__note b { color: var(--ink); }


/* --------------------------------------------------------------------------
   Print placement — Front / Back

   Styled as a segmented control rather than swatches: placement is a either/or
   choice between two whole products, not a property of this one, and the control
   navigates rather than mutating the current listing.
   -------------------------------------------------------------------------- */

.placements {
  display: inline-flex;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  overflow: hidden;
}
.placement {
  padding: 9px 18px;
  font-family: var(--f-display);
  font-weight: 600;
  font-size: var(--t-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  transition: background var(--fast), color var(--fast);
}
.placement + .placement { border-left: 1px solid var(--line-strong); }
.placement:hover { color: var(--ink); background: var(--surface-2); }
.placement.is-active {
  background: var(--accent);
  color: var(--accent-ink);
}

/* The size sheet — a poster's real proportions, with the art at its true size.
   A size button reading "36″ x 24″" says nothing about whether a portrait design
   fills it or floats in the middle of it; this shows which. */


/* ==========================================================================
   Outfit — the same design across the pieces that go together.

   A horizontal rail rather than a grid: an outfit is read left to right, and
   on a phone it scrolls as one gesture instead of pushing the related products
   a screen further down. The piece already being viewed leads the rail and is
   marked rather than made selectable, so the set reads as complete.
   ========================================================================== */

.outfit {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  overflow: hidden;
}

.outfit__rail {
  display: flex;
  gap: var(--s-4);
  list-style: none;
  margin: 0;
  padding: var(--s-4);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}

.outfit__piece {
  flex: 0 0 152px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  gap: 2px;
  transition: opacity var(--fast);
}
/* Deselected, not removed: the customer can see what they turned off. */
.outfit__piece.is-off { opacity: 0.38; }

.outfit__shot {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--s-2);
  background:
    radial-gradient(118% 86% at 50% 4%, var(--well-from) 0%, var(--well-to) 62%, var(--ground) 100%);
}
.outfit__shot img { position: absolute; inset: 6%; width: 88%; height: 88%; object-fit: contain; }
.outfit__shot[data-tier='photo'] { background: var(--surface-2); }
.outfit__shot[data-tier='photo'] img { inset: 0; width: 100%; height: 100%; object-fit: cover; }
.outfit__shot[data-tier='cutout'] img { filter: drop-shadow(var(--shadow-cut)); }
.outfit__shot[data-tier='raw'] img { inset: 10%; width: 80%; height: 80%; }

.outfit__piece.is-current .outfit__shot { box-shadow: inset 0 0 0 2px var(--accent); }

.outfit__type {
  font-family: var(--f-mono);
  font-size: var(--t-3xs);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--ink-3);
}
/* Names wrap to one or two lines depending on the product. Reserving both
   keeps every piece's size control on the same baseline, so the row reads as
   one outfit rather than a staircase. */
.outfit__name {
  font-size: var(--t-xs);
  line-height: 1.3;
  min-height: calc(2 * 1.3em);
}
.outfit__piece .outfit__field,
.outfit__piece .outfit__toggle { margin-top: auto; }
.outfit__piece .outfit__field + .outfit__toggle { margin-top: var(--s-2); }
.outfit__piece.is-current .outfit__name { color: var(--accent); }
.outfit__size { font-size: var(--t-2xs); color: var(--ink-3); }

.outfit__field select {
  width: 100%;
  margin-top: var(--s-2);
  background: var(--surface-3);
  border: 1px solid var(--line);
  color: var(--ink);
  font: inherit;
  font-size: var(--t-2xs);
  padding: 5px var(--s-2);
  border-radius: var(--radius);
}
.outfit__field select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.outfit__toggle {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-2);
  font-size: var(--t-xs);
  cursor: pointer;
}
.outfit__toggle input { accent-color: var(--accent); width: 15px; height: 15px; cursor: pointer; }

.outfit__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding: var(--s-4);
  border-top: 1px solid var(--line);
  background: var(--surface-2);
}
.outfit__total {
  font-family: var(--f-mono);
  font-size: var(--t-2xs);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--ink-2);
}

@media (max-width: 640px) {
  .outfit__piece { flex-basis: 132px; }
  .outfit__foot { flex-direction: column; align-items: stretch; }
  .outfit__foot .btn { width: 100%; }
}

/* A piece of the outfit is a link to that product as well as something to add
   to the bag, so the set can be browsed rather than only bought whole. */
.outfit__open {
  display: flex;
  flex-direction: column;
  gap: 2px;
  color: inherit;
  text-decoration: none;
}
.outfit__open .outfit__shot { display: block; }
.outfit__open .outfit__type,
.outfit__open .outfit__name { display: block; }
.outfit__open:hover .outfit__name { color: var(--accent); }
.outfit__open:hover .outfit__shot { box-shadow: inset 0 0 0 1px var(--line-strong); }
.outfit__open:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --- size chart -----------------------------------------------------------
   A measurement table inside the Size & fit panel. Digits are tabular so the
   columns line up, and the table scrolls inside its own box so a wide chart
   (waist / hip / inseam / sleeve) never makes the page scroll sideways. */
.chart { margin-top: 14px; }
.chart__blank {
  font-size: 12px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-2); margin-bottom: 8px;
}
.chart__note { font-size: 12px; color: var(--ink-2); margin-top: 8px; line-height: 1.5; }
.chart__scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.chart__caption {
  caption-side: top; text-align: left; font-size: 12px;
  color: var(--ink-2); padding-bottom: 6px;
}
.chart__table {
  width: 100%; border-collapse: collapse; font-size: 13px;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.chart__table th, .chart__table td {
  padding: 7px 10px; text-align: right; border-bottom: 1px solid var(--line);
}
.chart__table thead th, .chart__table tbody th {
  text-align: left; font-weight: 600; color: var(--ink);
}
.chart__table thead th { border-bottom-width: 2px; }
.chart__table tbody tr:last-child th,
.chart__table tbody tr:last-child td { border-bottom: 0; }
.chart__cm {
  display: block; font-size: 11px; color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.chart__cm::after { content: ' cm'; }

/* --- outfit saving and shipping honesty ----------------------------------- */
.outfit__was { color: var(--ink-2); text-decoration: line-through; }
.outfit__save {
  display: inline-block; margin-left: 10px; font-size: 12px; font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--accent-ink); background: var(--accent); padding: 2px 8px; border-radius: 2px;
}
.outfit__ship {
  margin-top: 12px; font-size: 12px; line-height: 1.5; color: var(--ink-2); max-width: 62ch;
}
.totals__ship {
  margin: 8px 0 0; font-size: 12px; line-height: 1.5; color: var(--ink-2);
}

/* --- manufacturer renders -------------------------------------------------
   The white ground of a Printify mockup, made the card's own.

   These are the lead image on every beanie and every rebuilt baby tee - any
   product the photography pipeline has not covered. Left on the dark gradient
   the card showed a bright white rectangle inset on a dark plate, which reads
   as a broken image rather than a product shot.

   Placed last deliberately: the halo section further up sets a gradient on the
   well at the same specificity, so this has to come after it to win.
   ------------------------------------------------------------------------- */
.card__well[data-tier='raw'] {
  background: #ffffff;
  background-image: none;
}

/* The colourway a card is currently showing. */
.card__swatch.is-active {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
@media (hover: hover) {
  .card__swatch:hover { transform: scale(1.25); }
}

/* The owner's private code field. Deliberately plain and unlabelled - it is not
   a promotion and nothing should invite a customer to hunt for a code. */
.totals__code {
  display: flex;
  gap: var(--s-3);
  margin-top: var(--s-4);
}
.totals__code input {
  flex: 1;
  min-width: 0;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink-1);
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.totals__code input::placeholder { color: var(--ink-3); letter-spacing: 0.08em; }
.totals__code .btn { flex: 0 0 auto; }
.totals__note--code { margin-top: var(--s-2); }
