/* ==========================================================================
   Nutcher Milk Company — reconstructed stylesheet
   Palette and type scale taken from the original site.
   ========================================================================== */

/* ---------- Fonts (self-hosted, subset as served by the original) --------- */
@font-face {
  font-family: 'Instrument Serif';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url('../fonts/instrument-serif-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url('../fonts/instrument-serif-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+1E00-1E9F, U+2020,
    U+20A0-20AB, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: italic; font-weight: 400; font-display: swap;
  src: url('../fonts/instrument-serif-italic-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Golden Custard';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url('../fonts/golden-custard.woff2') format('woff2');
}
@font-face {
  font-family: 'Colendra';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url('../fonts/colendra.woff2') format('woff2'),
       url('../fonts/colendra.woff') format('woff');
  /* Cap height 0.604em against Instrument Serif's 0.72em, normalised here so
     the existing sizes and line-heights carry over untouched. */
  size-adjust: 119%;
  ascent-override: 105%;
  descent-override: 30%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Colendra';
  font-style: normal; font-weight: 700; font-display: swap;
  src: url('../fonts/colendra-bold.woff2') format('woff2');
  /* Bold is drawn on a taller body (0.74em), so it needs its own adjustment
     to sit at the same optical size as the regular weight. */
  size-adjust: 97%;
  ascent-override: 105%;
  descent-override: 30%;
  line-gap-override: 0%;
}
/* Colendra Bold has no - & / ( ) @ $ % glyphs. Listing this alias (the regular
   file, accepted at any weight) next in the stack lets those characters fall
   through instead of dropping out of words like "FARM-FRESH" or "Half / Half". */
@font-face {
  font-family: 'Colendra Fill';
  font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('../fonts/colendra.woff2') format('woff2');
  size-adjust: 119%;
  ascent-override: 105%;
  descent-override: 30%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Asap';
  font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('../fonts/asap-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Asap';
  font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('../fonts/asap-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+1E00-1E9F, U+2020,
    U+20A0-20AB, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Asap';
  font-style: italic; font-weight: 100 900; font-display: swap;
  src: url('../fonts/asap-italic-latin.woff2') format('woff2');
}

/* ---------------------------------- Tokens -------------------------------- */
:root {
  --green:  #294422;
  --ink:    #1d1d1b;
  --cream:  #f5f3ef;
  --red:    #d20f18;
  --red-dk: #b00c14;
  --white:  #ffffff;
  --rule:   rgba(29, 29, 27, .15);

  --font-head: 'Colendra', 'Colendra Fill', 'Instrument Serif', Georgia, serif;
  --font-display: 'Colendra', 'Colendra Fill', 'Instrument Serif', Georgia, serif;
  --font-body: 'Colendra', 'Colendra Fill', 'Asap', system-ui, sans-serif;

  --sky: #e9f3fc;
  --radius: 18px;
  --gutter: clamp(20px, 4.4vw, 64px);
  --maxw: 1440px;

  --h1: clamp(2.475rem, 5.005vw, 4.51rem);
  --h2: clamp(2.09rem, 3.3vw, 2.97rem);
  --h3: clamp(1.65rem, 2.75vw, 2.486rem);
  --h4: 1.54rem;
  --lead: clamp(1.238rem, 1.98vw, 1.777rem);
}

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

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

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 1.1rem;
  line-height: 1.5;
  color: var(--ink);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }

/* ------------------------------- Scrollbars ------------------------------- */
/* Brand furniture in place of the OS default: a cream gutter and a brand-red
   thumb. Set on the root and left otherwise unscoped, so the locator list and
   the nutrition dialog match the page.

   This was green for a while, on the grounds that a short scroller renders its
   thumb at close to full height and a red column that size inside the locator
   shouted over the map. It reads differently now the locator sits on a red band
   in a black frame with red buttons down the list: the thumb is in company
   there rather than the only red thing on a white panel.

   The two mechanisms are mutually exclusive rather than complementary — an
   engine that understands scrollbar-width ignores the WebKit pseudo-elements
   for that scroller — so the fallback is fenced behind @supports instead of
   being written alongside and silently dropped. */
html { scrollbar-width: thin; scrollbar-color: var(--red) var(--cream); }

@supports not (scrollbar-width: thin) {
  ::-webkit-scrollbar { width: 13px; height: 13px; }
  ::-webkit-scrollbar-track { background: var(--cream); }
  ::-webkit-scrollbar-thumb {
    background: var(--red);
    /* A track-coloured border, not a margin: it is the only way to inset a
       WebKit thumb, and it leaves the pill floating clear of the gutter. */
    border: 3px solid var(--cream);
    border-radius: 999px;
  }
  ::-webkit-scrollbar-thumb:hover { background: var(--red-dk); }
  ::-webkit-scrollbar-corner { background: var(--cream); }
}

a { color: inherit; }

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-head);
  font-weight: 400;
  letter-spacing: -.02em;
  line-height: 1.05;
  margin: 0;
  color: var(--ink);
}
h1 { font-size: var(--h1); }
h2 { font-size: var(--h2); line-height: 1.11; }
h3 { font-size: var(--h3); line-height: 1.13; }
h4 { font-size: var(--h4); line-height: 1.17; }

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

.wrap {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: clamp(56px, 7vw, 104px); }
.section--cream { background: var(--cream); }
.section--green { background: var(--green); color: var(--white); }
.section--green h1, .section--green h2, .section--green h3 { color: var(--white); }
/* The store locator band. Full-strength brand red, the same ground the footer
   sits on, so the widget's own white panel reads as a card laid on it. */
.section--red { background: var(--red); color: var(--white); }
.section--red h1, .section--red h2, .section--red h3 { color: var(--white); }

.eyebrow {
  font-family: var(--font-head);
  font-size: var(--h2);
  text-transform: uppercase;
  letter-spacing: .01em;
  text-align: center;
}

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

/* Off-screen until tabbed to, then it lands over the header. */
.skip-link {
  position: absolute; top: 0; left: 50%;
  transform: translate(-50%, -110%);
  z-index: 100;
  padding: 12px 22px;
  background: var(--green); color: var(--white);
  font-size: 0.99rem; text-decoration: none;
  border-radius: 0 0 4px 4px;
  transition: transform .18s ease;
}
.skip-link:focus { transform: translate(-50%, 0); }

/* ------------------------------ Focus states ------------------------------ */
/* Keyboard-only, so pointer users see no change. The ring flips to white on the
   dark green and photographic panels where the green ring would disappear. */
:focus-visible {
  outline: 3px solid var(--green);
  outline-offset: 3px;
}
.btn:focus-visible { outline-color: var(--ink); }
.band :focus-visible,
.section--green :focus-visible,
.section--red :focus-visible { outline-color: var(--white); }
.product:focus-visible { outline-offset: 6px; }

/* ------------------------------- Buttons ---------------------------------- */
.btn {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 1.188rem;
  font-weight: 700;
  letter-spacing: .06em;
  line-height: 1;
  text-decoration: none;
  padding: 18px 31px;
  border-radius: 300px;
  border: 1px solid transparent;
  background: var(--red);
  color: var(--white);
  cursor: pointer;
  transition: background-color .2s ease, color .2s ease, border-color .2s ease,
              transform .2s ease, box-shadow .2s ease;
}
/* The lift is carried on transform and shadow only, so the pill never changes
   size and nothing around it reflows on hover. */
.btn:hover {
  background: var(--red-dk);
  transform: translateY(-2px);
  box-shadow: 0 10px 22px rgba(210, 15, 24, .24);
}
.btn:active { transform: translateY(0); box-shadow: none; }

.btn--green { background: var(--green); }
.btn--green:hover { background: #1d3118; box-shadow: 0 10px 22px rgba(41, 68, 34, .26); }

.btn--outline {
  background: transparent;
  border-color: rgba(255,255,255,.75);
  color: var(--white);
}
.btn--outline:hover {
  background: var(--white); color: var(--green);
  box-shadow: 0 10px 22px rgba(29, 29, 27, .20);
}

/* -------------------------------- Header ---------------------------------- */
.site-header {
  position: sticky; top: 0; z-index: 60;
  background: var(--white);
}
.site-header__inner {
  max-width: var(--maxw);
  margin-inline: auto;
  padding: 29px var(--gutter);
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px;
}
.site-logo { display: block; flex: 0 0 auto; }
.site-logo img { width: clamp(150px, 15.5vw, 221px); }

.site-nav { display: flex; align-items: center; gap: clamp(18px, 2vw, 30px); }
.site-nav__list {
  display: flex; align-items: center; gap: clamp(18px, 2vw, 30px);
  list-style: none; margin: 0; padding: 0;
}
.site-nav__list a {
  position: relative;
  font-size: 1.32rem;
  text-decoration: none;
  padding-bottom: 3px;
}
/* The rule is drawn as a pseudo-element rather than a border so it can grow
   out from the centre; a border can only appear all at once. */
.site-nav__list a::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px; background: currentColor;
  transform: scaleX(0);
  transition: transform .28s cubic-bezier(.32, .72, .28, 1);
}
.site-nav__list a:hover::after,
.site-nav__list a:focus-visible::after,
.site-nav__list a[aria-current='page']::after { transform: scaleX(1); }

.site-social { display: flex; align-items: center; gap: 14px; }
.site-social a { display: inline-flex; }
.site-social svg { width: 17px; height: 17px; fill: currentColor; }

.nav-toggle {
  display: none;
  width: 44px; height: 44px;
  border: 0; background: none; padding: 0; cursor: pointer;
  color: var(--ink);
}
.nav-toggle span {
  display: block; height: 2px; width: 24px; margin: 5px auto;
  background: currentColor; transition: transform .25s ease, opacity .25s ease;
}
.nav-toggle[aria-expanded='true'] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded='true'] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded='true'] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* A soft edge hanging under the sticky bar, carrying the header's white down
   into whatever is below it. Decorative, so it never takes pointer events.

   Two stacked contours slide across each other in opposite directions, which
   is what makes it read as liquid rather than as one shape being dragged: the
   crests meet and part instead of marching. Each layer is drawn two tiles wide
   over a 2880-unit box and its wave repeats every tile, so translating by half
   its own width lands on an identical frame and the loop has no seam. The
   swells have different periods (2 and 3 to a tile) so the two never fall back
   into step. Motion is transform-only, which keeps it off the main thread and
   lets the reduced-motion rule at the foot of this file stop it outright. */
