
/* Newsreader arrives after first paint, so text reflows as it swaps in. This
   fallback is Georgia scaled to Newsreader's measure, so the two occupy the
   same width and nothing moves when the swap happens.
   95.19% is measured, not guessed: the same string set at 200px is 3448px in
   Newsreader against 3622px in Georgia. Re-measure if the display face changes. */
@font-face{
  font-family:'Newsreader fallback';
  src:local('Georgia'),local('Times New Roman'),local('Times');
  size-adjust:95.19%}

/* Page-to-page navigation was a hard reload with a white flash between
   documents. This is the whole implementation — no JavaScript, no framework —
   and browsers without support simply navigate as before.
   Gate: occasional, and the purpose is preventing a jarring change. */
@view-transition{navigation:auto}

/* The outgoing page leaves faster than the incoming one arrives. The visitor
   has already decided to go; dwelling on the exit is time spent watching a
   page they are finished with. */
::view-transition-old(root),::view-transition-new(root){
  animation-timing-function:cubic-bezier(0.23,1,0.32,1)}
::view-transition-old(root){animation-duration:120ms}
::view-transition-new(root){animation-duration:200ms}

/* Named elements are treated as the same element on both pages, so the
   browser carries them across instead of crossfading them with the content.
   Without this the sticky header blinks on every navigation - which is every
   navigation on a five-page site whose header never changes. */
header{view-transition-name:site-header}
.float{view-transition-name:site-cta}
::view-transition-group(site-header),::view-transition-group(site-cta){
  animation-duration:200ms;
  animation-timing-function:cubic-bezier(0.23,1,0.32,1)}

@media (prefers-reduced-motion:reduce){
  @view-transition{navigation:none}
}

/* Monocle, adapted. Magazine structure and voice; brand colours held.

   Colour is spelled out here and nowhere else — a hex in a rule body is a
   light-mode value stranded in dark. Dark values live once as --dark-*
   sources; the two blocks below only map --x to var(--dark-x), so the OS
   setting and the toggle cannot drift apart. See design.md section 12. */
:root{
  color-scheme:light dark;

  --ground:#F2EFE7;      /* cream paper */
  /* Raised stock and rules share the cream's hue (OKLCH 90) and separate by
     lightness. A navy tint was tried first and read as lavender against the
     cream. Values and ratios in design.md section 18. */
  --paper:#E9E4D6;       /* raised stock, a deeper cream */
  --ink:#1C1A17;         /* warm near-black */
  --muted:#5A554C;
  --rule:#C5BDAA;        /* warm hairline */
  --rule-soft:#D9D4C6;
  /* The button fill was the logo green at 3.26:1 against its white label -
     below AA, on the one control the whole site converts through. It is now
     the same green darkened, which is the value --accent already carries for
     green text on cream, so nothing new enters the palette. 5.45:1. */
  --green:#017A3C;       /* button fill — a brand constant, holds in both modes */
  --accent:#006B32;      /* green text: 5.80:1 on cream, 5.25:1 on raised stock */
  --link:#303090;        /* navy doing duty as a link; diverges from the band in dark */
  --navy:#303090;        /* the band ground. A brand block: it never inverts */
  --olive:#5E6347;
  --grey:#8B8578;        /* rules and marks only — fails AA as text */

  /* On the navy band. The band is navy in both modes, so none of these flip —
     which is precisely why --link had to be split away from --navy. */
  --on-navy:#F2EFE7;
  --on-navy-rgb:242,239,231;
  --accent-on-navy:#8FE3B4;

  /* The green button. Light darkens on hover; dark brightens and flips the
     label to ink, because white on the lightened green reads 2.38:1. */
  --on-green:#FFFFFF;
  --green-rgb:1,122,60;
  --btn-hover-bg:#01A453;   /* hover brightens back toward the logo green */
  --btn-hover-fg:#14130F;   /* ink, because white on the brighter green is 3.26:1 */

  --plate-bg:rgba(16,16,40,0.32);
  --hero-scrim:linear-gradient(to top, rgba(10,10,34,0.97) 0%, rgba(16,16,52,0.94) 34%, rgba(26,26,80,0.78) 68%, rgba(38,38,116,0.64) 100%);
  /* From 900px: dark behind the copy on the left, clearing to the right so
     the works show, with the foot darkened behind the facts strip. Phones
     keep the vertical scrim; copy spans their full width. Section 18. */
  --hero-scrim-wide:linear-gradient(to top, rgba(8,8,26,0.92) 0%, rgba(8,8,26,0.88) 17%, rgba(8,8,26,0) 36%), linear-gradient(to right, rgba(10,10,34,0.95) 0%, rgba(14,14,44,0.90) 40%, rgba(16,16,52,0.86) 60%, rgba(22,22,66,0.40) 80%, rgba(30,30,90,0.14) 100%);
  --mark-light:block;
  --mark-dark:none;
  --shot-filter:none;
  /* design.md section 18: the one shadow tier. Floating button, contact
     sheet and map note only. */
  --shadow-lift:0 1px 2px rgba(20,19,15,0.06),0 10px 28px -6px rgba(20,19,15,0.18);

  /* Dark sources. Warm near-black rather than neutral grey: the cream has a
     yellow cast and the dark keeps it. Ratios recorded in design.md. */
  --dark-ground:#14130F;
  --dark-paper:#24221A;
  --dark-ink:#EDE9DE;
  --dark-muted:#ABA496;
  --dark-rule:#3F3C30;
  --dark-rule-soft:#302E24;
  --dark-green:#017A3C;
  --dark-accent:#4FD08A;
  --dark-link:#A8AAF5;
  --dark-navy:#303090;
  --dark-olive:#7E8564;
  --dark-grey:#6E695E;
  --dark-on-navy:#F2EFE7;
  --dark-accent-on-navy:#8FE3B4;
  --dark-on-green:#FFFFFF;
  --dark-btn-hover-bg:#3ACB80;
  --dark-btn-hover-fg:#14130F;
  --dark-plate-bg:rgba(8,8,20,0.46);
  --dark-hero-scrim:linear-gradient(to top, rgba(6,6,20,0.98) 0%, rgba(10,10,32,0.95) 34%, rgba(16,16,48,0.82) 68%, rgba(22,22,66,0.70) 100%);
  --dark-hero-scrim-wide:linear-gradient(to top, rgba(5,5,18,0.95) 0%, rgba(5,5,18,0.90) 17%, rgba(5,5,18,0) 36%), linear-gradient(to right, rgba(6,6,20,0.97) 0%, rgba(8,8,28,0.93) 40%, rgba(10,10,32,0.88) 60%, rgba(16,16,48,0.46) 80%, rgba(22,22,66,0.22) 100%);
  --dark-mark-light:none;
  --dark-mark-dark:block;
  --dark-shot-filter:brightness(.88) saturate(.94);
  --dark-shadow-lift:0 1px 2px rgba(0,0,0,0.40),0 12px 32px -6px rgba(0,0,0,0.60);

  --ease-out:cubic-bezier(0.23,1,0.32,1);
  --ease-in-out:cubic-bezier(0.77,0,0.175,1);
  --pad:20px;
  --hdr:70px;
  --lockh:44px;
  /* Section 18 loosened the square corners for controls and panels only.
     Photographs, sections and tables stay square. */
  --radius:6px;
  --radius-sm:4px;
}

