@charset "UTF-8";

/* ==========================================================================
   BLOOM — design system
   --------------------------------------------------------------------------
   A stark grotesk system: one type size does almost all the work, the page
   gutter is a single 15px unit, product images run edge to edge with no
   gutters at all, and every transition is an opacity fade. Colour comes from
   the flowers, never from the interface.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Font
   -------------------------------------------------------------------------- */

/* Neue Haas Grotesk Text Pro shipped as Regular only — one static cut,
   usWeightClass 400, no variable axis — so there was no heavier weight to
   call on and the interface had to be thickened with a stroke. This replaces
   it with a variable grotesk carrying a real 100–900 axis, so every weight
   below is a drawn weight and nothing is faked.

   To go back to Neue Haas Grotesk: add its Medium and Bold woff2 files to
   static/fonts/, swap the two @font-face blocks, and point --font at it. The
   weights in the tokens block stay as they are. */
@font-face {
  font-family: "Inter Variable";
  src: url("../fonts/Inter-Variable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Kept so the swap back is one line, not a re-download.
@font-face {
  font-family: "Neue Haas Grotesk Text Pro";
  src: url("../fonts/NeueHaasGroteskTextPro-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
*/

/* --------------------------------------------------------------------------
   2. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Colour.

     The shell used to be pure white so the photography carried every bit of
     hue. It still carries it — the shell has only moved off the neutral axis
     far enough to read as a material rather than as a screen.

     The grounds are warm and botanical; the one colour that is allowed to
     be a colour comes from the homepage photograph.

       --paper    a warm cream. Measured against the light-ground frames in
                  the catalogue it is ΔE 7 from the ground those photographs
                  are actually shot on, where pure white was 11 — so the page
                  and the picture read as one field rather than a picture on
                  a lightbox. Body, overlay menu, drawer, compact header.
       --surface  the sage the photography sits in: product tiles, the
                  gallery frame, cart thumbnails, editorial figures, and the
                  filter panel when it is open. Green is the one botanical
                  hue that competes with none of the blooms, which are blue,
                  purple, yellow, red and white.
       --accent   the bougainvillea off the homepage wall, taken to L* 45 and
                  deepened until it cleared AA. It is the only saturated
                  colour on the site and it marks three things, all of them
                  states rather than decoration: the filter you have chosen,
                  the page you are on, and a text link under the cursor.

     Type stays black. Everything clears AA for small text on both grounds —
     --gray at 5.5:1 and 4.9:1, --accent at 5.7:1 and 5.1:1 — where the old
     #808080 managed only 3.7:1 on white. --reverse is the white that type on
     black keeps, so a tinted page never tints a button label. */
  --ink: #000;
  --paper: #f8f6f0;
  --surface: #e7ebe2;
  --gray: #68645a;
  --rule: #e3e0d6;
  --rule-strong: #000;
  --accent: #95496a;
  --reverse: #fff;
  --veil: rgba(255, 255, 255, 0.94);

  /* Spacing — everything is a multiple of the 15px unit. */
  --u: 15px;
  --u-half: 7.5px;
  --u2: 30px;
  --u3: 45px;
  --u4: 60px;
  --u6: 90px;
  --u8: 120px;

  /* Type.
     The reference runs the entire site on one size: 12px / 400 / 0.02em.
     Nothing is larger except the wordmark, and 10px appears only on the
     smallest metadata, where tracking goes back to normal. Line height is
     0.8 for tight label stacks and 1.3 for anything read as a sentence. */
  --font: "Inter Variable", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --fs-body: 12px;
  --fs-small: 10px;

  /* Weights, all drawn rather than synthesised, chosen by measurement.
     Ink over bounding box, same string at 48px:
        reference UI text     0.2558
        this face at 520      0.2524   ← --fw-body
        this face at 400      0.2126   (what the old face gave, too light)
     The reference specifies 400, but in a face whose cap height is 0.700em;
     this one is 0.727em, so 520 is what puts the same amount of ink on the
     page. Matching the density is the point, not matching the number. */
  --fw-body: 520;
  --fw-strong: 600;
  /* The current page number. The reference sets its pager bold against a 400
     body, which measures 1.291 ink over the inactive number in its own face
     (canvas, same glyphs, 96px). This face at 710 measures 1.299 against
     --fw-body — the same step, not the same number. 600 gives only 1.12 and
     reads as a rendering accident rather than a state. */
  --fw-pager: 710;
  --ls: 0.02em;
  --ls-tight: -0.012em;
  --lh: 1.3;
  --lh-stack: 0.8;
  --lh-copy: 1.5;

  /* The reference lockup measures 0.2143 stem over cap height — a Black, not
     a Bold. This face reaches 0.2190 at 740, so the wordmark is a real
     weight too. Heights: 70px at desktop, 44px below 64em, 21px in the bar. */
  --fw-wordmark: 740;
  --wordmark-size: 58px;      /* lockup height 44px, below 64em */
  --wordmark-size-sp: 29px;   /* lockup height 21px, the compact bar */

  /* Motion */
  --fade: 0.3s ease;
  --fade-slow: 0.5s ease;
  --slide: 0.42s cubic-bezier(0.22, 0.61, 0.36, 1);

  /* Structure.
     The reference header is 38px of nav + 100px of wordmark + 15px of
     padding = 153px, and it is replaced below 1440px by a 45px bar. */
  --bar-h: 38px;
  --header-h: calc(100px + var(--bar-h) + var(--u));
  --bar-sp-h: 45px;
  --tile-ratio: 3 / 4;
  --z-header: 60;
  --z-menu: 70;
  --z-drawer: 80;
}

/* --------------------------------------------------------------------------
   3. Reset
   -------------------------------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  letter-spacing: var(--ls);
  line-height: var(--lh);
  overflow-x: hidden;
}

/* No -webkit-font-smoothing: antialiased. The reference leaves smoothing on
   auto, and forcing grayscale antialiasing is what thins type out on macOS. */

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }

/* Headings carry no size of their own. Nothing on this site is larger than
   body text except the wordmark, so the browser's 2em h1 has to go — it
   would otherwise leak in through any heading that skips a utility class. */
h1, h2, h3, h4, h5, h6 {
  font-size: inherit;
  font-weight: inherit;
}

img, video, svg { display: block; max-width: 100%; }

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

input, select, textarea {
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
}

/* Visible, unmistakable focus — the interface is quiet, so focus must not be. */
:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: 2px;
}
.is-inverted :focus-visible,
.hero :focus-visible {
  outline-color: var(--reverse);
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  top: 0; left: 0;
  z-index: 200;
  padding: var(--u-half) var(--u);
  background: var(--ink);
  color: var(--reverse);
  transform: translateY(-120%);
}
.skip-link:focus { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

/* --------------------------------------------------------------------------
   4. Type scale
   -------------------------------------------------------------------------- */

/* Every heading on the site is body size. t-h1 and t-h2 exist so the markup
   can stay semantic, not so the text can get bigger. */
.t-small {
  font-size: var(--fs-small);
  letter-spacing: normal;
}

.t-meta { color: var(--gray); }

.t-lead {
  line-height: var(--lh);
  max-width: 62ch;
}

.t-copy {
  line-height: var(--lh-copy);
  max-width: 68ch;
}
.t-copy + .t-copy { margin-top: 1.1em; }

.t-h1,
.t-h2 {
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  line-height: var(--lh);
  letter-spacing: var(--ls);
}

.t-rule {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: 0;
}

/* The wordmark — the only display element on the site. */
.wordmark {
  display: inline-block;
  font-size: var(--wordmark-size);
  font-weight: var(--fw-wordmark);
  line-height: 0.78;
  letter-spacing: var(--ls-tight);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   5. Layout primitives
   -------------------------------------------------------------------------- */

.bleed { width: 100%; }

.gutter { padding-inline: var(--u); }

.stack > * + * { margin-top: var(--u); }

.row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--u);
}

.main { display: block; }

/* --------------------------------------------------------------------------
   6. Header
   -------------------------------------------------------------------------- */

/* The reference runs two headers, and the switch is at 90em (1440px).
   Above it: a tall band, fixed, with no background at all — the page scrolls
   underneath the wordmark and the nav. Below it: a compact opaque bar.
   The transparent band spans the full width, so it must not swallow clicks
   meant for the content behind it; pointer-events are handed back to the
   controls only. */
.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: var(--z-header);
  background: transparent;
  pointer-events: none;
}
.header a,
.header button { pointer-events: auto; }