.header-wave {
  position: absolute; top: 100%; left: 0; right: 0;
  height: clamp(14px, 1.6vw, 26px);
  overflow: hidden;
  line-height: 0; pointer-events: none; z-index: 0;
}
.header-wave__layer {
  position: absolute; top: 0; left: 0;
  display: block;
  width: 200%; height: 100%;
  fill: var(--white);
  will-change: transform;
}
.header-wave__layer--back {
  opacity: .55;
  animation: wave-drift 21s linear infinite;
}
.header-wave__layer--front {
  animation: wave-drift 13s linear infinite reverse;
}
@keyframes wave-drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
/* Nothing on screen to swim for while the tab is in the background. */
html.is-tab-hidden .header-wave__layer { animation-play-state: paused; }

/* --------------------------------- Hero ----------------------------------- */
.hero {
  position: relative;
  min-height: min(888px, 78vh);
  display: flex; align-items: center;
  background: url('../img/hero.webp') center / cover no-repeat;
}
/* The copy sits over the left of the composite, where the tree, the fields and
   the fence all run behind it and the ink loses the contrast it needs. A veil
   weighted to that side lifts the type off the photograph and has faded out
   before it reaches the bottles, so the product art stays untouched. */
.hero::before {
  content: '';
  position: absolute; inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(97deg,
    rgba(255, 255, 255, .90) 0%,
    rgba(255, 255, 255, .84) 26%,
    rgba(255, 255, 255, .58) 42%,
    rgba(255, 255, 255, .20) 55%,
    rgba(255, 255, 255, 0)   66%);
}
/* The copy block is a fixed 620px, so the narrower the window the further
   across it reaches and the later the veil has to give out. Below this the
   lead was running its last line onto the jug and the badge on its shoulder. */
@media (max-width: 1200px) {
  .hero::before {
    background: linear-gradient(97deg,
      rgba(255, 255, 255, .90) 0%,
      rgba(255, 255, 255, .87) 46%,
      rgba(255, 255, 255, .70) 62%,
      rgba(255, 255, 255, .32) 76%,
      rgba(255, 255, 255, 0)   90%);
  }
}
.hero__inner {
  position: relative;
  z-index: 1;
  max-width: var(--maxw);
  margin-inline: auto;
  padding: clamp(64px, 8vw, 96px) var(--gutter);
  width: 100%;
}
.hero__content {
  max-width: 620px;
  text-align: center;
}
.hero__content h1 { font-size: clamp(3rem, 6.4vw, 5.8rem); }
.hero__content h1 + h1 { margin-top: .06em; }
.hero__lead {
  font-size: var(--lead);
  line-height: 1.5;
  margin: clamp(20px, 2.6vw, 34px) 0 clamp(24px, 3vw, 38px);
}

/* ------------------------------- Products --------------------------------- */
.product-grid {
  display: grid;
  gap: clamp(28px, 3.4vw, 52px) clamp(16px, 2.4vw, 36px);
  margin-top: clamp(40px, 5vw, 72px);
}
.product-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.product-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.product-grid--5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }

.product { text-align: center; text-decoration: none; display: block; cursor: pointer; }
.product__media {
  display: flex; align-items: flex-end; justify-content: center;
  height: clamp(180px, 18.4vw, 265px);
}
.product__media img {
  max-height: 100%; width: auto; object-fit: contain;
}

/* One bounce per hover. The lift this replaces was a state — it held for as
   long as the pointer was there; this is an event, so the bottle hops, settles
   and is still again while you keep hovering. Leaving and coming back plays it
   afresh, which is all a CSS hover animation needs to repeat: the declaration
   is removed on mouse-out and applied again on the next mouse-in.

   Each keyframe carries its own timing function, because a bounce is not one
   easing curve — it decelerates on the way up and accelerates on the way down,
   and swapping between the two at each apex and each landing is what makes it
   read as gravity rather than as a wobble. The two rebounds after the first
   landing decay to almost nothing, which is where the weight comes from.

   The bottles are transparent cut-outs, so the shadow that grounds the hop is a
   drop-shadow following the artwork rather than a box-shadow, which would only
   ever draw a rectangle around it. It is inside the keyframes rather than on
   the hover state so it belongs to the bounce and is gone at rest — a shadow
   sitting under a bottle that is not in the air just reads as a smudge. Both
   endpoints declare a fully transparent drop-shadow rather than `none`, so
   there is a real filter list to interpolate from at each end. */
@keyframes bottle-bounce {
  0% {
    transform: translateY(0);
    filter: drop-shadow(0 4px 3px rgba(29, 29, 27, 0));
    animation-timing-function: cubic-bezier(.3, 0, .2, 1);
  }
  25% {
    transform: translateY(-20px);
    filter: drop-shadow(0 22px 12px rgba(29, 29, 27, .16));
    animation-timing-function: cubic-bezier(.7, 0, .8, 1);
  }
  48% {
    transform: translateY(0);
    filter: drop-shadow(0 8px 6px rgba(29, 29, 27, .20));
    animation-timing-function: cubic-bezier(.3, 0, .2, 1);
  }
  66% {
    transform: translateY(-7px);
    filter: drop-shadow(0 14px 9px rgba(29, 29, 27, .13));
    animation-timing-function: cubic-bezier(.7, 0, .8, 1);
  }
  84% {
    transform: translateY(0);
    filter: drop-shadow(0 8px 6px rgba(29, 29, 27, .16));
    animation-timing-function: cubic-bezier(.3, 0, .2, 1);
  }
  92% {
    transform: translateY(-2px);
    filter: drop-shadow(0 10px 7px rgba(29, 29, 27, .09));
    animation-timing-function: cubic-bezier(.7, 0, .8, 1);
  }
  100% {
    transform: translateY(0);
    filter: drop-shadow(0 4px 3px rgba(29, 29, 27, 0));
  }
}
.product:hover .product__media img,
.product:focus-visible .product__media img {
  animation: bottle-bounce .78s both;
}
.product__name { margin-top: 18px; transition: color .25s ease; }
.product:hover .product__name { color: var(--red); }
.product__desc {
  font-size: 1.02rem; line-height: 1.5;
  max-width: 250px; margin-inline: auto; margin-top: 6px;
}
.product__pdf {
  display: inline-block; margin-top: 8px;
  font-size: 0.77rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--green); text-decoration: none; border-bottom: 1px solid currentColor;
  opacity: 0; transition: opacity .2s ease;
}
.product:hover .product__pdf, .product:focus-visible .product__pdf { opacity: 1; }

.rule { border: 0; border-top: 1px solid var(--rule); margin: clamp(48px, 6vw, 88px) 0 0; }

/* Rounded corners on the photographs. Product cut-outs, logos, the truck and the
   waving cow are transparent PNG/WebP, so a radius on those would clip nothing
   and is skipped — and a box-shadow on one would draw a rectangle out in the
   empty space around the artwork, which is why the cow is excluded by name. */
.story__media img,
.why__media img:not(.why__cow),
.band--photo img,
.review { border-radius: var(--radius); }

/* A shadow soft enough to read as the photograph sitting on the page rather
   than as a card. Kept off .review, which already carries its own border. */