/* The OS preference, unless the visitor has explicitly chosen light. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){color-scheme:dark;--ground:var(--dark-ground);--paper:var(--dark-paper);--ink:var(--dark-ink);--muted:var(--dark-muted);--rule:var(--dark-rule);--rule-soft:var(--dark-rule-soft);--green:var(--dark-green);--accent:var(--dark-accent);--link:var(--dark-link);--navy:var(--dark-navy);--olive:var(--dark-olive);--grey:var(--dark-grey);--on-navy:var(--dark-on-navy);--accent-on-navy:var(--dark-accent-on-navy);--on-green:var(--dark-on-green);--btn-hover-bg:var(--dark-btn-hover-bg);--btn-hover-fg:var(--dark-btn-hover-fg);--plate-bg:var(--dark-plate-bg);--hero-scrim:var(--dark-hero-scrim);--hero-scrim-wide:var(--dark-hero-scrim-wide);--mark-light:var(--dark-mark-light);--mark-dark:var(--dark-mark-dark);--shot-filter:var(--dark-shot-filter);--shadow-lift:var(--dark-shadow-lift)}
}
/* The toggle, which wins in both directions. */
:root[data-theme="dark"]{color-scheme:dark;--ground:var(--dark-ground);--paper:var(--dark-paper);--ink:var(--dark-ink);--muted:var(--dark-muted);--rule:var(--dark-rule);--rule-soft:var(--dark-rule-soft);--green:var(--dark-green);--accent:var(--dark-accent);--link:var(--dark-link);--navy:var(--dark-navy);--olive:var(--dark-olive);--grey:var(--dark-grey);--on-navy:var(--dark-on-navy);--accent-on-navy:var(--dark-accent-on-navy);--on-green:var(--dark-on-green);--btn-hover-bg:var(--dark-btn-hover-bg);--btn-hover-fg:var(--dark-btn-hover-fg);--plate-bg:var(--dark-plate-bg);--hero-scrim:var(--dark-hero-scrim);--hero-scrim-wide:var(--dark-hero-scrim-wide);--mark-light:var(--dark-mark-light);--mark-dark:var(--dark-mark-dark);--shot-filter:var(--dark-shot-filter);--shadow-lift:var(--dark-shadow-lift)}
:root[data-theme="light"]{color-scheme:light}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--ground);color:var(--ink);
  font-family:'Newsreader','Newsreader fallback',Georgia,serif;
  font-size:18px;line-height:1.6;text-wrap:pretty;-webkit-font-smoothing:antialiased;
  /* .lead and .mapbleed are 100vw, which includes a classic scrollbar, so the
     page scrolled sideways by its width on Windows. clip, not hidden: hidden
     would make body a scroll container and break the sticky header. */
  overflow-x:clip}
::selection{background:rgba(var(--green-rgb),0.2)}
img,svg{display:block;max-width:100%}
img{align-self:flex-start}
p{margin:0}
h1,h2,h3,h4{margin:0;font-family:'Newsreader','Newsreader fallback',Georgia,serif;font-weight:600;
  line-height:1.06;letter-spacing:-0.015em;text-wrap:balance}
h1{font-size:44px}
h2{font-size:32px}
h3{font-size:21px;letter-spacing:-0.01em}
a{color:var(--link);text-decoration:none;transition:color 150ms ease}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.mono{font-family:'IBM Plex Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  font-feature-settings:'tnum' 1}
.wrap{padding-left:var(--pad);padding-right:var(--pad);max-width:1360px;margin:0 auto}
/* Symmetric padding on every section made the pages read as a stack of
   identical slabs. Leading space is now larger than trailing space, which is
   how print leads into a section rather than fencing it. */
.sec{padding-top:60px;padding-bottom:44px}
.sec-tight{padding-top:44px;padding-bottom:36px}
.col{display:flex;flex-direction:column}
.row{display:flex;flex-direction:row}
.surface{background:var(--paper);border-top:1px solid var(--rule);
  border-bottom:1px solid var(--rule)}

/* three-deck hierarchy: mono kicker, serif headline, italic dek */
.eyebrow{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:11px;
  font-weight:500;letter-spacing:0.16em;text-transform:uppercase;color:var(--accent)}
.eyebrow .dim{color:var(--muted)}
.band .eyebrow .dim{color:rgba(var(--on-navy-rgb),0.55)}
.lede{font-size:21px;line-height:1.45;font-style:italic;color:var(--muted);max-width:38ch}
.muted{color:var(--muted)}
.small{font-size:16px;line-height:1.55}
.rule{height:1px;background:var(--rule);border:0;margin:0}
.rule-ink{height:1px;background:var(--ink);border:0;margin:0}
.sechead{display:flex;flex-direction:column;gap:14px;padding-bottom:32px}
.quote{font-family:'Newsreader','Newsreader fallback',Georgia,serif;font-style:italic;font-size:24px;
  line-height:1.4;letter-spacing:-0.01em}

/* magazine chrome: no radius anywhere, no shadow anywhere */
.chip{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:11px;
  letter-spacing:0.09em;text-transform:uppercase;color:var(--muted);
  border:1px solid var(--rule);border-radius:var(--radius-sm);padding:6px 10px;background:transparent;
  transition:border-color 150ms ease,color 150ms ease}
.pills{display:flex;flex-wrap:wrap;gap:0 20px;align-items:baseline;padding-top:26px;
  border-top:1px solid var(--rule-soft);margin-top:36px}
.pill{font-size:16px;color:var(--muted);padding:6px 0}
.pill::after{content:" /";color:var(--rule)}
.pill:last-child::after{content:""}

.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;
  background:var(--green);color:var(--on-green);font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;
  font-size:13px;font-weight:500;letter-spacing:0.1em;text-transform:uppercase;
  border-radius:var(--radius);line-height:1;min-height:52px;padding:0 26px;flex-wrap:nowrap;
  transition:background-color 180ms ease,transform 160ms var(--ease-out)}
.btn svg,.btn-ghost svg{flex:0 0 auto}
.btn:active{transform:scale(0.97)}
.btn-ghost{display:inline-flex;align-items:center;justify-content:center;gap:8px;
  color:var(--ink);border:1px solid var(--ink);border-radius:var(--radius);background:transparent;
  font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:13px;font-weight:500;
  letter-spacing:0.1em;text-transform:uppercase;line-height:1;min-height:52px;padding:0 22px;
  transition:background-color 180ms ease,color 180ms ease,transform 160ms var(--ease-out)}
.btn-ghost:active{transform:scale(0.97)}
.iconbtn:active{transform:scale(0.97)}

.kv{display:flex;justify-content:space-between;gap:16px;align-items:baseline;
  padding:12px 0;border-bottom:1px solid var(--rule-soft)}
.kvk{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:11px;
  letter-spacing:0.1em;text-transform:uppercase;color:var(--muted)}
.kvv{font-size:17px;text-align:right}