/* Content clears the header, then slides under it as the page scrolls.
   The hero is the exception: it runs full-bleed behind the bar. */
body:not(.page--home) { padding-top: var(--bar-sp-h); }

/* The hero keeps its own white-on-photograph treatment at every width. */
.header--overlay { color: var(--reverse); }

.header__bar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: stretch;
  height: var(--bar-sp-h);
  padding-inline: var(--u-half);
}

/* The groups fill the bar's height so the links and the hamburger inherit a
   full-height tap target instead of collapsing to the height of their ink. */
.header__group {
  display: flex;
  align-items: center;
  gap: 0;
}
.header__group--end { justify-content: flex-end; }
.header__group--center { justify-content: center; }

.header__link {
  display: inline-flex;
  align-items: center;
  height: 100%;
  padding: 0 var(--u-half);
  opacity: 1;
  transition: opacity var(--fade);
}
.header__link:hover { opacity: 0.45; }
.header__link[aria-expanded="true"] { opacity: 0.45; }

.header__count::before { content: "\00a0("; }
.header__count::after { content: ")"; }

/* Cart: a bag on the compact bar, the word on desktop. */
.header__cart {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.header__cart-icon {
  display: block;
  width: 18px;
  height: 18px;
}
.header__cart-label { display: none; }

@media (min-width: 64em) {
  .header__cart-icon { display: none; }
  .header__cart-label { display: inline; }
}

/* The hero bar keeps the word at every width — the reference's homepage does
   the same, and spells its actions out over the photograph. */
.header--overlay .header__cart-icon { display: none; }
.header--overlay .header__cart-label { display: inline; }

/* The compact wordmark that sits inside the bar below 1440px. */
.header__mark {
  display: inline-block;
  font-size: var(--wordmark-size-sp);
  font-weight: var(--fw-wordmark);
  line-height: 1;
  letter-spacing: var(--ls-tight);
  white-space: nowrap;
}

/* Hamburger: two hairlines, nothing more. */
.burger {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 30px;
  height: 100%;
  padding: 0 var(--u-half);
}
.burger span {
  display: block;
  width: 15px;
  height: 1px;
  background: currentColor;
  transition: transform var(--fade), opacity var(--fade);
}
.burger[aria-expanded="true"] span:first-child { transform: translateY(2.5px) rotate(45deg); }
.burger[aria-expanded="true"] span:last-child { transform: translateY(-2.5px) rotate(-45deg); }

/* Masthead — the wordmark block under the utility bar, desktop only. */
/* 100px of wordmark plus the 15px unit beneath it: with the 38px bar above,
   that is the reference's 153px header exactly. */
.masthead {
  display: none;
  justify-content: center;
  align-items: center;
  height: calc(100px + var(--u));
  padding: 0 var(--u) var(--u);
}
.masthead a { display: inline-block; }

/* --------------------------------------------------------------------------
   7. Overlay menu
   -------------------------------------------------------------------------- */

.menu {
  position: fixed;
  inset: 0;
  z-index: var(--z-menu);
  background: var(--paper);
  color: var(--ink);
  overflow-y: auto;
  overscroll-behavior: contain;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--fade), visibility var(--fade);
}
.menu.is-open {
  opacity: 1;
  visibility: visible;
}

