/* ===========================================================================
   sheet.css — the product line pages, as documents.

   THESE PAGES ARE NOT LEAVES, and that is the whole reason this sheet exists.
   Every leaf on this site is laid into ONE fixed-height card that never scrolls
   — a slide. A product page is not a slide. A buyer arriving on Optimus Foam
   wants the picture, then what it comes as, then why it works, then the sheet
   to download, then someone to call, in that order and without the page turning
   into a different page underneath them each time they touch the wheel. So a
   line page is now one continuous document: it keeps the site's typefaces, its
   five grounds, its hairlines and its glass, and gives up the card. The same
   decision css/legal.css made for the privacy notice, and the same note is at
   the top of that file.

   THE ORDER IS THE RESEARCHED ONE, not an invention:
     hero        picture, name, one line, what it comes in, the ask
     strip       one full-bleed photograph of the plant, as a breath
     variants    every variant, captioned, large — the gallery, not thumbnails
     why         the argument, with its supporting shots
     glance      the same facts again as a table, for the reader who skipped
     literature  the download, weighted before the click
     close       the ask again, inverted, with a person to call
     more        the previous and next line, and the index

   Two CTAs — one above the fold in the sticky bar, one at the foot — because a
   reader who has decided in three seconds and a reader who needs the whole page
   are different people and both have to be able to act without hunting.

   NOT OWNED HERE: `.pill` (css/chrome.css), `.mark` (css/chrome.css), and the
   liquid-glass plate treatment on `.glass` (css/band.css). This file places
   them; it does not restate them.
   =========================================================================== */

.sheet{
  background: var(--plate-carbon);
  color: var(--stencil);
  min-height: 100svh;
  /* The backdrop's top margin. On the body rather than on the bar, so the bar's
     STATIC position is 4.4svh down — the world shows above it at the top of the
     page — while its stuck position is still flush to the viewport. A margin on
     the bar itself would have been carried into the stuck position and left a
     permanent gap over it. */
  padding-top: 4.4svh;
  --on-plate: var(--stencil);
  --on-soft: var(--stencil-2);
  --on-rule: var(--rule);
  /* The page's own gutter and rhythm. Both scale with the viewport rather than
     the card, because there is no card here to be a container. */
  --pad: clamp(18px, 4.4vw, 64px);
  --measure: 1240px;
  --band: clamp(3.5rem, 9vh, 7.5rem);
}

.sheet main{ display: block }

/* ── The world the page floats over ─────────────────────────────────────────
   THE HOME PAGE'S BACKDROP, REBUILT WITHOUT THE SHADER. On the card build a
   fixed #backdrop holds a WebGL canvas that duotones a plant photograph to the
   active plate, and the card floats on it. These pages have no card and no
   shader — but the reason that backdrop exists is not the shader, it is that
   the site's own photography should be the ground everything else sits on. So
   it is reproduced here with the two layers card.css already falls back to when
   there is no WebGL: the photograph, and a colour layer over it at
   `mix-blend-mode: color` and 0.55, which is exactly what
   `html.no-webgl #backdrop .grade.is-on` does.

   Fixed, so it does not scroll with the document — the plate slides over a
   world that stays put, which is the whole feeling of the home page. */
.sheet__back{
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--plate-carbon);
  pointer-events: none;
}
.sheet__back img,
.sheet__back video{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* The duotone stand-in. Kraft, because every one of these pages runs warm and a
   cool grade under a cream plate reads as two different photographs.

   0.45 AND 0.16, NOT 0.55 AND 0.34. The first pass took card.css's no-webgl
   numbers and then added a dim on top of them, and the two together crushed the
   photograph to a flat dark brown — every roll, every pallet and the roof
   structure gone, which is not a backdrop, it is a colour. The card build can
   afford 0.55 because its shader does the dimming selectively and its card
   covers nine tenths of the frame; here the photograph is most of what you see
   around the plate, so it has to still be a photograph. */
.sheet__grade{
  position: absolute;
  inset: 0;
  background: var(--kraft-mid);
  mix-blend-mode: color;
  opacity: 0.45;
  pointer-events: none;
}
/* ATMOSPHERE — the same grain and vignette the card pages put over #backdrop,
   from the same two tokens, plus the flat dim that gives the plate's shadow
   something to fall on. Three layers, one element, each at its final strength:
   see the note on --grain in tokens.css for why none of them uses `opacity`. */
.sheet__back::after{
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--vignette), var(--grain);
  background-size: cover, 170px 170px;
  background-repeat: no-repeat, repeat;
  background-color: rgba(20, 22, 26, 0.10);
  pointer-events: none;
}

