/* ==========================================================================
   Starfish Group — catalogue.css
   products.html: the full range, grouped by the same three ranges as the hero.

   Cards carry a solid fill. Transparent cards let the wave field show through
   behind small type, which made weights and pack counts hard to read — cards
   are discrete objects, not full-width sections, so filling them does not cut
   the horizontal seam that the transparent-section rule exists to prevent.
   ========================================================================== */

.catalogue {
  /* This section is now the top of the page — clear the absolute header. */
  padding-top: 168px;
  padding-bottom: var(--s-100);
  --card-name: clamp(17px, 1.4vw, 20px);
}

/* --- Section head + filters ------------------------------------------------ */
/* Balanced. The old copy was four words and broke where it liked; this one
   is a sentence, and left to itself it hung "made" alone on a short line. */
.catalogue__heading { margin-top: var(--s-24); text-wrap: balance; }
.catalogue__lede { margin-top: var(--s-20); color: var(--ink-soft); }

/* No rule under this row. It sat 40px above the one on .group__head, and two
   hairlines that close together read as a mistake. The group head keeps its
   own, because that one separates a heading from its grid. */
.ranges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-40);
  margin-top: var(--s-40);
}

/* An index entry, not a control. The range name carries the weight and the
   count sits beside it as data — nothing here should look pressable, because
   the only thing that happens is that the page moves. */
.range {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-8);
  padding-bottom: var(--s-4);
  border-bottom: 1px solid transparent;
  color: var(--ink-soft);
  transition:
    color        var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}
.range:hover { color: var(--ink); border-bottom-color: var(--border-dark); }

/* Marked, not highlighted. */
.range[aria-current="true"] { color: var(--ink); border-bottom-color: var(--charcoal); }

.range__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(17px, 1.5vw, 22px);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-display);
}

.range__count {
  font-size: var(--text-caption);
  font-weight: 500;
  line-height: var(--leading-flat);
  letter-spacing: var(--track-micro);
  font-variant-numeric: tabular-nums;
  opacity: 0.7;
}

/* Same pill as the old filter row, but it marks position rather than state:
   catalogue.js sets aria-current on whichever range you are actually looking
   at, so the row tells you where you are as well as where you can go. */
.jump {
  padding: 9px var(--s-20);
  border: 1px solid var(--border-dark);
  border-radius: var(--radius-pill);
  background: transparent;
  font-size: var(--text-caption);
  font-weight: 500;
  line-height: var(--leading-flat);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink);
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color     var(--dur-fast) var(--ease-out),
    color            var(--dur-fast) var(--ease-out);
}
.jump:hover { border-color: var(--charcoal); }
.jump[aria-current="true"] {
  background: var(--charcoal);
  border-color: var(--charcoal);
  color: var(--cream);
}

/* --- Groups ---------------------------------------------------------------- */

.group__head {
  display: flex;
  /* wrap, so the lede can take a row of its own beneath the title row */
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-12);
  padding-bottom: var(--s-24);
  border-bottom: 1px solid var(--border);
}

/* FLUSH WITH THE BAR. This carried an extra 40px, from when the ranges were
   blocks inside one section and landing exactly at the top put the title
   against the navigation. They are full-width colour bands now, so those
   40px stopped being breathing room and became a strip of the PREVIOUS
   band showing under the bar — the jump looked like it had undershot.

   Landing flush is right for a band: its own 100px of top padding is what
   holds the title off the bar, and nothing of the section above survives. */
.group { scroll-margin-top: var(--header-h); }

/* --------------------------------------------------------------------------
   THE RANGE SWITCHER
   Sits in every range head so you can cross to another without going back to
   the top first. All three appear, with the one you are in marked rather than
   removed — dropping it would give each section a row of a different width and
   a different order, and lose the sense of where you are among three.
   -------------------------------------------------------------------------- */
.group__jump {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-16);
  margin-left: var(--s-24);
}

.group__jump-link {
  font-family: var(--font-body);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}

/* Where you already are. Muted and not a link, so the row says which of the
   three you are reading without offering to take you there again. */

.group__jump-link {
  color: var(--ink-soft);
  padding-bottom: 3px;
  border-bottom: 1px solid var(--border-dark);
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.group__jump-link:hover { color: var(--ink); border-bottom-color: var(--coral); }
.group__jump-link:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .group__jump-link { transition: none; }
}

/* Pushed to the far end of the head row. A jump link with nothing to come
   back to is a dead end — three of these sit a long way down the page. */