.menu__inner {
  padding: var(--bar-sp-h) var(--u) var(--u3);
}

.menu__close {
  position: absolute;
  top: 0; right: var(--u-half);
  height: var(--bar-sp-h);
  padding: 0 var(--u-half);
  transition: opacity var(--fade);
}
.menu__close:hover { opacity: 0.45; }

/* Columns rather than a grid: categories have wildly different child counts,
   and a grid would leave a tall empty cell under every short one. */
.menu__columns {
  columns: 4;
  column-gap: var(--u);
  padding-top: var(--u2);
}

.menu__column {
  break-inside: avoid;
  padding-bottom: var(--u3);
}

.menu__heading {
  display: block;
  padding-bottom: var(--u);
  transition: opacity var(--fade);
}
.menu__heading:hover { opacity: 0.45; }

.menu__list li + li { margin-top: 2px; }

.menu__sub {
  color: var(--gray);
  transition: color var(--fade);
}
.menu__sub:hover { color: var(--ink); }

.menu__foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--u);
  padding-top: var(--u3);
  color: var(--gray);
}
.menu__foot a:hover { color: var(--ink); }

/* --------------------------------------------------------------------------
   8. Hero — the homepage is one locked screen
   -------------------------------------------------------------------------- */

.page--home,
.page--home body { height: 100%; }

body.page--home {
  height: 100svh;
  overflow: hidden;
}

.hero {
  position: relative;
  width: 100%;
  height: 100svh;
  overflow: hidden;
  color: var(--reverse);
}

.hero__media {
  position: absolute;
  inset: 0;
}
.hero__media img,
.hero__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
}

/* The photograph is 2000x2555 — portrait, almost exactly 4:5. On a phone,
   cover trims only the sides and the full height of the frame is on screen,
   which is why mobile reads well. The moment the viewport is wider than the
   photograph the crop turns vertical instead, and it is severe: a 16:9
   desktop shows 44% of the frame's height, a 1024x768 one 59%. At the
   inherited 42% anchor that band lands in the middle of the picture, slicing
   the top off the window and cutting the urn through at the rim — the subject
   amputated at both ends, which is what reads as wrong on a large screen.

   24% frames a whole picture instead: the window complete, wall above it for
   the wordmark, the bougainvillea rising across the bottom. Nothing is cut
   through. Measured white-on-photograph at the five desktop sizes, it also
   leaves the type better off than 42% did — BLOOM 8.0-8.6:1, the collection
   line 3.3-5.0, SEE MORE 4.2-5.1.

   Scoped to viewports wider than the photograph's own ratio, so phones and
   portrait tablets are untouched. Below that ratio the vertical overflow is
   zero and object-position has no effect on the Y axis anyway; the query
   states the intent rather than relying on that. */