/* A SLOW DRIFT, and slow is the whole specification. The card pages get their
   life from the shader — the backdrop breathes with scroll velocity and the
   grade turns with the leaf. A line page has neither, so its backdrop was the
   one still thing on a site where nothing else is, and a photograph that never
   moves behind a page that does reads as a wallpaper.
   72 seconds, 4% of travel, alternating: at any moment you cannot see it
   happening, and over the length of the page the frame is not where it was. */
@keyframes sheet-drift{
  from{ transform: scale(1.04) translate3d(-0.9%, -0.6%, 0) }
  to  { transform: scale(1.08) translate3d(0.9%, 0.6%, 0) }
}
.sheet__back img{
  will-change: transform;
  animation: sheet-drift 72s var(--e-grade) infinite alternate;
}
@media (prefers-reduced-motion: reduce){
  .sheet__back img{ animation: none; will-change: auto }
}

/* ── The plate ──────────────────────────────────────────────────────────────
   The document, inset off the backdrop on the card's own geometry — 4.6vw, a
   20px radius and card.css's three-shadow elevation, so it reads as the same
   object as the home page's card and not as a page with a picture behind it.

   NO `overflow: hidden` HERE, and that is load-bearing: an overflow other than
   visible makes this a scroll container, and the sticky bar would then stick to
   IT rather than to the viewport — which, since the plate never scrolls
   internally, means it would not stick at all. The bar is a sibling above the
   plate for the same reason; it spans the full width, over the backdrop. */
.sheet__plate{
  position: relative;
  z-index: 1;
  margin: 0 4.6vw 4.4svh;
  border-radius: 20px;
  background: var(--plate-foam);
  box-shadow:
    0 1px 0 rgba(27,29,33,.05),
    0 2px 9px -4px rgba(20,22,26,.20),
    0 30px 64px -30px rgba(20,22,26,.44),
    inset 0 0 0 1px var(--rule);
}
/* The first and last sections carry the plate's corners, or a square section
   ground paints over the radius the plate just drew. */
.sheet__plate > main > :first-child{ border-radius: 20px 20px 0 0 }
.sheet__plate > .sheet__foot{ border-radius: 0 0 20px 20px }

@media (max-width: 1279px){
  .sheet__plate{ margin: 0 3.6vw 3.0svh; border-radius: 18px }
  .sheet__plate > main > :first-child{ border-radius: 18px 18px 0 0 }
  .sheet__plate > .sheet__foot{ border-radius: 0 0 18px 18px }
}

/* ── The bar ────────────────────────────────────────────────────────────────
   Sticky, quiet, and it carries the ask. It starts transparent over the hero
   and takes its ground the moment the page moves — js/sheet.js sets
   `data-stuck` — so the top of the page is the product and not a toolbar. */

.sheet__bar{
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: clamp(10px, 1.4vh, 16px) var(--pad);
  background: color-mix(in oklab, var(--plate-foam) 0%, transparent);
  border-bottom: 1px solid transparent;
  transition: background-color 320ms var(--e-grade),
              border-color 320ms var(--e-grade),
              padding 320ms var(--e-grade);
}

.sheet__bar[data-stuck]{
  background: color-mix(in oklab, var(--plate-foam) 86%, transparent);
  backdrop-filter: blur(12px);
  border-bottom-color: var(--rule);
  padding-block: clamp(7px, 1vh, 11px);
}

.sheet__bar .mark{ color: inherit; text-decoration: none }

.sheet__nav{
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 2vw, 1.75rem);
}

.sheet__nav a:not(.pill){
  font-family: var(--f-label);
  font-variation-settings: "wdth" 100, "wght" 500;
  font-size: var(--t-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--stencil-2);
  text-decoration: none;
  transition: color calc(var(--d-onplate) * 1) linear;
}
.sheet__nav a:not(.pill):hover,
.sheet__nav a:not(.pill):focus-visible{ color: var(--seal-blue) }

/* The bar's own CTA is the sticky one the research asks for. It is hidden until
   the page has moved, so the hero's own button is the only one on screen at the
   top — two identical buttons in one viewport is a choice, not a call. */
.sheet__bar .pill{
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms var(--e-grade);
}
.sheet__bar[data-stuck] .pill{ opacity: 1; pointer-events: auto }