.story__media img,
.why__media img:not(.why__cow),
.band--photo img { box-shadow: 0 18px 44px rgba(29, 29, 27, .11); }

/* A brand-red frame on the home page's two photographs. .story__media and
   .why__media only exist on index.html, so this needs no page class to stay
   there. box-sizing is border-box site-wide, so the frame is drawn inside the
   100% width and nothing reflows around it.

   Deliberately not applied to the rest of the page's images. The products, the
   retailer wordmarks, the cow and the footer marks are all transparent
   cut-outs, and a border on one of those draws a rectangle out in the empty
   space around the artwork rather than a frame around anything — the same
   reason the shadow above skips them. */
.story__media img,
.why__media img:not(.why__cow) { border: 5px solid var(--red); }

/* -------------------------------- Story ----------------------------------- */
.story {
  display: grid;
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 1fr);
  gap: clamp(32px, 5vw, 88px);
  align-items: center;
}
.story__body { font-size: 1.16rem; line-height: 1.62; }
.story h2 { margin-bottom: .6em; }
.story__media img { width: 100%; }

/* ------------------------------ Why glass --------------------------------- */
.why {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* Wider than the other paired blocks on purpose: the cow overhangs the
     photograph's right edge and this gutter is the room she does it in. At the
     old 80px she could only be two-thirds height before her hoof reached the
     copy. Set as a share of the viewport, not a fixed figure, so her clearance
     of the copy holds at every desktop width instead of only at one. It also
     lands the photograph at about 44% of the block, which is where the
     reference has it. */
  gap: clamp(32px, 11vw, 160px);
  align-items: center;
}
/* The cow straddles the photograph's right edge — most of her body over the
   picture, the raised hoof and tail out in the gutter, her feet just below the
   bottom corner. She is anchored to the media box rather than to the grid, so
   she holds that relationship at every desktop width instead of drifting as the
   columns resize. The gutter has room for the overhang: at 1fr 1fr she clears
   the text column by roughly the same margin as the reference. */
.why__media { position: relative; }
.why__cow {
  position: absolute;
  z-index: 2;
  /* Sized by height, not width: the relationship that carries the composition
     is that she stands taller than the picture, ears just over the top edge and
     feet just under the bottom one. Width follows from the artwork — which is
     wider in proportion than the cow in the reference, so she is held clear of
     the bottle's label rather than pushed as far left as that comp has her. */
  height: 108%;
  width: auto;
  left: 63%;
  bottom: -7%;
  max-width: none;
  pointer-events: none;
}
.why__list { list-style: none; margin: clamp(28px, 3.4vw, 46px) 0 0; padding: 0; }
.why__list li {
  display: grid; grid-template-columns: 52px 1fr; gap: 18px;
  align-items: start;
  margin-bottom: 30px;
}
.why__list li:last-child { margin-bottom: 0; }
.why__list img { width: 52px; }
/* This list was the smallest body copy on the site — a third under the 1.1rem
   base — and beside a full-height photograph it read as a caption rather than
   as the argument for the bottle. Heading, note and cow all come up together
   so the block keeps its proportions. */
.why__list h4 { font-size: 1.72rem; }
.why__list p { font-size: 1.14rem; line-height: 1.55; margin: 6px 0 0; }

/* ------------------------------ Retailers --------------------------------- */
/* Six wordmarks whose ratios run from 2.5:1 to 7:1. Capping only the height
   made the long ones five times wider than the square ones, so each carries a
   width computed from a blend of equal-height and equal-area — that keeps the
   optical weight even. Uniform cells hold them on a shared baseline. */
.retailers {
  list-style: none;
  margin: clamp(40px, 5vw, 72px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 2.6vw, 40px);
}
.retailers li {
  display: grid;
  place-items: center;
  min-height: 104px;
  padding: 10px;
}
.retailers img {
  width: var(--logo-w, 160px);
  max-width: 100%;
  height: auto;
}

/* Columns step down before a cell can get narrower than the widest wordmark
   (241px) — otherwise max-width clamps the long marks and the careful sizing
   collapses back to "everything the same width". */
@media (max-width: 900px) { .retailers { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .retailers { grid-template-columns: 1fr; } }

/* ------------------------------- Locator ---------------------------------- */
/* Replaces the Storepoint embed: data/stores.json, Leaflet, and arithmetic. The
   frame, the white panel and the red buttons are kept from the embed's styling
   so the swap is invisible against the rest of the page. */
.locator-wrap { padding-bottom: clamp(56px, 7vw, 104px); }

.locator {
  border: 4px solid var(--ink);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--white);
  color: var(--ink);
  text-align: left;
}

.locator__bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px;
  padding: 16px 18px;
  border-bottom: 2px solid var(--rule);
}
.locator__search { display: flex; flex: 1 1 320px; gap: 10px; margin: 0; }
.locator__search input {
  flex: 1 1 auto; min-width: 0;
  font: inherit; font-size: 1.06rem;
  padding: 11px 14px;
  border: 2px solid var(--rule); border-radius: 10px;
  background: var(--white); color: var(--ink);
}
.locator__search input:focus-visible { border-color: var(--green); }

.locator__geo {
  flex: 0 0 auto;
  font: inherit; font-size: .95rem; font-weight: 700;
  letter-spacing: .04em;
  padding: 11px 16px;
  border: 2px solid var(--green); border-radius: 999px;
  background: var(--white); color: var(--green);
  cursor: pointer;
  transition: background-color .2s ease, color .2s ease;
}
.locator__geo:hover { background: var(--green); color: var(--white); }
.locator__geo.is-busy, .locator__geo:disabled { opacity: .55; cursor: progress; }

.locator__tools { display: flex; align-items: center; gap: 14px; margin-left: auto; }
.locator__tools select {
  font: inherit; font-size: 1rem;
  padding: 10px 12px;
  border: 2px solid var(--rule); border-radius: 10px;
  background: var(--white); color: var(--ink);
}
.locator__count { margin: 0; font-size: .95rem; color: rgba(29, 29, 27, .66); white-space: nowrap; }

/* The list scrolls inside a fixed frame beside the map, the way the embed did;
   the height is the map's height so the two columns stay square with each. */
.locator__body {
  display: grid;
  grid-template-columns: minmax(0, 360px) minmax(0, 1fr);
  height: clamp(460px, 62vh, 640px);
}
.locator__list { overflow-y: auto; border-right: 2px solid var(--rule); }
.locator__map { height: 100%; background: var(--cream); }