@media (min-aspect-ratio: 2000 / 2555) {
  .hero__media img,
  .hero__media video {
    object-position: center 24%;
  }
}

/* A whisper of shade top and bottom so white type stays legible on any crop. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom, rgba(0, 0, 0, 0.32) 0%, rgba(0, 0, 0, 0) 28%),
    linear-gradient(to top, rgba(0, 0, 0, 0.42) 0%, rgba(0, 0, 0, 0) 24%);
  pointer-events: none;
}

.hero__content {
  position: relative;
  height: 100%;
  padding: calc(var(--bar-sp-h) + var(--u-half)) var(--u) var(--u2);
  text-align: center;
}

.hero__wordmark { display: block; }

/* The caption is pinned to the bottom of the viewport rather than laid out in
   the grid, so SEE MORE lands on the reference's offset: 30px from the bottom
   below 64em, 15px above it. */
.hero__caption {
  position: absolute;
  left: 0; right: 0;
  bottom: var(--u2);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--u-half);
}

.hero__collection {
  letter-spacing: var(--ls);
}

.hero__enter {
  display: inline-block;
  letter-spacing: var(--ls);
  /* The reference blinks this: opacity 0 to 1, 0.6s ease-in-out, 0.3s delay,
     infinite, alternate — so a 1.2s round trip that goes fully dark. Nothing
     moves, so there is no layout shift. */
  animation: blink 0.6s ease-in-out 0.3s infinite alternate;
}

@keyframes blink {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Reduced motion gets the destination, not the pulse. */
@media (prefers-reduced-motion: reduce) {
  .hero__enter {
    animation: none;
    text-decoration: underline;
    text-underline-offset: 3px;
  }
}

/* --------------------------------------------------------------------------
   9. Catalogue
   -------------------------------------------------------------------------- */

.crumb {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--u-half);
  justify-content: center;
  padding: var(--u) var(--u) var(--u2);
  color: var(--gray);
}
/* Above 1440px `main` already supplies the 30px the reference puts under the
   header, so the breadcrumb does not need its own top padding as well. */
@media (min-width: 90em) {
  .crumb { padding-top: 0; }
}
.crumb a:hover { color: var(--ink); }
.crumb [aria-current] { color: var(--ink); }

.intro {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--u);
  padding: 0 var(--u) var(--u3);
}
.intro p { line-height: var(--lh-copy); }

/* No rules, no sticky, no background plate. The reference sets FILTER and
   SORT straight onto the page at the 15px gutter with 15px of air beneath
   them, and lets the grid start immediately after. */
.toolbar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 0 var(--u) var(--u);
}

.toolbar__btn {
  transition: opacity var(--fade);
}
.toolbar__btn:hover { opacity: 0.45; }
.toolbar__btn[aria-expanded="true"] { opacity: 0.45; }

.toolbar__count { color: var(--gray); }

.panel {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  /* The one painted state: an open filter or sort panel sits on the sage
     rather than the page ground, so it reads as a surface laid over the page
     rather than a borderless region of it. */
  background: var(--surface);
  transition: grid-template-rows var(--slide);
}
.panel.is-open { grid-template-rows: 1fr; }
.panel__inner {
  min-height: 0;
  overflow: hidden;
}
.panel__body {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--u2) var(--u);
  padding: var(--u2) var(--u);
  border-bottom: 1px solid var(--rule);
}
.panel--sort .panel__body { grid-template-columns: repeat(2, 1fr); }

.filter__group-title {
  display: block;
  padding-bottom: var(--u);
  color: var(--gray);
  letter-spacing: var(--ls);
  font-size: var(--fs-small);
}

.filter__list li + li { margin-top: 2px; }

.filter__link {
  color: var(--gray);
  transition: color var(--fade);
}
.filter__link:hover { color: var(--ink); }
.filter__link.is-active {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.filter__clear {
  grid-column: 1 / -1;
  justify-self: start;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* --- the grid ------------------------------------------------------------ */

/* minmax(0, 1fr) matters: without it a long product name sets the column's
   min-content width and pushes the grid past the viewport. */
.grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;              /* images butt against each other, edge to edge */
}

.tile {
  position: relative;
  background: var(--surface);
}

.tile__link { display: block; }

.tile__media {
  position: relative;
  aspect-ratio: var(--tile-ratio);
  overflow: hidden;
}
.tile__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.tile__img--alt {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--fade-slow);
}

.tile__flag {
  position: absolute;
  top: var(--u);
  left: var(--u);
  z-index: 2;
  letter-spacing: var(--ls);
  font-size: var(--fs-small);
  mix-blend-mode: difference;
  color: #fff;
}