@media (max-width: 620px){
  .sheet__nav a:not(.pill){ display: none }
  .sheet__bar .pill{ opacity: 1; pointer-events: auto }
}

/* ── Section shell ──────────────────────────────────────────────────────────
   One width, one rhythm, five grounds. A section declares its ground and its
   ink together, in leaf.css's vocabulary, so anything written against
   --on-plate / --on-soft / --on-rule lands correctly on all of them. */

.block{
  padding: var(--band) var(--pad);
  background: var(--plate-foam);
}
/* MAX-WIDTH, NOT WIDTH. `width` here also applied to the inline-flex `.pill` in
   the closing section and stretched the button across the full 1155px measure —
   a call to action the width of the page reads as a divider, not a button. A
   max-width lets every block child fill up to the measure and centre on its auto
   margins exactly as before, and leaves anything inline at its own size. */
.block > *{
  max-width: min(100%, var(--measure));
  margin-inline: auto;
}
/* The block children that must actually FILL the measure rather than shrink to
   their content: a grid or a list would otherwise be as wide as its widest row. */
.block > :is(.block__head, .tiles, .facts, .videos, .why-grid){ width: 100% }

.block--cream{ background: var(--plate-cream) }
.block--kraft{ background: var(--plate-kraft) }
.block--seal{ background: var(--plate-seal) }
.block--carbon{
  background: var(--plate-carbon);
  --on-plate: var(--carbon-type);
  --on-soft: var(--carbon-soft);
  --on-rule: var(--rule-dark);
  color: var(--carbon-type);
}

/* The section head: a parenthetical label, a Bodoni heading, a hairline under
   both. The same three-part device on every section, so the page has a spine. */
.block__head{
  display: grid;
  gap: var(--s-2);
  padding-bottom: var(--s-4);
  margin-bottom: var(--band-inner, clamp(1.5rem, 3.5vh, 3rem));
  border-bottom: 1px solid var(--on-rule);
}
.block__label{
  margin: 0;
  font-family: var(--f-label);
  font-variation-settings: "wdth" 100, "wght" 400;
  font-size: var(--t-label);
  letter-spacing: 0;
  color: var(--on-soft);
}
.block__head h2{
  margin: 0;
  font-family: var(--f-display);
  font-variation-settings: "opsz" 40;
  font-weight: 400;
  font-size: min(clamp(1.6rem, 4vw, 3rem), 9vh);
  line-height: 1.04;
  letter-spacing: -0.014em;
  color: var(--on-plate);
  text-wrap: balance;
}

/* ── Hero ───────────────────────────────────────────────────────────────────
   Picture and words side by side, the words first in the source so the page
   still reads in the right order without the layout. The ghost numeral behind
   the name is the line's index in the deck — the one numeral on the page that
   is not a measurement, set enormous and nearly out of ink so it reads as
   pagination rather than as data. */

.hero{
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  align-items: center;
  gap: clamp(1.5rem, 5vw, 5rem);
  width: min(100%, var(--measure));
  margin-inline: auto;
  padding: clamp(1rem, 4vh, 3.5rem) var(--pad) var(--band);
  box-sizing: content-box;
  padding-inline: 0;
}
.sheet__hero-wrap{ padding-inline: var(--pad) }

.hero__text{ position: relative; z-index: 1; min-width: 0 }

.hero__num{
  position: absolute;
  top: -0.36em;
  left: -0.06em;
  z-index: -1;
  margin: 0;
  font-family: var(--f-display);
  font-variation-settings: "opsz" 96;
  font-weight: 400;
  font-size: min(clamp(6rem, 18vw, 15rem), 34vh);
  line-height: 0.8;
  letter-spacing: -0.03em;
  color: var(--stencil);
  opacity: 0.055;
  user-select: none;
}

.hero__name{
  margin: 0;
  font-family: var(--f-display);
  font-variation-settings: "opsz" 72;
  font-weight: 500;
  font-size: min(clamp(2.4rem, 6.4vw, 5rem), 13vh);
  line-height: 0.98;
  letter-spacing: -0.02em;
  color: var(--on-plate);
  text-wrap: balance;
}

.hero__lede{
  margin: var(--s-4) 0 0;
  font-family: var(--f-label);
  font-variation-settings: "wdth" 108;
  font-size: clamp(0.95rem, 1.15vw, 1.15rem);
  line-height: 1.6;
  color: var(--on-soft);
  max-width: 38ch;
}

/* The forms, as standing values. They are what the line COMES IN, all of them
   available at once — so they are set as a row of plain chips and deliberately
   not as anything that looks pressable. */