.band{background:var(--navy);color:var(--on-navy)}
/* The closing band. One measure, one rule, one row - see cta(). */
.cta-head{font-size:clamp(32px,4.8vw,60px);max-width:24ch}
.cta-rule{height:1px;background:rgba(var(--on-navy-rgb),0.34);
  margin-top:26px;margin-bottom:24px}
.cta-row{display:grid;gap:26px;grid-template-columns:minmax(0,1fr)}
.cta-dek{color:rgba(var(--on-navy-rgb),0.78);max-width:52ch}
.cta-act{gap:12px;align-items:stretch}
/* The button fills its track rather than sizing to its label: it is the one
   control the whole site converts through and it should not be the smallest
   thing in the row. The arrow travels on hover - 4px, on the site's own
   ease-out, which is the same vocabulary the float button already uses. */
.cta-act .btn{width:100%;justify-content:space-between;gap:14px}
.btn .arrow{transition:transform 160ms var(--ease-out)}
.band h1,.band h2,.band a{color:var(--on-navy)}
/* .band a (0,1,1) outranks .btn (0,1,0), so the WhatsApp button inside the
   CTA band was painted cream instead of the colour it declares - 2.84:1
   on the green, on the one control the whole site converts through. */
.band .btn{color:var(--on-green)}
.band .eyebrow{color:var(--accent-on-navy)}
.band .kvk{color:rgba(var(--on-navy-rgb),0.62)}
.band .lede,.band .muted,.band .small{color:rgba(var(--on-navy-rgb),0.78)}

.spec{width:100%;border-collapse:collapse;
  font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:14px}
.spec th{text-align:left;font-weight:500;letter-spacing:0.1em;text-transform:uppercase;
  color:var(--muted);font-size:11px;padding:10px 14px 10px 0;
  border-bottom:1px solid var(--ink);white-space:nowrap}
.spec td{padding:13px 14px 13px 0;border-bottom:1px solid var(--rule-soft);vertical-align:top}
.spec td:last-child{color:var(--ink);text-align:right;padding-right:0}
.spec tbody tr{transition:background-color 120ms ease}
.matrix{min-width:760px}
.scroll{position:relative}
.scroll-hint{display:none}
.micro{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:11px;
  letter-spacing:0.12em;text-transform:uppercase}
.micro-accent{color:var(--accent)}
.micro-grey{color:var(--muted)}
.matrix th[scope="row"]{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;
  font-size:12px;letter-spacing:0.06em;text-transform:none;color:var(--muted);
  font-weight:400;text-align:left;white-space:nowrap;padding-right:24px;
  border-bottom:1px solid var(--rule-soft)}
.matrix thead th{vertical-align:bottom;font-family:'Newsreader','Newsreader fallback',Georgia,serif;
  font-size:16px;letter-spacing:0;text-transform:none;color:var(--ink);
  font-weight:600;white-space:normal;min-width:150px}
.matrix thead th:first-child{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;
  font-size:11px;letter-spacing:0.1em;text-transform:uppercase;color:var(--muted);
  font-weight:500}
.matrix td{text-align:left;color:var(--ink)}

/* Cut to two edges during the boxiness pass, and put back. The theory was
   sound - a four-sided outline is the boxiest signal available - but this
   element is the exception that proves it: it is a table of the company's
   name, address and numbers, and a table wants to be closed. Two edges left
   the top rule running past a left border that stopped short, which read as
   an unfinished box rather than an open one. The boxiness fixes that hold
   are the ones about rhythm and asymmetry; this one was cargo-culted onto
   an element whose job is to look complete. */
.sheet{background:var(--ground);border:1px solid var(--ink);border-radius:var(--radius);
  padding:24px 26px 20px;color:var(--ink);box-shadow:var(--shadow-lift)}
.band .sheet{background:var(--ground)}
.band .sheet .kvk{color:var(--muted)}
.band .sheet .kvv,.band .sheet .mono{color:var(--ink)}
.band .sheet a{color:var(--link)}

.art{width:100%;height:auto;background:var(--paper);padding:16px;border-radius:0}
/* A paper ground behind every photograph framed each one in a visible box on
   a cream page. The ground is the page now, so a photograph ends where its
   own pixels end rather than at the edge of a tinted rectangle. */
.shot{width:100%;height:auto;object-fit:cover;background:transparent;border-radius:0}
/* Photographs read as glaring on a dark ground. Restrained on purpose: a
   buyer judging film clarity should see the film, not a mood. */
main img{filter:var(--shot-filter)}
.grid{display:grid;gap:20px;grid-template-columns:repeat(1,minmax(0,1fr))}
.grid-2{display:grid;gap:36px;grid-template-columns:repeat(1,minmax(0,1fr))}
.card{border:0;border-top:1px solid var(--ink);border-radius:0;background:transparent;
  padding-top:18px;transition:none}
.stat{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:clamp(44px,4.4vw,64px);
  letter-spacing:-0.03em;color:var(--ink);line-height:0.95;font-weight:500}
.band .stat{color:var(--on-navy)}
.unit{font-size:0.42em;letter-spacing:0;margin-left:3px;color:var(--accent)}
.band .unit{color:var(--accent-on-navy)}
.cell{padding:22px 0;display:flex;flex-direction:column;gap:10px}
.statrow,.statrow-light{border-top:1px solid var(--ink);border-bottom:1px solid var(--rule)}

/* the numbered editorial spread that replaces the card grid */
/* .feature is the one row that is a direct child of the padded .wrap, so its
   background stopped at the gutter while the page around it did not - a tinted
   rectangle with two cut edges rather than a highlighted row. The rule and the
   tint both move onto a pseudo-element that bleeds back out over the gutter, so
   they always share an edge and the row reads as one band whether it is
   highlighted or not. Bleeding by --pad lands exactly on the wrap's outer edge,
   which keeps it inside the 1360px measure on a wide screen.

   .client and .cert-row are not treated this way and do not need it: they sit
   inside .cell and .certs, fill those containers edge to edge already, and
   bleeding them would push them across the neighbouring cell's border-left. */
.feature{position:relative;isolation:isolate;
  display:grid;gap:20px;grid-template-columns:minmax(0,1fr);padding:32px 0}
.feature::before{content:"";position:absolute;z-index:-1;pointer-events:none;
  top:0;bottom:0;left:calc(var(--pad) * -1);right:calc(var(--pad) * -1);
  border-top:1px solid var(--rule);transition:background-color 180ms ease}
.feature:first-of-type::before{border-top-color:var(--ink)}
.feature h3{padding-top:8px;color:var(--ink);font-size:clamp(26px,2.6vw,36px)}
/* Why-us, section 18. The stacked list of section 16 became a numbered 2x2
   grid: the index numeral is the one large mark in the block, so four
   short claims read as four things to check rather than a paragraph. */
.why-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:0 56px;
  border-top:1px solid var(--ink)}
.why{display:flex;flex-direction:column;gap:10px;padding:28px 0 32px;
  border-bottom:1px solid var(--rule-soft)}
.why-num{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:44px;
  line-height:1;letter-spacing:-0.03em;font-weight:500;color:var(--accent);
  font-feature-settings:'tnum' 1;padding-bottom:6px}
