/* ==========================================================================
   Cunjindi V7 — bone premium agriculture.

   Landed from mockups/hero-c.html, which the client approved. The hero's
   typography, palette, figure row and unshadowed button are inherited here
   verbatim; every later section is built from the same three values.

   House rules, held by scripts/self-check.mjs:
     · three colours, plus alpha of those three. Nothing else.
     · no box-shadow, no text-shadow, anywhere.
     · border-radius 0-4px.
     · nothing below 13px.
     · every direction is logical (inline/block), never left/right, so the
       Arabic build mirrors without a second stylesheet.
     · entrances fade 400-600ms and travel no more than 24px, with no
       exception: nothing on this page is driven by scroll position.
   ========================================================================== */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("assets/fonts/inter-latin-var.woff2") format("woff2");
}

/* The display face. Fraunces is a variable serif with an optical-size axis
   (opsz 9-144): the same file draws a 94px headline with hairline serifs and
   high contrast, and a 21px sub-head with the sturdier cuts that size needs.
   One 67KB latin subset covers every display line on the site, and the axis is
   set per role below rather than left at the 9pt default, which is drawn for
   footnotes and reads as a novelty face at headline size.

   The face is display only. Every paragraph, label, field and button stays on
   Inter: a serif is the register of the thing being sold, not of the form the
   buyer has to fill in. */
@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("assets/fonts/fraunces-latin-var.woff2") format("woff2");
}

:root {
  /* palette — three values, and nothing outside them */
  --bone: #f4f1ea;
  --ink: #16211b;
  /* The third value carries every caption, eyebrow, attribution and field
     label on the page, all of them set at 13-15px. At its first weight it
     measured 2.9:1 on bone, which reads as haze on a projector and fails the
     4.5:1 an eye test would ask of body-size text. Dropped one step to 4.5:1
     and no further: this is still the quiet colour, it just survives being
     shown across a room. */
  --stone: #6b7065;

  /* The fourth value, and the last. Antique bronze-gold: the colour of a
     stamped seal on a bill of lading rather than of a gold-foil logo. It is
     rationed by rule, not by taste — eyebrows, the mark that divides two
     bands, the hung quotation mark, the timeline years, and the underline a
     link grows on hover. It never fills, never sets a paragraph, and never
     appears twice in one screen at any size above 13px.
     #7d6440 on #f4f1ea measures 4.94:1, so it clears 4.5:1 and can legally
     carry the 13px eyebrows, which the 3:1 large-text allowance would not. */
  --gilt: #7d6440;

  /* alpha derivatives */
  --ink-70: rgba(22, 33, 27, 0.7);
  --ink-55: rgba(22, 33, 27, 0.55);
  --rule: rgba(22, 33, 27, 0.14);
  --rule-soft: rgba(22, 33, 27, 0.08);
  --frame: rgba(22, 33, 27, 0.1);
  --tint: rgba(22, 33, 27, 0.035);
  --gilt-soft: rgba(125, 100, 64, 0.34);

  /* type */
  --f: "Inter", system-ui, sans-serif;
  --fd: "Fraunces", "Newsreader", Georgia, "Times New Roman", serif;
  /* 94px ceiling stepped down to 78px: the H1 copy is a ~61-character,
     three-clause sentence — display-s bucket, not full display. */
  --t-h1: clamp(40px, 5.6vw, 78px);
  --t-h2: clamp(32px, 4.4vw, 62px);
  --t-h3: clamp(21px, 2.1vw, 30px);
  --t-lede: clamp(17px, 1.45vw, 21px);
  --t-body: 18px;
  --t-micro: 13px;
  --t-stat: clamp(30px, 2.95vw, 42px);

  /* measure — three widths, and every block declares which one it is.
   *
   * Until 2026-08-15 there was only one, and it was wrong by the width of two
   * gutters. `.wrap` capped the *box* at --wrap and then subtracted the gutter
   * from the inside, so on a 1440px screen every band below the hero was laid
   * out in 970px with 235px of dead bone on each side — inside a container
   * that had already taken its 120px margin. The hero never had the bug: it
   * sizes itself calc(--wrap + --gutter * 2) and is the one block on the page
   * that fills its measure, which is most of why the hero looks composed and
   * the sections under it looked like a draft. The fix is to make every other
   * block size the way the hero already did.
   *
   * Three measures, because one measure is what made every band read the same
   * weight. A specification table, a six-tile photographic grid and a
   * paragraph of prose do not want the same width, and giving them the same
   * width is what a template does.
   *   --wrap        1200px  the spine: headings, tables, forms, cards
   *   --wrap-wide   1416px  photography and the twelve-month grid, which are
   *                         the two things on this site that are worth the
   *                         whole window
   *   --measure-*           reading columns, held here rather than re-typed
   *                         as a different em value in nine places */
  --gutter: clamp(24px, 8vw, 116px);
  --wrap: 1200px;
  --wrap-wide: 1416px;
  --measure-prose: 34em;
  --measure-lede: 46em;
  /* Was clamp(88px, 11vw, 168px). Two adjacent bands put that gap twice, so a
     1440px screen carried 316px of nothing between the end of one argument
     and the start of the next — enough that four separate screenshots of this
     site are of empty bone. Air is what makes the page read as expensive; a
     void is what makes it read as unfinished, and the line between them is
     about a third of what this was. */
  --band-y: clamp(72px, 8vw, 124px);

  /* motion */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --in: 520ms;
}

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

html {
  scroll-behavior: smooth;
  scroll-padding-block-start: 96px;
  background: var(--bone);
  -webkit-text-size-adjust: 100%;
  overflow-x: clip;
}

body {
  font-family: var(--f);
  font-size: var(--t-body);
  line-height: 1.62;
  color: var(--ink);
  background: var(--bone);
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

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

/* The webp/jpg wrapper must not generate a box of its own: an intermediate
   auto-height block breaks the percentage-height chain and every art-directed
   image collapses to its intrinsic ratio inside a sized frame. */
picture {
  display: contents;
}

img,
video {
  height: auto;
}

a {
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* ------------------------------------------------------------------ utility */

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

.u-skip {
  position: absolute;
  inset-block-start: -100px;
  inset-inline-start: 16px;
  z-index: 40;
  padding: 12px 18px;
  font-size: var(--t-micro);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--bone);
  background: var(--ink);
  text-decoration: none;
}

.u-skip:focus {
  inset-block-start: 16px;
}

/* The measure is the content, not the box: the cap has to include the padding
   it is about to give away, exactly as .hero__body has always done. Without
   the calc() this rule quietly costs the page 230px of every band. */
.wrap {
  inline-size: 100%;
  max-inline-size: calc(var(--wrap) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* The wide measure. Photography and the availability grid only: everything
   else stays on the spine, because a page where every block is a different
   width has no spine at all. */
.wrap--wide {
  max-inline-size: calc(var(--wrap-wide) + var(--gutter) * 2);
}

/* --------------------------------------------------------------- type scale */

.eyebrow {
  font-size: var(--t-micro);
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--stone);
}

.h2 {
  margin-block-start: 30px;
  font-size: var(--t-h2);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.032em;
  max-inline-size: 15em;
}

/* The section heading one step down. A page with three h2s all at the full
   62px display size has three first-rank headings and therefore none; this is
   for the bands that answer the lead band rather than open a new argument. */
.h2--s {
  font-size: clamp(28px, 3.2vw, 44px);
  max-inline-size: 13em;
}

.h3 {
  margin-block-start: 56px;
  font-size: var(--t-h3);
  font-weight: 400;
  line-height: 1.16;
  letter-spacing: -0.02em;
  max-inline-size: 18em;
}

.h3--s {
  margin-block-start: 40px;
  font-size: 21px;
}

.lede {
  margin-block-start: 26px;
  font-size: var(--t-lede);
  line-height: 1.55;
  color: var(--ink-70);
  max-inline-size: 34em;
}

.lede--s {
  font-size: var(--t-body);
  margin-block-start: 16px;
}

.body {
  margin-block-start: 20px;
  color: var(--ink-70);
  max-inline-size: 36em;
}

.body--k {
  color: var(--ink);
  margin-block-start: 34px;
}

.note {
  margin-block-start: 44px;
  font-size: var(--t-micro);
  line-height: 1.7;
  color: var(--stone);
  max-inline-size: 46em;
}

.note--tight {
  margin-block-start: 16px;
}

.link {
  position: relative;
  display: inline-block;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  border-block-end: 1px solid var(--rule);
  padding-block-end: 3px;
  transition: border-color 200ms var(--ease);
}

/* The hover underline arrives rather than switches on: a gilt hairline drawn
   over the resting rule from the inline start in 200ms. Width, not a
   transform, so the direction follows the document and the Arabic build needs
   no second origin. */
.link::after {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-end: -1px;
  inline-size: 0;
  block-size: 1px;
  background: var(--gilt);
  transition: inline-size 200ms var(--ease);
}

.link:hover::after,
.link:focus-visible::after {
  inline-size: 100%;
}

.link--b {
  font-size: 20px;
  letter-spacing: -0.01em;
  text-transform: none;
}

/* A line that reads as a link but is an instruction, not a destination: it
   sits under the lead video and points at the two full reports directly
   below it, which need no anchor because they are already on screen. */
.link--mute {
  color: var(--stone);
  border-block-end-color: transparent;
  cursor: default;
}

.link--mute::after {
  content: none;
}

/* ------------------------------------------------------- display typography */

/* Every line the buyer reads as a statement rather than as information. The
   optical size is set per role: the axis default is 9pt, drawn for footnotes,
   and left there a 94px headline comes out spindly and mannered. Weight is
   left to font-weight so the two axes stay independent. */
.hero__h1,
.h2,
.h3,
.act__h,
.pull__q,
.wholesale__q,
.stat__n,
.strength__n,
.report__h,
.process__grade dd,
.facts--tl dt time {
  font-family: var(--fd);
  font-optical-sizing: auto;
}

.hero__h1 {
  font-weight: 400;
  font-variation-settings: "opsz" 120;
}

.h2,
.act__h,
.pull__q,
.wholesale__q {
  font-variation-settings: "opsz" 72;
}

.h3,
.stat__n,
.strength__n,
.report__h,
.process__grade dd,
.facts--tl dt time {
  font-variation-settings: "opsz" 36;
}

/* Every number on this page is a figure a buyer may line up against another
   one — a grade band against a grade band, a year against a year. Lining
   figures of one width are the only way that comparison holds. */
.stat__n,
.strength__n,
.process__grade dd,
.facts--tl dt,
.facts__code,
.window__peak,
.report__date,
.steps__n {
  font-variant-numeric: tabular-nums;
}

/* The small-capital register, in one place. Eight different components were
   each declaring their own version of it and had drifted to three letter-
   spacings and two weights; they now all read as the same voice. */
.eyebrow,
.stat__l,
.strength__label,
.report__date,
.stamp,
.foot__k,
.caveat__k,
.process__label,
.window__k {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

/* Gilt is allowed on the eyebrow, and only on the eyebrow, among these. The
   rest stay stone: a page where every small-capital line is gold is a page
   with no accent at all. */
.eyebrow,
.eyebrow--gilt {
  color: var(--gilt);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 17px 32px;
  border: 1px solid var(--ink);
  border-radius: 1px;
  font-family: var(--f);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: opacity 200ms var(--ease);
}

.btn--solid {
  color: var(--bone);
  background: var(--ink);
}

@media (hover: hover) and (pointer: fine) {
  .btn:hover {
    opacity: 0.86;
  }
}

/* Touch has no hover: :active is the only pre-release feedback signal. */
.btn:active,
.head__toggle:active,
.form__copy:active {
  transform: translateY(1px);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

/* ------------------------------------------------------------------- header */

.head {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 28px;
  block-size: 80px;
  padding-inline: var(--gutter);
  /* Opaque, not 0.94. A fixed bar at 94% lets whatever is behind it print
     through at 6%, which on a page of 62px display type means ghost letters
     crossing the navigation on every screen that is not the top of the page.
     It reads as a rendering fault rather than as translucency. The bar sits on
     bone and the page is bone, so full opacity is invisible at rest and the
     only thing lost is the artefact. */
  background: var(--bone);
  border-block-end: 1px solid transparent;
  transition: border-color 300ms var(--ease);
}

.head.is-stuck {
  border-block-end-color: var(--rule-soft);
}

.head__brand {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
  margin-inline-end: auto;
}

.head__logo {
  inline-size: 44px;
  block-size: 44px;
  object-fit: contain;
  flex: none;
}

.head__text {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.head__mark {
  font-size: 21px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1;
}

.head__tag {
  white-space: nowrap;
  font-size: var(--t-micro);
  letter-spacing: 0.05em;
  line-height: 1;
  color: var(--stone);
}

.head__list {
  display: flex;
  align-items: center;
  gap: clamp(16px, 2vw, 30px);
  list-style: none;
}

.head__list a {
  white-space: nowrap;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-70);
  transition: color 200ms var(--ease);
}

.head__list a:hover,
.head__list a.is-here {
  color: var(--ink);
}

.head__cta {
  padding: 13px 24px;
}

/* The nav CTA as a typographic link, not a filled pill: the one filled
   button on the page should be the section CTAs, and the header should
   speak the same hairline-and-gilt language as the rest of the chrome. */
.head__cta.btn--solid {
  padding: 6px 2px;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-block-end: 1px solid var(--rule);
  border-radius: 0;
  transition: border-color 200ms var(--ease), opacity 200ms var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .head__cta.btn--solid:hover {
    opacity: 1;
    border-block-end-color: var(--ink);
  }
}

.head__cta-short {
  display: none;
}

.head__toggle {
  display: none;
  padding: 11px 16px;
  font-family: var(--f);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink);
  background: none;
  border: 1px solid var(--rule);
  border-radius: 1px;
  cursor: pointer;
}

/* --------------------------------------------------------------------- hero */

.hero {
  position: relative;
  min-block-size: 100svh;
  display: flex;
  overflow: hidden;
  background: var(--bone);
}

/* The invitation border. One hairline at 10% ink, inset from the edges of the
   window, drawn over the photograph and under the type. It does nothing but
   say that what is inside it was composed rather than uploaded — which is the
   whole difference between a trade page and a card.
 *
 * It starts 16px under the header rather than at the top inset, for the same
 * reason the scroll padding is 96px: the header is a fixed bone panel at 94%
 * and would otherwise eat the top edge, leaving two vertical rules and a
 * frame that reads as a rendering fault. Below, it runs into the orchard and
 * dissolves, because a 10% ink line has nothing to say against a photograph
 * and a fourth edge drawn in a colour strong enough to survive there would be
 * a box around the picture rather than a border around the page. */
.hero::after {
  content: "";
  position: absolute;
  inset: 96px clamp(14px, 1.8vw, 26px) clamp(14px, 1.8vw, 26px);
  z-index: 3;
  border: 1px solid var(--frame);
  pointer-events: none;
}

.hero__media,
.hero__media img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
}

.hero__media img {
  object-fit: cover;
  object-position: 50% 62%;
}

.hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    #f4f1ea 0%,
    #f4f1ea 55%,
    rgba(244, 241, 234, 0.93) 64%,
    rgba(244, 241, 234, 0.62) 76%,
    rgba(244, 241, 234, 0.16) 89%,
    rgba(244, 241, 234, 0) 100%
  );
}