.group__top {
  margin-left: auto;
  font-family: var(--font-body);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-meta);
  border-bottom: 1px solid transparent;
  transition: color var(--dur-fast) var(--ease-out);
}
.group__top .btn__arrow { transition: transform var(--dur-fast) var(--ease-out); }
.group__top:hover { color: var(--ink); border-bottom-color: var(--border-dark); }
.group__top:hover .btn__arrow { transform: translateY(-2px); }
.group__top:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .group__top, .group__top .btn__arrow { transition: none; }
}

.group__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-optical-sizing: auto;
  font-size: clamp(22px, 2vw, 28px);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-display);
}

/* One line under each range title saying what the range is and where it is
   made. It reads as a subtitle, which is what it is, and it is the only
   place on the page the words "fish balls" and "Thailand" sit together. */
.group__lede {
  /* The head is a flex row. Full width and last in order puts this on the
     line beneath, under the title, rather than wedged between it and the
     count — which is where it landed first. */
  flex-basis: 100%;
  order: 10;
  margin-top: var(--s-4);
  font-size: var(--text-small);
  line-height: 1.6;
  color: var(--ink-soft);
  /* NOT max-width. Flex decides where to wrap from each item’s clamped size,
     so a 52ch max made this a 457px item that still fitted beside the title.
     The box stays full width — which is what forces the new line — and the
     measure is held with padding instead. */
  padding-right: max(0px, calc(100% - 52ch));
}

.group__count {
  font-size: var(--text-caption);
  font-weight: 500;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* Shown when a filter leaves a group with nothing in it */
.group__empty {
  display: none;
  padding-top: var(--s-40);
  font-size: var(--text-small);
  color: var(--ink-soft);
}
/* --- Grid ------------------------------------------------------------------ */
.group__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s-24);
  margin-top: var(--s-40);
}

/* --- Card ------------------------------------------------------------------ */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  transition: border-color var(--dur-mid) var(--ease-out);
}
.card:hover { border-color: var(--border-dark); }
.card[hidden] { display: none; }

.card__media { position: relative; }

.card__img {
  width: 100%;
  /* 4/3, from a SEPARATE render to the one the sheet shows. This was square,
     with a note that 4/3 cropped the item — true then, because card and sheet
     shared one file and cropping it in CSS cut into the product. The card has
     its own frame now, composed 4/3 from the original with the same margins,
     so nothing is cut and the card loses 64px of empty backdrop. */
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background-color: var(--cream-mid);
}

/* The SKU is the thing a buyer quotes back to us, so it is on the card. */
.card__sku {
  margin-top: 2px;
  font-size: var(--text-caption);
  font-weight: 500;
  letter-spacing: var(--track-micro);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* Details is a button now — it opens the sheet rather than going anywhere. */
.card__link {
  display: inline-flex;
  align-items: center;
  gap: var(--s-8);
  padding-bottom: 3px;
  border-bottom: 1px solid var(--border-dark);
  font-size: var(--text-caption);
  font-weight: 500;
  line-height: var(--leading-flat);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink);
  transition: border-color var(--dur-fast) var(--ease-out);
}
.card__link:hover { border-bottom-color: var(--charcoal); }
.card__link:hover .btn__arrow { transform: translateX(3px); }

.card__badge {
  position: absolute;
  top: var(--s-12);
  left: var(--s-12);
  padding: 6px 10px;
  border-radius: var(--radius-pill);
  background: var(--charcoal);
  font-size: 10px;
  font-weight: 500;
  line-height: var(--leading-flat);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--cream);
}

.card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--s-20);
}

.card__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-optical-sizing: auto;
  font-size: var(--card-name);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-display);
}

.card__sub {
  margin-top: var(--s-4);
  font-size: var(--text-caption);
  line-height: 1.5;
  color: var(--ink-soft);
}

.card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-8);
  margin-top: var(--s-12);
}

.card__meta li {
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: 11px;
  line-height: var(--leading-flat);
  color: var(--ink-soft);
}

/* margin-top:auto pins the footer down however short the name runs, so every
   card in a row ends on the same line */
.card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-12);
  margin-top: auto;
  padding-top: var(--s-20);
  font-size: var(--text-caption);
}

.card__status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--ink-soft);
}
.card__status::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--blue);
}

.card__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 500;
  border-bottom: 1px solid transparent;
}

/* THE WHOLE CARD OPENS IT. The button stays the real control — it is what a
   screen reader announces and what the keyboard lands on — and this simply
   stretches its hit area over the card. No second handler, and no div
   pretending to be a button. */