.why .dek{font-style:italic;color:var(--muted);max-width:52ch}
/* The measure-of-us photograph runs the full measure, 4:3 on a phone and
   21:9 from 900px; boxed into half a row it left the section mostly empty.
   Its figures sit two by two on a phone and four across beneath it on a desk. */
.measure-shot .ph img{width:100%;aspect-ratio:4/3;object-fit:cover}
.statrow-light{grid-template-columns:repeat(2,minmax(0,1fr))}
.feature .dek{font-style:italic;color:var(--muted);padding-top:8px}
.feature .go{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:11px;
  letter-spacing:0.12em;text-transform:uppercase;padding-top:14px;display:inline-block}
/* The product photographs are square, and a fixed height with object-fit:cover
   threw away 43% of each one on a phone and 57% on a desktop - roll tops and bag
   handles cut off, which on a page whose whole argument is "here is exactly what
   we make" is the one thing the photograph must not do.

   The box is square to match the source, so nothing is cropped, and `contain`
   rather than `cover` because the owner will supply real photographs through the
   admin and they will not all be square. For today's assets the two are pixel
   identical; for tomorrow's, contain shows the whole product and pads with paper
   instead of cutting into it. */
.feature img{width:100%;aspect-ratio:1/1;object-fit:contain;
  background:transparent}

.clients{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 24px}
.client{font-size:16px;padding:10px 0;border-bottom:1px solid var(--rule-soft)}
.certs{display:flex;flex-direction:column;padding-top:30px;margin-top:30px;
  border-top:1px solid var(--rule);max-width:520px}
.quote-card{border-top:1px solid var(--ink);padding-top:20px;background:transparent}
.regs{border-top:1px solid var(--ink)}
/* Row separators sit on the hairline; only chapter breaks get --rule. */
.reg{display:grid;grid-template-columns:minmax(0,1fr);gap:14px;padding:30px 0;
  border-bottom:1px solid var(--rule-soft)}
.reg-title{font-size:26px}
.reg-date{font-size:11px;letter-spacing:0.14em;text-transform:uppercase;
  color:var(--accent);white-space:nowrap}
.reg-means{border-left:3px solid var(--accent);padding-left:20px;color:var(--ink)}
.gate{margin:0}
.gate .shot{aspect-ratio:16/10;object-fit:cover}
.cert-row{display:flex;justify-content:space-between;gap:24px;align-items:baseline;
  padding:20px 0;border-bottom:1px solid var(--rule-soft)}
.cert-row:first-child{border-top:1px solid var(--ink)}
/* The map and the address panel were two rectangles side by side - the most
   literal boxiness left on the site. The map now bleeds the full viewport
   and the panel sits over it, which is also how every map anyone has
   actually used behaves. */
.mapbleed{position:relative;width:100vw;margin-left:calc(50% - 50vw)}
.mapoverlay{position:static}
.mapnote{background:var(--ground);border-top:2px solid var(--ink);
  padding:24px var(--pad) 20px;display:flex;flex-direction:column;gap:16px}
.mapwrap{border:0;background:var(--ground);aspect-ratio:4/3;
  display:flex;align-items:center;justify-content:center}
.mapwrap{position:relative}
/* Over the facade rather than beside it, so the facade can still be on
   screen while the frame paints behind it. */
.mapwrap iframe{position:absolute;inset:0;width:100%;height:100%;border:0;
  display:block;opacity:0;transition:opacity 200ms var(--ease-out)}
.mapwrap iframe.painted{opacity:1}
.mapface{transition:opacity 200ms var(--ease-out)}
.mapface.gone{opacity:0}
.maprefresh{position:absolute;right:8px;bottom:8px;z-index:2;cursor:pointer;
  font:inherit;font-size:12px;letter-spacing:0.08em;text-transform:uppercase;
  padding:6px 10px;border:1px solid var(--ink);border-radius:var(--radius-sm);background:var(--ground);
  color:var(--ink)}
.maprefresh[hidden]{display:none}
.mapface{display:flex;flex-direction:column;gap:18px;align-items:center;
  text-align:center;padding:32px;max-width:38ch}
.ask{font-size:12px;letter-spacing:0.1em;text-transform:uppercase;color:var(--accent);
  align-self:flex-start;border-bottom:1px solid var(--rule);padding-bottom:4px}
/* The one photograph on the site that runs the full width of the viewport,
   past the 1360px measure everything else obeys. One per page, on About
   only: a bleed that repeats is just another column. */
.lead{width:100vw;margin-left:calc(50% - 50vw);overflow:hidden}
.lead img{width:100%;height:clamp(300px,52vw,640px);object-fit:cover}
/* Section 18. Two CSS columns broke a sentence across the gap and left the
   pull-quote half-width beneath with the right of the page empty. The text
   is one readable column; the quote stands beside it on a desk and after
   it on a phone, which is also the reading order in the markup. */
.story{display:grid;grid-template-columns:minmax(0,1fr);gap:36px;align-items:start}
.story-body{max-width:66ch}
.story-aside .pullquote{margin-top:0;max-width:none}
/* What is on the floor, section 18. Three equal photographs ran as a strip
   under the counts. Film blowing now leads, tall, with the other two stacked
   beside it; on a phone all three stack at 4:3. */
.plant-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:28px;padding-top:40px}
.plant-grid figure{margin:0;display:flex;flex-direction:column}
.plant-grid .shot{aspect-ratio:4/3;object-fit:cover}
/* The company register, section 18: two titled groups instead of eight rows
   in a narrow column. Delivery: the intro moves into the section head and
   the three facts fill the width as cells, the same pattern as the counts. */
.register{display:grid;grid-template-columns:minmax(0,1fr);gap:40px}
.register-head{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:11px;
  letter-spacing:0.14em;text-transform:uppercase;color:var(--ink);
  padding-bottom:12px;border-bottom:1px solid var(--ink)}
.register .kv:last-child{border-bottom:0}
.sechead-dek{max-width:46ch}
.deliver-v{font-size:clamp(21px,2vw,26px);line-height:1.25}
/* Sustainability, section 18. The page's two checkable numbers lead at
   display size; the honest position takes two columns aligned with the
   materials grid above it, and keeps its small type. */
.mat-figs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:24px;
  border-top:1px solid var(--ink);border-bottom:1px solid var(--rule)}
.honest{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}
.honest p{max-width:62ch}
/* Contact, section 18. The three things to send take the why-us numerals.
   The map band, blank until Google paints, sits on a quiet grid drawn in
   the palette's own hairline, with the facade as a card that names the
   place. The frame still crossfades over it when it arrives. */
.send-item{border-top:1px solid var(--ink);padding-top:24px;gap:10px}
.mapwrap{background-color:var(--paper);
  background-image:linear-gradient(var(--rule-soft) 1px,transparent 1px),
    linear-gradient(90deg,var(--rule-soft) 1px,transparent 1px);
  background-size:56px 56px;background-position:center}
.mapface{background:var(--ground);border:1px solid var(--rule);border-radius:var(--radius)}
.story .cols p{margin-bottom:18px}
.dropcap{float:left;font-size:80px;line-height:0.78;padding:6px 10px 0 0;
  font-weight:600;color:var(--accent)}