.forms-row{
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: var(--s-5) 0 0;
  padding: 0;
}
.forms-row li{
  padding: 5px 11px;
  border: 1px solid var(--on-rule);
  border-radius: 3px;
  font-family: var(--f-label);
  font-variation-settings: "wdth" 100, "wght" 500;
  font-size: var(--t-label);
  line-height: 1.2;
  letter-spacing: 0.02em;
  color: var(--on-plate);
  white-space: nowrap;
}

/* The two actions, and only two: the button, and the quiet download beside it.
   A page with two buttons of equal weight has no call to action. */
.hero__acts{
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: clamp(0.75rem, 2vw, 1.5rem);
  margin: var(--s-6) 0 0;
}

.doclink{
  display: inline-grid;
  gap: 1px;
  font-family: var(--f-label);
  font-variation-settings: "wdth" 100, "wght" 500;
  font-size: var(--t-body);
  line-height: 1.25;
  color: var(--on-plate);
  text-decoration: none;
  border-bottom: 1px solid var(--on-rule);
  padding-bottom: 3px;
  transition: color calc(var(--d-onplate) * 1) linear, border-color calc(var(--d-onplate) * 1) linear;
}
.doclink:hover,
.doclink:focus-visible{ color: var(--seal-blue); border-bottom-color: var(--seal-blue) }
.doclink .meta{
  font-variation-settings: "wdth" 84, "wght" 400;
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--on-soft);
}

/* The hero picture. THE 460px CEILING IS NOT A TASTE DECISION: the product
   photographs are the client's own 260px tiles and there is no larger original
   of any of them. Past about 1.6x they visibly soften, and a soft photograph of
   a product is worse than a smaller sharp one. Raise it when there is new
   photography, and not before. */
.hero__shot{
  margin: 0;
  justify-self: end;
  width: min(100%, 460px);
}
/* THE BOX IS SIZED BY THE RATIO, NOT BY THE IMAGE, and that is what lets the
   rotator cross-fade. js/sheet.js stacks two absolutely positioned frames in
   here; with the box relying on an in-flow <img> for its height, taking both
   images out of flow would collapse it to nothing. Declaring 4:5 on the glass
   itself means the frame is the frame whether one photograph is in it or two. */
.hero__shot .glass{
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: var(--r-photo);
  aspect-ratio: 4 / 5;
}
.hero__shot img{
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

/* The rotator's two layers. Only the transition duration matters to JS —
   HERO_FADE in js/sheet.js hands the frame down to the semantic layer when it
   elapses, so the two numbers have to agree. */
.hero__shot .hero__frame{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 760ms var(--e-grade);
}
.hero__shot .hero__frame[data-on]{ opacity: 1 }

/* The variant's name, under the frame it belongs to. A real <figcaption>, so the
   rotating photograph has an accessible name that changes with it. */
.hero__caption{
  margin: var(--s-3) 0 0;
  font-family: var(--f-label);
  font-variation-settings: "wdth" 88, "wght" 500;
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--on-soft);
}

/* The dots are BARS, not circles: they read as a strip of frames rather than as
   a generic carousel, and a 2px bar can be 26px wide without shouting — which is
   the difference between a target a thumb can hit and three dots it cannot. */
.hero__dots{
  list-style: none;
  display: flex;
  gap: 6px;
  margin: var(--s-3) 0 0;
  padding: 0;
}
.hero__dot{
  appearance: none;
  -webkit-appearance: none;
  display: block;
  width: 26px;
  height: 2px;
  padding: 9px 0;               /* the hit area, not the mark */
  border: 0;
  background: none;
  cursor: pointer;
}
.hero__dot::before{
  content: "";
  display: block;
  height: 2px;
  background: var(--on-rule);
  transition: background-color 320ms var(--e-grade);
}
.hero__dot[aria-pressed="true"]::before{ background: var(--on-plate) }
.hero__dot:hover::before{ background: var(--stencil-2) }
.hero__dot:focus-visible{ outline: 2px solid var(--seal-blue); outline-offset: 2px }

@media (prefers-reduced-motion: reduce){
  .hero__shot .hero__frame{ transition: none }
}

/* ── The strip ──────────────────────────────────────────────────────────────
   One full-bleed photograph of the plant between the hero and the detail — a
   breath, and the only place on the page an image touches both edges. Fixed
   attachment is deliberately NOT used: it costs a repaint on every scroll frame
   for an effect this page does not need. */