.card__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* Hover now belongs to the card, since that is the whole target. */
.card:hover .card__link { border-bottom-color: var(--border-dark); }
.card__link .btn__arrow { transition: transform var(--dur-fast) var(--ease-out); }
.card:hover .card__link .btn__arrow { transform: translateX(3px); }

/* The stretched pseudo-element covers the card, so the focus ring has to be
   drawn on the card or it lands on an invisible box. */
.card:focus-within { outline: 2px solid var(--ink); outline-offset: 3px; }
.card__link:focus-visible { outline: none; }

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .group__grid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 900px) {
  .catalogue { padding-top: 130px; padding-bottom: var(--s-64); }

  /* Two rows rather than one crushed one. Four things in a 327px line broke
     "Hot pot" across two lines and "Back to top" across three. The title,
     the count and the way back share the first row; the three ranges take
     the second. */
  .group__head { flex-wrap: wrap; row-gap: var(--s-16); }
  .group__top { order: 2; }
  .group__jump { order: 3; width: 100%; margin-left: 0; }
  .group { margin-top: var(--s-56); }
  .group__grid { grid-template-columns: repeat(2, 1fr); gap: var(--s-12); }
  .card__body { padding: var(--s-12); }
}

@media (max-width: 480px) {
  .group__grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   SPEC SHEET
   The dialog behind every Details button. A native <dialog>, so the backdrop,
   Escape, focus containment and the inertness of the page behind it are the
   browser's job rather than ours.

   The catalogue stays visible through the backdrop on purpose: this is a
   detail view of something you were already looking at, not a new place.
   -------------------------------------------------------------------------- */
.sheet {
  width: min(1240px, calc(100vw - var(--s-40)));
  max-height: min(86vh, 900px);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--surface-card);
  color: var(--ink);
  overflow: hidden;
}

/* Only a tint. A heavy scrim would hide the page this is a detail OF. */
.sheet::backdrop {
  background: rgba(27, 27, 25, 0.44);
}

/* THE PHOTOGRAPH SETS THE HEIGHT.

   The media column is 55% of the panel, and the photographs are square, so
   the panel is exactly as tall as that column is wide: 100/55 = 20/11. Get
   this ratio out of step with the columns and the gap comes straight back.

   Everything else was tried. Letting the spec column set the height left
   190-390px of empty column beside the picture at every width. Cropping the
   square to a portrait clips product: they are framed tight, filling 65-97%
   of the frame, and CR003 runs the full height. And no single width balances
   the two sides, because a narrower spec column is a taller one. */
.sheet__panel {
  position: relative;
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  aspect-ratio: 20 / 11;
  max-height: inherit;
}

/* NOT stretched to the column. The photographs are square and lit on a
   vignetted studio ground — pale blue in the middle, warm at the corners —
   so no flat colour sits behind one without a visible seam. Letting the
   figure be exactly as tall as the picture means there is no band to match:
   the card's own surface carries on below it. */
.sheet__media {
  margin: 0;
  overflow: hidden;
  background: var(--cream-mid);
}
.sheet__media img {
  width: 100%;
  height: 100%;
  /* The box is square by construction, so cover has nothing to crop. It is
     here for the one case that breaks the arithmetic: a window too short for
     the panel, where max-height squashes it. Then the crop is vertical,
     which keeps the full width of a crab stick. */
  object-fit: cover;
}

/* The only scrolling region, and now it does scroll: the panel is as tall as
   the photograph, not as tall as this list. min-height:0 is what lets a grid
   item shrink below its content — without it the item refuses, and the panel
   grows to fit after all. */
.sheet__body {
  min-height: 0;
  overflow-y: auto;
  padding: var(--s-40);
}

.sheet__sku {
  font-size: var(--text-caption);
  font-weight: 500;
  letter-spacing: var(--track-micro);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.sheet__name { margin-top: var(--s-8); }

/* Thai and Chinese are how the same product is asked for in two of our three
   biggest markets, so they sit with the name rather than in the spec list. */
.sheet__alt {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-8) var(--s-20);
  margin-top: var(--s-12);
  font-size: var(--text-small);
  line-height: var(--leading-body);
  color: var(--ink-soft);
}

/* The same line that appears on the card, so the sheet opens on something
   the visitor recognises rather than dropping them straight into a table. */
.sheet__desc {
  margin-top: var(--s-20);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--ink-soft);
}