.hero__body {
  position: relative;
  z-index: 2;
  inline-size: 100%;
  max-inline-size: calc(var(--wrap) + var(--gutter) * 2);
  margin-inline: auto;
  /* Bottom grown, top trimmed: the hero should settle into the page,
     not float off its own top edge (the fixed header already gets its
     clearance from .hero::after). */
  padding: 136px var(--gutter) 96px;
  display: flex;
  flex-direction: column;
}

.hero__h1 {
  margin-block-start: 56px;
  font-size: var(--t-h1);
  font-weight: 400;
  line-height: 0.97;
  letter-spacing: -0.038em;
  color: var(--ink);
  max-inline-size: 14.2em;
  overflow-wrap: anywhere;
  min-width: 0;
}

.hero__row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 60px;
  margin-block-start: 74px;
}

.hero__sub {
  font-size: 16px;
  line-height: 1.62;
  color: var(--ink-70);
  max-inline-size: 24em;
}

.hero__right {
  display: flex;
  align-items: flex-end;
  gap: 52px;
}

.stat {
  display: flex;
  flex-direction: column;
}

.stat__n {
  font-size: var(--t-stat);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--ink);
}

.stat__l {
  margin-block-start: 10px;
  color: var(--stone);
}

/* ------------------------------------------------------------ scale rail */

.strength {
  padding-block: clamp(34px, 3.8vw, 54px);
  color: var(--bone);
  background: var(--ink);
}

.strength__grid {
  display: grid;
  grid-template-columns: 1.2fr repeat(3, 1fr);
  gap: 0;
}

.strength__metric {
  display: grid;
  align-content: start;
  min-block-size: 132px;
  padding-inline: clamp(22px, 3vw, 46px);
  border-inline-start: 1px solid var(--gilt);
}

.strength__metric:first-child {
  padding-inline-start: 0;
  border-inline-start: 0;
}

.strength__n {
  font-size: clamp(38px, 4.1vw, 64px);
  font-weight: 400;
  line-height: 0.94;
  letter-spacing: -0.035em;
}

.strength__label {
  margin-block-start: 18px;
  color: var(--bone);
}

.strength__note {
  margin-block-start: 10px;
  max-inline-size: 24em;
  font-size: var(--t-micro);
  line-height: 1.55;
  color: var(--bone);
  opacity: 0.62;
}

/* -------------------------------------------------------------------- bands */

/* Bands used to be divided by a hairline drawn the full width of the window.
   A rule that long is architecture — it says "new floor" — and on a page that
   is already six storeys tall it read as scaffolding. It is replaced by a
   64px gilt mark on the centre line: the same job, at the scale of a printed
   ornament rather than a structural member. Centred with logical insets and
   auto margins, so it holds under RTL without a transform origin. */
.band {
  position: relative;
  padding-block: var(--band-y);
}

.band::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  inline-size: 64px;
  block-size: 1px;
  margin-inline: auto;
  background: var(--gilt);
}

/* The standalone mark, for a division inside a band — the reports and the
   registration are two different kinds of evidence and should not run into
   each other. */
.mark {
  inline-size: 64px;
  block-size: 1px;
  margin-block: clamp(64px, 7vw, 104px);
  margin-inline: auto;
  border: 0;
  background: var(--gilt-soft);
}

.band--tint {
  background: var(--tint);
}

.grid {
  display: grid;
  gap: clamp(32px, 3.4vw, 56px);
  margin-block-start: clamp(48px, 5vw, 76px);
}

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

/* ------------------------------------------------------------- fruit cards */

/* The one photograph on this page that breaks the measure.
 *
 * Everything else in the band sits on the spine, which is what makes a single
 * escape read as emphasis rather than as a second layout. The negative margin
 * is exactly the gutter the .wrap just applied, so the picture fills the
 * wrap's border box — full window at every width, without the section needing
 * to know how wide the window is. The caption is pushed back onto the spine
 * underneath, because a three-column caption running the full 1440 is a
 * caption nobody finishes reading. */
.range-stage {
  margin-block-start: clamp(48px, 5vw, 76px);
  margin-inline: calc(var(--gutter) * -1);
}

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

.range-stage__media {
  overflow: hidden;
  background: var(--tint);
}

.range-stage__media img {
  display: block;
  inline-size: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

.range-stage__caption {
  display: grid;
  grid-template-columns: 0.8fr 1.25fr 1fr;
  gap: clamp(24px, 3.5vw, 54px);
  padding-block: 24px;
  border-block-end: 1px solid var(--rule);
}

.range-stage__title {
  font-size: 20px;
  line-height: 1.35;
  color: var(--ink);
}

.range-stage__body,
.range-stage__source {
  font-size: 14px;
  line-height: 1.62;
  color: var(--ink-70);
}

.range-stage__source {
  color: var(--stone);
}

.card__media {
  /* The provenance label is absolutely positioned inside this box, so the box
     has to be the containing block. Without this the label escapes to the
     card and sits over the specification table. */
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--tint);
}

.card__media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* Two lines of room whether the name needs them or not, so the spec tables
   below start on the same line across the row. */
.card__h {
  margin-block-start: 22px;
  min-block-size: 2.28em;
  font-size: 25px;
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.02em;
}

.card__cn {
  margin-block-start: 6px;
  font-size: 15px;
  color: var(--stone);
}

.card {
  display: flex;
  flex-direction: column;
}

.card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.card__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 24px;
  margin-block-start: auto;
  padding-block-start: 22px;
}


.card__specs {
  margin-block-start: 22px;
  border-block-start: 1px solid var(--rule);
}

.spec {
  display: grid;
  grid-template-columns: 5.6em 1fr;
  gap: 14px;
  padding-block: 10px;
  border-block-end: 1px solid var(--rule-soft);
}

.spec__k {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--stone);
  line-height: 1.5;
}