.strip{
  margin: 0;
  height: clamp(180px, 34vh, 380px);
  overflow: hidden;
  background: var(--plate-carbon);
}
/* THE STRIP IS A CAROUSEL. Every frame is stacked in the same box and cross-faded
   — the page's own photograph (or its footage) first, then the four plant
   photographs the home page hero cycles, so the same four pictures that introduce
   the company reappear inside every line. js/sheet.js advances `data-on`.

   Absolutely positioned, so the box is sized by `.strip`'s own height and does not
   grow with the number of frames. A pure opacity cross-fade with no transform:
   this is scenery between two sections, and anything that moved here would
   compete with the reveal happening above and below it. */
.strip{ position: relative }

.strip__frame{
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1100ms var(--e-grade);
}
.strip__frame[data-on]{ opacity: 1 }

.strip img,
.strip video{
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 58%;
}

@media (prefers-reduced-motion: reduce){
  /* No cycling and no fade — the first frame, held. js/sheet.js does not start
     the rotator at all, so this is only making the handover instant if one is
     already mid-flight when the preference changes. */
  .strip__frame{ transition: none }
}

/* Optimus Foam and Optimus Bubble are the two lines with footage of their own,
   and this is where it goes: full width, silent, looping, at the speed it was
   shot at. NO CONTROLS and NO playbackRate — js/sheet.js only starts and stops
   it as it comes into view; see the note there for why the card build's half
   speed is wrong here. Taller than the photographic strip because a machine
   running deserves more than a band. */
.strip--film{ height: clamp(240px, 52vh, 560px) }
.strip--film video{ object-position: center center }

/* ── Thicker glass ──────────────────────────────────────────────────────────
   css/band.css owns the glass treatment and now applies it to these plates too —
   the rim light, the specular sweep and the lift shadow. Until this pass the
   hero and the variant tiles were not in that file's selector list at all, so
   they were rounded photographs with a hairline and nothing else, which is why
   the hero read flat next to the home page's line-up.

   THIS BLOCK IS THE PART THAT IS ONLY TRUE HERE: real thickness. A slab of glass
   does not just catch light at its edge, it BENDS what is behind it there — the
   picture goes soft and slightly brighter in the last few millimetres before the
   rim, and stays sharp through the middle. That is one `backdrop-filter` masked
   so it only exists at the edge, and it is the whole difference between "rounded
   corners with a highlight" and something that looks like it has depth.

   NOT ON THE HOME PAGE'S LINE-UP, deliberately, and the scope is the guard. Those
   cards ride a `preserve-3d` arc, and a backdrop-filter inside a 3D subtree
   forces the browser to flatten it — the carousel would collapse into a pile
   sorted by DOM order instead of by distance. band.css already carries that
   warning in capitals for the same reason. `.sheet` has no 3D anywhere. */
.sheet .glass::before{
  content: "";
  position: absolute;
  inset: 0;
  /* Above the photograph, below band.css's rim-and-specular layer at 2. */
  z-index: 1;
  border-radius: inherit;
  pointer-events: none;
  backdrop-filter: blur(7px) saturate(1.14) brightness(1.03);
  -webkit-backdrop-filter: blur(7px) saturate(1.14) brightness(1.03);
  /* Transparent through the middle, solid at the rim: the blur exists only in
     the outer band, which is where a thick edge would refract. */
  mask-image: radial-gradient(125% 125% at 50% 50%, rgba(0,0,0,0) 56%, rgba(0,0,0,1) 92%);
  -webkit-mask-image: radial-gradient(125% 125% at 50% 50%, rgba(0,0,0,0) 56%, rgba(0,0,0,1) 92%);
}

/* The veil — the "opacity over it". Laid over band.css's gradients as a flat
   background-COLOUR, not as a re-declared `background`, so the specular sweep and
   the return light underneath it survive. Barely there on purpose: enough to
   pull the photograph a step back behind the surface, not enough to wash it. */
.sheet .glass::after{
  background-color: rgba(255, 255, 255, 0.07);
}

/* The hero is the largest plate on the page, so it gets the deepest edge and the
   strongest lift — a bigger piece of glass is a thicker piece of glass. */