.sheet__specs {
  margin-top: var(--s-24);
  padding-top: var(--s-24);
  border-top: 1px solid var(--border);
}

.sheet__specs > div {
  display: grid;
  grid-template-columns: 8.5rem 1fr;
  gap: var(--s-20);
  padding-block: var(--s-8);
}

/* Ingredients and the rest run long, so they get the full width under their
   own label instead of a cramped second column. */
.sheet__specs--stack > div {
  grid-template-columns: 1fr;
  gap: var(--s-4);
  padding-block: var(--s-12);
}

.sheet__specs dt {
  font-size: var(--text-caption);
  font-weight: 500;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-soft);
}

.sheet__specs dd {
  margin: 0;
  font-size: var(--text-small);
  line-height: var(--leading-body);
}

/* A value we do not have yet must not read like one we do. */
.sheet__specs dd[data-empty] {
  color: var(--text-mid);
  font-style: italic;
}

/* The filled variant is cream-on-charcoal for use over photography. This
   sheet is a pale panel, so the fill has to invert or the button disappears
   into it. */
.sheet__cta {
  margin-top: var(--s-24);
  background: var(--charcoal);
  border-color: var(--charcoal);
  color: var(--cream);
}

.sheet__close {
  position: absolute;
  top: var(--s-12);
  right: var(--s-12);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-pill);
  background: var(--surface-card);
  color: var(--ink);
  transition: background-color var(--dur-fast) var(--ease-out);
}
.sheet__close:hover { background: var(--cream-dark); }
.sheet__close svg { width: 14px; height: 14px; }

@media (max-width: 760px) {
  .sheet {
    width: 100vw;
    max-width: 100vw;
    max-height: 92vh;
    /* Full-width at the bottom of the screen, which is where a thumb is. */
    margin: auto auto 0;
    border-radius: var(--radius-card) var(--radius-card) 0 0;
  }
  /* One column, so the ratio that made the panel as tall as the media
     COLUMN is meaningless here — left on, it squashed the whole sheet to
     205px and the photograph to a 79px band. */
  .sheet__panel { grid-template-columns: 1fr; aspect-ratio: auto; max-height: 92vh; }
  .sheet__media img { aspect-ratio: 16 / 10; }
  .sheet__body { padding: var(--s-24); }
  .sheet__specs > div { grid-template-columns: 1fr; gap: var(--s-4); }
}

@media (prefers-reduced-motion: reduce) {
  .sheet { transition: none; }
}

/* --------------------------------------------------------------------------
   STANDARDS
   Three things we will not trade away, stated before the range so the
   catalogue is read through them.

   Not numbered, and not a sequence: these are three positions held at the same
   time. A rule above each one groups them as a set of equals — the same job a
   01/02/03 would do, without asserting an order that is not there.
   -------------------------------------------------------------------------- */
/* A panel, so the pitch is a thing on the page rather than three sentences
   that happen to follow the lede.

   Translucent, not opaque: the field runs behind the whole site, and a solid
   fill here would cut a hole in it. 0.72 is enough to lift the panel off the
   waves and hold the body copy at contrast, while the field still reads
   through it. */
/* A strip, not a panel. Each claim is one sentence now, so the block that
   held three paragraphs is over-built — and it sat above the products, which
   put a wall of text on the first screen of a catalogue.

   Still translucent: the field runs behind the whole site, and a solid fill
   would cut a hole in it. */
.standards-block {
  margin-top: var(--s-40);
  padding: var(--s-24) var(--s-40) var(--s-24);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: rgba(253, 252, 250, 0.72);
}

.standards__tag { margin-bottom: var(--s-20); }
/* A short rule reading into the label, not a character in the copy — it is
   punctuation for the eye, and the text should not carry it to a screen
   reader. */
.standards__tag::before {
  content: "";
  display: inline-block;
  width: 18px;
  margin-right: 10px;
  vertical-align: 0.28em;
  border-top: 1px solid var(--ink-meta);
}

.standards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  margin-top: var(--s-24);
}

/* Three claims side by side, each with a mark. The rule that used to sit
   above each one is now BETWEEN them: with a heading on every item the
   horizontal rules read as three little tables, where a vertical divider just
   says "these are parallel". */
.standard {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-16);
  align-items: start;
  padding-inline: var(--s-32);
}
.standard:first-child { padding-left: 0; }
.standard:last-child  { padding-right: 0; }
.standard + .standard { border-left: 1px solid var(--border); }