.spec__v {
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.spec__v.is-open {
  color: var(--stone);
  font-style: normal;
}

.spec__src {
  display: block;
  margin-block-start: 4px;
  font-size: var(--t-micro);
  line-height: 1.5;
  color: var(--stone);
}

.season {
  margin-block-start: 20px;
}

.season__k {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--stone);
}

.season__v {
  color: var(--ink);
  letter-spacing: 0.02em;
}

.season__bar {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 3px;
  margin-block-start: 10px;
  list-style: none;
}

/* Twelve cells, on or off. The frame around each one was the only element on
   the page that looked like a control rather than a piece of setting, so the
   box is gone and the state is carried by fill alone: a wash for a month we
   do not ship, ink for one we do. */
.season__m {
  display: flex;
  align-items: center;
  justify-content: center;
  block-size: 26px;
  font-size: var(--t-micro);
  color: var(--stone);
  background: var(--rule-soft);
}

.season__m.is-on {
  color: var(--bone);
  background: var(--ink);
}

.season__peak {
  margin-block-start: 10px;
  font-size: var(--t-micro);
  color: var(--stone);
}

/* Pinned to the foot of the card so the six calls to action line up. */
.card .link {
  margin-block-start: auto;
  align-self: flex-start;
  padding-block-start: 22px;
}

/* ------------------------------------------------------------------ regions */

.region__media {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--tint);
}

.region__media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.region__h {
  margin-block-start: 22px;
  font-size: 25px;
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.02em;
}

.region__fruit {
  margin-block-start: 8px;
  font-size: 15px;
  color: var(--ink);
}

.region__rel {
  margin-block-start: 10px;
  padding-block-start: 10px;
  border-block-start: 1px solid var(--rule);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--stone);
}

.region__body {
  margin-block-start: 12px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink-70);
}

/* The company-scale proof uses only archive photographs. The packing hall is
   deliberately larger than its supporting frames: a buyer sees the operation
   first, then the orchard check and the finished carton that explain it. */
.operations {
  margin-block-start: clamp(96px, 10vw, 156px);
  padding-block-start: clamp(58px, 6vw, 88px);
  border-block-start: 1px solid var(--rule);
}

/* Stacked, not tag-left/heading-right: the eyebrow sits above its heading
   in one column, same as every other section head on the page (and same as
   this component's own sub-860px collapse always did). */
.operations__head {
  display: grid;
  grid-template-columns: 1fr;
  align-items: start;
  gap: 18px;
  max-inline-size: 56em;
}

.operations__title,
.operations__head .lede {
  margin-block-start: 0;
}

.operations__wall {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(22px, 2vw, 32px);
  margin-block-start: clamp(48px, 5vw, 76px);
}

.operations__frame {
  min-block-size: 0;
}

.operations__frame--lead {
  grid-column: 1 / -1;
}

.operations__frame picture {
  display: contents;
}

.operations__frame img {
  display: block;
  inline-size: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
}

.operations__frame--lead img {
  aspect-ratio: 16 / 9;
}

.operations__frame figcaption {
  display: flex;
  justify-content: space-between;
  gap: 18px;
  padding-block: 13px;
  border-block-end: 1px solid var(--rule);
  font-size: var(--t-micro);
  line-height: 1.45;
}

.operations__frame figcaption strong {
  font-weight: 500;
  color: var(--ink);
}

.operations__frame figcaption span {
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
}

.operations__cta {
  margin-block-start: 30px;
}

/* --------------------------------------------------------- grading + steps */

.opener {
  margin-block-start: clamp(40px, 4.4vw, 68px);
  overflow: hidden;
}

.opener img {
  inline-size: 100%;
  aspect-ratio: 21 / 9;
  object-fit: cover;
  background: var(--tint);
}

.checks {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 56px;
  margin-block-start: 48px;
  list-style: none;
  font-size: var(--t-h3);
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.checks li {
  display: flex;
  align-items: baseline;
  gap: 14px;
}

.checks__n {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  color: var(--stone);
}

/* The handling, one line at a time. Three columns of short pairs rather than
   six boxes: a box around a promise is the house style of every supplier site
   that has nothing behind the promise, so these sit on a hairline and nothing
   else, the same rule the fact rows use. */
.commit {
  margin-block-start: clamp(56px, 6vw, 88px);
}

.commit__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 0 clamp(24px, 2.6vw, 44px);
  margin-block-start: clamp(24px, 2.6vw, 36px);
  list-style: none;
  max-inline-size: 62em;
}

.commit__item {
  padding-block: 18px;
  border-block-start: 1px solid var(--rule);
}

.commit__k {
  font-weight: 500;
  line-height: 1.3;
}

.commit__v {
  margin-block-start: 6px;
  color: var(--ink-70);
  font-size: 15px;
  line-height: 1.5;
}

.process {
  margin-block-start: clamp(48px, 5vw, 76px);
  border-block-start: 1px solid var(--rule);
  list-style: none;
}

/* Photograph and copy, side by side, on a row tall enough to fill the screen
   and short enough that two rows are always in play. */
.process__step {
  --shot: 59fr;
  --copy: 41fr;
  display: grid;
  grid-template-columns: var(--shot) var(--copy);
  align-items: center;
  gap: clamp(24px, 3.2vw, 56px);
  padding-block: clamp(24px, 3vw, 44px);
  border-block-end: 1px solid var(--rule);
}

/* Alternate sides. Both the track widths and the item order flip, or the
   photograph lands in the narrow column and the rhythm reads as a mistake.
   Neither flip names a left or a right, so Arabic mirrors for free. */
.process__step:nth-child(even) {
  grid-template-columns: var(--copy) var(--shot);
}

.process__step:nth-child(even) .process__shot {
  order: 2;
}

/* 3:2 rather than the opener's cinema ratio: at this column width that is a
   ~360px photograph, which is what stops a row reading as a caption strip. */
.process__shot img {
  inline-size: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  background: var(--tint);
}

/* Size, tracking and case come from the shared small-capital token; only the
   spacing above it and its colour are this component's own. It had been
   carrying a third letter-spacing of its own and read as a different voice
   from the eyebrow six inches above it. */
.process__label {
  margin-block-start: 12px;
  color: var(--stone);
}

.process__n {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  color: var(--stone);
}

.process__h {
  margin-block-start: 10px;
  font-size: var(--t-h3);
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -0.02em;
}

.process__p {
  margin-block-start: 14px;
  color: var(--ink-70);
  max-inline-size: 30em;
}

/* Both weights, as a pair. Neither number is ever printed alone. */
.process__grades {
  margin-block-start: 20px;
  border-block-start: 1px solid var(--rule);
}

.process__grade {
  display: grid;
  grid-template-columns: minmax(7em, auto) 1fr;
  gap: 4px 20px;
  align-items: baseline;
  padding-block: 9px;
  border-block-end: 1px solid var(--rule);
}

.process__grade dt {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--stone);
}

.process__grade dd {
  font-size: 25px;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.process__src {
  margin-block-start: 12px;
  font-size: var(--t-micro);
  line-height: 1.6;
  color: var(--stone);
  max-inline-size: 34em;
}

/* ---------------------------------------------------------------- documents */

.facts {
  margin-block-start: clamp(40px, 4vw, 64px);
  border-block-start: 1px solid var(--rule);
  max-inline-size: 62em;
}

.facts--tight {
  margin-block-start: 20px;
}

.facts__row {
  display: grid;
  grid-template-columns: 15em 1fr;
  gap: 24px;
  padding-block: 16px;
  border-block-end: 1px solid var(--rule-soft);
}

.facts__row dt {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--stone);
  line-height: 1.7;
}

.facts__row dd {
  font-size: 17px;
  line-height: 1.55;
}

.facts__code {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
}

.facts + p {
  margin-block-start: 22px;
}

.region-showcase {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(44px, 5vw, 74px) clamp(28px, 3.4vw, 52px);
  margin-block-start: clamp(48px, 5vw, 76px);
}

.region:nth-child(-n + 2) {
  grid-column: span 6;
}

.region:nth-child(n + 3) {
  grid-column: span 6;
}

.region:last-child {
  grid-column: 4 / span 6;
}

.card__source,
.region__source {
  position: absolute;
  inset-inline-start: 0;
  inset-block-end: 0;
  padding: 10px 13px;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--bone);
}

.verify-help {
  max-inline-size: 52em;
  font-size: 15px;
  color: var(--ink);
}

.caveat {
  margin-block-start: 44px;
  padding-inline-start: 24px;
  border-inline-start: 1px solid var(--rule);
  max-inline-size: 46em;
}

.caveat__k {
  color: var(--stone);
}

.caveat p + p {
  margin-block-start: 16px;
  color: var(--ink-70);
  font-size: 17px;
}

.stamp {
  margin-block-start: 20px;
  color: var(--stone);
}

/* The two credential scans. A document is published at the size it can be
   read at, which on a phone is not the size it can be laid out at, so the card
   shows a whole page scaled into a column and the anchor around it opens the
   plain jpg at its delivered width. There is no lightbox, no dialog and no
   script: the picture is a link, which is the one interaction that still works
   with the stylesheet half-loaded and the script switched off.

   The page is never cropped to a thumbnail shape. A certificate cropped to a
   pleasing rectangle has had its scope line removed, and the scope line is the
   entire reason the document is on the page. */
.scans {
  margin-block-start: clamp(48px, 5vw, 76px);
}

.scans__wall {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(24px, 2.6vw, 40px);
  margin-block-start: clamp(28px, 3vw, 44px);
  max-inline-size: 62em;
}

.scan {
  min-inline-size: 0;
}

.scan__open {
  display: block;
  color: var(--ink);
  text-decoration: none;
}

.scan__open picture {
  display: contents;
}

.scan__open img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  border: 1px solid var(--frame);
  background: var(--bone);
}

.scan__cta {
  display: inline-block;
  margin-block-start: 12px;
  padding-block-end: 2px;
  border-block-end: 1px solid var(--rule);
  font-size: var(--t-micro);
  letter-spacing: 0.04em;
}

.scan__open:hover .scan__cta,
.scan__open:focus-visible .scan__cta {
  border-block-end-color: var(--ink);
}

.scan__cap {
  margin-block-start: 14px;
  padding-block-start: 13px;
  border-block-start: 1px solid var(--rule);
}

.scan__h {
  font-weight: 500;
  line-height: 1.3;
}

.scan__scope {
  margin-block-start: 4px;
  color: var(--ink-70);
  font-size: 15px;
  line-height: 1.45;
}