.sheet .hero__shot .glass::before{
  backdrop-filter: blur(10px) saturate(1.16) brightness(1.04);
  -webkit-backdrop-filter: blur(10px) saturate(1.16) brightness(1.04);
  mask-image: radial-gradient(122% 122% at 50% 46%, rgba(0,0,0,0) 60%, rgba(0,0,0,1) 94%);
  -webkit-mask-image: radial-gradient(122% 122% at 50% 46%, rgba(0,0,0,0) 60%, rgba(0,0,0,1) 94%);
}
.sheet .hero__shot .glass{
  box-shadow:
    0 1px 2px rgba(27, 29, 33, 0.07),
    0 26px 56px -26px rgba(20, 22, 26, 0.42);
}

/* Reduced motion is not the trigger here — this is not motion — but a device
   that cannot composite a backdrop-filter cheaply should not try on five tiles
   at once. `@supports not` catches the browsers with no support at all; they
   keep the rim and specular and lose only the refraction. */
@supports not (backdrop-filter: blur(1px)){
  .sheet .glass::before{ display: none }
}

/* ── Variant tiles ──────────────────────────────────────────────────────────
   Every variant, captioned, at a size worth looking at — not thumbnails under a
   hero. `auto-fit` rather than a fixed count because the lines carry three,
   four or five of them and a hard four-column rule leaves a hole on the ones
   that do not. */
.tiles{
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr));
  gap: clamp(0.75rem, 1.8vw, 1.75rem);
}
.tiles figure{ margin: 0; display: grid; gap: var(--s-3) }
.tiles .glass{ display: block; overflow: hidden; border-radius: var(--r-photo) }
.tiles img{
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}
.tiles figcaption{
  font-family: var(--f-label);
  font-variation-settings: "wdth" 88, "wght" 500;
  font-size: var(--t-body);
  line-height: 1.3;
  color: var(--on-plate);
}

/* ── Why ────────────────────────────────────────────────────────────────────
   The argument on the left, its supporting shots on the right. The numbered
   list keeps the site's ordinal voice; a line whose "why" is a paragraph rather
   than a list simply has no list, and the column holds either. */
/* THE SHOTS GET A ROW OF THEIR OWN, and the argument gets the width.
   They used to sit in the right-hand half of a two-column split, which is where
   the whole section came apart: three, five or six square photographs do not
   divide into a half-width column, so the last row always had a hole in it, the
   block ran 566px tall beside a 331px list, and the two halves of a section that
   should read as one thing were different heights and different rhythms.

   Stacked, both problems stop existing. The benefits take the full measure in two
   text columns, and the photographs take a single full-width row underneath —
   equal widths, one line, symmetrical at any count because the row IS the grid.
   That is also simply the better composition: the list is the argument and the
   photographs are the evidence, and evidence goes under the argument. */
.why-grid{
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.75rem, 4vh, 3.25rem);
  align-items: start;
}
.why-grid .why__label,
.why-grid .why__head{ display: none }   /* the section head above says both */

/* TWO COLUMNS OF TEXT, via `columns` and not via grid, and the reason is the
   count: the lines carry six benefits, or four, or a paragraph instead of a
   list. Multi-column balances whatever it is given and flows column-major — 01
   02 03 down the left, 04 05 06 down the right — which is the order a numbered
   list has to be read in. A grid would have needed the row count hard-coded and
   would have read 01 02 across. */
.why__list{ columns: 2; column-gap: clamp(1.5rem, 4vw, 4rem) }
.why__list li{ break-inside: avoid }
/* The paragraph lines keep a readable measure rather than running the full
   width of the plate. */
.why__lede{ max-width: 68ch }

@media (max-width: 900px){
  .why__list{ columns: 1 }
}

.why__list{
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0;
}
.why__list li{
  display: grid;
  grid-template-columns: 2.6em minmax(0, 1fr);
  align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-4) 0;
  border-bottom: 1px solid var(--on-rule);
}
.why__list li:first-child{ border-top: 1px solid var(--on-rule) }
.why__num{
  font-family: var(--f-label);
  font-variation-settings: "wdth" 76, "wght" 500;
  font-size: var(--t-num);
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--on-soft);
}
.why__text-item{
  font-family: var(--f-display);
  font-variation-settings: "opsz" 18;
  font-size: clamp(1rem, 1.5vw, 1.3rem);
  line-height: 1.24;
  color: var(--on-plate);
}
.why__lede{
  margin: 0;
  font-family: var(--f-label);
  font-variation-settings: "wdth" 108;
  font-size: clamp(0.95rem, 1.15vw, 1.1rem);
  line-height: 1.7;
  color: var(--on-soft);
}

.why__shots{ margin: 0 }