/* Line art in a hairline circle — the same drawing weight as the rest of the
   page, so it reads as a mark rather than an icon set bolted on. */
.standard__icon {
  width: 38px;
  height: 38px;
  padding: 8px;
  border: 1px solid var(--border-dark);
  border-radius: 9999px;
  color: var(--ink);
  flex: none;
}

/* Heavier than the body but well under the page heading: these are claims to
   be read, not a second title competing with the page's own. */
.standard__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(16px, 1.25vw, 18px);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-headline);
  color: var(--ink);
  text-wrap: balance;
  /* Two lines' worth, always. One of the three titles wraps and the other two
     do not, so without this the three bodies start at three different heights
     and the row stops reading as a row. */
  min-height: calc(2em * var(--leading-snug));
}

.standard__body {
  margin-top: var(--s-8);
  font-size: var(--text-small);
  line-height: 1.6;
  color: var(--ink-soft);
  text-wrap: pretty;
}

@media (max-width: 900px) {
  .standards-block { padding: var(--s-24); margin-top: var(--s-40); }
  .standards__lead { max-width: none; }
  .standards {
    grid-template-columns: 1fr;
    gap: var(--s-24);
    margin-top: var(--s-24);
    padding-top: var(--s-24);
  }
  .standard { padding-inline: 0; }
  /* Stacked, nothing is beside anything, so the reserved second line is just
     a hole under the short titles. */
  .standard__title { min-height: 0; }
  /* Stacked, a left-hand divider separates nothing. The rule goes back on top. */
  .standard + .standard {
    padding-top: var(--s-24);
    border-left: 0;
    border-top: 1px solid var(--border);
  }
}

/* ==========================================================================
   FOUR GROUNDS, ONE FLOW
   The page used to be a single section over the animated wave field, with the
   ranges as blocks inside it. Nothing separated one range from the next except
   vertical space, so a catalogue of twenty products read as one long scroll.

   It is four full-width bands now, the same device the homepage uses, and the
   colour is what tells you the chapter changed. Cool, warm, warm-grey, cool —
   so no two neighbours share a temperature and every seam is legible from the
   corner of your eye.
   ========================================================================== */
.catalogue-flow {
  position: relative;
  /* Horizontal only. The objects hang off both edges, so something has to stop
     them widening the document — but clipping the vertical axis would cut them
     off at exactly the seams they exist to straddle. */
  overflow-x: clip;
  overflow-y: visible;
}

.group--ivory       { background: var(--ivory); }
.group--cream       { background: var(--cream-mid); }
.group--seaglass    { background: var(--seaglass); }

/* The bands must touch. As blocks inside one section these carried a top
   margin; as bands that margin would open a stripe of body background between
   two grounds. */
.group { margin-top: 0; }
.group > .shell,
.catalogue > .shell { position: relative; z-index: 2; }

/* --------------------------------------------------------------------------
   THE HERO
   Cream ground, copy in a column on the left, the plate bleeding off the right
   edge and out of the band's foot. No panel and no card — on a pale ground the
   type is legible on its own, and a box drawn around a column that is already
   a column is an accessory.
   -------------------------------------------------------------------------- */
.catalogue {
  position: relative;
  background: var(--ivory);
  /* NOT overflow: hidden. The plate is meant to cross the band's foot and
     tuck under the sea-glass below it, and hidden here cut it off exactly at
     the seam. The horizontal axis is still clipped, on .catalogue-flow, so
     nothing widens the document. */
}

/* Supplied cut out, so nothing here needs a mask or a blend mode — the two
   things the previous frame needed and neither of which came out clean.

   The group is what gets positioned; the watercolour is positioned inside it,
   against the plate. Anchored to the band instead, it sat over the copy as
   soon as the layout stacked. */
.catalogue__figure {
  position: absolute;
  /* BELOW the standards band, not above it. Both sit at the same stacking
     level, and the band comes later in the document, so it paints over the
     part of the plate that crosses into it — which is the point: the plate
     tucks behind the sea-glass rather than sitting on top of it. */
  z-index: 0;
  /* Pulled in off the right edge, so it sits near the end of the Explore row
     instead of hard against the window. */
  right: 12%;
  /* Hangs past the foot of its own band. */
  bottom: -80px;
  width: min(620px, 34vw);
}