.pullquote{font-style:italic;font-size:28px;line-height:1.35;color:var(--ink);
  border-top:1px solid var(--ink);border-bottom:1px solid var(--ink);
  padding:26px 0;margin-top:34px;max-width:34ch}

/* No curve anywhere. The logo swoosh was tried three times as a band
   divider and once as a 64px arc under each section heading, and the arc
   was the version that finally made the point: at that size, in accent
   green, under a heading, it read as a stray hair on the screen rather than
   as a mark. Reported in exactly those words.

   The conclusion after four attempts is not that the execution was wrong
   each time. It is that this layout has no place for an ornament: every
   edge on the site is doing structural work, and a decorative line among
   them is read as one that failed to. Section 6's rule stands unqualified -
   no radius, no shadow, and now no curve. The mark lives in the logo, which
   is where it already worked. */

/* chrome */
header{border-bottom:1px solid var(--ink);background:var(--ground);
  position:sticky;top:0;z-index:20}
/* The header shrinks once the page has moved, which the research listed as
   adopt-with-shrink. The height lives on :root rather than on the header so
   that .jump, which is sticky directly beneath it, moves with it - otherwise
   the two separate by the difference and content slides through the gap. */
header .wrap{display:flex;justify-content:space-between;align-items:center;
  height:var(--hdr);transition:height 220ms var(--ease-out)}
.lock{display:flex;align-items:center;gap:10px}
.hdr-actions{display:flex;align-items:center;gap:8px}
/* Both marks ship and the theme picks one. See write_dark_logo() for why this
   is a second asset rather than a filter. */
.mark-light{display:var(--mark-light)}
.mark-dark{display:var(--mark-dark)}
.lock img{height:var(--lockh);width:auto;transition:height 220ms var(--ease-out)}
:root.scrolled{--hdr:58px;--lockh:34px}
.foot-sign img{height:30px;width:auto}
/* The button shows the state it is in. Without JS it renders "system", which
   is the truth: nothing is stored, so the OS preference is what applies. */
.themebtn svg{display:none}
/* .iconbtn sets display:flex, which would override the hidden attribute
   the toggle carries while dark mode is switched off (DARK_MODE). */
.themebtn[hidden]{display:none}
.themebtn[data-theme-toggle="system"] .i-system,
.themebtn[data-theme-toggle="light"] .i-light,
.themebtn[data-theme-toggle="dark"] .i-dark{display:block}
.iconbtn{width:44px;height:44px;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--rule);border-radius:var(--radius-sm);background:transparent;cursor:pointer}
nav.main{display:none}
#menu{display:grid;grid-template-rows:0fr;overflow:hidden;background:var(--ground);
  transition:grid-template-rows 180ms var(--ease-out)}
/* The rows animate to auto height, which is the point of the 0fr/1fr grid and
   why this is not a keyframe - it can be interrupted halfway. But height
   alone clips the links into view like a roller shutter, so the contents
   fade alongside it. */
#menu>div{min-height:0;overflow:hidden;opacity:0;
  transition:opacity 160ms ease}
#menu.open>div{opacity:1;transition:opacity 200ms ease 60ms}
#menu.open{grid-template-rows:1fr;border-bottom:1px solid var(--ink);
  transition:grid-template-rows 240ms var(--ease-out)}

/* The panel opened and the three bars stayed three bars. aria-expanded flipped,
   so a screen reader was told; nobody looking at the screen was. The outer two
   meet in the middle and cross, the middle one goes - the 240ms matches the
   panel above it, so glyph and panel land in the same frame.
   Purpose is state indication, and the gate is frequency: once a page. */
/* transform-box is view-box on an SVG child, so `center` is the centre of the
   24x24 viewBox, not of the bar. The order therefore matters: translate first,
   in the bar's own coordinates, to bring it onto the centre line, and rotate
   second, about that same centre, so each bar turns in place. Written the
   other way round the bars rotate off the centre and swing wide. */
.menu .bar{transform-box:view-box;transform-origin:center;
  transition:transform 240ms var(--ease-out),opacity 160ms ease}
.menu.open .bar1{transform:rotate(45deg) translateY(5px)}
.menu.open .bar2{opacity:0}
.menu.open .bar3{transform:rotate(-45deg) translateY(-5px)}
#menu a{display:block;padding:15px var(--pad);border-top:1px solid var(--rule);
  font-size:18px;color:var(--ink);max-width:1360px;margin:0 auto}
#menu a.here{color:var(--accent)}
/* Which of the six categories you are actually in. The chip already
   transitions colour and border at 150ms, so this needs no motion of its own -
   it needed a state. */
/* Six identical bordered rectangles in a row, directly under a bordered
   header. They are text links now, and the active one carries an accent
   rule rather than a box. */
.jump .jumplink{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;
  font-size:12px;letter-spacing:0.1em;text-transform:uppercase;color:var(--muted);
  padding:6px 0;border-bottom:2px solid transparent;
  transition:color 150ms ease,border-color 150ms ease}
.jump .here{color:var(--ink);border-bottom-color:var(--accent)}
.jump{position:sticky;top:var(--hdr);transition:top 220ms var(--ease-out);
  z-index:15;background:var(--ground);overflow:hidden;
  border-bottom:1px solid var(--rule);padding-top:12px;padding-bottom:12px}
/* Section 18: one indicator slides to the category in view, instead of
   each link switching its own underline. The link underline stays as the
   state for anyone the script does not reach; .jump-live retires it. The
   bar is 100px wide and scaled to each link, so only transform moves. */
.jump-row{position:relative}
.jump-ind{position:absolute;left:0;top:0;width:100px;height:2px;
  background:var(--accent);transform-origin:left top;opacity:0;pointer-events:none}
.jump-live .jumplink.here{border-bottom-color:transparent}
/* The range, section 18: the four figures a buyer scans for lead at display
   size, and the full table stands beside them. The table never animates. */
/* Not .range: the table carries that class, and a grid rule on it set the
   table's header beside its body. */
.range-block{display:grid;grid-template-columns:minmax(0,1fr);gap:28px}
.range-figs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:24px}
/* Set exactly like .spec th, so the two halves open on the same rule. */
.range-head{grid-column:1/-1;font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;
  font-size:11px;font-weight:500;letter-spacing:0.1em;text-transform:uppercase;
  color:var(--muted);padding:10px 0;border-bottom:1px solid var(--ink)}
.range-fig{display:flex;flex-direction:column;justify-content:center;gap:8px;
  padding:20px 0 22px;border-bottom:1px solid var(--rule-soft)}
.range-v{font-size:clamp(24px,2.2vw,32px);line-height:1.1;letter-spacing:-0.02em;color:var(--ink)}
/* The glossary: four left-aligned cards instead of right-aligned prose. */
.gloss{display:grid;grid-template-columns:minmax(0,1fr);gap:0 32px}
.gloss-item{display:flex;flex-direction:column;gap:12px;padding:24px 0 30px;
  border-top:1px solid var(--ink)}
.gloss .gloss-name{font-size:clamp(24px,2.2vw,32px);letter-spacing:-0.01em;
  text-transform:none;color:var(--ink);line-height:1.1}