.tile__info {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 2;
  display: flex;
  justify-content: space-between;
  gap: var(--u);
  padding: var(--u);
}

/* Desktop: the label is part of the hover reveal, exactly as the reference
   does it — image first, information second. */
@media (hover: hover) and (min-width: 64em) {
  .tile__info {
    opacity: 0;
    transition: opacity var(--fade);
  }
  .tile:hover .tile__info,
  .tile:focus-within .tile__info { opacity: 1; }
  .tile:hover .tile__img--alt,
  .tile:focus-within .tile__img--alt { opacity: 1; }
}

/* Touch: never hide the price behind a hover that cannot happen. The label
   stays an overlay on the bottom of the photograph, as the reference does —
   it does not drop onto a white strip beneath the image — and it stacks so a
   long name keeps the full column width instead of fighting the price. The
   name is clamped to one line; the photography is lit to stay pale at the
   bottom of the frame so the type holds. */
@media (hover: none), (max-width: 63.94em) {
  .tile__info {
    opacity: 1;
    flex-direction: column;
    gap: 0;
    line-height: var(--lh);
  }
  .tile__name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

.tile__name {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: break-word;
}
.tile__price { flex: 0 0 auto; white-space: nowrap; }
.tile__was {
  color: var(--gray);
  text-decoration: line-through;
  margin-right: 6px;
}
.tile__sold { color: var(--gray); }

.empty {
  padding: var(--u6) var(--u);
  text-align: center;
  color: var(--gray);
}

/* --- pagination ---------------------------------------------------------- */

/* Numbered pages. The reference sets them at body size in a centred row on a
   15px rhythm, 30px clear of the grid, with no rule above and no underlines:
   the current page is simply heavier. All three numbers are BLOOM's own
   tokens, which happen to be the same measurements. */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--u);
  margin-top: var(--u2);
  padding: 0 var(--u) var(--u3);
  font-size: var(--fs-body);
  letter-spacing: var(--ls);
}
.pagination__list {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--u);
}
.pagination__list li { line-height: 1; }
.pagination__current { font-weight: var(--fw-pager); color: var(--accent); }
.pagination a { text-decoration: none; }
.pagination a:hover { opacity: 0.45; }

/* The gallery chevron again, at the size the reference gives its own: 4 by 8. */
.pagination__arrow {
  display: flex;
  align-items: center;
}
.pagination__arrow svg {
  display: block;
  width: 4px;
  height: 9px;
}

/* --------------------------------------------------------------------------
   10. Product detail
   -------------------------------------------------------------------------- */

.pdp {
  display: grid;
  grid-template-columns: 1fr;
}

/* --- gallery: one frame per view, moving left to right ------------------- */

.pdp__media {
  position: relative;
  overflow: hidden;      /* the track scrolls, the column does not */
  /* How far down the page the frame starts, which is what is left of the
     window for it. Below 90em that is the 45px bar; at 90em and up the tall
     masthead and the 30px of air under it. */
  --pdp-media-top: var(--bar-sp-h);
}

.gallery {
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* The reference sets this so a vertical drag still scrolls the page while
     a horizontal one moves the gallery. */
  touch-action: pan-y;
}
.gallery::-webkit-scrollbar { display: none; }

/* The frame is 3:4, measured off the reference rather than chosen: four of
   its product pages all render the image frame at 712x950 on a 1440x900
   window — ratio 1.333, 49.5% of the viewport width — and the same 1.333 at
   1024, 1280, 1920 and on a phone.
 
   At 3:4 a half-width column is taller than the window on any desktop, so the
   cap stops the frame running past the fold. The ratio still sets the height;
   the cap only takes over once that height would exceed what is left of the
   window below the header. It bites on desktop (960 -> 717 at 1440x900,
   1280 -> 897 at 1920x1080) and never on a phone, where a full-width 3:4
   frame already fits. vh first, then svh, so a browser that does not know the
   newer unit still gets a working cap rather than no cap at all. */
/* The frame is 80% of the height described above — a fifth off, applied to
   both of the things that set it rather than to one of them, so it still
   tracks the column width and still stops short of the fold:

       ratio  3 / 4 -> width x 1.3333, x 0.8 -> width x 1.0667 = 15 / 16
       cap    100svh - top, x 0.8

   Measured before -> after: 683->546 at 1024, 755->604 at 1280, 717->574 at
   1440, 897->718 at 1920, and 468->374 on a 390 phone.

   The picture still fills the frame: object-fit stays `cover`, so the
   photograph keeps its own proportions and the frame simply shows less of
   its height. Nothing is scaled unevenly and nothing is letterboxed. */