/* ONE ROW, EQUAL TILES, WHATEVER THE COUNT.
   `grid-auto-flow: column` with `grid-auto-columns: 1fr` means the number of
   columns IS the number of photographs — five tiles make five equal columns, six
   make six, three make three. There is no wrap, so there is no last row, so
   there is no hole to close. That is the whole fix: the previous version tried
   to balance a wrapping grid against a count it could not know, and every line
   on the site has a different count.

   It is also why they are square. A single row of equal squares is symmetrical
   by construction and reads as a contact strip laid out on a bench, which is
   what these photographs are — the same subject from five angles, not five
   different things needing five different crops. */
.why__grid{
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: clamp(0.5rem, 1.1vw, 1rem);
  width: 100%;
}

.why__grid .glass{ display: block; overflow: hidden; border-radius: var(--r-photo) }
.why__grid img{
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

/* A phone cannot hold six squares in a row and keep them squares — at 375px they
   come out 52px, which is a thumbnail of a thumbnail. Below 900 the strip goes
   back to wrapping at three across, where the ragged last row costs nothing
   because there is no second column beside it to be ragged against. */
@media (max-width: 900px){
  .why__grid{
    grid-auto-flow: row;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (max-width: 520px){
  .why__grid{ grid-template-columns: repeat(2, minmax(0, 1fr)) }
}
.why__shots figcaption{
  margin-top: var(--s-3);
  font-family: var(--f-label);
  font-variation-settings: "wdth" 84;
  font-size: var(--t-label);
  color: var(--on-soft);
}

/* ── Videos ─────────────────────────────────────────────────────────────────
   On Demand only, the one line whose product is a machine rather than a
   material. NATIVE CONTROLS, deliberately: the card build used a custom player
   module, and a page that does not boot the card cannot have it — but a machine
   running is the single most useful thing this line can show, so it keeps the
   footage and gives up the custom chrome. `preload="metadata"` because these are
   several megabytes each and nobody has asked for them yet. */
.videos{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: clamp(0.75rem, 1.8vw, 1.75rem);
}
.vid{ margin: 0; display: grid; gap: var(--s-3) }
.vid video{
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--plate-carbon);
  border-radius: 12px;
  box-shadow: inset 0 0 0 1px var(--panel-edge);
}
.vid figcaption{
  display: grid;
  gap: 2px;
  font-family: var(--f-label);
  font-variation-settings: "wdth" 88, "wght" 600;
  font-size: var(--t-body);
  line-height: 1.25;
  color: var(--on-plate);
}
.vid figcaption span{
  font-variation-settings: "wdth" 100, "wght" 400;
  font-size: var(--t-label);
  color: var(--on-soft);
}

/* ── At a glance ────────────────────────────────────────────────────────────
   The same facts as the hero and the tiles, restated as rows. It is not
   redundancy: a page this long has two kinds of reader, and the one who came
   for a number scrolls to the table. The hairline draw is table.css's grammar,
   without the table. */
.facts{
  margin: 0;
  display: grid;
  gap: 0;
}
.facts > div{
  display: grid;
  grid-template-columns: minmax(140px, 0.28fr) minmax(0, 1fr);
  gap: clamp(0.75rem, 2vw, 2rem);
  align-items: baseline;
  padding: var(--s-4) 0;
  border-bottom: 1px solid var(--on-rule);
}
.facts > div:first-child{ border-top: 1px solid var(--on-rule) }
.facts dt{
  font-family: var(--f-label);
  font-variation-settings: "wdth" 88, "wght" 600;
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--on-soft);
}
.facts dd{
  margin: 0;
  font-family: var(--f-label);
  font-variation-settings: "wdth" 108;
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--on-plate);
}
.facts dd a{
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--on-rule);
  transition: color calc(var(--d-onplate) * 1) linear, border-color calc(var(--d-onplate) * 1) linear;
}
.facts dd a:hover,
.facts dd a:focus-visible{ color: var(--seal-blue); border-bottom-color: var(--seal-blue) }

/* The file weight, carried inside the literature link. `display: block` is what
   keeps it off the end of the sentence: css/compose.css set that for the leaf
   build and this page does not load compose.css, so without it the row read
   "…product sheetPDF, 557 KB" as one string. */
.line__doc-meta{
  display: block;
  font-variation-settings: "wdth" 84, "wght" 400;
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--on-soft);
}

/* ── Close ──────────────────────────────────────────────────────────────────
   Inverted, because the page ends on the ask and the ask should not look like
   one more section. The three ways to reach a person sit under the button in
   the site's meta voice. */