.gloss-item p{color:var(--muted)}
section[id]{scroll-margin-top:120px}
/* Nothing in the world appears out of nothing. The site's only conversion
   path arrives from 8px below rather than materialising in place.
   bottom adds the home-indicator inset on notched phones - env() falls back
   to 0 where it is unsupported, so this is additive, never a regression. */
.float{position:fixed;right:16px;bottom:calc(16px + env(safe-area-inset-bottom));z-index:30;opacity:0;
  transform:translateY(8px);pointer-events:none;
  transition:opacity 220ms var(--ease-out),transform 220ms var(--ease-out)}
.float.shown{opacity:1;transform:none;pointer-events:auto}
.float .btn{box-shadow:var(--shadow-lift)}
footer{border-top:1px solid var(--ink);background:var(--ground)}
.foot-cols{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:28px;
  padding-top:48px;padding-bottom:32px}
.foot-sign{display:flex;align-items:center;gap:12px}
.foot-sign img{height:30px;width:auto}
.foot-base{display:flex;flex-direction:column;gap:8px;padding-top:18px;
  padding-bottom:26px;border-top:1px solid var(--rule);
  font-size:11px;letter-spacing:0.09em;text-transform:uppercase;color:var(--muted)}
.skip{position:absolute;left:-9999px;top:0;z-index:100;background:var(--ink);
  color:var(--ground);padding:12px 18px;font-weight:600}
.skip:focus{left:0;color:var(--ground)}

/* hero: the works, full bleed, with the headline standing in the dark foot of
   the frame. The photograph is the argument, so it is barely blurred; the
   specification that used to share this band now sits directly beneath it. */
.hero{position:relative;isolation:isolate;overflow:hidden;
  min-height:76vh;display:flex;align-items:flex-end}
.hero .bg{position:absolute;inset:0;z-index:-2}
.hero .bg img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:saturate(1.04);opacity:0;transform:scale(1.1);
  animation:heroPlate 36s var(--ease-in-out) infinite}
.hero .bg img:nth-child(2){animation-delay:9s}
.hero .bg img:nth-child(3){animation-delay:18s}
.hero .bg img:nth-child(4){animation-delay:27s}
@keyframes heroPlate{
  0%{opacity:0;transform:scale(1.1)}
  4%{opacity:1}
  22%{opacity:1}
  26%{opacity:0}
  100%{opacity:0;transform:scale(1.17)}
}
.hero .scrim{position:absolute;inset:0;z-index:-1;background:var(--hero-scrim)}
.hero .wrap{width:100%}
.hero h1{font-size:clamp(40px,7.4vw,88px);line-height:1.03;letter-spacing:-0.015em;
  max-width:15ch;text-wrap:balance}
/* the specification, reduced to the four numbers a buyer scans for, on a rule
   under the CTA. The full sheet is one screen down. */
/* Section 18: the four figures a buyer scans for, as a strip across the
   hero's foot rather than a 12px line under the buttons. */
.hero .facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));margin-top:32px;
  border-top:1px solid rgba(var(--on-navy-rgb),0.26)}
.hero .fact{display:flex;flex-direction:column;gap:6px;padding:14px 14px 2px 0}
/* On a phone the 36-character materials value takes the strip's first row
   and the three short figures share the row beneath: still two rows. */
.hero .fact:first-child{grid-column:1/-1}
.hero .fact em{font-style:normal;font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;
  font-size:11px;letter-spacing:0.1em;text-transform:uppercase;color:rgba(var(--on-navy-rgb),0.68)}
.hero .fact b{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-weight:500;
  font-size:clamp(16px,1.5vw,22px);line-height:1.25;letter-spacing:-0.01em;color:var(--on-navy)}

/* motion: page-turn fades and a slow Ken Burns. Nothing lifts, nothing scales. */
@keyframes rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.rise>*{animation:rise 420ms var(--ease-out) both}
.rise>*:nth-child(2){animation-delay:70ms}
.rise>*:nth-child(3){animation-delay:140ms}
.rise>*:nth-child(4){animation-delay:210ms}
.rise>*:nth-child(5){animation-delay:280ms}
.rise-late{animation:rise 420ms var(--ease-out) both;animation-delay:210ms}
/* Opacity alone means the section was always there and merely became
   visible. The 6px lift makes it arrive, in the same vocabulary the hero
   already speaks, and 620ms was long enough that a brisk scroller read
   half-transparent text. */
[data-reveal]{opacity:0;transform:translateY(6px)}
[data-reveal].seen{opacity:1;transform:none;
  transition:opacity 420ms var(--ease-out),transform 420ms var(--ease-out)}

/* Press feedback. Three elements on the site used to acknowledge a touch -
   the two buttons and the icon button - and everything else was inert. The
   worst of it was .feature, which is the home page's centrepiece and a link
   whose only response was a hover colour, and hover does not exist on the
   phone most of these buyers arrive on.

   Rows and links flash their ground rather than scaling: a full-bleed row
   with a photograph in it wobbles when scaled, and the layout is built on
   flat rules, so paper is the language already in use. */
.client,.cert-row{transition:background-color 180ms ease}
.client:active,.cert-row:active{background:var(--paper)}
.feature:active::before{background:var(--paper)}
a.chip,.ask,nav.main .links a,.foot-cols a,#menu a{
  transition:color 150ms ease,border-color 150ms ease,background-color 150ms ease}
a.chip:active{background:var(--paper);border-color:var(--ink);color:var(--ink)}
.ask:active,nav.main .links a:active,.foot-cols a:active{color:var(--accent)}
#menu a:active{background:var(--paper)}

/* design.md section 18. GSAP owns the entrances; these are the parts the
   stylesheet still has to hold. A headline that is about to be split into
   lines waits unseen, but only behind .motion, which the script lifts on
   every path - GSAP missing, the CDN hanging, reduced motion, printing. */
.motion [data-lines]{visibility:hidden}
.motion .rise>[data-lines]{animation:none}
/* Each split line sits in an overflow-clipped mask, and a display line-height
   near 1.0 leaves the tails of y, g and p outside it. The mask gains room below
   the baseline and gives it back as a negative margin, so nothing moves. */
[data-lines] .line-mask{padding-bottom:0.14em;margin-bottom:-0.14em}
/* A photograph frame. The zoom uses the individual scale property, not
   transform, so it composes with the transform GSAP drifts the image by. */
.ph{display:block;overflow:hidden}
.ph img{transition:scale 400ms var(--ease-out)}
/* Links underline from the left rather than switching one on. */
nav.main .links a,.feature .go,.ask{position:relative}
nav.main .links a::after,.feature .go::after,.ask::after{content:"";position:absolute;
  left:0;right:0;bottom:-4px;height:1px;background:currentColor;
  transform:scaleX(0);transform-origin:left center;transition:transform 240ms var(--ease-out)}
.ask::after{bottom:-1px}
nav.main .links a.here::after{transform:scaleX(1)}

/* Hover is a pointer capability, not a given. A touchscreen fires it on tap
   and leaves it stuck until the next tap lands somewhere else, so every
   hover state on the site is gated here. */