.gallery__slide {
  position: relative;
  flex: 0 0 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  --pdp-frame-scale: 0.8;
  aspect-ratio: 15 / 16;
  max-height: calc((100vh - var(--pdp-media-top)) * var(--pdp-frame-scale));
  max-height: calc((100svh - var(--pdp-media-top)) * var(--pdp-frame-scale));
  background: var(--surface);
}
.gallery__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The arrows are the reference's, to the pixel: a bare 7x14 chevron drawn in
   a 1px stroke with a bevelled join, no button chrome, no circle, no plate,
   centred vertically and flush to the frame edge inside a 47x35 hit area.
   They stay visible rather than appearing on hover, and fade to 0.35 at the
   ends of the run instead of disappearing. */
.gallery__arrow {
  position: absolute;
  top: 50%;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 47px;
  height: 35px;
  transform: translateY(-50%);
  color: var(--ink);
  transition: opacity var(--fade);
}
.gallery__arrow svg { width: 7px; height: 14px; overflow: visible; }
.gallery__arrow--prev { left: 0; }
.gallery__arrow--next { right: 0; }
.gallery__arrow[disabled] {
  opacity: 0.35;
  pointer-events: none;
}

/* Phones get a shorter frame than the 3:4 the reference uses, and the reason
   is a structural difference rather than a different taste in proportions.

   Measured on the live AMBUSH product page at 390x844, reloaded under a real
   mobile user agent: the image frame is 390x520 — the same 3:4 BLOOM has, to
   the pixel — but it starts at document y=1 and the navigation floats over
   it, so the bar eats the top of the picture and the product name lands at
   y=536. BLOOM's bar is opaque and the body is padded below it, so the whole
   520 is visible, the frame ends at 565 and the name is pushed to 595.

   Same frame, 59px more of it before you reach the product. Matching their
   structure would mean letting an opaque bar cover the top of every photo;
   shortening the frame gets the same result without hiding any of the page.
   5:6 puts the name within 16px of the reference at all four phone widths:

                320    375    390    414
     AMBUSH     443    516    536    568
     BLOOM      459    525    543    572

   The photograph is not scaled down — object-fit: cover keeps it at full size
   and trims about 10% top and bottom. Desktop is untouched: above 48em the
   frame stays 3:4, which is what the reference renders there. */
@media (max-width: 47.99em) {
  /* 5 / 6 taken down the same fifth: width x 1.2, x 0.8 -> width x 0.96. */
  .gallery__slide { aspect-ratio: 25 / 24; }
}

@media (prefers-reduced-motion: reduce) {
  .gallery { scroll-behavior: auto; }
}

.pdp__panel {
  padding: var(--u2) var(--u) var(--u3);
}

.pdp__crumb {
  color: var(--gray);
  padding-bottom: var(--u2);
}
.pdp__crumb a:hover { color: var(--ink); }

.pdp__name {
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  letter-spacing: var(--ls);
  line-height: var(--lh);
}

.pdp__short {
  padding-top: var(--u-half);
  color: var(--gray);
}

.pdp__price {
  padding-top: var(--u);
}
.pdp__price .was {
  color: var(--gray);
  text-decoration: line-through;
  margin-right: var(--u-half);
}

.pdp__section {
  padding-top: var(--u2);
}
.pdp__section-title {
  display: block;
  padding-bottom: var(--u-half);
  color: var(--gray);
  font-size: var(--fs-small);
  letter-spacing: var(--ls);
}

.pdp__availability {
  padding-top: var(--u2);
}

/* The buy button closes the column: a full unit of air above it, and nothing
   after it, so it reads as the end of the product rather than a widget
   dropped into the middle of the copy. */
.pdp__section--buy {
  padding-top: var(--u3);
}

.spec {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 4px var(--u2);
}
.spec dt { color: var(--gray); }

.qty {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--rule);
}
.qty button {
  width: 36px;
  transition: opacity var(--fade);
}
.qty button:hover { opacity: 0.45; }
.qty input {
  width: 44px;
  text-align: center;
  border: 0;
  border-inline: 1px solid var(--rule);
  background: none;
  -moz-appearance: textfield;
  appearance: textfield;
}
.qty input::-webkit-outer-spin-button,
.qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* --- buttons ------------------------------------------------------------- */

/* 38px tall with a 5px radius — the one rounded corner in the reference. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 38px;
  padding: 0 var(--u2);
  background: var(--ink);
  color: var(--reverse);
  letter-spacing: var(--ls);
  border: 1px solid var(--ink);
  border-radius: 5px;
  transition: background-color var(--fade), color var(--fade);
}
.btn:hover { background: var(--paper); color: var(--ink); }
.btn:disabled,
.btn[aria-disabled="true"] {
  background: var(--rule);
  border-color: var(--rule);
  color: var(--gray);
  cursor: not-allowed;
}
.btn:disabled:hover { background: var(--rule); color: var(--gray); }

.btn--ghost {
  background: var(--paper);
  color: var(--ink);
}
.btn--ghost:hover { background: var(--ink); color: var(--reverse); }

.btn--inline { width: auto; }

.link-u {
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: opacity var(--fade);
}
.link-u:hover { color: var(--accent); opacity: 1; }

/* --- related ------------------------------------------------------------- */