.catalogue__plate {
  display: block;
  position: relative;
  z-index: 1;
  width: 100%;
  height: auto;
  /* WARMED INTO THE GROUND. The plate's rim photographs as a cool grey
     (#c1ada3) and the band is warm cream (#f4f0e7) — two different families
     sitting edge to edge, which is half of why a clean cut-out reads as
     pasted on. A trace of warmth moves it into the page's family without
     touching the food. */
  filter: sepia(0.10) saturate(1.04);
  pointer-events: none;
  user-select: none;
}

/* THE SURFACE IT SITS ON, not a drop shadow. The house rule is that depth
   comes from colour rather than shadows, so this is exactly that: a soft
   ellipse of the palette's own mid-cream under the plate, which gives the
   object somewhere to be. Without it the plate floats — the other half of
   why it read as pasted. */
.catalogue__figure::before {
  content: "";
  position: absolute;
  z-index: 0;
  left: 4%;
  right: 4%;
  bottom: 4%;
  height: 34%;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 50%,
              rgba(202, 190, 172, 0.55) 0%,
              rgba(202, 190, 172, 0.28) 46%,
              rgba(202, 190, 172, 0) 72%);
  pointer-events: none;
}

/* Behind the plate, over its top-right shoulder and off the right edge. */
.catalogue__deco {
  position: absolute;
  z-index: 0;
  /* Mostly hidden behind the plate. With the plate pulled in off the right
     edge this stopped being a corner bleed and became a large drawing
     floating in the gap, so it is smaller and sits lower into the overlap. */
  right: -4%;
  bottom: 54%;
  width: 40%;
  height: auto;
  pointer-events: none;
  user-select: none;
}

.catalogue__intro {
  position: relative;
  z-index: 2;
  max-width: min(56ch, 52%);
}

/* THE PAGE'S ONE RED. Coral is the warm accent everywhere else on this site,
   but coral on cream measures 2.68 and this is 13px type — it fails outright.
   Brand red measures 5.99 on the same ground, and the house rule allows red
   once per page. This is that once. */
.catalogue__eyebrow {
  font-family: var(--font-body);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--red);
}

.catalogue__heading {
  margin-top: var(--s-20);
  color: var(--ink);
  text-wrap: balance;
}

.catalogue__lede {
  margin-top: var(--s-24);
  font-size: var(--text-body-lg);
  line-height: var(--leading-body);
  color: var(--ink-soft);
  max-width: 44ch;
}

/* --------------------------------------------------------------------------
   THE RANGE LINKS
   Names only. The counts that stood here were answering a question nobody
   asks first, and each band states its own at the head.

   The rule under each is coral. It carries no information the text does not,
   so it is decoration rather than an affordance — the ink label and the
   underline's own presence are what say "link". Coral would fail as type
   here; as a line beneath type that measures 12.18 it is doing no work that
   contrast has to protect.
   -------------------------------------------------------------------------- */
.ranges-min {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-40);
  margin-top: var(--s-40);
}

/* Says what the row is for. aria-hidden because the nav already carries the
   label "Product ranges" for anyone not reading it by eye — announcing both
   would be the same thing twice. */
.ranges-min__label {
  margin-right: calc(var(--s-24) - var(--s-40));
  font-family: var(--font-body);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-meta);
}

.ranges-min__link {
  font-family: var(--font-body);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink);
  padding-bottom: 7px;
  border-bottom: 2px solid var(--coral);
  transition: border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.ranges-min__link:hover { color: var(--red); border-bottom-color: var(--red); }
.ranges-min__link:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }

@media (prefers-reduced-motion: reduce) {
  .ranges-min__link { transition: none; }
}

@media (max-width: 900px) {
  /* Beneath the copy rather than beside it — there is no room for a column
     and a plate at this width. */
  .catalogue { padding-bottom: 0; }
  .catalogue__intro { max-width: none; }
  /* In the flow, under the copy. The watercolour stays absolute INSIDE the
     group, so it follows the plate down instead of being left behind over
     the text. */
  .catalogue__figure {
    position: relative;
    right: auto;
    bottom: auto;
    width: 112%;
    max-width: none;
    margin: var(--s-24) -6% 0;
  }
  .catalogue__deco { right: -8%; bottom: 62%; width: 44%; }
  .ranges-min { gap: var(--s-24); margin-top: var(--s-32); }
}

/* --------------------------------------------------------------------------
   THE STANDARDS BAND
   Sea-glass, and each claim carries one of the page's own drawings instead of
   a line icon in a circle. The drawings are already the site's vocabulary and
   they say more: a fish for where the surimi comes from, cakes with the peas
   and carrot visible for real ingredients, a crab stick — the most uniform
   thing we make — for performing the same every batch.
   -------------------------------------------------------------------------- */