.scan__src {
  margin-block-start: 8px;
  color: var(--stone);
  font-size: var(--t-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Audit H1. Three equal columns inside a 1200px wrap left 298px of frame at
   1440 and 440px at 1080 — the widest screen got the smallest video, and a
   168px-tall frame gives about a fifth of itself back to the browser's own
   control bar. The strongest third-party asset on the site was its least
   prominent element.
 *
 * The block is now a lead and a pair. The lead is the fifty-second cut with
 * the subtitles burned in — the one a cold buyer will actually finish — at
 * 900px, and the two full reports sit under it two-up at ~466px each. The
 * container is no longer a grid: it holds the heading, the sub-line, the
 * stamp and the videos, and carries the #reports anchor both entry points
 * now land on. */
.reports {
  margin-block-start: clamp(48px, 5vw, 76px);
}

/* The heading opens the block rather than being pushed off the lede above it:
   the block's own margin is the air, so the anchor lands on the whole thing
   and not on a gap in front of it. */
.reports .h3--lead {
  margin-block-start: 0;
}

.report--lead {
  max-inline-size: 900px;
  margin-block-start: clamp(28px, 3vw, 44px);
}

.reports__pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(24px, 2.6vw, 40px);
  margin-block-start: clamp(40px, 4.4vw, 64px);
}

.report__v {
  inline-size: 100%;
  aspect-ratio: 16 / 9;
  background: var(--ink);
  border-radius: 1px;
}

/* Audit H2. The caption is a four-row track rather than normal flow: the
   third card carried no date line and no closing link, so a missing first row
   pulled its title 29px above its neighbours' and left the bottom edge ragged.
   On shared tracks a card can be missing a part without moving the others. */
.report__cap {
  margin-block-start: 18px;
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  align-content: start;
}

.report__date {
  color: var(--stone);
}

.report__h {
  margin-block-start: 8px;
  font-size: 20px;
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: -0.015em;
}

.report--lead .report__h {
  font-size: clamp(22px, 1.9vw, 27px);
}

/* The body sits after the h4, not after another p, so the sibling selector
   this used to be written as never matched and the sentence ran straight into
   the title it belonged under. Addressed by position in the caption's own
   grid instead, which cannot drift when a part is missing. */
.report__cap > p:not(.report__date) {
  margin-block-start: 12px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink-70);
}

.report--lead .report__cap > p:not(.report__date) {
  font-size: 16px;
  max-inline-size: 44em;
}

.report__cap .link {
  margin-block-start: 16px;
  justify-self: start;
}

video::cue {
  background: rgba(22, 33, 27, 0.72);
  color: #f4f1ea;
  font-family: var(--f);
  font-size: 1em;
  line-height: 1.35;
}

/* -------------------------------------------------------------------- story */

/* Six acts inside one section. With only three colours there is no fourth
   value to zone with, so the rhythm is built from a hairline, a measured
   silence and a hanging heading — a column arrangement that appears nowhere
   else on the site, which is how the reader knows the register has changed. */
.act {
  margin-block-start: clamp(44px, 5vw, 72px);
}

.act + .act {
  margin-block-start: clamp(64px, 7vh, 104px);
  padding-block-start: clamp(64px, 7vh, 104px);
  border-block-start: 1px solid var(--rule-soft);
}

.act__grid {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
  gap: clamp(28px, 4vw, 72px);
  align-items: start;
}

/* The .h3 rule carries a 56px lead-in for headings that follow copy; here the
   act itself has already paid for that space. */
.act__h {
  margin-block-start: 0;
  font-size: var(--t-h3);
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.02em;
  max-inline-size: 14em;
}

/* One act is the pivot of the chapter — the ground stops differing and the
   box starts being the same — so its heading leaves the hanging grid, takes
   the full measure and is set larger. Still well under the section h2. */
.act__h--turn {
  font-size: clamp(28px, 3.2vw, 44px);
  line-height: 1.08;
  max-inline-size: 20em;
  margin-block-end: clamp(32px, 3.6vw, 56px);
}

/* The heading column, and whatever small thing is asked to hold it down.
   A hanging heading leaves the start column empty for the length of the copy
   beside it, and on the three longest acts that came to five and six hundred
   pixels of nothing — which this client reads as an unfinished page rather
   than as air. Each of those acts now anchors its own column: the name act
   with the two characters it is about, the family act with the two portraits,
   the closing act with the way to reach the person it names. Nothing was
   invented to fill the space; the things that were already in the chapter
   were moved to where the space was. */
.act__side {
  min-inline-size: 0;
}

.act__copy > .body:first-child {
  margin-block-start: 0;
}

/* The characters themselves, once, at the only place on the page where the
   subject is the name. Decorative: the sentence beside it already carries
   寸金地 in reading order, so this repeat is hidden from assistive software. */
.act__mark {
  margin-block-start: clamp(28px, 3vw, 44px);
  font-size: clamp(52px, 6vw, 88px);
  line-height: 1;
  letter-spacing: 0.06em;
  color: var(--stone);
  user-select: none;
}

/* Every caption in the chapter, whatever it sits under. The first word does
   the work: an illustration says so, a company photograph says so.
 *
 * It is set as a plate label now — small capitals, letter-spaced, hung off an
 * em dash. The dash is drawn by the stylesheet and not written into en.json,
 * so the disclosure sentences themselves are untouched and translate as
 * written. Only the first line is capitalised: these captions run to two and
 * three lines and full small capitals at 13px stop being readable at that
 * length. */
.story__cap {
  margin-block-start: 14px;
  font-size: 14px;
  font-variant-caps: all-small-caps;
  letter-spacing: 0.055em;
  line-height: 1.55;
  color: var(--stone);
  max-inline-size: 34em;
}

/* The dash is drawn by the stylesheet, never written into en.json: the
   disclosure sentences translate as they stand and no locale has to remember
   to type an ornament. */
.story__cap::before {
  content: "— ";
  color: var(--gilt);
}

.story__opener {
  margin-block-end: clamp(32px, 3.6vw, 56px);
}

/* The subject is the ground in the foreground, not the sky. */
.story__opener img {
  object-position: center 60%;
}

.story__cn {
  color: var(--ink);
}

/* Act II — one region to a row, picture and copy each taking about half.
   Wider than the packing rows on purpose: land needs the room. */
.ground {
  margin-block-start: clamp(40px, 4.4vw, 64px);
  border-block-start: 1px solid var(--rule);
  list-style: none;
}

.ground__row {
  --shot: 62fr;
  --copy: 38fr;
  display: grid;
  grid-template-columns: var(--shot) var(--copy);
  align-items: center;
  gap: clamp(28px, 3.6vw, 64px);
  padding-block: clamp(40px, 5vw, 72px);
  border-block-end: 1px solid var(--rule);
}

/* Both the track widths and the item order flip, and neither flip names a
   side, so Arabic mirrors for free. */
.ground__row:nth-child(even) {
  grid-template-columns: var(--copy) var(--shot);
}

.ground__row:nth-child(even) .ground__shot {
  order: 2;
}

.ground__shot img {
  inline-size: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  object-position: center 55%;
  background: var(--tint);
}

.ground__place {
  font-size: var(--t-h3);
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.02em;
}

.ground__fruit {
  margin-block-start: 10px;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--stone);
}

.ground__p {
  margin-block-start: 16px;
  max-inline-size: 26em;
}

/* The two photographs that are actually ours, small and paired, after four
   illustrations. The size difference is the disclosure. */
.story__pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(20px, 2.4vw, 36px);
  margin-block-start: clamp(40px, 4.4vw, 64px);
}

/* The two company photographs and the still from the broadcast are the only
   pictures on the site that are records rather than illustrations, and they
   are mounted to say so: a bone mat drawn as the picture's own border, and a
   hairline set immediately outside it as an outline. Outline takes no space,
   so the mount adds a frame without moving a single grid track — and because
   both rings belong to the image box, neither can drift away from it at any
   width. */
.story__pair img,
.turn__media img,
.process__shot img {
  border: clamp(7px, 0.7vw, 11px) solid var(--bone);
  outline: 1px solid var(--rule);
  outline-offset: 0;
}

.story__pair img {
  inline-size: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: var(--tint);
}

/* Act III — the year, as six windows. The grid tightens from one row per
   region to six cells, and that change of density is the argument: this is
   no longer scenery, it is a shipping calendar. */
.windows {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(28px, 3vw, 48px);
  margin-block-start: clamp(40px, 4.4vw, 64px);
  list-style: none;
}

.window__shot img {
  inline-size: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  background: var(--tint);
}

.window__k {
  margin-block-start: 16px;
  font-size: 25px;
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* Locked to two lines whether the sentence needs them or not. Five of the six
   descriptions set on one line and the sixth on two, which pushed that cell's
   month bar and peak line 22px below its neighbours' and broke the only thing
   a calendar has to do: read straight across. */
.window__v {
  margin-block-start: 6px;
  min-block-size: 3em;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-70);
}

/* Same twelve cells as the fruit cards, drawn by the same helper; only the
   frame around them is new, so the two readings of a season stay identical. */
.window__bar {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 3px;
  margin-block-start: 12px;
  list-style: none;
}

.window__peak {
  margin-block-start: 8px;
  font-size: var(--t-micro);
  color: var(--stone);
}

/* Act IV — copy and the news frame side by side. Nothing here is pinned or
   driven by the scrollbar; the media column simply starts at the top. */
.turn__grid {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
}

.turn__media {
  align-self: start;
}

/* The still is a 5:2 crop of the broadcast frame, taken so that the station
   mark, the lower third and the weather crawl that ran under it are outside
   the picture. The ratio is the crop's own, so nothing is cut twice. */
.turn__media img {
  inline-size: 100%;
  aspect-ratio: 5 / 2;
  object-fit: cover;
  background: var(--tint);
}

.turn__media .link {
  margin-block-start: 14px;
}

/* The only enlarged quotation on the site. It used to carry a start rule,
   which was the same device the three disclosure panels use, and the effect
   was that the one sentence on the page worth remembering read at the rank of
   a footnote. The rule is gone. What promotes it now is silence either side
   and a hung opening quote mark, neither of which any caveat has. */
.pull {
  margin-block: clamp(64px, 7vw, 112px);
  max-inline-size: 17em;
}

.pull__q {
  text-indent: -0.42em;
  font-size: clamp(26px, 3.4vw, 46px);
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.025em;
  color: var(--ink);
}

/* The attribution is a child of the quotation, never a sibling: the sentence
   and the person who said it, on which day, to whom, do not travel apart. */
.pull__src {
  margin-block-start: 18px;
  font-size: var(--t-micro);
  line-height: 1.65;
  color: var(--stone);
  max-inline-size: 30em;
}