.section-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: var(--u3) var(--u) var(--u);
  letter-spacing: var(--ls);
}

/* --------------------------------------------------------------------------
   11. Cart
   -------------------------------------------------------------------------- */

.scrim {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-drawer) - 1);
  background: rgba(0, 0, 0, 0.25);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--fade), visibility var(--fade);
}
.scrim.is-open { opacity: 1; visibility: visible; }

.drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  z-index: var(--z-drawer);
  width: min(420px, 100vw);
  display: flex;
  flex-direction: column;
  background: var(--paper);
  transform: translateX(100%);
  visibility: hidden;
  transition: transform var(--slide), visibility var(--slide);
}
.drawer.is-open { transform: translateX(0); visibility: visible; }

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--bar-sp-h);
  padding-inline: var(--u);
  border-bottom: 1px solid var(--rule);
  letter-spacing: var(--ls);
}

/* The body is the flex column: the list scrolls, the totals and the checkout
   button stay in view however many lines the cart holds. */
.drawer__body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.drawer__list {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.drawer__foot {
  flex: 0 0 auto;
  border-top: 1px solid var(--rule);
  padding: var(--u);
}

.line {
  display: grid;
  grid-template-columns: 72px 1fr;
  gap: var(--u);
  padding: var(--u);
  border-bottom: 1px solid var(--rule);
}
.line__media {
  aspect-ratio: var(--tile-ratio);
  background: var(--surface);
}
.line__media img { width: 100%; height: 100%; object-fit: cover; }
.line__body { display: flex; flex-direction: column; gap: 4px; }
.line__top { display: flex; justify-content: space-between; gap: var(--u); }
.line__meta { color: var(--gray); }
.line__actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--u);
  margin-top: auto;
}

.totals {
  display: grid;
  gap: 4px;
  padding-bottom: var(--u);
}
.totals__row { display: flex; justify-content: space-between; }
.totals__row--grand {
  padding-top: var(--u-half);
  margin-top: var(--u-half);
  border-top: 1px solid var(--rule);
}
.totals__note { color: var(--gray); }

.cart-page {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--u3);
  padding: var(--u2) var(--u) var(--u6);
}

.cart-page__lines .line {
  grid-template-columns: 110px 1fr;
  padding-inline: 0;
}

/* --------------------------------------------------------------------------
   12. Forms / checkout
   -------------------------------------------------------------------------- */

.checkout {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--u3);
  padding: var(--u2) var(--u) var(--u6);
}

.fieldset { border: 0; padding: 0; margin: 0; }
.fieldset + .fieldset { margin-top: var(--u3); }

.fieldset__legend {
  display: block;
  padding-bottom: var(--u);
  color: var(--gray);
  font-size: var(--fs-small);
  letter-spacing: var(--ls);
}

.fields {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--u);
}
.field--wide { grid-column: 1 / -1; }

.field label {
  display: block;
  padding-bottom: 5px;
  font-size: var(--fs-small);
  letter-spacing: var(--ls);
  color: var(--gray);
}

.field__input {
  width: 100%;
  padding: 11px 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  transition: border-color var(--fade);
}
.field__input:focus {
  outline: none;
  border-bottom-color: var(--ink);
}
.field__input:focus-visible {
  outline: none;
  border-bottom-color: var(--ink);
  box-shadow: 0 1px 0 0 var(--ink);
}
textarea.field__input { resize: vertical; }

.field__error {
  padding-top: 5px;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.summary {
  align-self: start;
  border-top: 1px solid var(--rule-strong);
  padding-top: var(--u);
}

.confirmation {
  padding: var(--u6) var(--u);
  max-width: 640px;
  margin-inline: auto;
}

/* --------------------------------------------------------------------------
   13. Editorial pages
   -------------------------------------------------------------------------- */

.editorial { padding-bottom: var(--u6); }

.editorial__head {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--u2);
  padding: 0 var(--u) var(--u3);
}

.editorial__figure { aspect-ratio: 16 / 9; background: var(--surface); }
.editorial__figure img { width: 100%; height: 100%; object-fit: cover; }

.editorial__body {
  padding: var(--u3) var(--u) 0;
}

/* --------------------------------------------------------------------------
   14. Footer
   -------------------------------------------------------------------------- */

/* 90px of run-up, which is what the footer used on the product page when it
   carried no newsletter. The 157px it used elsewhere was the approach to the
   newsletter block; with that gone, the taller value would leave the columns
   floating in the middle of nowhere. */