.close{ text-align: left }
.close h2{
  margin: 0;
  font-family: var(--f-display);
  font-variation-settings: "opsz" 56;
  font-weight: 400;
  font-size: min(clamp(1.9rem, 5vw, 3.6rem), 11vh);
  line-height: 1.04;
  letter-spacing: -0.018em;
  color: var(--on-plate);
  max-width: 20ch;
  text-wrap: balance;
}
.close h2 em{ font-style: italic }
.close p{
  margin: var(--s-4) 0 0;
  font-family: var(--f-label);
  font-variation-settings: "wdth" 108;
  font-size: clamp(0.95rem, 1.15vw, 1.1rem);
  line-height: 1.7;
  color: var(--on-soft);
  max-width: 46ch;
}
.close .pill{ margin-top: var(--s-6) }
.close__ways{
  margin-top: var(--s-5);
  font-size: var(--t-label) !important;
  letter-spacing: 0.02em;
}
.close__ways a{ color: var(--on-plate); text-decoration: none; border-bottom: 1px solid var(--on-rule) }
.close__ways a:hover{ color: var(--film-cyan); border-bottom-color: currentColor }

/* ── More lines ─────────────────────────────────────────────────────────────
   Previous, index, next. Standard on a product page and useful here for the
   reason it is standard: someone who has read to the bottom of Optimus Foam and
   not sent the form is usually looking for the neighbouring line. */
.more{
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-4);
  align-items: center;
  padding: var(--band) var(--pad);
  border-top: 1px solid var(--rule);
}
.more > *{ min-width: 0 }
.more a{
  display: grid;
  gap: 3px;
  color: inherit;
  text-decoration: none;
}
.more .dir{
  font-family: var(--f-label);
  font-variation-settings: "wdth" 88, "wght" 600;
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--stencil-2);
}
.more .to{
  font-family: var(--f-display);
  font-variation-settings: "opsz" 24;
  font-size: clamp(1.05rem, 1.8vw, 1.5rem);
  line-height: 1.1;
  color: var(--stencil);
  transition: color calc(var(--d-onplate) * 1) linear;
}
.more a:hover .to,
.more a:focus-visible .to{ color: var(--seal-blue) }
.more__next{ text-align: right }
.more__all{ text-align: center }

/* ── Foot ───────────────────────────────────────────────────────────────────── */
.sheet__foot{
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-5) var(--pad) var(--s-7);
  border-top: 1px solid var(--rule);
  font-family: var(--f-label);
  font-variation-settings: "wdth" 100;
  font-size: var(--t-label);
  color: var(--stencil-2);
}
.sheet__foot a{ color: inherit }
.sheet__foot .rot{
  font-family: var(--f-display);
  font-style: italic;
  rotate: 180deg;
}

/* ── Reveal ─────────────────────────────────────────────────────────────────
   The one piece of motion on the page, and it is the site's own signature:
   opacity and a short rise, no blur on a document (blur on running text is a
   readability cost, not a flourish). js/sheet.js sets `data-seen` from an
   IntersectionObserver. Everything is visible with JS off — the hidden state is
   only ever applied by the `html.js` class the pages set inline. */
.js [data-reveal]{
  opacity: 0;
  translate: 0 14px;
  transition: opacity 620ms var(--e-lay), translate 620ms var(--e-lay);
}
.js [data-reveal][data-seen]{ opacity: 1; translate: 0 0 }
.js [data-reveal="1"]{ transition-delay: 70ms }
.js [data-reveal="2"]{ transition-delay: 140ms }
.js [data-reveal="3"]{ transition-delay: 210ms }

@media (prefers-reduced-motion: reduce){
  .js [data-reveal]{ opacity: 1; translate: none; transition: none }
  .sheet__bar{ transition: none }
}

/* ── Narrow ─────────────────────────────────────────────────────────────────
   One column, everything in source order. Nothing is dropped: a phone gets the
   same hero, the same variants, the same argument and the same table. */
@media (max-width: 900px){
  .hero{ grid-template-columns: minmax(0, 1fr); gap: clamp(1.5rem, 5vw, 2.5rem) }
  .hero__shot{ justify-self: start; width: min(100%, 420px) }
  .why-grid{ grid-template-columns: minmax(0, 1fr) }
}

@media (max-width: 620px){
  .more{ grid-template-columns: minmax(0, 1fr); gap: var(--s-5) }
  .more__next,
  .more__all{ text-align: left }
  .facts > div{ grid-template-columns: minmax(0, 1fr); gap: var(--s-1) }
}