/* Act V — the two portraits, now stacked in the act's own start column rather
   than sitting in a 576px block under it with the other half of the measure
   empty. Stacked they run at the full width of that column, which is roughly
   half again what they had, and they stop being the smallest pictures in a
   chapter that is otherwise all photographs.

   The division between them is kept and only turned: there is no photograph
   of the two of them together, and a visible rule says so rather than a
   montage pretending otherwise. The order is the order the timeline directly
   below prints — the stall, then the licence — not a ranking. */
.pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  inline-size: 100%;
  max-inline-size: 440px;
  margin-block-start: clamp(32px, 3.4vw, 48px);
}

.pair img {
  inline-size: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  background: var(--tint);
}

/* The space is paid for in equal halves either side of the rule, so the two
   portraits come out the same size. Hang it on one figure only and one of the
   two people is quietly printed smaller than the other. */
.pair__a {
  padding-block-end: clamp(20px, 2.4vw, 32px);
}

.pair__b {
  padding-block-start: clamp(20px, 2.4vw, 32px);
  border-block-start: 1px solid var(--rule-soft);
}

/* The timeline is the documents table again, on purpose: a date behaves like
   a field label, and the reader has already learned that this arrangement
   means "a record you can check". */
.facts--tl {
  max-inline-size: 58em;
}

.facts--tl dt {
  font-size: 15px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* Act VI — no picture, a shorter measure and full-strength ink. The chapter
   gets quiet here because the lines get shorter and the images stop, not
   because half a screen was left blank. */
/* The three values. Set in the same hanging register as the acts around them
   and given no ornament at all: a value stated in a coloured panel reads as
   marketing, and these are the only lines in the chapter with no document
   behind them, so they are the last place on the page that can afford to look
   like a claim. */
.values-block {
  margin-block-start: clamp(40px, 4.4vw, 64px);
  padding-block-start: clamp(28px, 3vw, 40px);
  border-block-start: 1px solid var(--rule);
}

.values__title {
  margin-block-end: clamp(18px, 2vw, 26px);
  font-family: var(--fd);
  font-variation-settings: "opsz" 32;
  font-size: clamp(21px, 2vw, 28px);
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.02em;
}

.values {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0 clamp(28px, 3vw, 56px);
  list-style: none;
  max-inline-size: 58em;
}

.values__item {
  padding-block: 20px;
  border-block-start: 1px solid var(--rule-soft);
}

.values__k {
  font-family: var(--fd);
  font-variation-settings: "opsz" 24;
  font-size: 21px;
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: -0.01em;
}

.values__v {
  margin-block-start: 8px;
  color: var(--ink-70);
  font-size: 16px;
  line-height: 1.55;
}

.act--limits {
  padding-block-start: clamp(96px, 14vh, 160px);
}

.act--limits .act__grid {
  grid-template-columns: minmax(0, 4fr) minmax(0, 6fr);
}

.act--limits .body {
  max-inline-size: 30em;
  color: var(--ink);
}

/* The two contact routes, inherited from the section this chapter replaced.
   Kept close to the closing line and set on tighter rows than the documents
   table: three short values do not need a full field table's air, and the
   last screen of the chapter should still be reading matter rather than the
   run-out into the next section. */
.story__contact {
  margin-block-start: clamp(32px, 4vh, 56px);
  max-inline-size: 34em;
}

/* Stacked rather than paired off against a 15em label column: this table now
   sets inside the act's start column, where a two-track row leaves an email
   address about 200px to sit in and it does not fit in 200px. Label over
   value, and the value is allowed to break inside a word rather than run out
   of its column and across the copy beside it. */
.story__contact .facts__row {
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
  padding-block: 12px;
}

.story__contact dd {
  overflow-wrap: anywhere;
}

/* The chapter ends on the only ask it makes, and until now that ask was an
   underlined phrase inside a paragraph. A page a buyer is meant to act on
   gets one solid target, on the same column as the address it would reach. */
.story__cta {
  display: inline-flex;
  margin-block-start: clamp(24px, 2.6vw, 36px);
}

/* -------------------------------------------------------------------- quote */

.two {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: clamp(40px, 5vw, 88px);
  margin-block-start: 30px;
  align-items: start;
}

.two--form {
  margin-block-start: clamp(48px, 5vw, 72px);
  grid-template-columns: 1.15fr 1fr;
  align-items: start;
}

/* The form is eleven fields and the panel beside it is three routes and three
   steps, so the second column ran out about a third of the way down and left
   a thousand pixels of bone next to the part of the form people abandon. It
   travels with the reader instead. The two things it carries are exactly the
   two a buyer wants at that moment: the reassurance that the form, the email
   and the WhatsApp number all reach the same desk, and what happens after
   they press send.
 *
 * Sticky rather than scripted, deliberately: the behaviour contract forbids
 * driving anything in the document from the scroll offset, and this is the
 * one layout on the site that wanted it. The offset matches the header
 * clearance already declared in scroll-padding-block-start. */
.direct {
  position: sticky;
  inset-block-start: 96px;
}

.field {
  margin-block-start: 22px;
}

.field__l {
  display: block;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--stone);
}

.field__req {
  /* Was --ink-55, which composites to 3.65:1 on bone and fails 4.5:1 at the
     13px floor these render at. --ink-70 is 5.79:1 and is already the
     secondary-copy colour, so this costs no new token against the four-colour
     contract. The required marker is the only cue that a field is mandatory;
     it cannot be the least legible thing in the label. */
  color: var(--ink-70);
}

/* Every field now states which it is. Marking only the required ones leaves
   the reader inferring the rest from an absence, and the point of making
   three of these optional is lost if the form does not say so. Same colour as
   the required marker: this is information, not a warning. */
.field__opt {
  color: var(--ink-70);
}

.field__i {
  inline-size: 100%;
  margin-block-start: 8px;
  /* 16px block padding sets the input at the same optical height as the
     adjacent submit button (~57px) instead of 5px shy of it. */
  padding: 16px 14px;
  font-family: var(--f);
  font-size: 17px;
  line-height: 1.4;
  color: var(--ink);
  background: var(--bone);
  /* The field background is the page background, so this border is the only
     thing that says where an input begins. --rule measures 1.32:1 against
     bone, under the 3:1 that WCAG requires of a control boundary, and it only
     became visible on :hover — which a touch or keyboard user never fires.
     --ink-55 is 3.63:1 and already in the palette. Verified by compositing
     the token over #f4f1ea and computing the ratio, not by eye. */
  border: 1px solid var(--ink-55);
  border-radius: 1px;
  transition: border-color 200ms var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  /* Raising the resting border to --ink-55 made hover a no-op. The hover
     state has to stay one step darker than rest or the affordance is gone. */
  .field__i:hover {
    border-color: var(--ink-70);
  }
}

.field__t {
  resize: vertical;
}

.field__i:user-invalid,
.field__i[aria-invalid="true"] {
  border-color: var(--ink);
}

.field__help,
.field__error {
  display: block;
  margin-block-start: 7px;
  font-size: 13px;
  line-height: 1.5;
}

.field__help {
  color: var(--ink-70);
}

.field__error {
  min-block-size: 1.5em;
  color: var(--ink);
}

.more {
  margin-block-start: 30px;
  border-block-start: 1px solid var(--rule);
  padding-block-start: 18px;
}

.more__s {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  cursor: pointer;
}

.more__body {
  padding-block-end: 8px;
}

.form__send {
  margin-block-start: 34px;
}

.form__note {
  margin-block-start: 18px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink-70);
  max-inline-size: 34em;
}

.direct__routes {
  margin-block-start: 22px;
  border-block-start: 1px solid var(--rule);
}

.direct__routes > div {
  display: grid;
  grid-template-columns: minmax(7em, 0.7fr) 1.3fr;
  gap: 14px;
  padding-block: 15px;
  border-block-end: 1px solid var(--rule-soft);
}

.direct__routes dt {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--stone);
}

.direct__routes dd {
  min-inline-size: 0;
  overflow-wrap: anywhere;
}

.direct__who {
  margin-block-start: 18px;
  font-size: var(--t-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--stone);
}

.steps {
  margin-block-start: 22px;
  list-style: none;
  border-block-start: 1px solid var(--rule);
}

.steps li {
  display: grid;
  grid-template-columns: 3.2em 1fr;
  gap: 4px 12px;
  padding-block: 18px;
  border-block-end: 1px solid var(--rule-soft);
}

.steps__n {
  grid-row: span 2;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  color: var(--stone);
  line-height: 1.7;
}

.steps b {
  font-size: 17px;
  font-weight: 500;
}

.steps span:last-child {
  font-size: 15px;
  color: var(--ink-70);
}

/* ------------------------------------------------------------------- footer */

.foot {
  padding-block: clamp(56px, 6vw, 88px) 40px;
  border-block-start: 1px solid var(--rule);
}

.foot__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(24px, 2.6vw, 44px);
}

/* Was the one small-capital line on the page tracked at 0.07em rather than
   0.09em — a difference nobody could name and everybody could feel. It takes
   the shared token now like the other seven. */
.foot__k {
  color: var(--stone);
  margin-block-end: 10px;
}

.foot__grid p {
  font-size: 15px;
  line-height: 1.6;
}

.foot__langs {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  font-size: 15px;
}

.foot__pending {
  color: var(--stone);
}

.foot__pending i {
  font-style: normal;
  font-size: var(--t-micro);
}

.foot__base {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px 40px;
  margin-block-start: clamp(44px, 4.4vw, 68px);
  padding-block-start: 22px;
  border-block-start: 1px solid var(--rule-soft);
  font-size: var(--t-micro);
  color: var(--stone);
}

/* -------------------------------------------------------- floating whatsapp */

.wa {
  position: fixed;
  inset-block-end: 24px;
  inset-inline-end: 24px;
  z-index: 25;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 20px;
  color: var(--bone);
  background: var(--ink);
  border-radius: 2px;
  text-decoration: none;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: opacity 200ms var(--ease), transform 200ms var(--ease);
}

.wa:hover {
  opacity: 0.86;
}

.wa.is-suppressed {
  opacity: 0;
  pointer-events: none;
  transform: translate3d(0, 12px, 0);
}

.wa svg {
  inline-size: 18px;
  block-size: 18px;
}

/* ---------------------------------------------------------- product pages */

.product-hero {
  /* End ≥ start so the hero settles into the page (gate 44a). */
  padding-block: 128px clamp(104px, 10vw, 168px);
}

.product-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  align-items: center;
  gap: clamp(48px, 7vw, 104px);
}

/* The trail that replaced the single back link. Set at the 13px floor and in
   --stone, so it reads as furniture above the title rather than competing
   with the eyebrow directly under it. Separators are generated, never typed,
   which keeps them out of the accessibility tree and out of a copy-paste. */
.crumbs {
  margin-block-end: 40px;
}