.standards-band {
  background: var(--seaglass);
  /* Tighter than a full section — it is a strip of three claims, not a
     chapter — and fluid, so a short window brings the claims up rather than
     leaving them under the fold. */
  padding-block: clamp(36px, 5vh, var(--s-64));
}
.standards-band .standards-block {
  margin-top: 0;
  padding: 0;
  border: 0;
  background: none;
}

.standards-band .standards { gap: var(--s-40); }
.standards-band .standard {
  padding-inline: 0;
  align-items: center;
  gap: var(--s-20);
}
.standards-band .standard + .standard { border-left: 0; }

.standard__art {
  /* A square box they are CONTAINED in, not a width. The three watercolours
     are 440x347, 268x440 and 440x268 — sized by width alone the upright sprig
     would tower over the other two. */
  width: clamp(78px, 7vw, 104px);
  height: clamp(78px, 7vw, 104px);
  object-fit: contain;
  flex: none;
  pointer-events: none;
  user-select: none;
  transition: transform 460ms var(--ease-out);
}

/* EACH ONE ANSWERS A HOVER ON ITS OWN POINT, and no two the same way — the
   same idea as the exclamation mark and the two drawings on the surimi page.
   They are watercolours of three different things, so a single shared nudge
   would read as a rollover state rather than as the drawing noticing you.

   The hover is on the whole point, not the picture: the picture has
   pointer-events: none and the text beside it is the larger target. */
.standard:hover .standard__art { transform: translateY(-5px) rotate(-4deg) scale(1.05); }
.standard:nth-child(2):hover .standard__art { transform: translateY(-6px) rotate(3deg) scale(1.06); }
.standard:nth-child(3):hover .standard__art { transform: translateY(-4px) rotate(-6deg) scale(1.04); }

.standards-band .standard__title { min-height: 0; }

@media (max-width: 900px) {
  .standards-band .standards { gap: var(--s-24); }
  .standard__art { width: 62px; height: 62px; }
}

@media (prefers-reduced-motion: reduce) {
  .standard__art { transition: none; }
  .standard:hover .standard__art,
  .standard:nth-child(2):hover .standard__art,
  .standard:nth-child(3):hover .standard__art { transform: none; }
}

/* --------------------------------------------------------------------------
   THE SEAM OBJECTS
   One per boundary, each parked ON it — half in the band above, half in the
   band below — and alternating sides. They are wayfinding rather than
   decoration: at the edge of the screen a change of chapter is visible before
   it is readable.

   Above the grounds and below the copy. catalogue-art.js writes the top of
   each one from the seam it marks, so adding a product cannot leave one
   stranded mid-band.
   -------------------------------------------------------------------------- */
.catalogue__art {
  position: absolute;
  z-index: 1;
  height: auto;
  pointer-events: none;
  user-select: none;
}
.catalogue__art--1 { right: -7%;  width: min(340px, 26vw); }
.catalogue__art--2 { left:  -8%;  width: min(300px, 23vw); }
.catalogue__art--3 { right: -8%;  width: min(320px, 25vw); }

@media (max-width: 900px) {
  .catalogue__art--1 { right: -22%; width: 46vw; }
  .catalogue__art--2 { left:  -24%; width: 42vw; }
  .catalogue__art--3 { right: -24%; width: 44vw; }
}

/* --------------------------------------------------------------------------
   INTRO ARTWORK
   The copy is capped at a reading measure, so the right of the intro was
   empty. Same construction as the other pages: cropped by the window on one
   axis, free to hang past the section on the other, so it crosses the seam
   as you scroll instead of ending where its container does.
   -------------------------------------------------------------------------- */
/* The section owns a stacking context at z-index 0, and the artwork sits at
   -1 INSIDE it. That is what keeps the drawing behind its own copy while still
   letting the NEXT section paint over it — sections later in the document win
   on tree order, so the illustration slides under the following block instead
   of floating on top of it. Without this the drawing covered the panels below.
   It cannot fall behind the WebGL field either: the field is a root-level
   layer, and this -1 is scoped to the section. */
.catalogue {
  position: relative;
  z-index: 0;
  overflow-x: clip;
  overflow-y: visible;
}

.catalogue > .shell { position: relative; z-index: 1; }

/* A second and third object further down, so the illustration is a thread
   through the page rather than a single flourish at the top. Both cross a
   section seam; both sit behind the copy. */