.footer {
  padding: var(--u6) var(--u) var(--u);
  border-top: 1px solid var(--rule);
}

.footer__columns {
  display: grid;
  /* Three columns since OCCASIONS left: a fourth track would hold nothing but
     air on the right-hand quarter of every page. */
  grid-template-columns: repeat(3, 1fr);
  gap: var(--u2) var(--u);
  padding-bottom: var(--u6);
}

.footer__title {
  display: block;
  padding-bottom: var(--u);
  color: var(--gray);
  font-size: var(--fs-small);
  letter-spacing: var(--ls);
}

.footer__list li + li { margin-top: 2px; }
.footer__list a { transition: opacity var(--fade); }
.footer__list a:hover { opacity: 0.45; }

.footer__base {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--u);
  color: var(--gray);
}
.footer__base a:hover { color: var(--ink); }
.footer__legal { display: flex; flex-wrap: wrap; gap: var(--u); }

/* --------------------------------------------------------------------------
   15. Breakpoints — 64em is the desktop switch, matching the reference
   -------------------------------------------------------------------------- */

@media (min-width: 48em) {
  .cart-page { grid-template-columns: 1.6fr 1fr; gap: var(--u4); }
  .checkout { grid-template-columns: 1.4fr 1fr; gap: var(--u4); }
  .editorial__head { grid-template-columns: 1fr 1fr; align-items: start; }
}

@media (min-width: 64em) {
  /* The hero lockup steps up to the reference's 70px painted height, and
     SEE MORE tightens to 15px off the bottom edge. */
  :root { --wordmark-size: 93px; }
  .hero__caption { bottom: var(--u); }

  .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  .pdp { grid-template-columns: 1fr 1fr; }

  /* The panel scrolls with the page rather than sticking: it is taller than a
     viewport once the specification and care notes are in, and a nested scroll
     area would bury the bottom of it. The image column is far taller, so the
     panel still reads as pinned to the top of the product.

     The content sits in a narrow measure centred in its half, as the
     reference does — 348px of text inside a 712px column. */
  .pdp__panel {
    align-self: start;
    padding: var(--u2) var(--u3) var(--u3);
  }
  .pdp__inner {
    width: min(348px, 100%);
    margin-inline: auto;
  }

  .intro { grid-template-columns: repeat(3, 1fr); }
}

/* 90em (1440px) is where the reference swaps the compact bar for the tall
   transparent band. Below it the bar is opaque and the masthead is gone. */
@media (min-width: 90em) {
  /* The masthead pushes the product column down, so the gallery has less of
     the window to work with; its cap follows. */
  .pdp__media { --pdp-media-top: calc(var(--header-h) + var(--u2)); }

  body:not(.page--home) { padding-top: var(--header-h); }

  /* 30px of air under the header before the first line of the page — but not
     on the hero, which runs full-bleed from the very top. */
  body:not(.page--home) .main { padding-top: var(--u2); }

  .header__bar { height: var(--bar-h); }
  .header__mark { display: none; }
  .masthead { display: flex; }
}

@media (max-width: 89.94em) {
  /* Opaque, because there is no room for a photograph to read behind it. */
  .header:not(.header--overlay) { background: var(--paper); }
  .masthead { display: none; }
}

@media (max-width: 63.94em) {
  .menu__columns { columns: 1; }
  .menu__column { padding-bottom: var(--u2); }
  .menu__inner { padding-top: calc(var(--bar-sp-h) + var(--u)); }
  .panel__body { grid-template-columns: repeat(2, 1fr); }
  .panel--sort .panel__body { grid-template-columns: 1fr; }
  .intro { grid-template-columns: 1fr; }
  .footer__columns { grid-template-columns: repeat(2, 1fr); }
  .footer { padding-top: var(--u6); }
  .masthead { padding-bottom: var(--u2); }

  /* Larger tap targets on touch. */
  .header__link { padding-inline: var(--u-half); }
  .menu__list a,
  .filter__link,
  .footer__list a { display: inline-block; padding-block: 5px; }
}

@media (max-width: 30em) {
  .fields { grid-template-columns: 1fr; }
  .cart-page__lines .line { grid-template-columns: 80px 1fr; }
  .footer__columns { grid-template-columns: 1fr; }
}

/* Tiny screens: two columns are still right, but the label needs room. */
@media (max-width: 22.5em) {
  :root { --u: 12px; --u-half: 6px; }
}


/* --- pickup code ----------------------------------------------------------
   The one number on the confirmation a customer has to read out loud, so it
   is set large, spaced and tabular: legible across a counter, and hard to
   misread a 6 for an 8 from a phone held at arm's length. */
.pickup-code {
  font-size: 34px;
  font-weight: 700;
  letter-spacing: 0.22em;
  font-variant-numeric: tabular-nums;
  margin: 0 0 var(--u-half);
}