.crumbs__list {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 10px;
  list-style: none;
  font-size: var(--t-micro);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--stone);
}

.crumbs__i {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

/* The separator inherits --stone from the list rather than taking the gilt.
   The accent has a budget — 4 to 12 uses site-wide, which self-check counts —
   and spending one of those on a punctuation mark that repeats twice per
   fruit page is the wrong place for it. */
.crumbs__i + .crumbs__i::before {
  content: "/";
}

.crumbs__i[aria-current="page"] {
  color: var(--ink-70);
}

.product-hero__h1 {
  margin-block-start: 24px;
  font-family: var(--fd);
  font-size: clamp(44px, 6vw, 82px);
  font-weight: 400;
  font-optical-sizing: auto;
  line-height: 0.98;
  letter-spacing: -0.035em;
  overflow-wrap: anywhere;
  min-width: 0;
}

.product-hero__cn {
  margin-block-start: 16px;
  font-size: 18px;
  color: var(--stone);
}

.product-hero__copy .lede {
  margin-block-start: 34px;
}

.product-supply {
  margin-block-start: 22px;
  padding-inline-start: 18px;
  border-inline-start: 1px solid var(--gilt);
  color: var(--ink-70);
}

.product-actions,
.product-close {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 20px 28px;
  margin-block-start: 36px;
}

.product-hero__media img {
  display: block;
  inline-size: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* Specification and shipping window as two columns of one table rather than
   one column with the window a screen further down. The hairline between them
   is the only thing dividing them, which is the right weight: they are two
   readings of the same lot, not two sections. */
.product-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: clamp(36px, 5vw, 84px);
  margin-block-start: clamp(40px, 4vw, 60px);
}

.product-grid__col:last-child {
  padding-inline-start: clamp(0px, 3vw, 44px);
  border-inline-start: 1px solid var(--rule);
}

/* Wider key column than a summary card's 5.6em: "Shipping window" and
   "Typical peak" are two words, and at 5.6em both wrapped to two lines against
   a one-line value, which reads as a table that has run out of room. */
.product-facts {
  margin-block-start: 20px;
}

.product-facts .spec {
  grid-template-columns: 8.6em 1fr;
}

.product-season {
  margin-block-start: 28px;
}

/* Commercial terms. The panel is a slot with a shape, held whether or not the
   figures exist yet: heading on the spine, table and copy in the wide column,
   so the answered questionnaire drops into a layout that was already drawn
   rather than arriving as a table nobody made room for. */
.product-terms__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.72fr) minmax(0, 1.28fr);
  gap: clamp(32px, 5vw, 88px);
  align-items: start;
}

.product-terms__lead .h2 {
  margin-block-start: 20px;
}

.product-terms {
  margin-block-end: 34px;
}

/* The label column is wider here than on a summary card: "Carriage
   temperature" and "Count per carton" are two words where the card's keys are
   one, and a key that wraps to two lines against a one-line value makes a
   table look broken. */
.product-terms .spec {
  grid-template-columns: 11em 1fr;
}

.product-terms__k {
  margin-block-start: 44px;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--stone);
}

/* Two cards, not three: the third was the terms card, and terms now have a
   band of their own. Unequal columns so the row reads as a composed argument
   rather than a template's equal tiles. */
.product-proof__grid {
  display: grid;
  grid-template-columns: 1.25fr minmax(0, 1fr);
  gap: clamp(28px, 4vw, 56px);
}

/* The rest of the range, in calendar order. A detail page needs an exit that
   is not the back button: five small plates, each with the month range under
   it, so the reason to click is on the tile rather than behind it. */
.range-row {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 30px);
  margin-block-start: clamp(36px, 4vw, 54px);
}

.range-card {
  display: grid;
  align-content: start;
  color: inherit;
  text-decoration: none;
}

.range-card__media {
  display: block;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--tint);
}

.range-card__media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: transform 1200ms var(--ease);
}

/* Two lines of room whether the name needs them or not, so the five month
   ranges under the five names sit on one line across the strip. "Ehime No.38
   Jelly Orange" is the one that wraps, and without this it dragged its own
   window a line below its neighbours' and made the row look unaligned. */
.range-card__h {
  margin-block-start: 16px;
  min-block-size: 2.4em;
  font-family: var(--fd);
  font-size: 19px;
  line-height: 1.2;
  letter-spacing: -0.015em;
}

.range-card__w {
  margin-block-start: 6px;
  font-size: var(--t-micro);
  color: var(--stone);
}

/* The tile grows its own hairline on hover rather than lifting: there are no
   shadows on this site, so depth is not available as a hover language and a
   rule appearing under the name is. */
.range-card__h {
  padding-block-end: 4px;
  border-block-end: 1px solid transparent;
  justify-self: start;
  transition: border-color 300ms var(--ease);
}

.product-close {
  margin-block-start: clamp(40px, 4vw, 60px);
}

.product-proof__grid article {
  padding-block-start: 24px;
  border-block-start: 1px solid var(--rule);
}

.product-proof__grid .h3 {
  margin-block-start: 0;
}

.product-proof__grid article > p:last-child {
  margin-block-start: 18px;
  color: var(--ink-70);
}

/* ----------------------------------------------------------------- entrances */

.reveal {
  opacity: 0;
  transform: translate3d(0, 20px, 0);
}

.reveal.is-in {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--in) var(--ease),
    transform var(--in) var(--ease);
}

/* The hero settles by itself and is finished inside 1.2s: a 300ms delay plus
   a 520ms fade on the last element in the sequence. */
.hero__eyebrow,
.hero__row {
  animation: rise var(--in) var(--ease) both;
}

.hero__eyebrow {
  animation-delay: 60ms;
}

.hero__row {
  animation-delay: 160ms;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translate3d(0, 18px, 0);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ---------------------------------------------------------------- wholesale */

/* The one band addressed to the buyer who reads in containers rather than in
   kilos. It sits between the chapter and the form because that is where a
   reader who has finished the story decides whether this is a supplier of
   their size. Nothing in it is a figure we do not already hold: no minimum
   order, no annual tonnage, no country count. The proof of scale is the
   specificity of the sentence, not a number attached to it. */
.wholesale {
  background: var(--tint);
  padding-block: clamp(64px, 7vw, 104px);
}

.wholesale__grid {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(32px, 4vw, 72px) clamp(40px, 5vw, 88px);
  align-items: start;
}

.wholesale__q {
  margin-block-start: 22px;
  font-size: clamp(23px, 2.4vw, 33px);
  font-weight: 400;
  line-height: 1.24;
  letter-spacing: -0.022em;
  color: var(--ink);
  max-inline-size: 17em;
}

.wholesale__list {
  list-style: none;
  margin-block-start: clamp(6px, 1vw, 14px);
}

.wholesale__list li {
  padding-block: 14px;
  border-block-start: 1px solid var(--gilt-soft);
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink-70);
}

.wholesale__cta {
  grid-column: 1 / -1;
  justify-self: start;
}

/* --------------------------------------------------------------------- faq */

/* Same two-column spine as the wholesale band above it, so the two read as a
   pair of answers rather than two unrelated interruptions between the
   documents and the chapter. On the fruit pages the same list runs full
   measure under a heading, with no lead column — there are three questions
   there, not six, and a column of white beside three lines would look like a
   missing block. */
.faq {
  padding-block: clamp(64px, 7vw, 104px);
}

.faq__grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(28px, 3.5vw, 56px) clamp(40px, 5vw, 88px);
  align-items: start;
}

.faq__lead .note {
  margin-block-start: 18px;
  max-inline-size: var(--measure-lede);
}

.faq__list {
  max-inline-size: var(--measure-prose);
}

.product-faq .faq__list {
  max-inline-size: var(--measure-lede);
  margin-block-start: clamp(24px, 3vw, 40px);
}

.faq__i {
  padding-block: clamp(18px, 2vw, 26px);
  border-block-start: 1px solid var(--rule);
}

.faq__i:first-child {
  border-block-start: 0;
  padding-block-start: 0;
}

.faq__q {
  font-family: var(--fd);
  font-size: clamp(19px, 1.6vw, 23px);
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: -0.012em;
  color: var(--ink);
}

.faq__a {
  margin-block-start: 10px;
  font-size: 16px;
  line-height: 1.62;
  color: var(--ink-70);
}

@media (max-width: 860px) {
  .faq__grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .faq__list,
  .product-faq .faq__list {
    max-inline-size: none;
  }
}

/* ------------------------------------------------------------- rfq fallback */

/* Audit B2. The note said "copy the brief below" and there was no brief
   below: the lines went into a data attribute that nothing rendered. For a
   buyer whose browser has no mail handler registered — a large share of the
   overseas trade, who read webmail in a tab — the mailto: did nothing and the
   inquiry was lost with no error and no trace. The brief is printed now, and
   is selectable and copyable whether or not the clipboard API is available. */
/* The honeypot. Taken out of the flow rather than out of the document: a
   submitter that reads the stylesheet before deciding what to fill in will
   skip `display: none`, and the whole value of the field is that it looks
   like an ordinary one to anything that is not looking at the screen. */
.field--trap {
  position: absolute;
  inset-inline-start: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

/* The reference the buyer is given once the desk has the inquiry. It is the
   proof the submission is real — the one thing a mailto: never gave them. */
.form__ref {
  display: inline-block;
  margin-block-start: 10px;
  padding: 6px 12px;
  border: 1px solid var(--rule);
  background: var(--bone);
  font-family: ui-monospace, "SFMono-Regular", "Menlo", "Consolas", monospace;
  font-size: 14px;
  letter-spacing: 0.04em;
  color: var(--ink);
  user-select: all;
}

.form__brief {
  margin-block-start: 16px;
  padding: 18px 20px;
  border: 1px solid var(--rule);
  background: var(--bone);
  font-family: ui-monospace, "SFMono-Regular", "Menlo", "Consolas", monospace;
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  user-select: text;
}

.form__briefhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-block-start: 20px;
}

.form__copy {
  padding: 9px 18px;
  border: 1px solid var(--ink);
  border-radius: 1px;
  background: transparent;
  font-family: var(--f);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink);
  cursor: pointer;
  transition: background-color 200ms var(--ease), color 200ms var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .form__copy:hover {
    background: var(--ink);
    color: var(--bone);
  }
}

/* ------------------------------------------------------------------- motion */

/* A photograph under the cursor breathes and does not jump: 1.5% over 1.2s is
   below the threshold at which the eye reads it as an animation and above the
   one at which it reads as nothing. The frames clip, so the picture grows
   inside its own edge rather than pushing the layout. */
.ground__shot,
.window__shot {
  overflow: hidden;
}