.store {
  padding: 18px 20px;
  border-bottom: 1px solid var(--rule);
  cursor: pointer;
  transition: background-color .18s ease;
}
.store:hover { background: var(--cream); }
.store.is-active { background: #fdf1f1; box-shadow: inset 4px 0 0 var(--red); }
/* The locator sits inside .section--red on the stores page, whose heading rule
   paints every h1/h2/h3 white. This card is on white, so it has to win that —
   two classes beats one class and a tag. */
.locator .store__name { font-size: 1.24rem; margin: 0 0 4px; color: var(--ink); }
.store__addr { margin: 0; font-size: 1rem; line-height: 1.45; color: rgba(29, 29, 27, .78); }
.store__meta { margin: 6px 0 0; font-size: 1rem; }
.store__go { margin-top: 12px; }
.store__dist {
  margin: 10px 0 0;
  font-size: .9rem; font-weight: 700; letter-spacing: .06em;
  color: var(--green);
}
.store-empty { padding: 26px 20px; margin: 0; font-size: 1.05rem; line-height: 1.5; }

/* A flat brand-red dot rather than Leaflet's default teardrop PNG, so the pins
   match the buttons and cost no image request. */
.store-pin span {
  display: block; width: 100%; height: 100%;
  border-radius: 50%;
  background: var(--red);
  border: 2.5px solid var(--white);
  box-shadow: 0 2px 6px rgba(29, 29, 27, .4);
}
.leaflet-container { font: inherit; }
.leaflet-popup-content { font-size: 1rem; line-height: 1.45; }
.leaflet-popup-content a { color: var(--red); }

@media (max-width: 860px) {
  /* One column: the map first at a workable height, the list under it. */
  .locator__body { grid-template-columns: 1fr; height: auto; }
  .locator__map { height: 340px; order: -1; }
  .locator__list { max-height: 420px; border-right: 0; border-top: 2px solid var(--rule); }
  .locator__tools { margin-left: 0; width: 100%; }
  /* Side by side, the field is squeezed to about half a placeholder. */
  .locator__search { flex-wrap: wrap; }
  .locator__search input { flex: 1 1 100%; }
  .locator__geo { flex: 1 1 100%; }
}

/* ------------------------------- Textures --------------------------------- */
/* One full-frame section ground. Paper grain was tried on the cream sections and
   removed: a noise overlay cannot be carried across the shape dividers, whose
   waves are SVG fills, so the same cream rendered as two tones with a seam at
   every divider edge. */
/* The stripe ground survives that problem by being painted once for a whole
   run - the striped section together with the dividers that close it - instead
   of once per section. A repeating gradient restarts at each box's own origin,
   so a divider painting its own copy would land the pattern out of phase and
   leave a bald band across the wave; a single shared box carries every stripe
   through the divider unbroken. */
.stripes-run {
  position: relative;
  isolation: isolate;
  background: var(--white);
}
.stripes-run::before {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    102deg,
    rgba(41, 68, 34, .035) 0 2px,
    transparent 2px 15px
  );
  /* The run opens straight against a plain white section with no wave between
     them, so the pattern is faded up over the section's own top padding rather
     than started on a hard horizontal edge. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 clamp(56px, 7vw, 104px));
          mask-image: linear-gradient(to bottom, transparent 0, #000 clamp(56px, 7vw, 104px));
}

/* A divider inside the run paints no ground of its own: the run's pattern shows
   through above the contour, and the wave carries the next section's colour
   below it. This works for a divider closing a run. One opening a run would
   need the inverse contour - the previous colour laid over the pattern - since
   an SVG fill cannot pick up the run's gradient. */
.stripes-run > .divider { background: none; }

/* ------------------------------- Entrances -------------------------------- */
/* Everything here is gated behind .js, which the inline script in each page's
   head sets before the first paint. Nothing is hidden until that class exists,
   so a failed or blocked script leaves the page fully rendered rather than
   blank — and the same is true of print, where .is-in is forced on below.

   main.js releases the opening screenful on load and hands the rest to an
   IntersectionObserver, so the page keeps arriving as it is read instead of
   all at once. The order within a group is set by --rise-delay below rather
   than in the script, so retiming a cascade is a stylesheet change. */
.js .rise {
  opacity: 0;
  transform: translate3d(0, var(--rise-from, 18px), 0);
  transition: opacity .68s cubic-bezier(.22, .68, .3, 1),
              transform .68s cubic-bezier(.22, .68, .3, 1);
  transition-delay: var(--rise-delay, 0ms);
}
.js .rise.is-in {
  opacity: 1;
  transform: none;
}

/* The masthead drops in rather than rising, so it reads as the frame arriving
   around the page instead of another item in the same queue. */
.js .site-header__inner { --rise-from: -14px; }

/* A hand-set cascade down the hero: the two title lines, the lead, the button.
   Written on the children rather than on the block so the eye is led through
   the sentence in reading order. */
.js .hero__content > *:nth-child(1) { --rise-delay: 90ms; }
.js .hero__content > *:nth-child(2) { --rise-delay: 210ms; }
.js .hero__content > *:nth-child(3) { --rise-delay: 340ms; }
.js .hero__content > *:nth-child(4) { --rise-delay: 470ms; --rise-from: 22px; }

/* Grid children stagger across the row. Capped at the widest grid on the site
   so a long row never trails far behind its own heading. */
.js .product-grid > .product:nth-child(1),
.js .retailers > li:nth-child(1),
.js .ft-cols > .ft-col:nth-child(1) { --rise-delay: 0ms; }
.js .product-grid > .product:nth-child(2),
.js .retailers > li:nth-child(2),
.js .ft-cols > .ft-col:nth-child(2) { --rise-delay: 80ms; }
.js .product-grid > .product:nth-child(3),
.js .retailers > li:nth-child(3),
.js .ft-cols > .ft-col:nth-child(3) { --rise-delay: 160ms; }
.js .product-grid > .product:nth-child(4),
.js .retailers > li:nth-child(4) { --rise-delay: 240ms; }
.js .product-grid > .product:nth-child(5),
.js .retailers > li:nth-child(5) { --rise-delay: 320ms; }

/* Paired media/text blocks come in from their own side of the gutter. */
.js .story__media, .js .why__media { --rise-from: 26px; }

/* ---------------------------- Section dividers ---------------------------- */
/* Soft-wave transitions, one shape per colour change, every one of them
   liquid: two contours per divider sliding across each other in opposite
   directions, the same construction as the header wave and the clouds. Each
   layer is drawn two tiles wide over a 2880-unit box from a sum of sines whose
   periods all divide the 1440-unit tile, so the contour repeats once per tile
   and translating a layer by half its own width lands on an identical frame —
   the loop has no seam by construction rather than by a guessed distance.

   Going liquid cost the bespoke silhouettes their exact contours: a shape that
   has to repeat every tile cannot also be a one-off drawing. Each divider keeps
   its own signature instead — its own periods, amplitudes and phase — so the
   set still reads as eight different waves rather than one wave reused.

   Heights step at breakpoints instead of scaling fluidly so the edge always
   lands on a whole pixel; with the 1px overlaps that keeps a hairline from
   opening between a divider and its neighbours at any width or zoom. The height
   now sits on the container, because the layers are taken out of flow. */
.divider {
  position: relative;
  height: 46px;
  overflow: hidden;
  line-height: 0;
  margin-top: -1px;
  margin-bottom: -1px;
  pointer-events: none;
}
@media (min-width: 700px)  { .divider { height: 66px; } }
@media (min-width: 1100px) { .divider { height: 88px; } }

.divider__layer {
  position: absolute; top: 0; left: 0;
  display: block;
  width: 200%; height: 100%;
}
/* The trailing contour is half-strength, so where it runs above the leading one
   it reads as the colour welling up rather than as a second hard edge. */
.divider__layer--back  { opacity: .5; animation: wave-drift 27s linear infinite; }
.divider__layer--front { animation: wave-drift 18s linear infinite reverse; }

/* Nothing to swim for while the tab is in the background, or while the divider
   is nowhere near the viewport — main.js parks both. Left running rather than
   removed if the observer is unavailable. */
html.is-tab-hidden .divider__layer,
.divider.is-offscreen .divider__layer { animation-play-state: paused; }

/* Background is the section above; the wave carries the colour of the section
   below, and overruns the viewBox foot so the bottom row is always that colour.
   Every --to-* pairing here must exist, or the path falls back to currentColor
   and renders as black ink. */
.divider--from-white { background: var(--white); }
.divider--from-cream { background: var(--cream); }
.divider--to-white { color: var(--white); }
.divider--to-cream { color: var(--cream); }
.divider--to-sky   { color: var(--sky); }
.divider--to-green { color: var(--green); }
.divider--to-red   { color: var(--red); }
.divider--from-red { background: var(--red); }

/* Over photography the wave cannot butt against the image, so here the trailing
   layer is not an echo of the leading one but the same contour carried well
   above it and filled with a gradient in the divider's own colour — at full
   strength, since the gradient is already doing the fading. The two still shear
   past each other, which is what dissolves the picture into the shape. */
.divider--overlay {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 96px;
  margin: 0;
  z-index: 2;
}
@media (min-width: 700px)  { .divider--overlay { height: 132px; } }
@media (min-width: 1100px) { .divider--overlay { height: 168px; } }
.divider--overlay .divider__layer--back { opacity: 1; }

/* The mirror of it, at the head of a photographic band rather than the foot:
   the colour arrives from the section above and the picture emerges from under
   it. Its paths fill upward and its gradient runs opaque-to-clear downward, so
   the only thing to change here is which edge it is pinned to. It still takes a
   --to-* for the wave colour, not a --from-*, because the modifier that would
   read more naturally sets a background — and a background here would paint a
   band straight over the photograph. */
.divider--overlay-top { top: 0; bottom: auto; }

/* Let the following section cover the host's last pixel row, which the overlay
   has already painted in the destination colour. */
.hero, .band--photo { margin-bottom: -1px; }

/* --------------------------- Banded sections ------------------------------ */
.band {
  position: relative; overflow: hidden;
  background: var(--green); color: var(--white);
  padding-block: clamp(48px, 5.5vw, 80px);
}
.band__watermark {
  position: absolute; inset: 0; z-index: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-head); font-size: 13.2vw; letter-spacing: .06em;
  color: rgba(255,255,255,.05); white-space: nowrap;
  pointer-events: none; user-select: none;
}
.band__inner {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(24px, 4vw, 64px); align-items: center;
}
.band h2, .band h3 { color: var(--white); }
.band p { font-size: 0.99rem; margin-top: 12px; max-width: 46ch; }

.band--photo {
  background: url('../img/rolling-hills.webp') center / cover no-repeat;
  color: var(--white);
  padding-block: clamp(64px, 8vw, 120px);
}
/* Scrim so the copy stays legible over the bright sky in the photograph. */
.band--photo::before {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(100deg, rgba(29,29,27,.72) 0%, rgba(29,29,27,.45) 42%, rgba(29,29,27,0) 72%);
}
.band--photo .band__inner > * { position: relative; }
.band--photo .band__inner { grid-template-columns: minmax(0, 1fr) auto; }
.band--photo h2 { font-size: var(--h2); }
.band--photo p { font-size: 1.045rem; max-width: 44ch; }
.band--photo img { width: clamp(110px, 12vw, 168px); }