.catalogue__art--mid,
.catalogue__art--low {
  position: absolute;
  z-index: -1;
  height: auto;
  pointer-events: none;
  user-select: none;
}

.catalogue__art--mid {
  top: 1180px;
  left: -7%;
  width: min(420px, 30vw);
  transform: rotate(12deg);
}

.catalogue__art--low {
  top: 2560px;
  right: -9%;
  width: min(480px, 34vw);
  transform: rotate(-9deg);
}

@media (max-width: 900px) {
  .catalogue__art--mid { top: 1560px; left: -24%; width: 52vw; }
  .catalogue__art--low { top: 3200px; right: -26%; width: 54vw; }
}

/* ==========================================================================
   THE FIRST SCREEN
   Everything from the eyebrow to the last standard is one thought — what we
   make, and what goes into it — and it only works as one if you can see it
   at once. So the top of this page is sized in vh rather than px: every gap
   and the picture boxes give ground as the window gets shorter, instead of
   holding their size and pushing the standards under the fold.

   The clamps are floors and ceilings, not preferences. The ceiling is the
   old fixed value, so nothing changes on a tall screen; the floor is the
   smallest each part can be and still read. Desktop only: the formula is
   part viewport WIDTH, and on a phone that reads as a punishment rather than
   a fit — the page scrolls there anyway.
   ========================================================================== */
/* Measured FROM the header, not from the top of the section. The header is
   fixed and 79 tall, so a bare 94px of padding leaves the eyebrow 15px under
   the wordmark, reading as part of the navigation. Only the gap below it
   flexes. */
@media (min-width: 901px) {
  .catalogue {
  /* A FLOOR OF 44 UNDER THE BAR. This was 34, and on a short window the
     clamp bottomed out at 19px of gap — the eyebrow sat directly beneath the
     navigation and read as part of it. */
  padding-top: calc(var(--header-h) + clamp(44px, 5vh, 68px));
  /* THINNER, AND FLUID. This was a flat 156px, which is what pushed the
     standards block off the bottom of a 720px screen — and left it clearing
     by as little as 3px on several others, so any browser zoom cut it. The
     plate is anchored to this foot, so it rises with the band. */
  padding-bottom: clamp(56px, 9vh, 156px);
}

  /* The headline is three lines of a real sentence now, so it is the single
     biggest thing on the screen and the first place to look for room. */
  .catalogue__heading { font-size: clamp(33px, 2.5vw + 1.7vh, var(--text-heading)); }
  .catalogue__lede { margin-top: clamp(12px, 1.8vh, var(--s-20)); }

  /* The floor is set by what is inside: the name block over the foot needs 84,
     and below about 210 the platter has less room than the type does. */

  .standards-block { margin-top: clamp(20px, 3.2vh, var(--s-40)); padding-block: clamp(16px, 2.4vh, var(--s-24)); }
  .standards__tag { margin-bottom: clamp(12px, 2vh, var(--s-20)); }
  .standards { margin-top: clamp(12px, 2vh, var(--s-24)); }
}

/* Short windows. This used to squeeze the gap under the bar down to 26px,
   from a time when the hero carried three range-card pictures as well; the
   hero is much thinner now and does not need to buy space there. The gap
   keeps its floor and the saving comes from the copy instead. */
@media (min-width: 901px) and (max-height: 780px) {
  .standards-block { margin-top: 16px; }
  .standard__body { font-size: 13px; }
}

/* The ask stays put while the specs scroll under it. On a wide screen the
   column does not scroll at all and this changes nothing; on a short or
   narrow one it is the difference between the button being there and being
   177px below the fold. */
.sheet__foot {
  position: sticky;
  bottom: calc(var(--s-40) * -1);
  margin-top: var(--s-32);
  padding-block: var(--s-20) var(--s-40);
  /* Opaque, and pulled out to the column's edges, so nothing scrolls through
     the space around the pill. */
  margin-inline: calc(var(--s-40) * -1);
  padding-inline: var(--s-40);
  background: var(--surface-card);
}

/* 760, not 900: that is where the sheet itself stacks. Mismatched, the band
   took the narrow padding while the column around it kept the wide one, and
   the list scrolled through a 16px strip down each side. */
@media (max-width: 760px) {
  .sheet__foot {
    bottom: calc(var(--s-24) * -1);
    margin-inline: calc(var(--s-24) * -1);
    padding-inline: var(--s-24);
    padding-block: var(--s-16) var(--s-24);
  }
}