@media (hover:hover) and (pointer:fine){
  a:hover{color:var(--accent)}
  a.chip:hover{border-color:var(--ink);color:var(--ink)}
  .btn:hover{color:var(--btn-hover-fg);background:var(--btn-hover-bg)}
  .btn:hover .arrow{transform:translateX(4px)}
  .btn-ghost:hover{background:var(--ink);color:var(--ground)}
  .band .btn:hover{color:var(--btn-hover-fg)}
  .maprefresh:hover{background:var(--ink);color:var(--ground)}
  .ask:hover{color:var(--ink);border-bottom-color:var(--ink)}
  .spec tbody tr:hover{background:var(--paper)}
  .feature:hover::before{background:var(--paper)}
  .feature:hover h3{color:var(--ink)}
  .feature:hover .go{color:var(--accent)}
  .client:hover,.cert-row:hover{background:var(--paper)}
  nav.main .links a:hover::after,.feature:hover .go::after,.ask:hover::after{transform:scaleX(1)}
  .ph:hover img,a:hover .ph img{scale:1.04}
}
@media print{
  [data-reveal]{opacity:1 !important}
  [data-lines]{visibility:visible !important}
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .hero .bg img{animation:none;opacity:0;transform:scale(1.08)}
  .hero .bg img:first-child{opacity:1}
  /* The hero's own entrance. Reduced motion means gentler, not absent: the
     stagger and the 8px lift go, the content stays. This was the one moving
     thing on the page that the setting did not reach. */
  .rise>*,.rise-late{animation:none}
  [data-reveal],[data-reveal].seen{opacity:1;transform:none;transition:none}
  .float{transform:none}
  .btn:active,.btn-ghost:active,.iconbtn:active{transform:none}
  .btn .arrow{transition:none}
  .btn:hover .arrow{transform:none}
  header .wrap,.jump,.lock img{transition:none}
  /* The glyph keeps its end state - an X still means close - and loses only
     the travel between the two. Gentler, not absent. */
  .menu .bar{transition:none}
  .mapwrap iframe,.mapface{transition:none}
  .ph img,nav.main .links a::after,.feature .go::after,.ask::after{transition:none}
  .ph:hover img,a:hover .ph img{scale:none}
}