/* -------------------------------- Contact --------------------------------- */
.contact h1 { color: var(--green); }

/* The details column is three short lines against a six-field form, so it ran
   out well above the fold of the block. She fills that tail. Capped and pushed
   off the left edge so she reads as sitting in the column rather than as
   another item in the list. */
.contact__cow {
  display: block;
  width: min(78%, 300px);
  height: auto;
  margin: clamp(28px, 4vw, 52px) 0 0 clamp(0px, 4vw, 40px);
}

/* --------------------------- Locator intro -------------------------------- */
/* The heading and its one line of copy left the right half of the band empty. */
.locator-intro {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .82fr);
  gap: clamp(24px, 5vw, 64px);
  align-items: center;
}
.locator-intro__cow {
  display: block;
  width: 100%; height: auto;
  max-width: 460px;
  margin-inline: auto;
}

/* ---------------------------- Contact form -------------------------------- */
/* Off-screen rather than display:none — a hidden field is skipped by some bots,
   but one that is merely moved is not, which is the point of a honeypot. */
.hp {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
}

/* Filled in by main.js from the ?sent= flag send.php redirects back with. */
.form-status {
  margin: 4px 0 0;
  padding: 14px 18px;
  border-radius: 12px;
  font-size: 1.05rem;
  line-height: 1.5;
}
.form-status[data-state='ok']   { background: #eaf5e8; color: #1d3118; }
.form-status[data-state='fail'] { background: #fdeaea; color: #7a0c12; }

.contact { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: clamp(36px, 6vw, 96px); }
.contact__list { list-style: none; margin: clamp(28px, 3vw, 42px) 0 0; padding: 0; }
.contact__list li { display: grid; grid-template-columns: 44px 1fr; gap: 16px; align-items: center; margin-bottom: 20px; }
.contact__list img { width: 44px; }
.contact__list a { text-decoration: none; font-size: 1.045rem; }
.contact__list a:hover { text-decoration: underline; }

.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field--full { grid-column: 1 / -1; }
.field label { font-size: 0.858rem; letter-spacing: .02em; }
.field label span { color: rgba(29,29,27,.72); }
.field input, .field textarea {
  font: inherit; font-size: 0.99rem;
  padding: 11px 12px; border: 1px solid rgba(29,29,27,.35); border-radius: 3px;
  background: var(--white); color: var(--ink);
}
.field input:focus, .field textarea:focus { outline: 2px solid var(--green); outline-offset: 1px; }
.field textarea { min-height: 120px; resize: vertical; }
.check { grid-column: 1 / -1; display: flex; gap: 8px; align-items: center; font-size: 0.858rem; }

/* --------------------------- Nutrition facts popup ------------------------ */
/* Opened from any product card. Uses <dialog>, so Esc, focus trapping and the
   backdrop come from the platform; if showModal is missing the card falls back
   to opening its printable PDF. */
.nutrition {
  width: min(94vw, 580px);
  max-height: min(90vh, 900px);
  padding: 0;
  border: 0;
  background: var(--white);
  color: var(--ink);
  box-shadow: 0 30px 70px rgba(29, 29, 27, .34);
  overflow: hidden;
}
.nutrition[open] { display: flex; flex-direction: column; }
.nutrition::backdrop { background: rgba(29, 29, 27, .62); }

.nutrition__head {
  position: relative;
  flex: 0 0 auto;
  background: linear-gradient(180deg, var(--cream) 0%, #faf8f4 100%);
  padding: clamp(26px, 4vw, 36px) clamp(24px, 5vw, 40px) clamp(22px, 3vw, 30px);
  display: grid;
  grid-template-columns: clamp(96px, 22%, 140px) minmax(0, 1fr);
  gap: clamp(18px, 3vw, 28px);
  align-items: center;
}
.nutrition__shot {
  display: grid; place-items: center;
  aspect-ratio: 1;
  background: var(--white);
  border-radius: var(--radius);
  box-shadow: 0 8px 22px rgba(29, 29, 27, .09);
  padding: 10px;
}
.nutrition__shot img { width: 100%; height: 100%; object-fit: contain; }
.nutrition__intro { min-width: 0; }
.nutrition__eyebrow {
  margin: 0 0 4px;
  font-size: 0.748rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--green);
}
.nutrition__title { font-size: clamp(1.87rem, 4.4vw, 2.42rem); }
.nutrition__blurb { margin: 8px 0 0; font-size: 0.95rem; line-height: 1.5; }
.nutrition__flavor { margin: 8px 0 0; font-size: .82rem; color: rgba(29,29,27,.62); }

.nutrition__close {
  position: absolute; top: 14px; right: 14px; z-index: 2;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border: 0; border-radius: 300px;
  background: var(--white); color: var(--ink);
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(29, 29, 27, .14);
  transition: background-color .2s ease, color .2s ease;
}
.nutrition__close:hover { background: var(--red); color: var(--white); }
.nutrition__close svg { width: 16px; height: 16px; }

.nutrition__scroll {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: clamp(20px, 4vw, 30px) clamp(24px, 5vw, 40px) clamp(28px, 5vw, 40px);
}

/* The panel itself, set like a printed label. */
.label { border-top: 8px solid var(--ink); padding-top: 8px; }
.label p { margin: 0; }
.label__serving { display: flex; justify-content: space-between; gap: 12px; font-size: 0.902rem; }
.label__serving + .label__serving { margin-top: 2px; }
.label__serving span { font-weight: 700; }

.label__amount {
  margin-top: 10px; padding-top: 6px;
  border-top: 5px solid var(--ink);
  font-size: 0.792rem; font-weight: 700;
}
.label__cal {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding-bottom: 4px;
  border-bottom: 5px solid var(--ink);
}
.label__cal b { font-family: var(--font-head); font-size: 2.31rem; font-weight: 400; letter-spacing: -.02em; }
.label__cal span { font-size: 1.65rem; font-family: var(--font-head); }
.label__fromfat { font-size: 0.814rem; color: rgba(29,29,27,.7); }

.label__dv { padding: 4px 0; font-size: 0.792rem; font-weight: 700; text-align: right; }

.label__rows, .label__micros { margin: 0; padding: 0; list-style: none; }
.label__rows li, .label__micros li {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 5px 0;
  border-top: 1px solid rgba(29,29,27,.35);
  font-size: 0.902rem;
}
.label__rows li.is-indent { padding-left: 18px; }
.label__rows li b, .label__micros li b { font-weight: 700; }
.label__rows { border-bottom: 5px solid var(--ink); }
.label__micros { border-bottom: 8px solid var(--ink); }

.label__note { margin-top: 10px; font-size: 0.748rem; line-height: 1.5; color: rgba(29,29,27,.7); }

.label__ingredients { margin-top: 20px; }
.label__ingredients h3 {
  font-family: var(--font-body);
  font-size: 0.748rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  margin-bottom: 6px;
}
.label__ingredients p { font-size: 0.902rem; line-height: 1.6; }

.label__pdf {
  display: inline-block; margin-top: 20px;
  font-size: 0.792rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--green); text-decoration: none;
  border-bottom: 1px solid currentColor;
}
.label__pdf:hover { color: var(--red); }

/* Hold the page still behind the open dialog. */
html.has-dialog, html.has-dialog body { overflow: hidden; }

/* --------------------------------- Footer --------------------------------- */
/* Rebuilt from the earlier Nutcher build: bottle-cap scallops, a two-tone
   horizon with the delivery truck, then the sign-off and a brown bar. It
   carries that build's warmer palette, scoped here so none of it leaks into
   the rest of the site.

   The sign-off ground is the brand red itself — the same #d20f18 as the pill
   buttons — with the horizon's near hill carrying it up and a lighter tint of
   it behind, so the shape divider still matches what it divides.

   That ground is too dark for the dark brown ink this footer used, so the type
   on it is white: 5.5:1, which the brown could not reach at any weight. The
   knock-on is the cards. They were a translucent cream, which over red lifts to
   about #eb8f8a and drops white text to 2.4:1 — so on red they tint the other
   way, a light black wash that deepens the ground and holds white at roughly
   6.8:1.

   The bottom bar went with it. Left brown under a full-strength red it read as
   dirt rather than as a second tone, so it is now the red carried down dark
   (#9d0b12), which keeps its cream type at 7.9:1. Brown survives only as the
   ink on the deposit coin. */
.site-footer {
  --ft-cream:      #fff7e8;
  --ft-brown-ink:  #4a331e;
  --ft-red-dark:   #9d0b12;
  --ft-red:        #ef4c52;
  --ft-red-deep:   #d20f18;
  /* The social hover is pulled onto the same warm axis as the ground; the old
     cool pink read as a second, competing hue beside it. */
  --ft-pink-soft:  #fbdcd4;
  background: var(--white);
}

.footer-section { padding-block: clamp(44px, 5vw, 76px); }
.footer-reviews { background: var(--white); }
.footer-heading { font-size: var(--h2); text-align: center; margin-bottom: clamp(26px, 3vw, 42px); }

/* ------------------------------ Drifting clouds --------------------------- */
/* A soft sky behind the reviews with puffy clouds drifting across it, built
   from the same cloud silhouette the earlier Nutcher build used. Drawn as
   inline data URIs rather than an SVG file, and driven by CSS transforms rather
   than SMIL, so the pause control, prefers-reduced-motion and the offscreen
   pause all still apply. Each band is exactly one tile wider than its container
   and travels exactly one tile width, so the loop is seamless by construction. */
.footer-reviews {
  position: relative;
  isolation: isolate;
  background: linear-gradient(180deg, var(--sky) 0%, #f6fafe 52%, var(--white) 100%);
}
.clouds {
  position: absolute; inset: 0;
  overflow: hidden;
  pointer-events: none;
  user-select: none;
  z-index: 0;
}
.cloud-band {
  position: absolute;
  left: 0;
  height: 260px;
  background-repeat: repeat-x;
  will-change: transform;
}
.cloud-band--far {
  top: 4%;
  width: calc(100% + 900px);
  background-size: 900px 260px;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27900%27%20height%3D%27260%27%20viewBox%3D%270%200%20900%20260%27%3E%3Cpath%20d%3D%27M84.717%2C33.597c0.791-2.503%2C1.186-5.138%2C1.186-7.773C85.903%2C11.594%2C74.308%2C0%2C60.079%2C0c-9.881%2C0-18.445%2C5.534-22.793%2C13.702c-1.581-0.527-3.426-0.791-5.138-0.791c-9.486%2C0-17.128%2C7.642-17.128%2C17.128c0%2C1.186%2C0.132%2C2.372%2C0.395%2C3.426C6.719%2C34.783%2C0%2C42.424%2C0%2C51.515C0%2C61.66%2C8.169%2C69.829%2C18.314%2C69.829h63.373C91.831%2C69.829%2C100%2C61.66%2C100%2C51.515C99.868%2C42.556%2C93.281%2C35.046%2C84.717%2C33.597z%27%20fill%3D%27%23ffffff%27%20fill-opacity%3D%27.95%27%20transform%3D%27translate%2860%2C130%29%20scale%281.15%29%27%2F%3E%3Cpath%20d%3D%27M84.717%2C33.597c0.791-2.503%2C1.186-5.138%2C1.186-7.773C85.903%2C11.594%2C74.308%2C0%2C60.079%2C0c-9.881%2C0-18.445%2C5.534-22.793%2C13.702c-1.581-0.527-3.426-0.791-5.138-0.791c-9.486%2C0-17.128%2C7.642-17.128%2C17.128c0%2C1.186%2C0.132%2C2.372%2C0.395%2C3.426C6.719%2C34.783%2C0%2C42.424%2C0%2C51.515C0%2C61.66%2C8.169%2C69.829%2C18.314%2C69.829h63.373C91.831%2C69.829%2C100%2C61.66%2C100%2C51.515C99.868%2C42.556%2C93.281%2C35.046%2C84.717%2C33.597z%27%20fill%3D%27%23dcecfb%27%20fill-opacity%3D%27.9%27%20transform%3D%27translate%28250%2C46%29%20scale%280.78%29%27%2F%3E%3Cpath%20d%3D%27M84.717%2C33.597c0.791-2.503%2C1.186-5.138%2C1.186-7.773C85.903%2C11.594%2C74.308%2C0%2C60.079%2C0c-9.881%2C0-18.445%2C5.534-22.793%2C13.702c-1.581-0.527-3.426-0.791-5.138-0.791c-9.486%2C0-17.128%2C7.642-17.128%2C17.128c0%2C1.186%2C0.132%2C2.372%2C0.395%2C3.426C6.719%2C34.783%2C0%2C42.424%2C0%2C51.515C0%2C61.66%2C8.169%2C69.829%2C18.314%2C69.829h63.373C91.831%2C69.829%2C100%2C61.66%2C100%2C51.515C99.868%2C42.556%2C93.281%2C35.046%2C84.717%2C33.597z%27%20fill%3D%27%23ffffff%27%20fill-opacity%3D%27.9%27%20transform%3D%27translate%28430%2C150%29%20scale%281.4%29%27%2F%3E%3Cpath%20d%3D%27M84.717%2C33.597c0.791-2.503%2C1.186-5.138%2C1.186-7.773C85.903%2C11.594%2C74.308%2C0%2C60.079%2C0c-9.881%2C0-18.445%2C5.534-22.793%2C13.702c-1.581-0.527-3.426-0.791-5.138-0.791c-9.486%2C0-17.128%2C7.642-17.128%2C17.128c0%2C1.186%2C0.132%2C2.372%2C0.395%2C3.426C6.719%2C34.783%2C0%2C42.424%2C0%2C51.515C0%2C61.66%2C8.169%2C69.829%2C18.314%2C69.829h63.373C91.831%2C69.829%2C100%2C61.66%2C100%2C51.515C99.868%2C42.556%2C93.281%2C35.046%2C84.717%2C33.597z%27%20fill%3D%27%23e6f1fc%27%20fill-opacity%3D%27.95%27%20transform%3D%27translate%28640%2C66%29%20scale%280.95%29%27%2F%3E%3C%2Fsvg%3E");
  animation: cloud-drift-far 84s linear infinite;
}
.cloud-band--near {
  top: 34%;
  width: calc(100% + 700px);
  background-size: 700px 260px;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27700%27%20height%3D%27260%27%20viewBox%3D%270%200%20700%20260%27%3E%3Cpath%20d%3D%27M84.717%2C33.597c0.791-2.503%2C1.186-5.138%2C1.186-7.773C85.903%2C11.594%2C74.308%2C0%2C60.079%2C0c-9.881%2C0-18.445%2C5.534-22.793%2C13.702c-1.581-0.527-3.426-0.791-5.138-0.791c-9.486%2C0-17.128%2C7.642-17.128%2C17.128c0%2C1.186%2C0.132%2C2.372%2C0.395%2C3.426C6.719%2C34.783%2C0%2C42.424%2C0%2C51.515C0%2C61.66%2C8.169%2C69.829%2C18.314%2C69.829h63.373C91.831%2C69.829%2C100%2C61.66%2C100%2C51.515C99.868%2C42.556%2C93.281%2C35.046%2C84.717%2C33.597z%27%20fill%3D%27%23ffffff%27%20fill-opacity%3D%271%27%20transform%3D%27translate%2870%2C96%29%20scale%281.0%29%27%2F%3E%3Cpath%20d%3D%27M84.717%2C33.597c0.791-2.503%2C1.186-5.138%2C1.186-7.773C85.903%2C11.594%2C74.308%2C0%2C60.079%2C0c-9.881%2C0-18.445%2C5.534-22.793%2C13.702c-1.581-0.527-3.426-0.791-5.138-0.791c-9.486%2C0-17.128%2C7.642-17.128%2C17.128c0%2C1.186%2C0.132%2C2.372%2C0.395%2C3.426C6.719%2C34.783%2C0%2C42.424%2C0%2C51.515C0%2C61.66%2C8.169%2C69.829%2C18.314%2C69.829h63.373C91.831%2C69.829%2C100%2C61.66%2C100%2C51.515C99.868%2C42.556%2C93.281%2C35.046%2C84.717%2C33.597z%27%20fill%3D%27%23ffffff%27%20fill-opacity%3D%27.85%27%20transform%3D%27translate%28300%2C26%29%20scale%280.68%29%27%2F%3E%3Cpath%20d%3D%27M84.717%2C33.597c0.791-2.503%2C1.186-5.138%2C1.186-7.773C85.903%2C11.594%2C74.308%2C0%2C60.079%2C0c-9.881%2C0-18.445%2C5.534-22.793%2C13.702c-1.581-0.527-3.426-0.791-5.138-0.791c-9.486%2C0-17.128%2C7.642-17.128%2C17.128c0%2C1.186%2C0.132%2C2.372%2C0.395%2C3.426C6.719%2C34.783%2C0%2C42.424%2C0%2C51.515C0%2C61.66%2C8.169%2C69.829%2C18.314%2C69.829h63.373C91.831%2C69.829%2C100%2C61.66%2C100%2C51.515C99.868%2C42.556%2C93.281%2C35.046%2C84.717%2C33.597z%27%20fill%3D%27%23eaf3fd%27%20fill-opacity%3D%27.95%27%20transform%3D%27translate%28470%2C132%29%20scale%281.2%29%27%2F%3E%3C%2Fsvg%3E");
  animation: cloud-drift-near 58s linear infinite;
}
@keyframes cloud-drift-far  { from { transform: translate3d(0,0,0); }
                              to   { transform: translate3d(-900px,0,0); } }
@keyframes cloud-drift-near { from { transform: translate3d(0,0,0); }
                              to   { transform: translate3d(-700px,0,0); } }

/* Content sits above the weather. */
.footer-reviews > .wrap, .footer-reviews .review-marquee { position: relative; z-index: 1; }

/* Motion still stops while the section is offscreen or the tab is hidden. */
.footer-reviews.is-offscreen .cloud-band,
.footer-reviews.is-offscreen .review-track { animation-play-state: paused; }

/* Autoplay also pauses on hover and on keyboard focus. */
.footer-reviews:hover .cloud-band,
.footer-reviews:focus-within .cloud-band { animation-play-state: paused; }

/* ------------------------------ Review marquee ---------------------------- */
/* Full-bleed so the row reads as continuous, with the edges faded out rather
   than cut, and paused whenever a reader hovers or tabs into it. */
.review-marquee {
  --review-gap: clamp(16px, 2vw, 26px);
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.review-track {
  display: flex;
  gap: var(--review-gap);
  width: max-content;
  padding-inline: calc(var(--review-gap) / 2);
  animation: review-scroll 110s linear infinite;
}
.review-marquee:hover .review-track,
.review-marquee:focus-within .review-track { animation-play-state: paused; }

@keyframes review-scroll {
  from { transform: translateX(0); }
  /* The half-gap padding on each end makes the track exactly two sets wide,
     so 50% lands the duplicate precisely where the original started. */
  to   { transform: translateX(-50%); }
}

.review {
  margin: 0;
  flex: 0 0 auto;
  width: clamp(290px, 29vw, 380px);
  background: var(--cream);
  border: 1px solid rgba(29, 29, 27, .07);
  padding: 26px 24px;
  display: flex; flex-direction: column; gap: 14px;
}
.review img { width: 128px; height: auto; }
.review blockquote { margin: 0; font-size: 1.08rem; line-height: 1.62; flex: 1 1 auto; }
.review cite {
  font-style: normal; font-size: 0.86rem; letter-spacing: .06em;
  text-transform: uppercase; color: rgba(29, 29, 27, .6);
}

/* --- horizon --- */
/* The truck overhangs the hills by ~80px, so the horizon carries headroom
   above it — otherwise it clips the bottom of the review cards. */
.ft-horizon { position: relative; line-height: 0; margin-top: -1px; padding-top: clamp(52px, 6vw, 96px); }
/* Two hills on the same tiling construction as the dividers, at the slowest
   pair of speeds on the site — a horizon that hurried would look like scenery
   being wheeled past. The truck is a sibling of this box, not a child, so it
   holds its place and the near hill rolls underneath it. */
.ft-horizon__hills {
  position: relative;
  height: clamp(70px, 10vw, 150px);
  overflow: hidden;
}
.ft-horizon__layer {
  position: absolute; top: 0; left: 0;
  display: block;
  width: 200%; height: 100%;
}
.ft-horizon__layer--far  { animation: wave-drift 38s linear infinite; }
.ft-horizon__layer--near { animation: wave-drift 26s linear infinite reverse; }
html.is-tab-hidden .ft-horizon__layer,
.ft-horizon.is-offscreen .ft-horizon__layer { animation-play-state: paused; }
.ft-horizon .hill-1 { fill: var(--ft-red); }
.ft-horizon .hill-2 { fill: var(--ft-red-deep); }
.ft-truck {
  position: absolute; z-index: 3;
  bottom: 6px; left: 50%; translate: -50% 0;
  width: clamp(200px, 26vw, 340px); height: auto;
  animation: truckBob 3.4s ease-in-out infinite;
}
@keyframes truckBob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}

/* --- sign-off --- */
.ft-main {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ft-red-deep);
  color: var(--white);
  padding: clamp(40px, 5vw, 70px) 0 clamp(36px, 4vw, 56px);
  text-align: center;
  margin-top: -1px;
}
.ft-main > .wrap { position: relative; z-index: 1; }

/* --------------------------- Footer type wall ----------------------------- */
/* A tilted wall of the company name behind the sign-off, drawn as outline only
   so it reads as embossing in the red rather than as a second block of copy.
   Purely decorative: aria-hidden in the markup and pointer-events:none here, so
   it is neither announced nor selectable over the real text.

   Each row holds two identical sets and travels exactly -50%, which is one set
   to the pixel — the loop is seamless by construction rather than by a guessed
   distance, the same rule the header wave and the clouds follow. Rows alternate
   direction and carry four different durations so they never lock into step and
   form a readable vertical seam down the wall.

   Motion is transform-only. The stroke is held down at .105 alpha because the
   sign-off, the blurb and the sitemap all sit on top of it — at the busiest
   frame the white copy needs the flat red behind it, not a lattice of glyph
   edges crossing its own strokes. */
.typewall {
  position: absolute;
  /* Overspill enough that the tilt cannot expose a bare corner: a 7-degree
     rotation lifts each end by about half the box's other dimension times
     sin 7°. Fixed vertically rather than a percentage — on the tall stacked
     mobile footer a percentage inflated the box by hundreds of pixels and left
     the rows unable to fill it. */
  inset: -60px -12%;
  z-index: 0;
  pointer-events: none;
  user-select: none;
  display: flex;
  align-items: center;
  /* The wall is clipped by .ft-main, whose top edge is exactly where the horizon
     hills hand over. Cut flat there, the glyphs are sliced along a straight line
     the moment the wave above them ends. Fading in below that line instead lets
     the type surface out from under the hills rather than starting at them. The
     first 60px are the box's own overspill and never show, so the ramp begins
     just after it; the tail fade keeps the same from happening in reverse where
     the ground meets the darker bottom bar. */
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 40px, #000 clamp(150px, 18vw, 240px),
    #000 calc(100% - 130px), transparent calc(100% - 30px));
          mask-image: linear-gradient(to bottom,
    transparent 40px, #000 clamp(150px, 18vw, 240px),
    #000 calc(100% - 130px), transparent calc(100% - 30px));
}
.typewall__tilt {
  width: 100%;
  transform: rotate(-7deg);
}
.typewall__row {
  display: flex;
  width: max-content;
  font-family: var(--font-display);
  /* The floor matters as much as the cap: at the old 2.4rem the rows were
     37px on a phone and could not fill a 1,100px footer at any sane count. */
  font-size: clamp(3.4rem, 5.6vw, 5.2rem);
  line-height: .96;
  letter-spacing: .01em;
  white-space: nowrap;
  color: transparent;
  -webkit-text-stroke: 1.3px rgba(255, 255, 255, .105);
  animation: typewall-run var(--dur, 56s) linear infinite;
}
.typewall__row--rtl { animation-direction: reverse; }
.typewall__set { display: flex; }
.typewall__set > span { padding-right: .38em; }

@keyframes typewall-run {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* Parked while the footer is out of view, while the tab is hidden, and on
   hover or keyboard focus anywhere in the sign-off. */
html.is-tab-hidden .typewall__row,
.typewall.is-offscreen .typewall__row,
.ft-main:hover .typewall__row,
.ft-main:focus-within .typewall__row { animation-play-state: paused; }
.ft-script {
  font-family: 'Golden Custard', 'Snell Roundhand', cursive;
  font-size: clamp(37.4px, 4.84vw, 61.6px);
  line-height: 1.1;
  color: var(--white);
  margin: 0;
}
.ft-logo {
  display: inline-block; margin-top: 18px;
  background: var(--ft-cream); padding: 20px 34px; border-radius: 999px;
  box-shadow: 0 10px 30px rgba(110, 70, 38, .12);
}
.ft-logo img { display: block; width: clamp(220px, 26vw, 320px); height: auto; }
/* The footer ran a notch under the 1.1rem body size all through. On the red
   ground that undersizing showed, so every rank below is lifted to or past body
   size and the tracked-out uppercase runs get more leading with it. */
.ft-blurb { max-width: 560px; margin: 22px auto 0; font-size: 1.24rem; line-height: 1.6; }

.ft-cols {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 40px);
  margin-top: clamp(34px, 4vw, 54px);
}
.ft-col {
  background: rgba(0, 0, 0, .13); border-radius: 18px; padding: 26px 20px;
  transition: background-color .25s ease, transform .25s ease;
}
.ft-col:hover, .ft-col:focus-within { background: rgba(0, 0, 0, .21); transform: translateY(-3px); }
.ft-h {
  font-family: var(--font-display);
  font-size: 1.82rem; letter-spacing: 0; line-height: 1.15;
  color: var(--white); margin-bottom: 12px;
}
.ft-col p { font-size: 1.19rem; line-height: 1.6; }
.ft-col a { color: var(--white); }

.ft-social { list-style: none; display: flex; justify-content: center; gap: 10px; margin: 6px 0 0; padding: 0; }
.ft-social a {
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center; background: var(--white);
  transition: transform .15s ease, background .15s ease;
}
.ft-social a:hover { transform: translateY(-3px); background: var(--ft-pink-soft); }
.ft-social svg { width: 21px; height: 21px; fill: var(--red); }
.ft-handle {
  margin-top: 10px;
  font-family: 'Golden Custard', 'Snell Roundhand', cursive;
  font-size: 1.5rem; color: var(--white);
}

.ft-sitemap {
  margin-top: clamp(30px, 4vw, 46px);
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 10px 30px;
  font-weight: 700; font-size: 1.02rem;
  letter-spacing: .12em; text-transform: uppercase;
}
.ft-sitemap a { color: var(--white); text-decoration: none; }
.ft-sitemap a:hover { text-decoration: underline; }

/* --- bottom bar --- */
.ft-bar { background: var(--ft-red-dark); color: var(--ft-cream); font-size: 1.075rem; }
.ft-bar-in {
  /* The deposit line that used to sit opposite the copyright is gone, so this
     centres its one remaining child rather than pinning it to the left edge
     with space-between. */
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 10px; padding: 16px var(--gutter);
  max-width: var(--maxw); margin-inline: auto;
}
.ft-bar p { margin: 0; }

/* Outlined pill, from the same build. */
.btn-line {
  background: transparent;
  color: var(--white);
  box-shadow: inset 0 0 0 2.5px rgba(255, 255, 255, .7);
}
.btn-line:hover {
  background: var(--white); color: var(--red);
  box-shadow: inset 0 0 0 2.5px var(--white);
}
.btn-sm { padding: 11px 20px; font-size: 0.96rem; }
.ft-col .btn { margin-top: 14px; }

/* Focus has to stay visible against the cream cards and the brown bar. */
.ft-main :focus-visible { outline-color: var(--white); }
.ft-bar :focus-visible { outline-color: var(--ft-cream); }

/* Titles are set bold; body copy stays at the regular weight. */
h1, h2, h3, h4, h5, h6,
.eyebrow, .footer-heading, .ft-h, .nutrition__title { font-weight: 700; }

/* Section titles carry the display face. Deliberately scoped to the heading that
   names a section — not to product names, feature headings or the nutrition
   panel, which stay in Instrument Serif so they read as body-level. */
.hero h1,
.eyebrow,
.story h2,
.why h2,
.band h2,
.section > .wrap > h1,
.contact h1,
.footer-heading {
  font-family: var(--font-display);
  letter-spacing: 0;
  line-height: 1.12;
}

/* ------------------------------ Responsive -------------------------------- */
@media (max-width: 1100px) {
  .ft-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  .nav-toggle { display: block; }
  .site-header { position: sticky; }

  .site-nav {
    /* Anchored to the header itself so it never depends on a hard-coded height. */
    position: absolute; top: 100%; left: 0; right: 0;
    /* Sits behind the header's own background so the closed panel is hidden. */
    z-index: -1;
    background: var(--white);
    flex-direction: column; align-items: stretch;
    gap: 0; padding: 8px var(--gutter) 28px;
    border-top: 1px solid var(--rule);
    transform: translateY(-120%);
    transition: transform .3s ease;
    box-shadow: 0 18px 30px rgba(0,0,0,.08);
  }
  .site-nav[data-open='true'] { transform: translateY(0); }

  /* The wave rides the bottom of the open panel. --nav-drop is the panel's
     measured height, set by main.js; with no script it stays 0 and the wave
     simply sits under the bar as before. Same duration and easing as the panel
     so the two arrive together. */
  .header-wave {
    transform: translateY(var(--nav-drop, 0px));
    transition: transform .3s ease;
  }
  .site-nav__list { flex-direction: column; align-items: stretch; gap: 0; }
  .site-nav__list a { display: block; padding: 14px 0; font-size: 1.42rem; border-bottom: 1px solid var(--rule); }
  /* In the drop-down panel the rule is a divider between rows, not an
     indicator, so the sliding underline is stood down. */
  .site-nav__list a::after { display: none; }
  .site-nav > .btn { margin-top: 18px; text-align: center; }
  .site-social { padding-top: 18px; }
  .site-header__inner { padding: 14px var(--gutter); }

  /* The hero art is a wide composite; on narrow screens pull focus toward the
     farm side. The copy now runs the full width, so the veil has to turn with
     it — top-down instead of the left-weighted ramp used on wide screens. */
  .hero { min-height: 560px; background-position: 64% center; }
  .hero::before {
    background: linear-gradient(to bottom, rgba(255,255,255,.88) 0%, rgba(255,255,255,.70) 55%, rgba(255,255,255,.38) 100%);
  }

  .story, .why, .contact { grid-template-columns: 1fr; }
  /* One column, full-width photograph: the cow would sit on the copy. */
  .why__cow { display: none; }
  .story__media { order: -1; }
  .product-grid--4, .product-grid--5 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .product-grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .band__inner, .band--photo .band__inner { grid-template-columns: 1fr; }
  .hero__content { max-width: none; }
}

@media (max-width: 560px) {
  .nutrition__head { grid-template-columns: 1fr; justify-items: center; text-align: center; }
  .nutrition__shot { width: 132px; }
  .nutrition__blurb { max-width: 34ch; margin-inline: auto; }
}

@media (max-width: 640px) {
  .form-grid { grid-template-columns: 1fr; }
  .ft-cols { grid-template-columns: 1fr; max-width: 420px; margin-inline: auto; }
  .ft-bar-in { justify-content: center; text-align: center; }
  .product-grid--3, .product-grid--4, .product-grid--5 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --------------------------------- Print ---------------------------------- */
@media print {
  .site-header, .divider, .band__watermark, .nutrition,
  .locator-wrap, .skip-link, .site-social { display: none !important; }

  html, body { background: #fff !important; color: #000; font-size: 11pt; }
  .section, .band { padding-block: 12pt !important; background: #fff !important; }
  .band, .band--photo { color: #000 !important; background: none !important; }
  .band h2, .band p { color: #000 !important; }
  .rise { opacity: 1 !important; transform: none !important; }
  .section--cream, .site-footer, .footer-reviews,
  .ft-main, .ft-bar, .ft-col { background: #fff !important; }
  /* .ft-col a and .btn-line are set white for the red ground, as direct
     declarations that inheritance from .ft-main will not override — so they
     have to be named here or they print white on white. */
  .ft-main, .ft-bar, .ft-script, .ft-h, .ft-handle,
  .ft-sitemap a, .ft-col a, .btn-line, .section--red { color: #000 !important; }
  .btn-line { box-shadow: none !important; }
  .ft-horizon, .ft-truck, .ft-logo, .why__cow { display: none !important; }
  .hero { min-height: auto; background: none !important; color: #000; }
  .hero__inner { padding-block: 12pt; }

  .product, .story, .why, .contact, .review { break-inside: avoid; }
  .product-grid { gap: 12pt; }
  a { text-decoration: none; color: #000; }
  /* Links are useless in print unless the destination is spelled out. */
  .ft-col a[href^="http"]::after,
  .ft-col a[href^="mailto"]::after { content: " (" attr(href) ")"; font-size: 9pt; }
  /* A looping row cannot print: show the set once, stacked. */
  .review-marquee { overflow: visible; -webkit-mask-image: none; mask-image: none; }
  .review-track { display: block; width: auto; animation: none; }
  .review[aria-hidden="true"] { display: none; }
  .review { width: auto; border: none; padding: 6pt 0; }
  .btn { border: 1pt solid #000; color: #000 !important; background: none !important; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; animation: none !important; }
  /* main.js already skips the entrances under this setting; this is the
     belt-and-braces copy, so nothing can be left mid-transition and invisible. */
  .js .rise { opacity: 1 !important; transform: none !important; }
  /* Settle the clouds on a composed frame instead of the loop's start. */
  .cloud-band--far  { transform: translate3d(-190px, 0, 0); }
  .cloud-band--near { transform: translate3d(-330px, 0, 0); }

  /* Same for the type wall: parking every row at 0 would stack the word breaks
     into one vertical line straight down the footer. Offsetting alternate rows
     lands it on a composed frame that still reads as a wall. */
  .typewall__row              { transform: translate3d(-12%, 0, 0); }
  .typewall__row--rtl         { transform: translate3d(-31%, 0, 0); }

  /* With the marquee halted the row must still be reachable by hand. */
  .review-marquee { overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
  .review-track { scroll-snap-type: x mandatory; }
  .review { scroll-snap-align: start; }
}