.card__media img,
.region__media img,
.range-stage__media img,
.operations__frame img,
.ground__shot img,
.window__shot img,
.opener img {
  transition: transform 1200ms var(--ease);
}

/* Hover-only devices: on touch, a tapped card would stick in its zoomed
   state until the next tap lands elsewhere. */
@media (hover: hover) and (pointer: fine) {
  .card:hover .card__media img,
  .region:hover .region__media img,
  .range-stage:hover .range-stage__media img,
  .operations__frame:hover img,
  .ground__row:hover .ground__shot img,
  .window:hover .window__shot img,
  .range-card:hover .range-card__media img,
  .opener:hover img {
    transform: scale(1.015);
  }

  .range-card:hover .range-card__h {
    border-block-end-color: var(--gilt);
  }
}

/* -------------------------------------------------------------- breakpoints */

@media (max-width: 1080px) {
  .grid--3 {
    grid-template-columns: repeat(2, 1fr);
  }

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

  .strength__grid {
    grid-template-columns: repeat(2, 1fr);
    row-gap: 34px;
  }

  .strength__metric:nth-child(3) {
    padding-inline-start: 0;
    border-inline-start: 0;
  }

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

  .region:nth-child(n) {
    grid-column: auto;
  }

  .two,
  .two--form {
    grid-template-columns: 1fr;
  }

  /* Stacked, there is no second column for the panel to travel beside, and a
     sticky block in a single column pins itself over the form it was meant to
     sit next to. */
  .direct {
    position: static;
  }

  /* The photograph keeps more than half the row until the copy would have to
     hyphenate; below that the row stacks. */
  .process__step {
    --shot: 54fr;
    --copy: 46fr;
  }

  .facts__row {
    grid-template-columns: 11em 1fr;
  }

  /* Same reasoning as the packing rows: the copy column gives ground first,
     and the window grid drops to two before it drops to one. */
  .ground__row {
    --shot: 57fr;
    --copy: 43fr;
  }

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

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

@media (max-width: 860px) {
  .range-stage__caption,
  .operations__head {
    grid-template-columns: 1fr;
  }

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

  .operations__frame--lead {
    grid-column: auto;
    grid-row: auto;
  }

  .operations__frame img {
    aspect-ratio: 16 / 9;
  }

  .product-hero__grid,
  .product-proof__grid,
  .product-grid,
  .product-terms__grid {
    grid-template-columns: 1fr;
  }

  /* The hairline was the divider between two columns; stacked, the same rule
     has to move to the top of the second block or it draws down the side of
     nothing. */
  .product-grid__col:last-child {
    padding-inline-start: 0;
    padding-block-start: clamp(28px, 5vw, 40px);
    border-inline-start: 0;
    border-block-start: 1px solid var(--rule);
  }

  /* Five across becomes three, then two on a phone. Never one: the point of
     the strip is that a buyer sees the range is wider than the page they
     landed on, and a single column reads as one more suggestion. */
  .range-row {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .product-hero {
    padding-block-start: 120px;
  }

  /* The hanging heading becomes a plain heading: below this the title column
     sets at under twenty characters a line. */
  .act__grid,
  .act--limits .act__grid {
    grid-template-columns: 1fr;
    gap: 20px;
  }

  /* The wholesale sentence and its three lines stack: side by side below this
     the sentence sets at nine words a line and stops being a sentence. */
  .wholesale__grid {
    grid-template-columns: 1fr;
  }

  .form__briefhead {
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
  }

  /* Every region row leads with its photograph, so the alternation is dropped
     rather than half-kept. */
  .ground__row,
  .ground__row:nth-child(even) {
    grid-template-columns: 1fr;
    gap: 20px;
    padding-block: 36px;
  }

  .ground__row:nth-child(even) .ground__shot {
    order: 0;
  }

  /* 21:9 across 390px is a 167px band that reads as a colour strip rather
     than a photograph of ground. */
  .story__opener img {
    aspect-ratio: 3 / 2;
  }

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

  .act--limits {
    padding-block-start: 96px;
  }

  /* One column puts the start column's contents above the copy, and in the
     closing act that would hand the reader an email address before the four
     paragraphs that say what the page is not — the ask arriving before the
     thing it is asking about. The wrapper drops out of the box tree so its
     three children can be ordered against the copy directly: heading, the
     disclaimers, then the way to reply. */
  .act--limits .act__grid {
    display: flex;
    flex-direction: column;
    gap: 20px;
  }

  .act--limits .act__side {
    display: contents;
  }

  .act--limits .act__h {
    order: 1;
  }

  .act--limits .act__copy {
    order: 2;
  }

  .act--limits .story__contact {
    order: 3;
    margin-block-start: 0;
  }

  .act--limits .story__cta {
    order: 4;
    align-self: start;
    margin-block-start: 0;
  }

  /* Below this the copy column would set at under thirty characters a line, so
     the rows stack: photograph, then its number, heading and paragraph. Every
     row now leads with the picture, so the alternation is dropped rather than
     half-kept. */
  .process__step,
  .process__step:nth-child(even) {
    grid-template-columns: 1fr;
    gap: 20px;
    padding-block: 32px;
  }

  .process__step:nth-child(even) .process__shot {
    order: 0;
  }

  /* Mark, then menu, then the quote button on the end edge. */
  .head__toggle {
    display: block;
    order: 2;
  }

  .head__nav {
    position: absolute;
    inset-block-start: 80px;
    inset-inline: 0;
    padding: 8px var(--gutter) 24px;
    background: var(--bone);
    border-block-end: 1px solid var(--rule);
    display: none;
  }

  .head__nav.is-open {
    display: block;
  }

  .head__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  .head__list a {
    display: block;
    padding-block: 15px;
    border-block-end: 1px solid var(--rule-soft);
  }

  .head__cta {
    order: 3;
  }

  .head__tag {
    display: none;
  }

  .hero__body {
    padding-block-start: 128px;
  }

  .hero__row {
    flex-direction: column;
    align-items: flex-start;
    gap: 36px;
    margin-block-start: 48px;
  }

  .hero__right {
    flex-wrap: wrap;
    gap: 28px 36px;
  }

  .hero__cta {
    inline-size: 100%;
  }
}

@media (max-width: 640px) {
  .grid--3,
  .grid--4,
  .grid--products,
  .region-showcase,
  .reports__pair {
    grid-template-columns: 1fr;
  }

  .strength__grid {
    grid-template-columns: 1fr;
    row-gap: 0;
  }

  .strength__metric,
  .strength__metric:first-child,
  .strength__metric:nth-child(3) {
    min-block-size: 0;
    padding: 26px 0;
    border-inline-start: 0;
    border-block-start: 1px solid var(--gilt);
  }

  .strength__metric:first-child {
    padding-block-start: 0;
    border-block-start: 0;
  }

  .range-stage__caption {
    gap: 14px;
  }

  .facts__row,
  .spec {
    grid-template-columns: 1fr;
    gap: 4px;
  }

  /* 390px has to hold the mark, the menu button and the quote button without
     any of them shrinking into an ellipsis, so the row loses its air first. */
  .head {
    gap: 10px;
  }

  .head__mark {
    font-size: 17px;
  }

  .head__cta {
    padding: 11px 14px;
    letter-spacing: 0.06em;
  }

  .head__cta-full {
    display: none;
  }

  .head__cta-short {
    display: inline;
  }

  .head__toggle {
    padding: 11px 12px;
    letter-spacing: 0.06em;
  }

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

  .wa {
    inset-block-end: max(12px, env(safe-area-inset-bottom));
    inset-inline-end: 12px;
    inline-size: 52px;
    block-size: 52px;
    justify-content: center;
    padding: 0;
    border-radius: 2px;
  }

  .wa svg {
    inline-size: 22px;
    block-size: 22px;
  }

  .wa__label {
    display: none;
  }

  .product-hero__h1 {
    font-size: clamp(40px, 13vw, 58px);
  }

  .product-actions,
  .product-close {
    align-items: flex-start;
    flex-direction: column;
  }

  .checks {
    gap: 10px 32px;
  }

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

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

  /* One window to a row makes the cell as wide as the screen, and a 3:2 frame
     at that width pushes the month bar off the fold. */
  .window__shot img {
    aspect-ratio: 16 / 9;
  }

  /* The portraits are stacked at every width now, so the narrow screen needs
     no rule of its own beyond letting them take the measure: two 143px faces
     side by side were the smallest pictures on a page that has no other
     faces at all. */
  .pair {
    max-inline-size: none;
  }
}

/* ------------------------------------------------------------ reduced motion */

/* ==========================================================================
   V8 — the evidence run under the hero, and the availability calendar.

   Three components, added 2026-08-07 to answer one brief: a buyer should meet
   the size of the operation and the paperwork behind it inside two scrolls,
   not nine thousand pixels down. Nothing here introduces a colour, a shadow or
   a radius the house rules at the top of this file do not already allow.
   ========================================================================== */

/* The proof plate. Full bleed, because a photograph of forty people on a
   packing floor or two people reading an opened fruit is the one argument on
   this page that needs no sentence, and cropping it into a column throws that
   away. Height is capped in vh so it can never eat a phone screen whole. */
.proof {
  position: relative;
  display: grid;
  align-items: end;
  min-block-size: clamp(420px, 66vh, 720px);
  overflow: hidden;
  background: var(--ink);
}

/* `align-self: stretch` is load-bearing on both of these, and the scrim is the
   reason. The section is a grid with `align-items: end` so the copy can sit on
   the bottom edge, and an end-aligned grid item is sized to its own content —
   which for an empty scrim div is zero. It rendered as a 0px band at the foot
   of the plate, invisible, while the headline sat directly on a sunlit white
   shirt at 0.9:1. Stretching pins both layers to the full cell. */
.proof__media,
.proof__scrim {
  align-self: stretch;
}

.proof__media,
.proof__media img {
  grid-area: 1 / 1;
  inline-size: 100%;
  block-size: 100%;
}

.proof__media img {
  object-fit: cover;
  object-position: 50% 42%;
  display: block;
}

/* Bone type over a photograph needs the ground under it made dependable, and
   the only honest way to do that is to darken the picture rather than put a
   panel on top of it. The gradient is alpha of --ink and reaches full strength
   only in the bottom third, where the words are. */
/* Measured, not guessed. This photograph is high-key in exactly the corner the
   words sit in — white shirts, sunlit leaf, bright sky through the branches —
   and the first gradient here, which looked reasonable written down, left the
   headline at roughly 2:1 against the pixels behind it. The stops below were
   set by sampling the rendered frame under each line of type and pushing until
   the worst pixel behind the smallest text cleared 4.5:1; scripts/qa-v7.mjs
   samples the same pixels on every run so it cannot quietly regress when the
   plate is next re-cut. */
.proof__scrim {
  grid-area: 1 / 1;
  background: linear-gradient(
    to bottom,
    rgba(22, 33, 27, 0) 34%,
    rgba(22, 33, 27, 0.14) 50%,
    rgba(22, 33, 27, 0.52) 68%,
    rgba(22, 33, 27, 0.84) 82%,
    rgba(22, 33, 27, 0.93) 100%
  );
}

.proof__body {
  grid-area: 1 / 1;
  align-self: end;
  padding-block-end: clamp(30px, 4vw, 58px);
  color: var(--bone);
}

/* Both short lines are sized to their own text rather than left as full-width
   blocks.
 *
 * This is a contrast fix as much as a layout one. qa-v7 samples every pixel
 * inside each line's border box and composites the line's colour over it, and
 * a two-word attribution left as a block was reporting a box 1200px wide with
 * 110px of ink in it — so the check was reading the sunlit inline-end of the
 * photograph, where no letter is ever drawn, and calling it the ground behind
 * the words. Widening the measure in August 2026 pushed that box far enough
 * into the bright corner to fail on pixels the reader will never see type
 * over. fit-content makes the measured region the region that actually
 * carries ink, which is what the check was written to look at. The scrim is
 * untouched: it was set by measurement against this photograph and re-cutting
 * it to answer a box-sizing accident would be the wrong repair. */
.proof__eyebrow,
.proof__src {
  inline-size: fit-content;
  max-inline-size: 100%;
}

/* Full strength. Quietness here comes from size, weight and letterspacing,
   which cost nothing in contrast; dimming bone to 0.78 over a photograph
   spends the one budget this plate has none of. */
.proof__eyebrow {
  color: var(--bone);
}

.proof__line {
  margin-block-start: 14px;
  max-inline-size: 22em;
  font-family: var(--fd);
  font-optical-sizing: auto;
  font-variation-settings: "opsz" 48;
  font-size: clamp(23px, 2.5vw, 38px);
  font-weight: 400;
  line-height: 1.16;
  letter-spacing: -0.015em;
  color: var(--bone);
}

/* The provenance label, in the same words the rest of the site uses. A
   photograph presented this large without one is the exact place a reader
   would assume a stock library. */
.proof__src {
  margin-block-start: 18px;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--bone);
  opacity: 0.82;
}

/* The assurance strip. Five tiles, each naming the kind of evidence it is
   before it names the evidence — see the comment on assurance() in build.mjs
   for why the kind label is load-bearing rather than decorative. */
.assure {
  padding-block: clamp(56px, 6vw, 92px);
  background: var(--tint);
  border-block: 1px solid var(--rule-soft);
}

.assure__h {
  font-family: var(--fd);
  font-optical-sizing: auto;
  font-variation-settings: "opsz" 48;
  font-size: clamp(25px, 2.6vw, 38px);
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -0.015em;
  max-inline-size: 16em;
}

.assure__list {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0;
  margin-block-start: clamp(30px, 3.4vw, 48px);
  list-style: none;
}

.assure__item {
  padding-inline: clamp(16px, 1.8vw, 30px);
  border-inline-start: 1px solid var(--rule);
}

.assure__item:first-child {
  padding-inline-start: 0;
  border-inline-start: 0;
}

.assure__kind {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--gilt);
}