@media (min-width:900px){
  :root{--pad:80px}
  /* 62/44 is a 1.4x step: headings and subheadings carried near-equal
     weight, so every section header looked like every other one. A bigger
     jump at the top and a smaller one below is the print convention. */
  h1{font-size:80px}
  h2{font-size:46px}
  h3{font-size:25px}
  .lede{font-size:25px}
  .sec{padding-top:110px;padding-bottom:78px}
  .sec-tight{padding-top:72px;padding-bottom:56px}
  .sec + .sec{padding-top:64px}
  .sechead{flex-direction:row;justify-content:space-between;align-items:end;
    gap:32px;padding-bottom:44px}
  .grid{grid-template-columns:repeat(4,minmax(0,1fr));gap:28px}
  .grid-2{grid-template-columns:repeat(2,minmax(0,1fr));gap:64px}
  .hero .grid-2{grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);gap:56px}
  .grid-3{grid-template-columns:repeat(3,minmax(0,1fr))}
  :root{--hdr:88px;--lockh:56px}
  :root.scrolled{--hdr:66px;--lockh:40px}
  nav.main{display:flex;align-items:center;gap:34px}
  nav.main .links{display:flex;gap:30px;font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;
    font-size:12px;letter-spacing:0.1em;text-transform:uppercase}
  nav.main .links a{color:var(--ink)}
  nav.main .links a.here{color:var(--accent)}
  nav.main .tel{font-family:'IBM Plex Mono',ui-monospace,Menlo,monospace;font-size:12px;
    letter-spacing:0.08em;color:var(--ink);border-left:1px solid var(--rule);padding-left:26px}
  .iconbtn.menu{display:none}
  .btn,.btn-ghost{width:auto}
  .cell{padding:30px 30px;border-left:1px solid var(--rule)}
  .cell:first-child{padding-left:0;border-left:0}
  .cell:last-child{padding-right:0}
  .band .cell{border-left-color:rgba(var(--on-navy-rgb),0.24)}
  .float{right:24px;bottom:calc(24px + env(safe-area-inset-bottom))}
  section[id]{scroll-margin-top:150px}
  /* Over the map rather than beside it. .wrap does the horizontal placement,
     so the panel lands on the same left edge as every other page element
     and needs no viewport arithmetic of its own. */
  .mapwrap{aspect-ratio:21/9}
  /* The address note holds the left of the band, so the facade takes the
     right edge of the measure rather than crowding it from the centre. */
  .mapwrap{justify-content:flex-end;
    padding-right:max(var(--pad), calc((100vw - 1360px) / 2 + var(--pad)))}
  .mapoverlay{position:absolute;inset:0;display:flex;align-items:center;
    pointer-events:none}
  .mapoverlay > .wrap{width:100%}
  .mapnote{pointer-events:auto;max-width:390px;padding:26px 28px 22px;
    border-top:0;border-radius:var(--radius);box-shadow:var(--shadow-lift)}
  .reg{grid-template-columns:190px minmax(0,1fr);gap:40px;padding:36px 0}
  .reg-title{font-size:30px}
  .story{grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:88px}
  .story-aside{order:-1;position:sticky;top:calc(var(--hdr) + 40px)}
  .plant-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
  .plant-grid .shot{aspect-ratio:16/10}
  .register{grid-template-columns:repeat(2,minmax(0,1fr));gap:72px}
  /* Two halves of equal weight, divided by a rule. The figures open on a
     header row matching the table's, and their four rows stretch to the
     table's height, so both halves start and end on the same line. */
  .range-block{grid-template-columns:minmax(0,1fr) minmax(0,1fr);column-gap:0;
    row-gap:22px;align-items:stretch}
  .range-figs{grid-template-columns:minmax(0,1fr);grid-template-rows:auto repeat(4,minmax(0,1fr));
    padding-right:64px}
  .range-fig{padding:0}
  .range-sheet{padding-left:64px;border-left:1px solid var(--rule)}
  .range-note{grid-column:2;padding-left:64px}
  .gloss{grid-template-columns:repeat(4,minmax(0,1fr))}
  .hero .scrim{background:var(--hero-scrim-wide)}
  /* The materials value is 36 characters against about 9 for the others,
     so its cell is wider and its value a step smaller: one line from
     about 1100px up, where the rest already sit on one line. */
  .hero .facts{grid-template-columns:minmax(0,1.7fr) repeat(3,minmax(0,1fr));margin-top:44px}
  .hero .fact:first-child b{font-size:clamp(14px,1.25vw,18px)}
  /* The strip made the hero taller; the section's 110px lead-in would put
     it on the fold line at 1440x900 and below it at 1900x860. */
  .hero .sec{padding-top:64px;padding-bottom:44px}
  .hero .fact{padding:22px 28px 4px;border-left:1px solid rgba(var(--on-navy-rgb),0.18)}
  .hero .fact:first-child{padding-left:0;border-left:0;grid-column:auto}
  .mat-figs{column-gap:0}
  .honest{grid-template-columns:repeat(2,minmax(0,1fr));gap:64px;align-items:baseline}
  .plant-grid figure:first-child{grid-column:span 2;grid-row:span 2}
  /* The tall photograph takes whatever height the two stacked beside it
     set, so it is positioned into its frame rather than sized by ratio. */
  .plant-grid figure:first-child .ph{flex:1;position:relative;min-height:0}
  .plant-grid figure:first-child .shot{position:absolute;inset:0;height:100%;aspect-ratio:auto}
  .pullquote{font-size:clamp(36px,3.6vw,52px);line-height:1.15;max-width:30ch}
  /* Section 18: the photograph was a 300px thumbnail beside a column of
     empty space. It now takes about 40% of the row. */
  .feature{grid-template-columns:minmax(0,1fr) minmax(0,0.7fr);
    gap:72px;align-items:center;padding:48px 0}
  .why-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .why{padding:40px 0 44px}
  .why-num{font-size:56px}
  .measure-shot .ph img{aspect-ratio:21/9}
  .sechead .eyebrow{margin-left:-56px}
  /* Products alternated nothing: four identical spreads in a row. The flip
     mirrors the block above each spec table; the tables stay identical,
     because comparability is the point of publishing them. */
  .spread-flip .shot-col{order:2}
  .spread-flip .copy-col{order:1}
  /* The last even grid on the site. The sign-off block carries the mark and
     the address and needs the room; the two link columns do not. */
  .cta-row{grid-template-columns:minmax(0,1fr) minmax(0,400px);
    gap:72px;align-items:start}
  .foot-cols{grid-template-columns:minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr) minmax(0,1.2fr);
    gap:48px;padding-top:64px}
  .foot-sign img{height:34px}
  .foot-base{flex-direction:row;justify-content:space-between}
}
@media (max-width:899px){
  .btn,.btn-ghost{width:100%}
  .scroll{overflow-x:auto;margin:0 calc(var(--pad) * -1);padding:0 var(--pad)}

  /* 1. A readable floor. The 10px mono label is the editorial voice and it
     survives on desktop, but on a phone it is below what a buyer can
     comfortably read — and the research says buyers arrive on a phone. */
  .jump .jumplink,.hero .fact em,.range-head,.register-head,.eyebrow,.kvk,.fig .l,.chip,.foot-base,.reg-date,.spec th,
  .matrix th[scope="row"],.matrix thead th:first-child,.ask,.feature .go,
  .scroll-hint,.micro{font-size:13px}

  /* 2. Tap targets. These were text links sitting at 23px. */
  a.kvv,.kvv a,.sechead a.chip,.sechead a.mono,.feature .go,.ask,
  .jump .jumplink{display:inline-flex;align-items:center;min-height:44px}

  /* 2a. The footer links need the same 44px target, but inline-flex sizes a
     box to its content and refuses to wrap. The 24-character mailto then ran
     wider than its half-width grid track and printed over the Pages column.
     display:flex fills the track instead, so the text wraps inside it. */
  .foot-cols a{display:flex;align-items:center;min-height:44px;
    overflow-wrap:anywhere}

  /* 2b. .float is fixed 16px from the bottom and is about 56px tall, so it
     lands on whatever the page ends with - here the registration number. The
     button is the site's only conversion path, so the footer yields, not it. */
  .foot-base{padding-bottom:96px}

  /* 2c. Half-width columns broke the email mid-word and the hours mid-range.
     One column reads each line whole, in the same order as the markup; the
     page links run as a wrapping row so the footer does not grow a screen. */
  .foot-cols{grid-template-columns:minmax(0,1fr);gap:22px}
  .foot-cols > :last-child{flex-direction:row;flex-wrap:wrap}
  .foot-cols > :last-child .kvk{flex-basis:100%}
  .foot-cols > :last-child a{padding-right:18px}

  /* 3. The category bar runs as one scrolling row instead of wrapping to
     three, which took 131px of a 844px screen. */
  .jump-row{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;
    -webkit-overflow-scrolling:touch;scroll-padding-left:var(--pad)}
  .jump-row::-webkit-scrollbar{display:none}
  .jump .jumplink{white-space:nowrap;flex:0 0 auto}

  /* 4. Anchors must clear the sticky stack: 71px header + 69px chip bar,
     measured, plus 16px so the heading is not flush against it. */
  section[id]{scroll-margin-top:156px}

  /* 5. Hero capped so the next section shows above the fold. */
  .hero .sec{padding-top:26px;padding-bottom:30px}
  .hero .grid-2{gap:20px}
  .hero .rise{gap:16px}
  .hero h1{font-size:36px}
  .hero .lede{font-size:18px}

  /* 7. The matrix is wider than the screen; make that visible. */
  .scroll::after{content:"";position:absolute;top:0;right:0;bottom:0;width:44px;
    pointer-events:none;background:linear-gradient(to left,var(--ground),transparent)}

  /* ...and stop saying so once there is nothing left to scroll to. The fade
     was static, so at the right-hand end it went on promising more table.
     An affordance that lies costs more than no affordance, because the next
     one is not believed either.

     No JavaScript and no scroll handler: the timeline is the scroller itself.
     Gated on @supports because without the timeline the duration falls back
     to 0s and fill:both would hide the gradient outright - the failure mode
     is the opposite of the one being fixed. */
  @supports (animation-timeline: scroll(self inline)){
    @keyframes fadeEdge{0%,88%{opacity:1}100%{opacity:0}}
    .scroll::after{animation-name:fadeEdge;animation-timing-function:linear;
      animation-fill-mode:both;animation-timeline:scroll(self inline)}
  }
  .surface .scroll::after{background:linear-gradient(to left,var(--paper),transparent)}
  /* A table that fits has no scroll timeline to run, so the fade stayed at
     full strength over the last letters of its values. Only a table the
     script has measured as overflowing carries it. */
  .scroll:not(.overflows)::after{display:none}
  .scroll-hint{display:block;padding-top:10px;color:var(--muted);
    letter-spacing:0.1em;text-transform:uppercase}
}

/* The hero was tuned against a 390x844 screen and fitted only that one. On a
   375x667 or 360x740 it ran past the fold again, so the cap keys off viewport
   height as well as width. */
@media (max-width:899px) and (max-height:820px){
  .hero .sec{padding-top:16px;padding-bottom:20px}
  .hero h1{font-size:31px}
  .hero .lede{font-size:17px}
  .hero .grid-2{gap:14px}
  .hero .rise{gap:12px}
  .hero .facts{margin-top:18px}
  .hero .fact{padding-top:10px}
}

/* Shorter still (iPhone SE at 667, small Androids at 640). The plate is the
   bulk of the hero, so the last of the height comes out of its rows. */
@media (max-width:899px) and (max-height:760px){
  .hero .sec{padding-top:12px;padding-bottom:14px}
  .hero h1{font-size:28px}
  .hero .lede{font-size:16px}
  .hero .btn,.hero .btn-ghost{min-height:46px}
  /* the below-plate is outside .hero, so the hero's reduced figure size does
     not reach it; without this the numerals collide across columns */
  .hero .grid-2{gap:12px}
}