.assure__t {
  margin-block-start: 12px;
  font-size: clamp(16px, 1.25vw, 19px);
  font-weight: 500;
  line-height: 1.25;
}

.assure__n {
  margin-block-start: 9px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--stone);
}

.assure__cta {
  margin-block-start: clamp(28px, 3vw, 40px);
  display: inline-block;
}

/* The availability calendar. Twelve columns of state against six rows, which
   is the one shape that shows a reader the windows tile the year. */
.calendar {
  margin-block-start: clamp(72px, 7vw, 108px);
  padding-block-start: clamp(48px, 5vw, 72px);
  border-block-start: 1px solid var(--rule);
}

/* Twelve columns will not fold below roughly 640px and must not be allowed to
   push the document sideways, so the table — and only the table — scrolls.
   tabindex makes that scroller reachable from the keyboard, which a scrolling
   region with no focusable child otherwise is not. */
.cal__scroll {
  margin-block-start: clamp(28px, 3vw, 44px);
  overflow-x: auto;
}

.cal {
  inline-size: 100%;
  min-inline-size: 620px;
  border-collapse: collapse;
  font-size: 14px;
}

.cal th,
.cal td {
  padding-block: 12px;
  text-align: start;
  font-weight: 400;
  border-block-end: 1px solid var(--rule-soft);
}

.cal thead th {
  padding-block-end: 14px;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--stone);
  border-block-end-color: var(--rule);
}

.cal thead .cal__m {
  text-align: center;
}

.cal__m abbr {
  text-decoration: none;
  border: 0;
}

.cal__fruit {
  inline-size: 30%;
  padding-inline-end: 20px;
  font-weight: 500;
}

.cal__fruit a {
  color: inherit;
  text-decoration: none;
  border-block-end: 1px solid var(--rule);
}

.cal__fruit a:hover,
.cal__fruit a:focus-visible {
  border-block-end-color: var(--gilt);
}

/* An in-season cell is a filled block, an out-of-season one is a hairline.
   Colour is not carrying this on its own: the two states differ in fill, and
   the screen-reader text names the month on the cells that are on. */
.cal__c {
  position: relative;
  inline-size: 4.2%;
  padding-inline: 3px;
}

.cal__c::after {
  content: "";
  display: block;
  block-size: 16px;
  background: var(--rule-soft);
}

.cal__c.is-on::after {
  background: var(--ink);
}

.cal__peak {
  inline-size: 20%;
  padding-inline-start: 20px;
  color: var(--stone);
  font-variant-numeric: tabular-nums;
  text-align: end;
}

.cal tbody tr:last-child th,
.cal tbody tr:last-child td {
  border-block-end: 0;
}

/* A card that folded its unconfirmed rows away says where they went. */
.card__more {
  margin-block-start: 14px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--stone);
}

/* A heading that opens a section has the band's own padding above it already
   and does not need the lead margin as well. */
.h3--first {
  margin-block-start: 0;
}

@media (max-width: 1080px) {
  .assure__list {
    grid-template-columns: repeat(2, 1fr);
    row-gap: clamp(26px, 3.4vw, 38px);
  }

  .assure__item:nth-child(odd) {
    padding-inline-start: 0;
    border-inline-start: 0;
  }
}

@media (max-width: 640px) {
  .assure__list {
    grid-template-columns: 1fr;
    row-gap: 26px;
  }

  .assure__item {
    padding-inline: 0;
    border-inline-start: 0;
    border-block-start: 1px solid var(--rule);
    padding-block-start: 22px;
  }

  .assure__item:first-child {
    border-block-start: 0;
    padding-block-start: 0;
  }

  .proof__line {
    max-inline-size: none;
  }

  .proof__scrim {
    background: linear-gradient(
      to bottom,
      rgba(22, 33, 27, 0) 16%,
      rgba(22, 33, 27, 0.2) 38%,
      rgba(22, 33, 27, 0.66) 56%,
      rgba(22, 33, 27, 0.9) 74%,
      rgba(22, 33, 27, 0.95) 100%
    );
  }
}

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

  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    transition-duration: 1ms !important;
  }

  .reveal {
    opacity: 1;
    transform: none;
  }

  /* Grading is four rows of photograph and copy either way: there is no
     scroll-driven state left in this stylesheet to unwind. */
  .process__step {
    opacity: 1;
  }

  /* None of the story components carries a base opacity of its own — the zero
     only ever arrives on the .reveal class, which the script does not add in
     this mode. Stated anyway, so that a later hand cannot make one of them
     fade-dependent without noticing. */
  .ground__row,
  .window,
  .pull,
  .story__pair,
  .turn__media,
  .act__copy,
  .act__h {
    opacity: 1;
  }

  /* The two hover refinements are motion whether or not they are entrances.
     The blanket 1ms above would still let the picture arrive at its enlarged
     size and the underline snap to full width, so both are removed outright
     rather than sped up. */
  .card:hover .card__media img,
  .region:hover .region__media img,
  .ground__row:hover .ground__shot img,
  .window:hover .window__shot img,
  .opener:hover img {
    transform: none;
  }

  .link::after {
    transition: none;
  }
}

/* ---------------------------------------------------------------- knowledge
 *
 * Added 2026-09-03 with /knowledge/. No new colour, no new radius, no new type
 * step and no new easing: the notes are set in the measures the rest of the
 * site already uses, and the card is the range card with the picture taken
 * out. The accent is deliberately not spent here — it is rationed to the six
 * roles it was introduced for, and a fifth surface would end it as an accent.
 */
.know-head .crumbs { margin-block-end: 20px; }
.know-body { max-inline-size: 34em; margin-block-start: 30px; }
.know-sec { margin-block-start: 34px; }
.know-sec .h3 { margin-block-end: 12px; }
.know-body .body { margin-block-end: 14px; }
.know-meta { color: var(--stone); font-size: var(--t-micro); margin-block-start: 18px; }

.know-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.know-list--s { margin-block-start: 18px; }
.know-card {
  display: grid;
  gap: 8px;
  padding: 26px;
  block-size: 100%;
  background: var(--tint);
  border: 1px solid var(--rule-soft);
  border-radius: 2px;
  color: inherit;
  text-decoration: none;
  text-align: start;
}
.know-card:hover { border-color: var(--rule); }
.know-card__h { font-family: var(--fd); font-size: var(--t-h3); line-height: 1.22; font-weight: 400; }
.know-card__b { color: var(--ink-70); }
.know-card__go { color: var(--stone); font-size: var(--t-micro); letter-spacing: .08em; text-transform: uppercase; }
.know-card--s { padding: 20px 22px; }
.know-card--s .know-card__h { font-size: var(--t-lede); }

.know-facts { display: grid; gap: 26px; margin-block-start: 18px; }
.know-facts__go { margin-block-start: 10px; }
.know-sources { list-style: none; margin: 20px 0 0; padding: 0; display: grid; gap: 15px; max-inline-size: 46em; }
.know-source { display: grid; gap: 4px; }
.know-source__m { color: var(--stone); font-size: var(--t-micro); }
.know-back { margin-block-start: 30px; }
.know-more .product-actions { margin-block: 20px 34px; }

.doc-download { margin-block-start: 26px; }
.foot__links { margin-block-start: 10px; }

@media (min-width: 760px) {
  .know-list { grid-template-columns: repeat(2, 1fr); }
  .know-facts { grid-template-columns: repeat(2, 1fr); }
}
