/* ============================================================
   PORCEMALL - design system
   "The Architect's Catalog"
   Warm paper ground · hard edges · hairline ink rules ·
   crimson used structurally, never decoratively.
   ============================================================ */

/* ---------- web fonts (self-hosted) ----------
   Archivo and Spline Sans Mono, variable, served from this origin.
   Google's own woff2 bytes, fetched 2026-09-22 and verified by sha256;
   no third-party request, no visitor IP handed to fonts.gstatic.com.
   Filenames carry a content hash, so /assets/fonts/ is cached immutable
   in vercel.json: a changed file is a changed name.
   Subsets: latin + latin-ext. The vietnamese subset Google also offers is
   not shipped - no page on this site contains a codepoint in its range.
   Both axes are intact: Archivo keeps wdth 62..125 and wght 100..900
   (the design uses wdth 90/92/96/100/110/112 and wght 400..800), and the
   italic face is kept because <em> appears throughout the copy.
   ---------- */
@font-face {
  font-family: 'Archivo';
  font-style: italic;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url(../fonts/archivo-italic-latin-ext.745260b7.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo';
  font-style: italic;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url(../fonts/archivo-italic-latin.d63de74e.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url(../fonts/archivo-normal-latin-ext.5717f370.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url(../fonts/archivo-normal-latin.e3a28ead.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Spline Sans Mono';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(../fonts/spline-sans-mono-normal-latin-ext.0ca9a398.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Spline Sans Mono';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(../fonts/spline-sans-mono-normal-latin.46b7dcaf.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------- tokens ---------- */
:root {
  /* ground */
  --paper:      #fbf9f4;   /* near-white paper - warmth at a whisper (D50) */
  --paper-lift: #fefdfa;   /* slightly lifted panels              */
  --paper-deep: #f1eee6;   /* recessed bands                      */
  --ink:        #141110;   /* near-black, warm                    */
  --ink-soft:   #3d3831;   /* secondary text - clearly dark        */
  --ink-faint:  #575049;   /* tertiary - footer legal line ONLY   */
  --line:       rgba(20, 17, 16, 0.38);  /* hairline - real presence */
  --line-strong:#141110;   /* structural rules                    */
  --red:        #a02030;   /* PORCEMALL crimson                   */
  --red-deep:   #7c1421;

  /* type */
  --font-display: "Archivo", "Helvetica Neue", sans-serif;
  --font-ui:      "Archivo", "Helvetica Neue", sans-serif;
  --font-mono:    "Spline Sans Mono", "SFMono-Regular", monospace;

  /* shadows - warm-tinted, for the floating planks */
  --float-shadow:
    0 1px 2px   rgba(46, 32, 14, 0.10),
    0 10px 22px -6px  rgba(46, 32, 14, 0.16),
    0 30px 60px -18px rgba(46, 32, 14, 0.30);
  --float-shadow-hover:
    0 2px 4px   rgba(46, 32, 14, 0.10),
    0 18px 34px -8px  rgba(46, 32, 14, 0.20),
    0 48px 90px -20px rgba(46, 32, 14, 0.38);

  --max: 1440px;
  --topbar-h: 36px; /* ink strip is ~37px - masthead tucks 1px under it so no seam shows */
  --gutter: clamp(20px, 3.4vw, 48px);

  /* Light only, and no browser may darken it. A page that says `only
     light` is left alone by Chrome's auto dark mode, and by Samsung
     Internet's wherever it honours the page (README, "light only").
     The same words are in a <meta> in every head, which arrives
     before this sheet does; tools/check_color_scheme.py checks both. */
  color-scheme: only light;
}

/* ---------- reset-ish ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 17px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img { display: block; max-width: 100%; }
a { color: inherit; }
button { font: inherit; color: inherit; background: none; border: none; padding: 0; cursor: pointer; }
input, select { font: inherit; color: inherit; }
::selection { background: var(--red); color: var(--paper-lift); }

/* grain of the paper - one fixed overlay, almost subliminal */
body::after {
  content: "";
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 60;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");
}

/* ---------- shared primitives ---------- */
.wrap {
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.kicker {
  display: flex;
  flex-wrap: wrap;
  column-gap: 9px;
  row-gap: 3px;
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--red);
}
.kicker .k-seg { white-space: nowrap; }
/* when the whole eyebrow can't share a line, split cleanly (dot hidden by JS) */
.kicker.stacked { flex-direction: column; }
.kicker.stacked .k-dot { display: none; }

/* origin flags - hard-edged, hairline-bound */
.flag {
  display: inline-block;
  width: 21px;
  height: 14px;
  margin-right: 8px;
  vertical-align: -2px;
}

/* poster headlines - every section title is fitted edge-to-edge of the
   container by fitToWidth() in product.js, like the product-name lockup */
.section-head {
  display: flex;
  flex-direction: column;
  margin-bottom: clamp(28px, 4vw, 52px);
}
/* one uniform headline scale - taller ratio, not fat (stretch under 100%) */
.section-head h2,
.lib-head h2 {
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  font-size: clamp(30px, 3.4vw, 48px);
  line-height: 1;
  letter-spacing: 0;
  margin: 0;
}
.section-head .head-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 24px;
  margin-top: 14px;
}
.section-head .aside {
  margin-left: auto;
  max-width: 44ch;
  font-size: 15px;
  color: var(--ink-soft);
  text-align: right;
  line-height: 1.5;
  margin-top: 14px;
}
.section-head .head-row .aside { margin-top: 0; }

section { padding-block: clamp(36px, 5vw, 72px); }

/* reveal-on-scroll */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}
.reveal.in-view { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

/* ============================================================
   HEADER
   ============================================================ */
/* topbar - ink strip capping the page */
.topbar {
  position: sticky;
  top: 0;
  z-index: 90;
  background: var(--ink);
  font-size: 13.5px;
  letter-spacing: 0.06em;
  color: var(--paper);
}
@media (hover: hover) {
  .topbar a:hover { color: var(--red); }
}
.topbar .lang-opt { color: rgba(251, 249, 244, 0.7); }
.topbar .lang-opt.active { color: var(--paper); }
@media (hover: hover) {
  .topbar .lang-opt:not(.active):hover { color: var(--red); }
}
.topbar .lang-sep { color: rgba(251, 249, 244, 0.5); }
.topbar .dot { color: var(--red); }
.topbar .wrap {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  /* 8px, not 7: the strip used to be held open to ~37px by the toggle's
     underline, which no longer takes up space in the box */
  padding-block: 8px;
}
/* the toggle sits on the same baseline as the contact text beside it: the two
   run at different sizes, so they are aligned by baseline rather than by box
   centre, and the red rule under the active language is drawn out of flow so
   it adds no height to the box */
.lang { display: inline-flex; align-items: baseline; gap: 8px; }
.lang-opt {
  position: relative;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.1em;
  color: var(--ink-soft);
  transition: color 0.15s ease;
}
.lang-opt.active { color: var(--ink); }
.lang-opt.active::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -3px;
  height: 2px;
  background: var(--red);
}
@media (hover: hover) {
  .lang-opt:not(.active):hover { color: var(--red); }
}
.lang-sep { color: var(--ink-soft); }
.topbar a { text-decoration: none; }
@media (hover: hover) {
  .topbar a:hover { color: var(--red); }
}
.topbar .dot { color: var(--red); padding-inline: 6px; }
/* the strip on a small phone. The toggle and the contact details together
   ask for about 352px, so from roughly 392 down they start eating the
   gutter and by 320 the row runs off both ends at once: the EN loses its E
   on the left and the email loses its .com on the right. Neither block can
   go - the toggle is the only language switch above the fold (the phone
   menu carries none, only the footer does) and the number and the address
   are what the strip is for. So the type eases down a point, the tracking
   closes and the spacing tightens as the viewport narrows, which seats the
   whole row inside 320 with a gutter to spare. Every value lands back on
   its full-size self at 420, so there is no step: above that the base
   rules above are already what is drawn. */
@media (max-width: 420px) {
  .topbar {
    font-size: clamp(12.2px, calc(1.6px + 3.3vw), 13.5px);
    letter-spacing: clamp(0.24px, calc(-1.57px + 0.57vw), 0.81px); /* 0.81px is 0.06em at 13.5px */
  }
  .topbar .wrap {
    gap: clamp(10px, calc(-14px + 7.5vw), 16px);
    padding-inline: clamp(12px, calc(-13.6px + 8vw), 20px);
  }
  .topbar .dot { padding-inline: clamp(4px, calc(-2.4px + 2vw), 6px); }
  .lang { gap: clamp(6px, calc(-0.4px + 2vw), 8px); }
  .lang-opt { font-size: clamp(11.7px, calc(1.5px + 3.19vw), 13px); }
}

/* the masthead travels with you - all devices */
.masthead {
  position: sticky;
  top: var(--topbar-h);
  z-index: 80;
  background: var(--paper);
  border-bottom: 1.5px solid var(--line-strong);
}
.masthead .wrap {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding-block: 20px;
}
.masthead .logo { justify-self: center; }
.masthead nav { display: flex; gap: 26px; align-items: center; }
.masthead nav.right { justify-content: flex-end; }
.masthead nav a {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  padding-bottom: 2px;
  border-bottom: 1.5px solid transparent;
}
@media (hover: hover) {
  .masthead nav a:hover { border-bottom-color: var(--red); }
}
/* the underline stays lit on the section you are in */
.masthead nav a.current { border-bottom-color: var(--red); }


/* mega menus - JS-driven full-width panels on the masthead grid.
   Triggers are plain nav links (data-mega); panels are hairline-ruled
   columns + one lifestyle feature, stacked inside one deck that is anchored
   under the masthead. The deck is the surface: paper, bottom rule, shadow,
   and the open panel's height. The panels carry only their own contents. */
.masthead nav a.open { border-bottom-color: var(--red); }
.mega-deck {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  box-sizing: content-box; /* the height JS sets is the panel's, rule aside */
  height: 0;
  overflow: hidden;
  background: var(--paper);
  border-bottom: 1.5px solid var(--line-strong);
  z-index: 85;
}
.mega {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  /* every panel is opaque, so an arriving one covers the one it replaces and
     the page behind is never seen through the swap */
  background: var(--paper);
  padding: clamp(28px, 3vw, 42px) 0 clamp(30px, 3.2vw, 48px);
  z-index: 2;
}
.mega-wrap {
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.mega-cols { display: grid; }
.mega-cols.c4 { grid-template-columns: 1fr 1fr 1fr minmax(280px, 1.15fr); }
.mega-cols.c3 { grid-template-columns: 1.15fr 1.15fr minmax(280px, 1fr); }
.mega-cols.c2 { grid-template-columns: 1fr 340px; }
.mega-col {
  min-width: 0;
  padding: 0 clamp(24px, 2.6vw, 46px);
  border-left: 1px solid var(--line);
}
.mega-col:first-child { padding-left: 0; border-left: none; }
.mega-col:last-child { padding-right: 0; }
.mega-kicker {
  display: block;
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red);
  border-bottom: 2px solid var(--line-strong);
  padding-bottom: 10px;
}
.masthead .mega a {
  display: block;
  padding: 10px 0;
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: none;
  text-decoration: none; /* panels live outside the nav, so the UA default underline must die here */
  border-bottom: 1px solid var(--line);
}
@media (hover: hover) {
  .masthead .mega a:hover { color: var(--red); border-bottom-color: var(--line); }
}
.masthead .mega a:last-child { border-bottom: none; }
/* a second group in a column (On offer, under Shop by color) takes a breath
   above its kicker, so it reads as its own group and not as one more colour */
.mega-col .mega-kicker:not(:first-child) { margin-top: 26px; }
.masthead .mega a.mega-feature { padding: 0; border-bottom: none; }
.mega-feature img, .mega-feature video {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1.5px solid var(--ink);
  display: block;
}

/* Decorative video never offers a control. Safari paints its own big start
   button over a clip it has declined to autoplay (per-site Auto-Play set to
   Never, or Low Power Mode), and that button is the one thing this design
   must not show. The shadow parts below are the whole of that overlay, so
   hiding them leaves the first frame sitting on the ink background instead.
   The clips carry no sound and answer to nothing, so they take no pointer. */
.gi-hero > video,
.mega-feature video,
.mm-feature video,
video[data-ambient] {
  pointer-events: none;
}
.gi-hero > video::-webkit-media-controls,
.gi-hero > video::-webkit-media-controls-start-playback-button,
.gi-hero > video::-webkit-media-controls-panel,
.gi-hero > video::-webkit-media-controls-play-button,
.gi-hero > video::-webkit-media-controls-overlay-play-button,
.gi-hero > video::-webkit-media-controls-container,
.mega-feature video::-webkit-media-controls,
.mega-feature video::-webkit-media-controls-start-playback-button,
.mega-feature video::-webkit-media-controls-panel,
.mega-feature video::-webkit-media-controls-play-button,
.mega-feature video::-webkit-media-controls-overlay-play-button,
.mega-feature video::-webkit-media-controls-container,
.mm-feature video::-webkit-media-controls,
.mm-feature video::-webkit-media-controls-start-playback-button,
.mm-feature video::-webkit-media-controls-panel,
.mm-feature video::-webkit-media-controls-play-button,
.mm-feature video::-webkit-media-controls-overlay-play-button,
.mm-feature video::-webkit-media-controls-container,
video[data-ambient]::-webkit-media-controls,
video[data-ambient]::-webkit-media-controls-start-playback-button,
video[data-ambient]::-webkit-media-controls-panel,
video[data-ambient]::-webkit-media-controls-play-button,
video[data-ambient]::-webkit-media-controls-overlay-play-button,
video[data-ambient]::-webkit-media-controls-container {
  display: none !important;
  -webkit-appearance: none;
  opacity: 0 !important;
}
/* A clip that is a feature's resting picture keeps the scenes its links ask
   for: chrome.js lays each one over the clip, in this layer, in the same
   frame, and takes it away again. */
.masthead .mega a.mega-feature { position: relative; }
.mega-feature .mega-scene { position: absolute; top: 0; left: 0; opacity: 0; }
/* A page clip sits over its own first frame, a plain <img> the page paints
   first, and is shown only once it is really moving (chrome.js, AMBIENT
   VIDEO). Until then, and whenever it is held to its still, the img is what
   is seen. */
video.amb-over:not(.amb-on) { opacity: 0; }
.mega-feature > span {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 12px 0 0;
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.mega-feature .arrow { color: var(--red); font-size: 17px; line-height: 1; }

/* the inspiration panel - a picture menu, not a list. Four equal photo cards,
   one room type each, in the order the inspiration page files them. No column
   rules and no kicker rule: the photographs do the dividing, so the only lines
   in the panel are the borders of the pictures themselves. */
.mega-rooms {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(16px, 1.9vw, 28px);
}
.masthead .mega a.mega-room {
  display: block;
  padding: 0;
  border-bottom: none;
  color: var(--ink);
}
.mega-room img {
  width: 100%;
  aspect-ratio: 5 / 4;
  object-fit: cover;
  border: 1.5px solid var(--ink);
  display: block;
}
.mega-room .mr-kicker {
  display: block;
  margin-top: 13px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: var(--red);
}
.mega-room .mr-title {
  display: block;
  margin-top: 13px;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  font-size: clamp(16px, 1.35vw, 19px);
  line-height: 1.05;
  letter-spacing: 0.01em;
}
.mega-room .mr-copy {
  display: block;
  margin-top: 7px;
  font-size: 13.5px;
  font-weight: 400;
  line-height: 1.45;
  color: var(--ink-soft);
}
@media (hover: hover) {
  .masthead .mega a.mega-room:hover .mr-copy { color: var(--ink-soft); }
}
/* the fifth thing in the panel is a link, not a column */
.masthead .mega a.mega-all {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  width: auto;
  margin-top: clamp(18px, 2vw, 26px);
  padding: 0;
  border-bottom: none;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--red);
}
.mega-all .arrow { color: var(--red); }

/* mega menu motion - one surface, crossfaded. The four panels share the deck
   under the masthead, so a move from Products to Inspiration is a change of
   contents on a surface that never leaves: the arriving panel, opaque, fades
   over the one it replaces while the deck's height travels to the new
   panel's. Nothing collapses, no frame shows the page behind, and the
   masthead's own blended state is driven by the deck rather than by either
   panel, so it cannot blink mid-switch.

     open     260ms  the deck fades up, its shadow with it, and the contents
                     settle down from -10px over 420ms, a touch behind
     close    260ms  the same, reversed, on the same curve
     switch   260ms  the arriving panel fades in over the leaver, and the
                     deck's height runs to the new one over 360ms

   Two curves, and the split matters. Anything that moves - the contents'
   settle, the deck's height - rides cubic-bezier(0.32, 0.72, 0, 1): quick off
   the mark and a long settle, the way a thing with weight arrives. Opacity
   rides cubic-bezier(0.4, 0, 0.2, 1) instead, which spends its time evenly:
   on that first curve a fade is perceptually over in a third of its duration,
   which is exactly the snatched feeling this is meant to cure. The fade curve
   is the same in both directions, so the panel leaves at the speed it came.
   Height animates during a switch and at no other time - opening sets it
   outright under a surface that is still invisible, and a late-loading
   photograph resizes the deck under a surface that is already open, with no
   travel to notice.

   The classes, all set by JS:
     .mega-deck.is-live   the deck holds a panel, opening, open or fading out
     .mega-deck.is-open   the deck is lit
     .is-swapping         a switch is running; the one time height moves
     .mega.mega-in        this panel is the lit one
     .mega.mega-live      its contents have settled (the -10px is released)
     .mega.mega-under     the panel being replaced: opaque, and underneath
     .mega.mega-now       worn for a single frame, to land a state with no fade
   `hidden` stays the settled closed state, so a shut panel's links are never
   in the tab order; JS drops it only while the panel is on screen. Under
   prefers-reduced-motion every one of these lands instantly. */
.mega-deck {
  --mega-ease: cubic-bezier(0.32, 0.72, 0, 1);
  --mega-fade-ease: cubic-bezier(0.4, 0, 0.2, 1);
  --mega-fade: 280ms;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  box-shadow: 0 30px 50px -30px rgba(20, 17, 16, 0);
  transition:
    opacity var(--mega-fade) var(--mega-fade-ease),
    box-shadow var(--mega-fade) var(--mega-fade-ease),
    visibility 0s linear var(--mega-fade);
}
.mega-deck.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  box-shadow: 0 30px 50px -30px rgba(20, 17, 16, 0.28);
  transition:
    opacity var(--mega-fade) var(--mega-fade-ease),
    box-shadow var(--mega-fade) var(--mega-fade-ease),
    visibility 0s;
}
.mega-deck.is-open.is-swapping {
  transition:
    height 360ms var(--mega-ease),
    opacity var(--mega-fade) var(--mega-fade-ease),
    box-shadow var(--mega-fade) var(--mega-fade-ease),
    visibility 0s;
}
.mega {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--mega-fade) var(--mega-fade-ease);
}
.mega.mega-in { opacity: 1; pointer-events: auto; }
.mega.mega-under { z-index: 1; }
.mega.mega-now { transition: none; }
.mega .mega-wrap {
  transform: translateY(-10px);
  transition: transform 420ms var(--mega-ease);
}
.mega.mega-live .mega-wrap { transform: none; }

/* The pointer must never cross dead paper. The triggers sit in the middle of
   the masthead row with 26px between them and the deck a stretch below, so an
   open trigger grows an invisible bridge over that padding - out to meet its
   neighbours, and down to the deck. Leaving Products for Inspiration, or for
   the panel underneath, never leaves a hover region at all, and the 300ms
   grace in chrome.js is left to cover only a real departure. Only the open
   trigger carries it, so a closed nav is exactly as big as it looks. */
.masthead nav a.nav-mega { position: relative; }
.masthead nav a.nav-mega.open::after {
  content: "";
  position: absolute;
  /* the 26px gap, and one pixel over the halfway line so the two regions
     overlap rather than meet on an edge the pointer can fall through */
  left: -15px;
  right: -15px;
  top: -24px;
  bottom: -40px;
}
@media (prefers-reduced-motion: reduce) {
  .mega-deck,
  .mega-deck.is-open,
  .mega-deck.is-open.is-swapping,
  .mega,
  .mega .mega-wrap { transition: none; }
  .mega .mega-wrap { transform: none; }
}

/* ============================================================
   THE SEARCH SHEET
   One layout, two surfaces. The header's search icon opens .pms-sheet over
   the page, seated under the masthead so the header stays whole and the page
   behind is covered rather than dimmed; /search/ carries the same blocks as
   .pms-page, inline. Either way the order never changes:

     back or close · one full-width field with its reset · a row of
     standing links · a row of written suggestions · Products, with the
     count in its heading and a grid of cards · Collections, which here
     are the categories and the looks.

   With nothing typed the two headings read as the popular searches and the
   popular products; with something typed they carry counts; with something
   typed and nothing found, one line says so and the popular state returns.
   ============================================================ */
.nav-search svg { width: 17px; height: 17px; display: block; }

.pms-sheet {
  position: fixed;
  inset: 0;
  z-index: 70;          /* under the masthead (80) and the topbar (90) */
  background: var(--paper);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-top: var(--pms-top, 0px);
  -webkit-overflow-scrolling: touch;
}
.pms-sheet[hidden] { display: none; }
/* the page surface and the blocks inside both surfaces are sections, and the
   site's standard section padding is not the rhythm this layout keeps */
.pms { padding-bottom: clamp(48px, 7vw, 110px); }
.pms-page { padding-top: 0; }
.pms-group { padding-block: 0; }
.pms .wrap { max-width: var(--max); }

/* A masthead blended into a hero photo is no longer over a photo once the
   sheet is open: while it is, the header takes its solid colours back. Written
   long enough to outweigh the homepage's own blended rules, which are page
   local and therefore later in the cascade. */
html body.home.pms-open .masthead { background: var(--paper); border-bottom-color: var(--line-strong); }
html body.home.pms-open .masthead nav a { color: var(--ink); text-shadow: none; }
html body.home.pms-open .masthead .logo img { filter: none; }
html body.home.pms-open .burger span { background: var(--ink); box-shadow: none; }
html body.home.pms-open .mhq a { color: var(--ink); filter: none; }

/* the way out, in the slot Zia keeps for its Back */
.pms-back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: clamp(16px, 2.2vw, 26px) 0 clamp(12px, 1.4vw, 18px);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-decoration: none;
  cursor: pointer;
}
@media (hover: hover) {
  .pms-back:hover { color: var(--red); }
}
.pms-back:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }
.pms-back-ic svg { width: 14px; height: 14px; display: block; }

/* the field: one line the width of the page, the size of a headline */
.pms-field {
  display: flex;
  align-items: center;
  gap: clamp(12px, 2vw, 28px);
  min-height: clamp(52px, 6.2vw, 88px);
  border-bottom: 1.5px solid var(--line-strong);
}
.pms-field input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
  border: 0;
  outline: none;
  background: none;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  font-size: clamp(26px, 4.4vw, 60px);
  line-height: 1.08;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.pms-field input::placeholder { color: var(--line); opacity: 1; }
/* the field carries its own reset, so the browser's bare cancel glyph goes */
.pms-field input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; display: none; }
.pms-clear {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: clamp(20px, 2.4vw, 34px);
  height: clamp(20px, 2.4vw, 34px);
  color: var(--ink);
  cursor: pointer;
}
.pms-clear svg { width: 100%; height: 100%; display: block; }
@media (hover: hover) {
  .pms-clear:hover { color: var(--red); }
}
.pms-clear:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

/* the line that says nothing was found, and the one that says what was read */
.pms-note {
  margin: clamp(20px, 2.4vw, 30px) 0 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  font-size: clamp(15px, 1.5vw, 20px);
  line-height: 1.35;
  color: var(--ink);
}
.pms-note--fix { color: var(--red); }

/* the quiet head over the popular state */
.pms-lbl {
  margin: clamp(20px, 2.4vw, 30px) 0 0;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* the two rows: standing links, then the written suggestions. Both read as
   one line of headings; on a phone they hold that line and scroll sideways
   inside themselves rather than wrap or push the page wide. */
.pms-links,
.pms-sugs-list {
  display: flex;
  flex-wrap: wrap;
  gap: 14px clamp(20px, 2.2vw, 30px);
  margin: clamp(18px, 2.2vw, 30px) 0 0;
  padding: 0;
  list-style: none;
}
.pms-sugs-list { margin-top: clamp(16px, 2vw, 24px); }
.pms-sug { display: block; }
.pms-item {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  font-size: clamp(15px, 1.5vw, 20px);
  line-height: 1.45;
  letter-spacing: -0.005em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  cursor: pointer;
}
.pms-item.on { color: var(--red); border-bottom-color: var(--red); }
@media (hover: hover) {
  .pms-item:hover { color: var(--red); border-bottom-color: var(--red); }
}
.pms-item:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

/* a block of results, with its count in the heading */
.pms-h {
  margin: 0;
  padding: clamp(28px, 3.6vw, 52px) 0 clamp(14px, 1.6vw, 22px);
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  font-size: clamp(20px, 2.1vw, 28px);
  line-height: 1.1;
  letter-spacing: -0.012em;
  text-transform: none;
}
.pms-group--collections .pms-h { padding-top: clamp(48px, 8.2vw, 120px); }
.pms-group--products .pms-grid { margin-top: clamp(14px, 1.6vw, 22px); }

.pms-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(28px, 3vw, 40px) clamp(18px, 2.4vw, 34px);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* a floor: its face on a paper ground, its name, its kind, its spec line */
.pms-card { position: relative; min-width: 0; }
.pms-card__image {
  display: grid;
  place-items: center;
  aspect-ratio: 341 / 435;
  padding: 12.4% 2.4%;
  background: var(--paper-deep);
  overflow: hidden;
}
.pms-card__image img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  transition: transform 0.5s cubic-bezier(0.32, 0.72, 0, 1);
}
@media (hover: hover) {
  .pms-card:hover .pms-card__image img { transform: scale(1.04); }
}
.pms-card__save {
  position: absolute;
  top: 14px;
  right: 12px;
  width: 18px;
  height: 18px;
  color: var(--ink);
  cursor: pointer;
}
.pms-card__save svg { width: 100%; height: 100%; display: block; }
@media (hover: hover) {
  .pms-card__save:hover { color: var(--red); }
}
.pms-card__save.on svg { fill: var(--red); color: var(--red); }
.pms-card__save:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }
.pms-card__details { margin-top: 10px; }
.pms-card__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  font-size: 14.5px;
  line-height: 1.28;
  text-transform: none;
  letter-spacing: 0;
}
.pms-card__title a { text-decoration: none; color: var(--ink); }
@media (hover: hover) {
  .pms-card__title a:hover { color: var(--red); }
}
.pms-card__title em { font-style: normal; font-weight: 500; }
.pms-card__type {
  display: block;
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}
.pms-card__spec {
  margin: 8px 0 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* the way to the rest of them */
.pms-more {
  display: block;
  min-width: 250px;
  margin: clamp(28px, 3.2vw, 44px) auto 0;
  padding: 13px 22px;
  border: 2px solid var(--ink);
  background: none;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease;
}
@media (hover: hover) {
  .pms-more:hover { background: var(--ink); color: var(--paper); }
}
.pms-more:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

/* the collections: half the width of a card row each, name left, way in right */
.pms-grid--coll { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px 12px; }
.pms-coll { min-width: 0; }
.pms-coll__media {
  display: block;
  aspect-ratio: 694 / 390;
  background: var(--paper-deep);
  overflow: hidden;
}
.pms-coll__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.5s cubic-bezier(0.32, 0.72, 0, 1);
}
@media (hover: hover) {
  .pms-coll:hover .pms-coll__media img { transform: scale(1.03); }
}
.pms-coll__content {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: 15px;
  padding-top: 15px;
}
.pms-coll__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  font-size: clamp(15px, 1.4vw, 19px);
  line-height: 1.2;
  text-transform: none;
  letter-spacing: -0.008em;
}
.pms-coll__link {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 4px;
}
@media (hover: hover) {
  .pms-coll__link:hover { color: var(--red); }
}

@media (max-width: 1080px) {
  .pms-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 860px) {
  .pms-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pms-grid--coll { grid-template-columns: minmax(0, 1fr); }
  /* the two rows hold one line and scroll inside themselves: the page itself
     never grows sideways, whatever is in them */
  .pms-links,
  .pms-sugs-list {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 2px;
  }
  .pms-links::-webkit-scrollbar,
  .pms-sugs-list::-webkit-scrollbar { display: none; }
  .pms-more { min-width: 0; width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .pms-card__image img,
  .pms-coll__media img { transition: none; }
  @media (hover: hover) {
    .pms-card:hover .pms-card__image img,
    .pms-coll:hover .pms-coll__media img { transform: none; }
  }
}

/* mobile menu - drill-in sub-pages, feature image at panel foot */
.mm-panel { display: flex; flex-direction: column; }
.mm-panel[hidden] { display: none; }
/* a panel arrives from the side it came from: forward from the right, Back
   from the left. Same curve and the same 28px the mega drawer's contents
   ride, so the phone menu moves the way the desktop menu moves. */
@keyframes mm-in-fwd {
  from { transform: translateX(28px); opacity: 0; }
  to { transform: none; opacity: 1; }
}
@keyframes mm-in-back {
  from { transform: translateX(-28px); opacity: 0; }
  to { transform: none; opacity: 1; }
}
.mm-panel.mm-in-fwd { animation: mm-in-fwd 340ms cubic-bezier(0.32, 0.72, 0, 1) both; }
.mm-panel.mm-in-back { animation: mm-in-back 340ms cubic-bezier(0.32, 0.72, 0, 1) both; }
@media (prefers-reduced-motion: reduce) {
  .mm-panel.mm-in-fwd,
  .mm-panel.mm-in-back { animation: none; }
}
/* the panel says where you are, once, under the Back row - and says it in
   the red underline the masthead already uses for the section you are in.
   .masthead nav a.current and a.open take 1.5px of var(--red) under the word,
   and the drawer is that same menu on a phone, so the panel you have opened
   wears the same mark at the same weight in the same token. It is drawn at
   the foot of the title's band, which is where every rule in this drawer sits.
   It is painted by ::after rather than by border-bottom, which is how the
   topbar's own red underline is drawn (.lang-opt.active::after), and here the
   reason is measurable: a 1.5px border is snapped to whole device pixels, so
   it takes 1px of layout on a 1x or 3x screen and 1.5px on a 2x one, and no
   fixed padding can give back an amount that changes with the screen. Out of
   flow it costs nothing at all: the band stays 67.5px and every row below it
   stays where it was.
   The word is centred between the two lines that bound it, the Back row's rule
   and this red one, as every label in the drawer is centred between its own.
   The 22/14 it kept when the red line arrived was tuned for a band that had no
   line at its foot, and it read 26.4px over the capitals against 19.0 under
   them. The line-height is a whole 32px (1.05 was 31.5) because Archivo's own
   ascent and descent come to 32.6 at 30px: with 31.5 the negative half-leading
   is floored to a different pixel at 1x and at 3x, and the word landed 0.7px
   apart on the two; at 32 both put the baseline 26.0 under the line box's
   top. 17.3/18.2 then sets 22.7 over the 20.6px capitals and 22.7 under them.
   17.3 + 32 + 18.2 is the 67.5 it always was, so the red line does not move. */
/* The 2px is a RIGHT inset and always was: every indented row in this drawer
   sets its own left and keeps the same 2 on the right (.mm-sub > a is
   15.25px 2px 14.75px 16px, .mm-sub2 > a is 13.5px 2px 12.5px 32px), so the right is
   the value someone chose and the left was only the shorthand mirroring it.
   It is not a focus ring's room - nothing in this sheet gives these rows a
   focus style, and a ring is drawn outside the border box in any case. On the
   left it cost the drawer its one alignment line: the row boxes and their
   rules start at 20, and so do the promo caption, the catalogue photograph,
   the wordmark and the phone number, but the six labels started at 22. The
   left goes to 0 and the right is left exactly as it was. */
.mm-links .mm-title {
  position: relative;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  font-size: 30px;
  letter-spacing: 0.01em;
  line-height: 32px;
  margin: 0;
  padding: 17.3px 2px 18.2px 0;
}
.mm-links .mm-title::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1.5px;
  background: var(--red);
}
.mm-links button.mm-next,
.mm-links button.mm-back,
.mm-links button.mm-search {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* 17.5/16.5, not 17/17: the box would be centred, the capitals would not.
     Archivo hangs 1.1px more under its baseline than over its caps, so half of
     it moves from the foot to the head. Same row height, same tap target. */
  padding: 17.5px 2px 16.5px 0;
  border-bottom: 1px solid var(--line);
  text-align: left;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  font-size: 22px;
}
.mm-links button.mm-back {
  justify-content: flex-start;
  gap: 10px;
  /* 13.5/12.5: Spline Sans Mono hangs more under the baseline than over the
     caps, so the band reads even around the word rather than around the box.
     14/12 was set against a capital 9px tall; the outline's is 8.7 at 12px,
     which left 0.7px more over the word than under it, and 1.3 on a screen
     that rounds the font at 3x. 13.5/12.5 is within 0.35 on both. */
  padding: 13.5px 2px 12.5px 0;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--ink-soft);
  border-bottom: 1.5px solid var(--line-strong);
}
@media (hover: hover) {
  .mm-links button.mm-back:hover { color: var(--ink); }
}
/* the chevron's drawing starts 5.1px inside its 18px box (the point of the
   miter at x = 6.8 of 24), so the box on the drawer's line at 20 left the one
   mark at the head of the column standing 5px in from everything under it.
   The box steps out by that much and the chevron's point is on 20; the word
   keeps its 10px from the chevron and moves with it. */
.mm-links button.mm-back .mm-arrow { color: var(--ink-soft); margin-left: -5.1px; }
@media (hover: hover) {
  .mm-links button.mm-back:hover .mm-arrow { color: var(--red); }
}
.mm-links .mm-arrow { color: var(--red); }
@media (hover: hover) {
  .mm-links button:hover { color: var(--red); }
}
.mm-kicker {
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red);
  margin: 26px 0 4px;
}
/* the feature's frame IS the last row's rule. Every row in this drawer is a
   band with its label centred between two lines, and a line is shared by the
   two things it separates - the head's rule is the first row's rule, each
   row's rule is the next row's - so nothing here ever stands between two
   lines without being centred in them, and no line has empty paper on both
   sides of it. SEARCH had no line of its own (the rule under it is dropped
   below) and a 28px margin in its place, so its label sat 26.4px under the
   rule above and 54.6px over the frame: the owner's "double gap", and the one
   row in the list that read as floating. With the margin gone the frame's top
   edge lands exactly where SEARCH's rule would have been, so SEARCH is centred
   between the rule above it and the frame (26.4 over, 26.6 under, the same as
   every other row) at every width and every density, and the 1.5px of ink
   closes the list the way the heavier line closes a block elsewhere in this
   drawer. A hairline and a gap of its own under SEARCH would have made the one
   band in the drawer with nothing in it.
   The 20.5 under it gives the caption the lower half of a row's band: its
   baseline is 26.6 over the foot of the link, as a label's is over its rule,
   so .mm-contact below can stand at one distance from whatever ends the panel
   (see there). */
.mm-links a.mm-feature { padding: 0 0 20.5px; border-bottom: none; margin-top: 0; }
.mm-feature img,
.mm-feature video {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1.5px solid var(--ink);
  display: block;
}
/* the caption sits 15px under the frame, closer than anything else in the
   drawer sits to anything, because it belongs to the picture: the link is one
   thing, picture and name. The 2.3 on the right puts its chevron's point on
   the same line as the rows' (theirs is 18px with the rows' 2px inset, this
   one is 17px, and its point is 0.3 further into its box). */
.mm-feature > span {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 10px;
  padding-right: 2.3px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.mm-feature .arrow { color: var(--red); }

/* mobile menu - rows that open where they stand. Inside the Products panel
   the ways the desktop mega-menu sorts the catalogue - Category, Look, Color,
   In offer - each open onto their own list without leaving the panel, and
   Trade still opens under itself on level one. One step in, the type steps
   down to the mono the menu already uses for its small print, and the indent
   says which level you are on. */
.mm-sub { display: flex; flex-direction: column; }
.mm-sub[hidden] { display: none; }
.mm-links .mm-sub > a,
.mm-links .mm-sub > button.mm-next {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* 15.25/14.75: at 15/15 every one of these sat 0.46px above centre (19.9
     over the capitals, 20.4 under), the same 0.46 however the font is
     rounded, so a quarter pixel moves from the foot to the head. The row
     stays 50.4 tall. */
  padding: 15.25px 2px 14.75px 16px;
  border-bottom: 1px solid var(--line);
}
.mm-links .mm-sub2 > a {
  padding: 13.5px 2px 12.5px 32px;   /* the Back row's split, same type, same reason */
  font-size: 12px;
  color: var(--ink-soft);
}
@media (hover: hover) {
  .mm-links .mm-sub2 > a:hover { color: var(--red); }
}
/* a heavier line seals the block, so the last row inside it cannot be read as
   the first row of whatever comes next at the level above */
.mm-links .mm-sub > *:last-child { border-bottom: 1.5px solid var(--line-strong); }
/* Trade ends on a nested block rather than a plain row (Products ends on the
   catalog link). An open nested block is already sealed by its own last row,
   so the parent must not draw a second line under it; while it is shut the
   row that opens it carries the seal instead. */
.mm-links .mm-sub > .mm-sub:last-child { border-bottom: none; }
.mm-links .mm-sub > button.mm-next:has(+ .mm-sub[hidden]:last-child) {
  border-bottom: 1.5px solid var(--line-strong);
}
/* the chevron lies down when the row is open - same drawing, quarter turn */
.mm-links [data-acc] .mm-arrow { transition: transform 0.2s ease; }
.mm-links [data-acc][aria-expanded="true"] .mm-arrow { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) {
  .mm-links [data-acc] .mm-arrow { transition: none; }
}

/* no rule under the row a picture follows: the picture's frame is that rule
   (see .mm-feature). Every other row keeps its own, the LAST row of a panel
   included - ALL PRODUCTS and ALL INSPIRATION used to drop theirs, and read as
   floating over the contact block exactly as SEARCH did over the clip. */
.mm-links a:has(+ .mm-feature),
.mm-links button:has(+ .mm-feature),
.mm-links a:has(+ .mm-visual),
.mm-links button:has(+ .mm-visual) { border-bottom: none; }
.mm-visual { display: block; margin-top: 0; }
.mm-visual img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1.5px solid var(--ink);
  display: block;
}

/* the arrow - square-cap SVG, same drawing as the scene carousel */
.arrow, .mm-arrow { display: inline-flex; align-items: center; }
.arrow svg { width: 19px; height: 19px; display: block; }
.btn .arrow svg { width: 22px; height: 22px; }
.mm-arrow svg { width: 18px; height: 18px; display: block; }
.mega-feature .arrow svg, .mm-feature .arrow svg { width: 17px; height: 17px; }

/* favorites - heart in the header, on the buybox, on cards; localStorage */
.nav-fav { position: relative; }
.nav-fav svg { width: 17px; height: 17px; display: block; }
.fav-count {
  position: absolute;
  top: -8px;
  right: -10px;
  background: var(--red);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  line-height: 1.3;
  padding: 1px 4px;
}
.fav-btn {
  border: 2px solid var(--ink);
  padding: 12px 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
.fav-btn svg { width: 18px; height: 18px; display: block; }
@media (hover: hover) {
  .fav-btn:hover { background: var(--ink); color: var(--paper); }
}
.fav-btn.on { color: var(--red); border-color: var(--red); }
@media (hover: hover) {
  .fav-btn.on:hover { background: var(--red); color: #fff; }
}
.fav-btn.on svg { fill: currentColor; }
.masthead .logo img { height: 34px; width: auto; }

/* the phone's two shortcuts - the heart and the search, at the top left of the
   screen and never behind the menu. They ride the masthead row itself, which
   is already sticky (fixed on the homepage), so they stay on screen while the
   page scrolls without a second floating layer to keep out of the topbar's
   way; they sit out of flow exactly like the burger they mirror, which leaves
   the icons, the logo and the burger on one clean line. */
.mhq {
  display: none;
  position: absolute;
  left: calc(var(--gutter) - 11px); /* the 44px targets overhang so the glyphs land on the gutter */
  top: 50%;
  transform: translateY(-50%);
  align-items: center;
  gap: 0px;
}
.mhq a {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  color: var(--ink);
  text-decoration: none;
  transition: color 0.28s ease, filter 0.28s ease;
}
.mhq a svg { width: 21px; height: 21px; display: block; }
/* the magnifier tucks in toward the heart; the 44px targets still clear */
.mhq a + a { margin-left: -8px; }
/* the count rides inside the tap target rather than off its corner */
.mhq .fav-count { top: 2px; right: 2px; }

/* on the homepage the masthead is transparent over the hero until the page
   moves, so the pair takes the same paper-and-shadow treatment as the burger */
.home:not(.pm-solid) .mhq a {
  color: var(--paper);
  filter: drop-shadow(0 1px 3px rgba(20, 17, 16, 0.45));
}
.home.pm-instant .mhq a { transition: none; }

/* burger + mobile menu (shown ≤720px) */
.burger {
  display: none;
  position: absolute;
  right: var(--gutter);
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  place-items: center;
  align-content: center;
  gap: 5px;
}
.burger span {
  display: block;
  width: 24px;
  height: 2.5px;
  background: var(--ink);
}
.mobile-menu {
  /* not built at all on a desk: the burger that opens it is hidden above
     1080, so above 1080 there is nothing here to lay out */
  display: none;
  position: fixed;
  inset: 0;
  z-index: 95;
  background: var(--paper);
  flex-direction: column;
  /* 20.5 at the foot: the last line's baseline then stands 27.0px over the
     end of the scroll, as the wordmark's ink stands 27.0 under the top of
     the sheet. 28 left 34.5 there. */
  padding: 0 var(--gutter) 20.5px;
  overflow-y: auto;
  /* only ever seen while the sheet is in flight, because an open sheet
     covers the viewport and this falls off its left edge. It is the mega
     deck's shadow at the mega deck's weight, turned on its side. */
  box-shadow: -18px 0 44px -22px rgba(20, 17, 16, 0.34);
}
/* ---- the drawer arrives, and leaves the same way ----
   It was a display switch, which cannot be transitioned, so it appeared and
   vanished. It slides now, and it slides BOTH ways.

   THE CURVE is the masthead's, cubic-bezier(0.32, 0.72, 0, 1): the one the
   mega deck's height rides and the one this drawer's own panels already ride
   at 340ms. Nothing new was invented for it.

   THE DURATION is 380ms in and 280ms out. Longer in than a panel's 340
   because this is the largest thing the site moves and it travels a whole
   viewport rather than 28px; shorter out than in because that is the deck's
   own asymmetry - it opens over 340ms and shuts over 260 - and because a
   thing you have dismissed should not keep you waiting.

   IT COMES FROM THE RIGHT. Two reasons and they agree. The burger stands at
   the right of the masthead (position: absolute; right: var(--gutter)) and
   the Close that replaces it stands in the same place inside the sheet, so
   the sheet arrives from under the finger that called it. And this drawer
   already has a grammar for sideways travel: mm-in-fwd brings new content in
   from the right, mm-in-back brings the level above back in from the left. A
   sheet entering from the left would be opening the menu with the gesture
   that means go back.

   DISPLAY IS NO LONGER THE SWITCH, visibility is, on the deck's own idiom:
   flipped at once on the way in, and held by `visibility 0s linear <out>`
   until the slide has landed on the way out. That delay is the only reason
   the exit can be seen at all.

   NOTHING FADES. The sheet is opaque paper, and paper that fades while it
   travels is a window onto the page sliding underneath it. One transform,
   and the shadow above does the rest. */
@media (max-width: 1080px) {
  .mobile-menu {
    display: flex;
    visibility: hidden;
    pointer-events: none;
    transform: translateX(100%);
    transition:
      transform 280ms cubic-bezier(0.32, 0.72, 0, 1),
      visibility 0s linear 280ms;
  }
  .mobile-menu.open {
    visibility: visible;
    pointer-events: auto;
    transform: none;
    transition:
      transform 380ms cubic-bezier(0.32, 0.72, 0, 1),
      visibility 0s;
  }
}
/* the drawer still opens, still shuts, still takes the keyboard - it simply
   arrives where it is going. The open state is carried by the .open class
   alone and not by the end of an animation, so there is no final value here
   that can be lost by turning the motion off. */
@media (prefers-reduced-motion: reduce) {
  .mobile-menu,
  .mobile-menu.open { transition: none; }
}
.mm-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: 20px;
  border-bottom: 1.5px solid var(--line-strong);
}
.mm-head img { height: 28px; width: auto; }
.mm-close {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border: 1.5px solid var(--ink);
  /* 8.4/7.6, not 8/8: Archivo sits its capitals high in a 1.55 line, and at
     8/8 CLOSE read 13.1px under the frame's top and 14.1 over its foot where
     the font is rounded in CSS pixels (how WebKit sizes type), though within
     0.4 where it is rounded at 3x. 8.4/7.6 is within 0.3 of centre on the
     first and 0.4 on the second. The frame keeps its height. */
  padding: 8.4px 14px 7.6px;
}
/* drawn, not typed - U+2715 is a pictographic character. The drawing is
   centred in its 12px box, and at -1.5px that box's centre is the capitals'
   centre; at -2 the cross sat half a pixel under the word. */
.mm-close .mm-x { display: inline-flex; align-items: center; vertical-align: -1.5px; }
.mm-close .mm-x svg { width: 12px; height: 12px; display: block; }
@media (hover: hover) {
  .mm-close:hover { background: var(--red); border-color: var(--red); color: #fff; }
}
/* no lead of its own: the head's rule is the first row's rule, exactly as every
   row's rule is the next one's. A 10px margin here was charged to the first row
   of EVERY panel and to nothing else, which is why BACK sat 27.5px under the
   head rule and 19px over its own. */
.mm-links { display: flex; flex-direction: column; }
.mm-links a {
  padding: 17.5px 2px 16.5px 0;   /* the button rows' padding, same type, same reason */
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  font-size: 22px;
  letter-spacing: 0.01em;
}
@media (hover: hover) {
  .mm-links a:hover { color: var(--red); }
}
/* the contact block stands where the next row's label would: its first line's
   capitals are 26.5px under the line that closes the panel above it, as every
   row's are under its rule. In a drilled panel that line is the last row's
   own hairline; at the root it is the foot of the catalogue link, whose
   caption keeps a row's lower half-band for exactly this (.mm-feature). The
   20.5 is that 26.5 less the 6.0 of the line box over the phone number's
   capitals. It is not pushed to the foot of the sheet any more
   (margin-top: auto was): in a short panel that left a band of empty paper
   between the last row and the contact block, 229px on a 390x844 phone,
   and the drawer's rhythm stopped at the last row. The paper under it now
   stays empty instead. */
.mm-contact {
  padding-top: 20.5px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 14.5px;
}
.mm-contact a { text-decoration: none; font-weight: 600; }
@media (hover: hover) {
  .mm-contact a:hover { color: var(--red); }
}
.mm-contact span { color: var(--ink-soft); }

/* breadcrumbs - no rule of their own; the masthead line carries the header */
.crumbs .wrap {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-block: 12px;
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--ink);
}
.crumbs a { text-decoration: none; }
@media (hover: hover) {
  .crumbs a:hover { color: var(--red); }
}
.crumbs .sep { color: var(--ink-soft); }

/* ============================================================
   HERO - media stage + info panel
   ============================================================ */
.hero { padding-block: 0; }
.hero .wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(380px, 470px);
}
.hero .wrap > * { min-width: 0; }
/* the fitted title must never dictate the column's minimum width */

/* ----- media stage (left) ----- */
.stage-col {
  padding: clamp(24px, 3vw, 44px) clamp(20px, 3vw, 48px) clamp(24px, 3vw, 44px) 0;
  display: flex;
  flex-direction: column;
  justify-content: center; /* toggle hugs the media - the whole cluster centers as one */
  gap: 22px;
  min-height: 720px;
}

.mode-toggle {
  display: inline-flex;
  align-self: flex-start;
  border: 2px solid var(--line-strong);
  /* cancel the stage's bottom padding so the gap above matches the gap below */
  margin-top: calc(-1 * clamp(16px, 2vw, 28px));
  /* nudge off the column edge to sit under the media, not the notes below */
  margin-left: clamp(32px, 4vw, 56px);
  /* the negative margin overlaps the stage, which is positioned - paint above it */
  position: relative;
  z-index: 1;
}
.mode-toggle button {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 7px 22px 7px 7px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transition: background 0.18s ease, color 0.18s ease;
}
.mode-toggle button + button { border-left: 2px solid var(--line-strong); }
.mode-toggle button.active { background: var(--ink); color: var(--paper); }
@media (hover: hover) {
  .mode-toggle button:not(.active):hover { color: var(--red); }
}
/* per-mode thumbnails - the inactive segment is a visual promise of the other view */
.mode-toggle .seg-thumb {
  flex: none;
  width: 36px;
  height: 36px;
  background-size: cover;
  background-position: center;
  border: 1px solid var(--line-strong);
}
.mode-toggle button.active .seg-thumb {
  border-color: var(--paper);
  opacity: 0.8; /* active dims slightly; the eye goes to where you can still switch */
}
@media (max-width: 720px) {
  .mode-toggle button { gap: 9px; padding-right: 16px; }
  .mode-toggle .seg-thumb { width: 30px; height: 30px; }
}

.stage {
  flex: 0 1 auto;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 2vw, 28px);
  transition: background 0.25s ease;
}
/* dark backdrops re-map the ink variables so every caption inverts */
.stage.dark-bd {
  --ink: #fbf9f4;
  --ink-soft: rgba(245, 239, 225, 0.78);
  --line: rgba(245, 239, 225, 0.42);
  --line-strong: #fbf9f4;
  --paper: #141110;
}

/* disclaimers - one per mode, short and legible */
.stage-note {
  margin: 0;
  font-size: 13.5px;
  color: var(--ink-soft);
  line-height: 1.55;
  max-width: 62ch;
  border-top: 1px solid var(--line);
  padding-top: 10px;
}
/* tokens only - the dark-bd remap must invert this cleanly */
.stage-note .note-kicker {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 3px;
}
.stage-note .note-kicker::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  background: currentColor;
  margin-right: 8px;
  vertical-align: 1px;
}

/* backdrop tester */
.backdrop-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  border-top: 1px solid var(--line);
  padding-top: 16px;
}
.bd-label {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.bd-chips { display: flex; gap: 10px; }
.bd-chip {
  width: 28px;
  height: 28px;
  border: 1px solid var(--line);
  cursor: pointer;
  transition: transform 0.15s ease;
}
@media (hover: hover) {
  .bd-chip:hover { transform: translateY(-2px); }
}
.bd-chip.active { outline: 2px solid var(--ink); outline-offset: 2px; }
.bd-custom {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.bd-custom input[type="color"] {
  appearance: none;
  -webkit-appearance: none;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--line);
  background: none;
  cursor: pointer;
}
.bd-custom input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
.bd-custom input[type="color"]::-webkit-color-swatch { border: none; }
.bd-custom.active input { outline: 2px solid var(--ink); outline-offset: 2px; }

/* plank mode - three production faces stacked as a mini floor */
.stage-plank {
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 34px;
}
.hero-stack {
  display: flex;
  flex-direction: column;
  gap: 18px;
  width: 100%;
}
.hero-stack .hp { width: 78%; }
.hero-stack .hp:nth-child(2) { align-self: flex-end; }
.hero-stack .hp:nth-child(3) { margin-left: 9%; }
.hero-stack img {
  width: 100%;
  box-shadow: var(--float-shadow);
  transition: box-shadow 0.3s ease, transform 0.3s ease;
  cursor: zoom-in;
}
@media (hover: hover) {
  .hero-stack img:hover {
    transform: translateY(-4px);
    box-shadow: var(--float-shadow-hover);
  }
}
/* caption stacks as rows - sentences flow instead of squeezing into columns */
.stage-caption {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: 13.5px;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
}
.stage-caption .face-name { color: var(--ink); font-weight: 600; }
.stage-caption .rule { display: none; }

/* scene mode - 4:5 editorial photograph, controls in one row BELOW it */
.stage-scene {
  width: 100%;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
}
.scene-frame { position: relative; width: min(100%, calc(74vh * 0.8), 680px); aspect-ratio: 4 / 5; }
.scene-frame img {
  width: 100%; height: 100%;
  object-fit: cover;
  box-shadow: var(--float-shadow);
  cursor: zoom-in;
}
.scene-side {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 22px;
  width: min(100%, calc(74vh * 0.8), 680px);
}
.scene-side .counter {
  flex: 0 0 auto;
  color: var(--ink); /* declared (not inherited) so dark backdrops re-resolve it */
  font-family: var(--font-display);
  font-size: 34px;
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  line-height: 0.95;
}
.scene-side .counter small {
  font-size: 15px;
  color: var(--ink-soft);
  font-family: var(--font-mono);
  letter-spacing: 0.06em;
}
.scene-side .scene-title { flex: 1; font-size: 15px; color: var(--ink); line-height: 1.45; }
.scene-nav { display: inline-flex; border: 1.5px solid var(--line-strong); margin-left: auto; }
.scene-nav button {
  width: 50px; height: 50px;
  display: grid; place-items: center;
  color: var(--ink);
  transition: background 0.15s ease, color 0.15s ease;
}
/* arrow glyphs are SVG everywhere - identical geometry, perfectly centered */
.scene-nav button svg, .rail-nav button svg, .lb-arrow svg {
  width: 24px;
  height: 24px;
  display: block;
}
.scene-nav button + button { border-left: 1.5px solid var(--line-strong); }
@media (hover: hover) {
  .scene-nav button:hover { background: var(--ink); color: var(--paper); }
}

/* ----- info panel (right) ----- */
.info-col { padding: clamp(24px, 3vw, 44px) 0 clamp(24px, 3vw, 44px) clamp(24px, 3vw, 48px); }
.info-col .sticky { position: sticky; top: calc(var(--topbar-h) + 100px); } /* clears topbar + 76px masthead + breathing room */

/* product title - fixed, tasteful scale (fit-to-width lockup retired 2026-07-16) */
h1.product-title {
  font-family: var(--font-display);
  font-size: clamp(32px, 2.7vw, 42px);
  font-weight: 800;
  font-stretch: 110%;
  text-transform: uppercase;
  line-height: 1.04;
  letter-spacing: 0;
  margin: 10px 0 10px;
}
/* the title holds at 32px all the way down to 320, and the catalogue has one
   word that does not fit it: UNDERLAYMENT asks for 311px where a 320 phone
   leaves 280 of column, so letting the line wrap is not enough on its own.
   Below about 364 the title tracks the column it stands in instead, and the
   word fits whole rather than breaking. 8.8vw is that word measured at 32px
   and written as a share of the viewport, with a little under it: at 320 it
   puts UNDERLAYMENT at 273.7px against 280 of column. It reaches the
   designed 32px at 363.6, which is why the query ends at 364 - the two meet
   there, so there is no step. Nothing above it moves. The same shape as the
   guide headline's step (def199349), for the same reason. */
@media (max-width: 364px) {
  h1.product-title {
    font-size: clamp(26px, 8.8vw, 32px);
    /* the net under the rule above, and only here: a name longer than any in
       the catalogue today breaks rather than leaving the page. It is not
       wanted any wider, where breaking a 42px title would be the louder
       fault. */
    overflow-wrap: break-word;
  }
}
/* words stay intact; short names sit on one line, long ones wrap naturally.
   Each half of the title is a NAME and stays one unit: inline-block, so the
   red half drops to its own line whole rather than flowing up beside the
   black one. What the unit must not do is refuse to wrap inside ITSELF.
   nowrap did that, and it only ever showed on the one title long enough to
   need it: "Laminate Flooring Underlayment" asked for 710px of a 280px
   column and carried the whole page sideways at every width, 410px of it at
   320 and 388px at 1440. normal is what the line above always meant - CSS
   breaks no word on its own, so words still stay intact, and a name too long
   for its column now wraps between its own words. Nothing that fitted moves:
   an inline-block shrink-to-fits against the column, never against what is
   left of the current line, so every title whose halves fit on one line is
   laid out exactly as before. Measured over all 246 catalogue titles at 320
   to 1920. */
.product-title .fit-line {
  display: inline-block;
  white-space: normal;
  font-size: inherit;
}
.product-title em {
  font-style: normal;
  color: var(--red);
}
.lede {
  font-size: 17px;
  color: var(--ink-soft);
  margin: 0 0 26px;
  line-height: 1.6;
}

.field { border-top: 1px solid var(--line); padding: 14px 0 16px; }
/* Those 14/16 are the ORDER CALCULATOR's form row, where the rule is the top
   of a field you type in. On a product page the same .field is a choice
   group, and the 3D pages stopped drawing the form row over theirs: they cut
   a rule out of the gap between groups and centre it, and the ink stands
   15.4px to 16.3px either side of it. The one product page with no 3D stage
   is not .p3d, so it kept the form row, and the rule under its size chips sat
   16.00 above against 19.00 below - the 3px is the padding-top carrying the
   label's cap slack on one side only, where the chips above end on a painted
   edge with no slack at all.
   The gap is 30px of padding across the rule and it stays 30px, so the column
   stands exactly as tall as it did; only the split moves, 16/14 to 17.5/12.5,
   which is the 5.0px of cap slack under the label shared out. Scoped by the
   title, not by the column: h1.product-title is this page's title and no 3D
   page has one, so this cannot reach .p3d and cannot reach the calculator's
   own rows further down the page, which keep their form rule because there
   the rule really is the top of a form field. Spanish reads 17.5/14.5 at the
   ORDER CODE group because the O of CODIGO carries an accent above the cap
   line, exactly as it does on the 3D pages. */
h1.product-title ~ .field { padding: 12.5px 0 17.5px; }
/* The FIRST of those groups is the exception, because what stands over its
   rule is not another group: above 1080 it is the lede, and the lede's own
   26px margin-bottom sits outside the field system and stacks on the 12.5px
   the rule already has under it. Measured on the one product page with no 3D
   stage, at 1440, the FORMAT rule stood 32.00px of ink over against 17.00px
   under - very nearly twice as far from the paragraph above it as from the
   label below. The 3D pages have no such rule at all (.p3d .field carries
   border-top: 0 and only .field + .field draws one), but this page cannot
   simply drop it: below 1081 the lede here is hidden and that rule is the
   only thing between the BACKDROP control and the column.
   So the rule is centred where it is wrong rather than removed, by the same
   method as the groups below it: the gap across it stays exactly 38.5px, so
   the column stands exactly as tall as it did, and only the split moves.
   26/12.5 becomes 19/19.5 - the half-pixel is the lede's descender slack set
   against the label's 5.0px of cap slack - and the ink then reads 25.00 over
   against 24.00 under at 1440 and 24.00 against 24.00 at 1280 in English,
   25.25/24.00 and 24.25/24.00 in Spanish. It is not the same figure at every
   width because the lede wraps at a different word and its last line carries
   different descenders; no single value removes that, and these sit within
   1.25px of centre against the 15px they replace.
   Scoped three ways so it reaches nothing else:
   by .info-col, so the mobile .m-title lede keeps its own 6px; by
   h1.product-title, which is this page's title and no 3D page has one; and
   by the query, because below 1081 this lede is display:none and the rule is
   doing the other job. */
@media (min-width: 1081px) {
  .info-col h1.product-title ~ .lede { margin-bottom: 19px; }
  .info-col h1.product-title ~ .lede + .field { padding-top: 19.5px; }
}
.field-label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 10px;
}
.field-label .hint { font-family: var(--font-mono); text-transform: none; letter-spacing: 0.03em; color: var(--ink-soft); font-weight: 400; }

/* colorways - full-width grid, big confident labels */
.swatches { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.swatch {
  text-align: center;
  text-decoration: none;
  color: var(--ink);
}
.swatch .chip {
  display: block;
  height: 64px;
  background-size: 300% auto;
  background-position: center;
  border: 1.5px solid var(--ink);
  margin-bottom: 8px;
  transition: transform 0.2s ease, box-shadow 0.2s ease, outline-color 0.2s ease;
}
.swatch .swatch-label {
  display: block;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink);
  transition: color 0.15s ease;
}
.swatch.current .chip { outline: 2px solid var(--ink); outline-offset: 3px; }
.swatch.current .swatch-label { color: var(--red); }
@media (hover: hover) {
  .swatch:not(.current):hover .chip { transform: translateY(-2px); box-shadow: var(--float-shadow); }
  .swatch:not(.current):hover .swatch-label { color: var(--red); }
}

/* size selector - the plank IS the button, dimensioned like a technical drawing */
.sizes { display: flex; flex-direction: column; gap: 30px; }
.size-opt,
.ls-opt {
  display: block;
  position: relative;
  width: 100%;
  margin: 0;
  padding: 30px 0 0 44px;
  border: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
  transition: filter 0.2s ease, opacity 0.2s ease;
}
.size-opt .face,
.ls-opt .face {
  display: block;
  width: 100%;
  background: var(--paper-deep) center / cover no-repeat;
  border: 1.5px solid var(--ink);
  filter: grayscale(35%) opacity(0.55);
  transition: filter 0.2s ease, opacity 0.2s ease, outline-color 0.2s ease;
}
.size-opt[data-size="1248"] .face { aspect-ratio: 4 / 1; }
.size-opt[data-size="0848"] .face { aspect-ratio: 6 / 1; }
@media (hover: hover) {
  .size-opt:hover .face,
  .ls-opt:hover .face { filter: none; opacity: 1; }
}
.size-opt.active .face,
.ls-opt.active .face {
  filter: none;
  opacity: 1;
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}
.size-opt:focus-visible,
.ls-opt:focus-visible { outline: none; }
.size-opt:focus-visible .face,
.ls-opt:focus-visible .face { outline: 2px solid var(--red); outline-offset: 3px; }

/* dimension line - top edge (length) */
.size-opt .dim-top,
.ls-opt .dim-top {
  position: absolute;
  top: 22px;
  left: 44px;
  right: 0;
  height: 1px;
  background: var(--ink);
}
.size-opt .dim-top::before,
.size-opt .dim-top::after,
.ls-opt .dim-top::before,
.ls-opt .dim-top::after {
  content: "";
  position: absolute;
  top: -4px;
  width: 1px;
  height: 9px;
  background: var(--ink);
}
.size-opt .dim-top::before,
.ls-opt .dim-top::before { left: 0; }
.size-opt .dim-top::after,
.ls-opt .dim-top::after { right: 0; }
.size-opt .dim-top .dim-label,
.ls-opt .dim-top .dim-label {
  position: absolute;
  top: -22px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--paper);
  padding: 0 6px;
  white-space: nowrap;
}

/* dimension line - left edge (width) */
.size-opt .dim-left,
.ls-opt .dim-left {
  position: absolute;
  top: 30px;
  bottom: 0;
  left: 24px;
  width: 1px;
  background: var(--ink);
}
.size-opt .dim-left::before,
.size-opt .dim-left::after,
.ls-opt .dim-left::before,
.ls-opt .dim-left::after {
  content: "";
  position: absolute;
  left: -4px;
  width: 9px;
  height: 1px;
  background: var(--ink);
}
.size-opt .dim-left::before,
.ls-opt .dim-left::before { top: 0; }
.size-opt .dim-left::after,
.ls-opt .dim-left::after { bottom: 0; }
.size-opt .dim-left .dim-label,
.ls-opt .dim-left .dim-label {
  position: absolute;
  top: 50%;
  left: -6px;
  transform: translate(-100%, -50%) rotate(-90deg);
  transform-origin: right center;
  background: var(--paper);
  padding: 0 6px;
  white-space: nowrap;
}

.size-opt .dim-label,
.ls-opt .dim-label {
  font-family: var(--font-mono);
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink);
}
.size-opt.active .dim-label,
.ls-opt.active .dim-label { color: var(--red); }

/* metric equivalent - bottom-right of the plank face */
.size-opt .cm,
.ls-opt .cm {
  position: absolute;
  right: 8px;
  bottom: 8px;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  background: var(--paper);
  padding: 2px 7px;
}

/* ---- square-format variant (Halley template) ---------------------- */
/* three formats at TRUE relative scale: 48×48 base, 40×40 at 5/6 width,
   24×48 same width as 48 but half height */
.sizes.sizes-sq { gap: 36px; }
.sizes-sq .size-opt[data-size="4848"] { width: calc(44px + (100% - 44px) * 0.52); }
.sizes-sq .size-opt[data-size="4040"] { width: calc(44px + (100% - 44px) * 0.4333); }
.sizes-sq .size-opt[data-size="2448"] { width: calc(44px + (100% - 44px) * 0.52); }
.size-opt[data-size="4848"] .face { aspect-ratio: 1 / 1; }
.size-opt[data-size="4040"] .face { aspect-ratio: 1 / 1; }
.size-opt[data-size="2448"] .face { aspect-ratio: 2 / 1; }
.size-opt[data-size="3636"] .face { aspect-ratio: 1 / 1; }
.size-opt[data-size="1224"] .face { aspect-ratio: 2 / 1; }
.sizes-sq .size-opt[data-size="3636"] { width: calc(44px + (100% - 44px) * 0.39); }
.sizes-sq .size-opt[data-size="1224"] { width: calc(44px + (100% - 44px) * 0.26); }

/* SPC vinyl plank (Eagle): 7.25″ × 48″ single format */
.size-opt[data-size="748"] .face { aspect-ratio: 6.62 / 1; }

/* five-colorway swatch row */
.swatches.five { grid-template-columns: repeat(5, 1fr); gap: 9px; }
.swatches.five .swatch-label { font-size: 12px; letter-spacing: 0.02em; }

/* five-card family grid */
.family-grid.five { grid-template-columns: repeat(5, 1fr); gap: clamp(12px, 1.6vw, 22px); }
.family-grid.five h3 { font-size: 15px; }
.family-grid.five .status { font-size: 10.5px; }
@media (max-width: 1080px) { .family-grid.five { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 720px) { .family-grid.five { grid-template-columns: 1fr; } }

/* supporting detail shots under the hero face */
.variety-row { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; width: 100%; }
.variety-row img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  box-shadow: var(--float-shadow);
  cursor: zoom-in;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
@media (hover: hover) {
  .variety-row img:hover { transform: translateY(-4px); box-shadow: var(--float-shadow-hover); }
}
@media (max-width: 720px) { .variety-row { grid-template-columns: 1fr; } }

/* accessories - "Complete the floor" module */
.acc-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(14px, 2vw, 26px);
}
.acc-card {
  border: 1.5px solid var(--line-strong);
  background: #fff;
  display: flex;
  flex-direction: column;
}
.acc-card img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  cursor: zoom-in;
}
.acc-card .acc-meta {
  border-top: 1.5px solid var(--line-strong);
  background: var(--paper-lift);
  padding: 14px 16px 16px;
}
.acc-card h3 {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  letter-spacing: 0;
  font-size: 17px;
}
.acc-card .acc-code {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
}
.acc-card .copy-btn {
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
  border-bottom: 1px solid var(--ink);
}
@media (hover: hover) {
  .acc-card .copy-btn:hover { color: var(--red); border-color: var(--red); }
}
@media (max-width: 1080px) { .acc-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 720px) { .acc-grid { grid-template-columns: 1fr 1fr; } .acc-card h3 { font-size: 14px; } }

/* finish selector - same hard segmented control as the mode toggle */
.fin-seg { display: inline-flex; border: 2px solid var(--line-strong); }
.fin-seg .fin-opt {
  padding: 10px 24px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  transition: background 0.18s ease, color 0.18s ease;
}
.fin-seg .fin-opt + .fin-opt { border-left: 2px solid var(--line-strong); }
.fin-seg .fin-opt.active { background: var(--ink); color: var(--paper); }
@media (hover: hover) {
  .fin-seg .fin-opt:not(.active):hover { color: var(--red); }
}

/* hero mini-floor for squares: 2×2 grid of production faces */
.hero-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  width: min(100%, 760px);
  margin-inline: auto;
}
.hero-grid img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  box-shadow: var(--float-shadow);
  cursor: zoom-in;
  transition: box-shadow 0.3s ease, transform 0.3s ease;
}
@media (hover: hover) {
  .hero-grid img:hover { transform: translateY(-4px); box-shadow: var(--float-shadow-hover); }
}
.stage-caption a { color: var(--ink); font-weight: 600; }
@media (hover: hover) {
  .stage-caption a:hover { color: var(--red); }
}

/* grain library grids - squares dense, rectangles wider */
.face-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 14px;
}
.face-grid.rc { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.face-grid button {
  position: relative;
  padding: 0;
  cursor: zoom-in;
  transition: transform 0.2s ease;
}
@media (hover: hover) {
  .face-grid button:hover { transform: translateY(-4px); }
}
.face-grid img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  box-shadow: 0 3px 10px rgba(46, 32, 14, 0.18);
  transition: box-shadow 0.2s ease;
}
.face-grid.rc img { aspect-ratio: 2 / 1; }
@media (hover: hover) {
  .face-grid button:hover img { box-shadow: 0 14px 28px -8px rgba(46, 32, 14, 0.32); }
}
@media (max-width: 720px) {
  .face-grid { grid-template-columns: repeat(3, 1fr); gap: 10px; }
  .face-grid.rc { grid-template-columns: 1fr 1fr; }
  .sizes-sq .size-opt[data-size="4848"],
  .sizes-sq .size-opt[data-size="4040"],
  .sizes-sq .size-opt[data-size="2448"] { min-width: 220px; }
}

/* order code */
.order-code {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-mono);
}
.order-code .code { font-size: 17px; letter-spacing: 0.06em; font-weight: 600; }
.order-code .copy-btn {
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  border-bottom: 1px solid var(--ink);
  padding-bottom: 1px;
  transition: color 0.15s ease, border-color 0.15s ease;
}
@media (hover: hover) {
  .order-code .copy-btn:hover { color: var(--red); border-color: var(--red); }
}
.order-code .copied { color: var(--red); }

/* quick facts strip */
.facts {
  display: grid;
  /* minmax(0, ...), never a bare 1fr, for the reason def199349 took the same
     token out of the guide headline: a bare 1fr is minmax(auto, 1fr), and that
     auto floor is the track's min-content - the longest unbreakable word in
     the cell. Measured over every value and label the catalogue puts in this
     strip, in both languages, the widest are IMPERMEABLE at 109.00px (the
     Spanish label for Waterproof, and the true worst case) and Rectificado /
     Porcelanico / Porcelanato at 108.91px (11 mono characters of 9.9px),
     against the 80.33px a third of a 320px phone leaves. The three tracks
     asked for 331.55px of a 280px strip and carried the page 31.55px off the
     side of the screen in Spanish, 6.38px in English. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border: 1px solid var(--line);
  border-width: 1px 0;
  /* the strip is not a fixed share of the window - it is 280px wide on a
     320px phone, 432px on a 1280px desk and 716px at 768 where the column
     stacks - so the type below tracks THIS box and not the viewport. */
  container-type: inline-size;
}
.facts div {
  padding: 10px 6px 12px;
  text-align: center;
  /* a grid item's min-width is auto, which is content-based. Without this the
     cell overruns a track minmax(0, ...) has already bound, and the overflow
     moves rather than goes away. */
  min-width: 0;
}
.facts div + div { border-left: 1px solid var(--line); }
.facts div:nth-child(n + 4) { border-top: 1px solid var(--line); }
.facts div:nth-child(4) { border-left: none; }
/* Binding the track is only half of it - the word still has to FIT. A cell
   holds a third of the strip less 12px of padding and, for the two that carry
   an inner border, 1px more; the 14 below is that 13 with a pixel of slack
   over it. So the value's ceiling is (C / 3 - 14) / 6.6, 6.6 being 11 mono
   characters at 0.6em each, and the label's is the same over 9.0833, which is
   IMPERMEABLE measured at the designed 12px. Both are affine in C rather than
   proportional to it - the padding and the borders are constants a percentage
   cannot absorb - which is why these are calc() ramps and not the plain vw of
   def199349. Each meets its designed size at C = 369 and is clamped there, so
   every width at which the strip already fitted is untouched: every desk
   width, and every phone from 414 up. The floors are each ramp's own value at
   C = 280, the narrowest strip the site draws, so nothing steps at 320.
   overflow-wrap is the net under both, for a container narrower than that or
   a word longer than any the catalogue carries today. */
.facts .v {
  font-family: var(--font-mono);
  font-size: clamp(12px, calc((100cqi / 3 - 14px) / 6.6), 16.5px);
  font-weight: 600;
  display: block;
  overflow-wrap: break-word;
}
.facts .k {
  font-size: clamp(8.7px, calc((100cqi / 3 - 14px) / 9.0833), 12px);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  display: block;
  margin-top: 3px;
  overflow-wrap: break-word;
}
/* cells with a matching performance row link down to it - keep the outer div,
   the grid borders are drawn by div child selectors above */
.facts a.fact-link {
  display: block;
  text-decoration: none;
  color: inherit;
}
@media (hover: hover) {
  .fact-link:hover .v,
  .fact-link:hover .k { color: var(--red); }
}

.btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 24px;
  padding: 16px 20px;
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  text-decoration: none;
  background: var(--red);
  color: var(--paper);
  border: 2px solid var(--red);
  transition: background 0.18s ease, border-color 0.18s ease;
}
.btn { position: relative; }
.btn .arrow {
  font-size: 22px;
  line-height: 1;
  color: #fff;
  transition: color 0.18s ease, transform 0.18s ease;
}
/* badge that half-overlaps the button's top edge */
.btn .btn-badge {
  position: absolute;
  top: 0;
  right: 16px;
  transform: translateY(-55%);
  background: var(--ink);
  color: var(--paper);
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 4px 10px;
  pointer-events: none;
}
@media (hover: hover) {
  .btn:hover .btn-badge { background: var(--red); color: #fff; }
  .btn:hover { background: var(--ink); border-color: var(--ink); }
  .btn:hover .arrow { color: var(--red); transform: translateX(4px); }
}
/* the sheet, the full specifications and the save button: one row at every
   width. The two link tracks are minmax(0, 1fr) rather than 1fr because a
   1fr track will not shrink under its longest word, and the Spanish
   "Especificaciones completas" pushed the button clean off the row's right
   edge on a phone - 78px past it at 320. */
.link-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; gap: 10px; margin-top: 12px; }
.link-row a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 10px;
  border: 2px solid var(--ink);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  text-align: center;
  line-height: 1.3;
  color: var(--ink);
  transition: background 0.18s ease, color 0.18s ease;
}
@media (hover: hover) {
  .link-row a:hover { background: var(--ink); color: var(--paper); }
}
.link-row .tag {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  color: var(--paper-lift);
  background: var(--ink);
  padding: 2px 6px;
}
@media (hover: hover) {
  .link-row a:hover .tag { background: var(--paper); color: var(--ink); }
}

/* ============================================================
   FEATURES - benefit rows + technologies (Mohawk pattern).
   Replaces the spectrum meters and the why-split; the snapshot
   keeps both. Badge squares are placeholders for designer visuals.
   ============================================================ */
.feat { padding: clamp(40px, 6vw, 80px) 0 clamp(36px, 5vw, 64px); }
.feat-cols {
  display: grid;
  grid-template-columns: 1.5fr 1fr;
  gap: clamp(36px, 6vw, 96px);
  align-items: start;
}
.feat-kicker {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red);
  border-bottom: 2px solid var(--line-strong);
  padding-bottom: 10px;
}
.feat-row {
  display: flex;
  gap: 18px;
  align-items: flex-start;
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}
.feat-row:last-child { border-bottom: none; }
.feat-badge {
  flex: none;
  width: 72px;
  height: 72px;
  border: 1.5px solid var(--ink);
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1.2;
  padding: 6px 4px;
}
.feat-badge.small { font-size: 10.5px; } /* longer tokens (MASCOTAS) */
.feat-badge .fb-ic { width: 36px; height: 36px; flex: none; }
.feat-badge .fb-ic .ic-r { stroke: var(--red); }
.feat-copy { margin: 2px 0 0; font-size: 15.5px; color: var(--ink-soft); line-height: 1.6; max-width: 56ch; }
.feat-copy b { display: block; color: var(--ink); font-size: 16.5px; margin-bottom: 2px; }
.tech-item { padding: 16px 0; border-bottom: 1px solid var(--line); }
.feat-tech .tech-item:last-of-type { border-bottom: none; }
.tech-item h3 { margin: 0 0 6px; font-size: 15px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; }
.tech-item p { margin: 0; font-size: 14.5px; color: var(--ink-soft); line-height: 1.6; max-width: 44ch; }
.feat .ws-quote { border-top: none; padding-top: 0; } /* the guide button above closes the column - no extra rule */
.perf-guide {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 26px;
  padding: 14px 16px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  border: 2px solid var(--ink);
  transition: background 0.18s ease, color 0.18s ease;
}
.perf-guide .arrow { color: var(--red); font-size: 20px; line-height: 1; }
@media (hover: hover) {
  .perf-guide:hover { background: var(--ink); color: var(--paper); }
  .perf-guide:hover .arrow { color: #c93a4b; }
}
@media (max-width: 1080px) {
  /* tablets get the phone header: the full nav needs more than 1080px, so
     from here down the burger carries the menu */
  .masthead nav { display: none; }
  .masthead .wrap { grid-template-columns: 1fr; justify-items: center; position: relative; }
  .burger { display: grid; }
  /* the heart and the search leave the hidden nav and take the empty left of
     the row, so neither one needs the menu opened first */
  .mhq { display: flex; }
  .feat-cols { grid-template-columns: 1fr; }
}
/* a phone has 335px of header between its gutters, and the wordmark at its
   desk size leaves the shortcuts nowhere to stand: at the narrow end the logo
   takes the height it already wears inside the mobile menu, and the three
   pieces sit on one line with air to spare */
@media (max-width: 560px) {
  .masthead .logo img { height: 28px; }
}

/* headline + intro stacked left, format tiles right - ONE row, so there is
   no dead air under the text block (client, 2026-08-08). The section rule
   line moves onto this container so it still spans the full width. */
.lib-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: clamp(28px, 5vw, 80px);
  border-top: 1.5px solid var(--line-strong);
  padding-top: 16px;
  margin-bottom: clamp(28px, 4vw, 52px);
}
.grain .lib-head .intro { margin: 18px 0 0; }
.lib-head .lib-switch { margin: 0; flex: none; }
@media (max-width: 1080px) {
  .lib-head { display: block; }
  .lib-head .lib-switch { margin: 28px 0 0; }
}

/* library format switcher - the same technical-drawing control as the buybox.
   Both tiles share one height so the control matches the intro block beside
   it instead of towering under it (client, 2026-08-08). */
.lib-switch {
  display: grid;
  grid-template-columns: 132px 264px;
  gap: 32px;
  margin: -6px 0 48px;
  align-items: end; /* tiles share a floor - the rectangle's bottom sits on the square's */
}
.lib-switch .ls-opt { padding: 0; } /* no dimension lines here - the group label carries the sizes */
.ls-opt.sq .face { aspect-ratio: 1 / 1; }
.ls-opt.rc .face { aspect-ratio: 2 / 1; }

/* ============================================================
   GRAIN LIBRARY
   ============================================================ */
.grain .intro {
  max-width: 60ch;
  font-size: 17px;
  color: var(--ink-soft);
  line-height: 1.7;
  margin: -14px 0 46px;
}
.grain .intro b { color: var(--ink); }

.grain-group + .grain-group { margin-top: 56px; }
.grain-group .group-label {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 24px;
  font-family: var(--font-mono);
  font-size: 13.5px;
  letter-spacing: 0.06em;
  color: var(--ink);
}
.grain-group .group-label .rule { flex: 1; height: 1px; background: var(--line); align-self: center; }
.grain-group .group-label .count { color: var(--ink); font-weight: 600; }

.grain-stack { display: flex; flex-direction: column; gap: 16px; }
.grain-row { display: flex; }
.grain-row:nth-child(2) { padding-left: 14%; }
.grain-row:nth-child(3) { padding-left: 7%; }
.grain-row figure {
  margin: 0;
  position: relative;
  width: 68%;
}
.grain-row img {
  width: 100%;
  box-shadow: var(--float-shadow);
  cursor: zoom-in;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
@media (hover: hover) {
  .grain-row figure:hover img {
    transform: translateY(-6px);
    box-shadow: var(--float-shadow-hover);
  }
}
.grain-row figcaption {
  position: absolute;
  left: 0;
  bottom: calc(100% + 8px);
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.08em;
  color: var(--ink);
  text-transform: uppercase;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.25s ease, transform 0.25s ease;
}
@media (hover: hover) {
  .grain-row figure:hover figcaption { opacity: 1; transform: none; }
}

/* ============================================================
   WHY + ROOM - split section: reasons left, 4:5 photograph right,
   image runs to the seams (viewport edge, no frame)
   ============================================================ */
.why-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  padding-block: 0;
}
.ws-media {
  order: 2;
  margin: 0;
  position: relative;
}
.ws-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  cursor: zoom-in;
}
.ws-content {
  order: 1;
  display: flex;
  flex-direction: column;
  justify-content: center; /* the whole text block breathes as ONE unit on tall screens */
  padding: clamp(36px, 5vw, 64px) clamp(28px, 4vw, 72px) clamp(36px, 5vw, 64px)
           max(var(--gutter), calc((100vw - var(--max)) / 2 + var(--gutter)));
}
/* the media column needs intrinsic height: 4:5 of half the viewport */
.ws-media { min-height: calc(50vw * 1.25); }
.ws-cards { display: flex; flex-direction: column; }
.ws-cards article { padding: 20px 0 22px; }
.ws-cards article + article { border-top: 1px solid var(--line); }
.ws-cards h3 {
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  letter-spacing: 0;
  font-size: 19px;
  color: var(--ink);
  margin: 0 0 6px;
  line-height: 1.1;
}
.ws-cards p { margin: 0; font-size: 15.5px; color: var(--ink-soft); line-height: 1.6; max-width: 52ch; }
.ws-quote {
  margin: clamp(26px, 3vw, 44px) 0 0; /* fixed gap - travels with the text block */
  padding-top: clamp(22px, 3vw, 34px);
  border-top: 1.5px solid var(--line-strong);
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  font-size: clamp(20px, 1.8vw, 27px);
  line-height: 1.2;
  letter-spacing: 0;
  color: var(--ink);
  max-width: 24ch;
}
.ws-quote span { color: var(--red); }
@media (max-width: 1080px) {
  .why-split { grid-template-columns: 1fr; }
  .ws-media { order: 1; min-height: 0; aspect-ratio: 4 / 5; }
  .ws-media img { position: static; height: 100%; }
  .ws-content { order: 2; padding: 26px var(--gutter) clamp(36px, 5vw, 56px); }
  .ws-quote { max-width: none; }
}

/* ============================================================
   WHY PORCELAIN - four-column argument (legacy, unused)
   ============================================================ */
/* no bounding rules - column dividers only; the next section's rule closes it */
.why-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.why-grid article { padding: 6px 26px 12px; }
.why-grid article + article { border-left: 1px solid var(--line); }
.why-grid .why-ico { display: block; color: var(--ink); }
.why-grid h3 {
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  font-size: 20px;
  color: var(--ink);
  letter-spacing: 0;
  margin: 0;
  line-height: 1.15;
}
.why-grid p { margin: 8px 0 0; font-size: 15.5px; color: var(--ink-soft); line-height: 1.6; }

/* ============================================================
   SPECIFICATIONS
   ============================================================ */
.specs .cols {
  display: grid;
  grid-template-columns: minmax(260px, 380px) 1fr;
  gap: clamp(32px, 5vw, 80px);
  align-items: start;
}
.specs .left { position: sticky; top: calc(var(--topbar-h) + 100px); } /* clears topbar + 76px masthead + breathing room */
.specs .left p { font-size: 16px; color: var(--ink-soft); line-height: 1.7; max-width: 34ch; }

.doc-list { margin-top: 26px; display: flex; flex-direction: column; }
.doc-list a {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 2px;
  border-top: 1px solid var(--line);
  text-decoration: none;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
  transition: padding-left 0.18s ease, color 0.18s ease;
}
.doc-list a:last-child { border-bottom: 1px solid var(--line); }
@media (hover: hover) {
  .doc-list a:hover { color: var(--red); padding-left: 8px; }
}
.doc-list .tag {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--paper-lift);
  background: var(--ink);
  padding: 2px 6px;
}
@media (hover: hover) {
  .doc-list a:hover .tag { background: var(--red); }
}
.doc-list .size { margin-left: auto; font-family: var(--font-mono); font-size: 12.5px; color: var(--ink-soft); }

.spec-duo {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 clamp(28px, 3.4vw, 56px);
  align-items: start;
  margin-bottom: 26px;
}
.spec-table { width: 100%; border-collapse: collapse; }
.spec-table th.group {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-align: left;
  color: var(--red);
  border-bottom: 2px solid var(--line-strong);
  padding: 26px 0 8px;
}
.spec-table tr:first-child th.group { padding-top: 0; }
.spec-table td {
  border-bottom: 1px solid var(--line);
  padding: 10px 0;
  font-size: 15px;
  vertical-align: top;
}
.spec-table td.k { color: var(--ink-soft); width: 44%; padding-right: 20px; }
.spec-table td.v { font-family: var(--font-mono); font-size: 14.5px; letter-spacing: 0.01em; }
.spec-table td.v .std { color: var(--ink-soft); font-size: 13px; display: block; margin-top: 2px; }


/* ============================================================
   CALCULATOR
   ============================================================ */
.calc {
  background: var(--ink);
  color: var(--paper);
  border-block: 1.5px solid var(--line-strong);
}
.calc .section-head { border-top-color: rgba(251, 249, 244, 0.9); }
.calc .section-head .aside { color: rgba(251, 249, 244, 0.78); }
.calc-grid {
  display: grid;
  grid-template-columns: minmax(300px, 460px) 1fr;
  gap: clamp(32px, 5vw, 90px);
  align-items: start;
}
.calc-form .field { border-top-color: rgba(251, 249, 244, 0.3); }
.calc-form .field-label { color: rgba(251, 249, 244, 0.9); }
.calc-form .field-label .hint { color: rgba(251, 249, 244, 0.7); }
.calc-form input[type="number"] {
  width: 100%;
  background: transparent;
  border: 1.5px solid rgba(251, 249, 244, 0.4);
  padding: 13px 15px;
  font-family: var(--font-mono);
  font-size: 20px;
  color: var(--paper);
  border-radius: 0;
}
.calc-form input[type="number"]:focus { outline: none; border-color: var(--paper); }
.seg { display: inline-flex; border: 1.5px solid rgba(251, 249, 244, 0.4); }
.seg button {
  padding: 9px 18px;
  font-family: var(--font-mono);
  font-size: 14.5px;
  color: rgba(251, 249, 244, 0.72);
  transition: background 0.15s ease, color 0.15s ease;
}
.seg button + button { border-left: 1.5px solid rgba(251, 249, 244, 0.4); }
.seg button.active { background: var(--paper); color: var(--ink); }
.calc-form .size-echo {
  font-family: var(--font-mono);
  font-size: 14px;
  color: rgba(251, 249, 244, 0.8);
}
.calc-form .size-echo b { color: var(--paper); font-weight: 500; }

.calc-out { display: grid; grid-template-columns: repeat(3, auto); gap: clamp(24px, 4vw, 70px); align-items: end; }
.calc-out .cell .big {
  font-family: var(--font-display);
  font-size: clamp(56px, 7vw, 104px);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  line-height: 0.95;
  letter-spacing: -0.01em;
}
.calc-out .cell .big em { font-style: normal; font-size: 0.55em; color: rgba(251, 249, 244, 0.6); }
.calc-out .cell .lbl {
  display: block;
  margin-top: 10px;
  font-size: 12.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(251, 249, 244, 0.85);
}
.calc-out .cell .sub {
  display: block;
  margin-top: 4px;
  font-family: var(--font-mono);
  font-size: 13.5px;
  color: rgba(251, 249, 244, 0.6);
}
.calc-note {
  grid-column: 1 / -1;
  font-size: 14.5px;
  color: rgba(251, 249, 244, 0.75);
  border-top: 1px solid rgba(251, 249, 244, 0.22);
  padding-top: 14px;
  max-width: 62ch;
  line-height: 1.65;
}

/* ============================================================
   FAMILY - the Hollywood series
   ============================================================ */
.family-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(18px, 2.4vw, 36px);
}
.family-card {
  text-decoration: none;
  display: block;
}
.family-card .strip {
  aspect-ratio: 10 / 4.2;
  overflow: hidden;
  box-shadow: var(--float-shadow);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
@media (hover: hover) {
  .family-card:hover .strip {
    transform: translateY(-5px);
    box-shadow: var(--float-shadow-hover);
  }
}
.family-card .strip img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s ease; }
@media (hover: hover) {
  .family-card:hover .strip img { transform: scale(1.045); }
}
.family-card .meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: 18px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}
@media (hover: hover) {
  .family-card:hover .meta { border-top-color: var(--ink); }
}
.family-card h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  letter-spacing: 0;
  line-height: 1;
  font-size: 19px;
}
.family-card h3 em { font-style: normal; color: var(--red); }
.family-card .status { font-family: var(--font-mono); font-size: 12px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-soft); }
.family-card.current .meta { border-top: 2px solid var(--red); }
.family-card.current .status { color: var(--red); }

/* ============================================================
   FOOTER
   The Deerfield building at dusk, full bleed and duotoned cold,
   with the words standing on it. A two axis scrim does the work:
   near opaque at the left where the statement sits and at the
   right where the two offices sit, open through the middle so
   the sign on the wall breathes, and falling down the page to
   about 93 percent, which still lets the stucco and the palm
   shadow live behind the index. The house red, #a02030, is used
   twice and no more: the cut under the statement, and the rule
   under the language you are reading. Nothing hovers TO red,
   because red on near black cannot be read; those links go white
   or underline, and every hover is colour only, so a finger
   latches nothing. If the photograph never arrives, the ink
   ground and the scrim carry the type on their own.
   ============================================================ */
footer.site-foot {
  --ft-paper: #fbf9f4;
  --ft-soft: rgba(251, 249, 244, 0.86);
  --ft-quiet: rgba(251, 249, 244, 0.82);
  --ft-faint: rgba(251, 249, 244, 0.50);
  --ft-ink: #0b0a09;
  --ft-rule: rgba(251, 249, 244, 0.15);
  /* Each of the two INTERIOR rules stands in a gap owned by the band above
     it and the band below it, and neither gap was split evenly. Measured as
     ink - the baseline of the last line above, the cap line of the first
     below, and the wordmark's own alpha box, which carries 1.76px of
     transparent lead at 30px tall - the base rule read 51.86 above against
     27.76 below at 1280 and 54.34 against 27.76 at 1920: the gap above is
     clamped and grows with the viewport while the gap below was flat 26, so
     the error grew with the window. The index rule had the same shape the
     other way, 64.00 against 52.62 at 1280.
     These two tokens are the WHOLE distance across each rule. Each side is
     half of it, offset by the slack the type leaves on that side: 8.34px
     under the index links' 14px/1.5 line box against 1.76px over the
     wordmark (so 6.6px), and nothing under the offices' underline against
     3.98px over the mono group key (so 4px). The rule is cut out of the gap
     and never added to it, so the footer stands exactly as tall as it did -
     the two paddings still sum to what they summed to at every width. The
     footer's own top rule and its seam to the page above are not in this:
     .ft-top keeps its padding-top untouched. */
  --ft-gap-index: calc(clamp(46px, 5vw, 74px) + clamp(34px, 3.8vw, 52px));
  --ft-gap-base: calc(clamp(30px, 3.4vw, 46px) + 26px);
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ft-ink);
  color: var(--ft-paper);
  box-shadow: inset 0 1px 0 rgba(251, 249, 244, 0.13);
  /* the footer opens with the rule the masthead closes with: the same
     --line-strong token at the same 1.5px weight, at the very top edge.
     It spans the viewport because the footer is a block in normal flow,
     so there is no 100vw here and nothing that can scroll the page
     sideways. It sits in the border box, so the photograph, the scrim
     and the grain all begin below it. */
  border-top: 1.5px solid var(--line-strong);
  margin-top: clamp(30px, 5vw, 70px);
}
.site-foot a { color: inherit; text-decoration: none; }
.site-foot p { margin: 0; }
.site-foot h2 { margin: 0; font-weight: inherit; }
.site-foot ul { margin: 0; padding: 0; list-style: none; }
/* the offices and the base are <section>s and the site gives every
   section a block pad; both take their own below, by class. */
.site-foot .ft-offices { padding-block: 0; }

/* layer 1: the photograph. A real <img> rather than a background, so it
   loads lazily, decodes off thread, and holds its box before it lands. */
.site-foot img.ft-photo {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 48% 8%;
  border: 0;
}
/* layer 2: the scrim. Near opaque under the words at the two sides, and
   open through the middle of the top, where the sign on the wall is.
   It is painted in CSS, so it is there whether or not the photo is. */
.ft-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(90deg, rgba(8, 7, 7, 0.84) 0%, rgba(8, 7, 7, 0.58) 19%, rgba(8, 7, 7, 0.05) 37%,
      rgba(8, 7, 7, 0) 46%, rgba(8, 7, 7, 0.22) 56%, rgba(8, 7, 7, 0.78) 70%, rgba(8, 7, 7, 0.86) 100%),
    linear-gradient(180deg, rgba(9, 8, 8, 0.20) 0px, rgba(9, 8, 8, 0.24) 120px, rgba(9, 8, 8, 0.40) 280px,
      rgba(9, 8, 8, 0.84) 395px, rgba(9, 8, 8, 0.925) 480px, rgba(9, 8, 8, 0.935) 620px),
    linear-gradient(180deg, rgba(8, 7, 7, 0) 78%, rgba(8, 7, 7, 0.45) 100%);
}
/* layer 3: the film grain. One inline fractal-noise tile over the whole
   footer at one strength, because the footer is one surface. No file. */
.ft-grain {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0.13;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 220px 220px;
  mix-blend-mode: overlay;
}
.ft-in { position: relative; z-index: 4; }

/* --- 1. the top band, standing on the picture: the statement to the
   left, the two offices to the right, the sign between them --- */
.ft-top {
  display: grid;
  grid-template-columns: minmax(0, 43fr) minmax(0, 54fr);
  gap: clamp(30px, 4vw, 56px);
  align-items: end;
  padding-block: clamp(66px, 7.6vw, 122px) calc((var(--ft-gap-index) + 4px) / 2);
}
.site-foot .ft-say {
  font-family: var(--font-display);
  font-variation-settings: "wdth" 92, "wght" 800;
  font-size: clamp(34px, 5.6vw, 80px);
  line-height: 0.94;
  letter-spacing: -0.03em;
  text-wrap: balance;
  text-shadow: 0 2px 40px rgba(5, 4, 4, 0.88), 0 1px 4px rgba(5, 4, 4, 0.55);
}
.site-foot .ft-cut {
  display: block;
  width: clamp(90px, 11vw, 160px);
  height: 4px;
  background: var(--red);
  margin-top: clamp(22px, 2.4vw, 32px);
}

/* --- 2. the two numbers, the loud element. A distributor is reached by
   phone, so the number leads and the address is its caption. --- */
.ft-offices {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 30px clamp(24px, 3vw, 52px);
}
.ft-office { min-width: 0; text-shadow: 0 1px 20px rgba(5, 4, 4, 0.8); }
/* which of the two you are reading: Deerfield is the head office, Orlando
   sells. Set in the same mono key the index groups use further down, so a
   small label reads one way throughout the footer. */
.site-foot .ft-role {
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(251, 249, 244, 0.66);
  margin-bottom: 9px;
}
.site-foot .ft-num {
  display: inline-block;
  font-family: var(--font-display);
  font-variation-settings: "wdth" 90, "wght" 700;
  font-size: clamp(26px, 2.4vw, 35px);
  line-height: 1;
  letter-spacing: -0.022em;
  color: var(--ft-paper);
  border-bottom: 2px solid var(--red);
  padding-bottom: 8px;
}
.site-foot .ft-city {
  margin-top: 13px;
  font-family: var(--font-display);
  font-variation-settings: "wdth" 100, "wght" 700;
  font-size: 15px;
  color: var(--ft-paper);
}
.site-foot .ft-addr {
  margin-top: 4px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--ft-soft);
}
.site-foot .ft-hours {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1.6;
  color: rgba(251, 249, 244, 0.70);
}
.site-foot .ft-dir-row { margin-top: 12px; }
.site-foot .ft-dir {
  display: inline-block;
  font-size: 13.5px;
  font-variation-settings: "wght" 600;
  color: var(--ft-paper);
  border-bottom: 1px solid rgba(251, 249, 244, 0.44);
  padding-bottom: 2px;
}
/* drawn, not typed. U+2197 carries emoji presentation on iOS, so the
   arrow is an inline SVG on currentColor like every other arrow here. */
.site-foot .ft-dir .ft-arrow { display: inline-flex; align-items: center; margin-left: 6px; vertical-align: -1px; }
.site-foot .ft-dir .ft-arrow svg { width: 13px; height: 13px; display: block; }

/* --- 3. the index, down where the picture has gone to ink --- */
.ft-index {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 28px clamp(20px, 2.4vw, 40px);
  align-items: start;
  border-top: 1px solid rgba(251, 249, 244, 0.16);
  padding-block: calc((var(--ft-gap-index) - 4px) / 2) calc((var(--ft-gap-base) - 6.6px) / 2);
}
.ft-group { min-width: 0; }
.site-foot .ft-gkey {
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ft-faint);
}
.ft-list { margin-top: 14px; }
.ft-list li { margin-top: 5px; }
.site-foot .ft-list a {
  font-size: 14px;
  line-height: 1.5;
  color: var(--ft-quiet);
}

/* --- 4. the base --- */
.site-foot .ft-base {
  border-top: 1px solid var(--ft-rule);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px 28px;
  padding-block: calc((var(--ft-gap-base) + 6.6px) / 2) 32px;
}
.site-foot img.ft-word { height: 30px; width: auto; filter: brightness(0) invert(1); }
.site-foot .ft-mail { font-family: var(--font-mono); font-size: 12.5px; color: var(--ft-paper); }
.site-foot .ft-legal {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ft-faint);
  margin-left: auto;
}
.site-foot .ft-legal a,
.site-foot .ft-legal .ft-c { color: inherit; white-space: nowrap; }
.site-foot .ft-legal a { margin-left: 10px; }
.site-foot .ft-lang {
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: 0.1em;
}
.site-foot .ft-lang a + a { margin-left: 10px; }
.site-foot .ft-lang .is-on { color: var(--ft-paper); border-bottom: 2px solid var(--red); padding-bottom: 2px; }
.site-foot .ft-lang .is-off { color: var(--ft-faint); }

@media (hover: hover) {
  .site-foot .ft-list a:hover { color: var(--ft-paper); }
  .site-foot .ft-num:hover { border-bottom-color: var(--ft-paper); }
  .site-foot .ft-dir:hover { border-bottom-color: var(--ft-paper); }
  .site-foot .ft-mail:hover,
  .site-foot .ft-legal a:hover,
  .site-foot .ft-lang .is-off:hover {
    color: var(--ft-paper);
    text-decoration: underline;
    text-underline-offset: 5px;
    text-decoration-thickness: 1px;
  }
}

/* the footer below a desk: the statement stops sharing the band with the
   offices and takes its own line, the index halves to two columns, and
   the picture zooms so the building still reads at a narrow measure.
   The scrim is redrawn for the stack, because the words have moved. */
@media (max-width: 1000px) {
  .ft-top { grid-template-columns: minmax(0, 1fr); align-items: start; gap: clamp(34px, 5vw, 50px); }
  .ft-index { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px 28px; }
  .site-foot img.ft-photo {
    inset: auto;
    top: 0;
    left: -40%;
    width: 200%;
    max-width: none;   /* the site caps every img at 100%; this one zooms */
    height: auto;
    object-fit: fill;
  }
  .ft-scrim {
    background:
      linear-gradient(90deg, rgba(8, 7, 7, 0.62) 0%, rgba(8, 7, 7, 0.20) 52%, rgba(8, 7, 7, 0.34) 100%),
      linear-gradient(180deg, rgba(9, 8, 8, 0.26) 0px, rgba(9, 8, 8, 0.32) 110px, rgba(9, 8, 8, 0.70) 250px,
        rgba(9, 8, 8, 0.90) 330px, rgba(9, 8, 8, 0.935) 430px, rgba(9, 8, 8, 0.94) 560px),
      linear-gradient(180deg, rgba(8, 7, 7, 0) 84%, rgba(8, 7, 7, 0.45) 100%);
  }
  .site-foot .ft-legal { margin-left: 0; }
  .site-foot .ft-base { gap: 16px 22px; }
}
/* on a phone the offices take a full line each, which is what keeps the
   number the loud thing at 390, and the base unstacks into wordmark,
   email, fine print, language, in that order. */
/* owner, 2026-09-22: on a phone the two offices stand side by side rather
   than one above the other. That leaves about 134px a column at 320, so the
   number, the role key and the address all step down with the viewport
   instead of overrunning it; the number keeps a tall hit box by padding
   upward into the space the role key gives back. */
@media (max-width: 620px) {
  .ft-offices { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px clamp(12px, 3.6vw, 20px); }
  .site-foot .ft-role { font-size: 9.5px; letter-spacing: 0.08em; margin-bottom: 0; }
  .site-foot .ft-num { font-size: clamp(17px, 5.4vw, 26px); padding-top: 9px; padding-bottom: 7px; }
  .site-foot .ft-city { margin-top: 10px; font-size: 13.5px; }
  .site-foot .ft-addr { font-size: 12.5px; line-height: 1.4; }
  .site-foot .ft-hours { font-size: 9.5px; }
  /* Deerfield's address runs to three lines and Orlando's to two, so the
     two "Get directions" links would sit at different heights. The office
     is a column and the link is pushed to its foot, which lines them up
     across the pair however the addresses fall. */
  .site-foot .ft-office { display: flex; flex-direction: column; }
  .site-foot .ft-dir-row { margin-top: auto; padding-top: 12px; }
  .site-foot .ft-dir { font-size: 12.5px; }
  .site-foot .ft-dir .ft-arrow svg { width: 11px; height: 11px; }
  .site-foot .ft-base { flex-direction: column; align-items: flex-start; }
  .site-foot .ft-legal a { margin-left: 0; display: inline-block; margin-right: 12px; }
}
/* the copyright is held on one line so it never breaks mid-phrase, which is
   right everywhere it fits. The Spanish line is 317px wide and the footer
   clips, so under about 360 the sentence was losing its last two letters.
   It wraps there instead, and takes a line of its own while it does: the
   markup runs the sentence straight into the first link with no space
   between them, and it was only the full-width line that kept them apart. */
@media (max-width: 360px) {
  .site-foot .ft-legal .ft-c { white-space: normal; display: block; }
}

/* ============================================================
   LIGHTBOX
   ============================================================ */
.lightbox {
  position: fixed; inset: 0;
  z-index: 100;
  background: rgba(25, 21, 18, 0.94);
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(20px, 3vh, 34px);
  padding: clamp(20px, 4vw, 60px);
}
.lightbox.open { display: flex; }
.lightbox img {
  max-width: 100%;
  max-height: 72vh;
  object-fit: contain;
  box-shadow: 0 40px 120px rgba(0, 0, 0, 0.5);
}
/* controls sit BELOW the image - never on it */
.lightbox .lb-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(18px, 2.4vw, 32px);
  max-width: 100%;
}
.lightbox .lb-caption {
  font-family: var(--font-mono);
  font-size: 17px;
  font-weight: 500;
  letter-spacing: 0.05em;
  line-height: 1.5;
  color: rgba(251, 249, 244, 0.95);
  text-align: center;
  max-width: 56ch;
}
.lightbox .lb-close {
  position: absolute;
  top: 20px; right: 24px;
  color: var(--paper);
  font-size: 14px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border: 1px solid rgba(251, 249, 244, 0.4);
  padding: 8px 16px;
}
@media (hover: hover) {
  .lightbox .lb-close:hover { background: var(--red); border-color: var(--red); }
}
.lightbox .lb-arrow {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  color: var(--paper);
  border: 1px solid rgba(251, 249, 244, 0.4);
  transition: background 0.15s ease, border-color 0.15s ease;
}
@media (hover: hover) {
  .lightbox .lb-arrow:hover { background: var(--red); border-color: var(--red); }
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
/* mobile title block - name leads the page when the hero stacks */
.m-title { display: none; }
.m-title .lede { margin-bottom: 6px; }

@media (max-width: 1080px) {
  .hero .wrap { grid-template-columns: 1fr; }
  .stage-col { border-right: none; padding-right: 0; padding-bottom: 6px; min-height: 0; }
  .info-col { padding-left: 0; }
  .m-title { display: block; }
  .d-only { display: none !important; }
  .why-grid { grid-template-columns: 1fr 1fr; }
  .why-grid article:nth-child(3) { border-left: none; }
  .why-grid article:nth-child(n + 3) { border-top: 1px solid var(--line); }
  .spec-duo { grid-template-columns: 1fr; }
  /* stacked tables: the second table's first group header needs its spacing back */
  .spec-duo table + table tr:first-child th.group { padding-top: 26px; }
  .specs .cols { grid-template-columns: 1fr; }
  .specs .left { position: static; }
  .calc-grid { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
  .topbar .loc { display: none; }
  .topbar .wrap { justify-content: center; }
  /* the buy box's two-link row may stack here; the documents row on a 3D
     product page may not - it is the only .link-row drawn as flex, and it
     holds its three items on one line at every width (product-3d.css). */
  .link-row:not(.docs) { flex-direction: column; gap: 12px; }
  .backdrop-bar .bd-label { flex-basis: 100%; }
  .crumbs .wrap { white-space: normal; flex-wrap: wrap; overflow-x: visible; }
  .section-head .head-row { flex-direction: column; align-items: flex-start; gap: 14px; }
  .section-head .head-row .aside { margin-left: 0; text-align: left; }
  .crumbs .wrap { overflow-x: auto; white-space: nowrap; flex-wrap: nowrap; }
  .stage { min-height: 320px; }
  .stage-col { gap: 14px; }
  .stage-scene { flex-direction: column; }
  .scene-frame { width: 100%; height: auto; }
  .why-grid { grid-template-columns: 1fr; }
  .why-grid article { padding: 20px 0 22px; }
  .why-grid article:first-child { padding-top: 4px; }
  .why-grid article + article { border-left: none; border-top: 1px solid var(--line); }
  .why-grid article:nth-child(3) { border-top: 1px solid var(--line); }
  .family-grid { grid-template-columns: 1fr; }
  /* calculator results: number left, label + subtext beside it - short rows, no
     tower. Explicit placement, no baseline alignment: iOS Safari mispaints
     baseline-aligned row-spanning grids after scroll (observed live). */
  .calc-out { grid-template-columns: 1fr; gap: 22px; }
  .calc-out .cell {
    display: grid;
    grid-template-columns: max-content 1fr;
    grid-template-rows: auto auto;
    column-gap: 18px;
    align-items: end;
  }
  .calc-out .cell .big { grid-column: 1; grid-row: 1 / 3; font-size: 58px; }
  .calc-out .cell .lbl { grid-column: 2; grid-row: 1; align-self: end; margin-top: 0; }
  .calc-out .cell .sub { grid-column: 2; grid-row: 2; align-self: start; margin-top: 3px; }
  .hero-stack .hp { width: 92%; }
  .hero-stack .hp:nth-child(3) { margin-left: 4%; }
  .grain-row figure { width: 84%; }
  .grain-row:nth-child(2) { padding-left: 8%; }
  .grain-row:nth-child(3) { padding-left: 4%; }
  .scene-card { width: clamp(300px, 88vw, 420px); }
  .cert-strip { flex-wrap: wrap; }
  .cert-strip .rule { display: none; }
  .section-head .aside { margin-left: 0; text-align: left; }
}
/* The narrowest phones. The row keeps all three - it never drops one to a
   second line and never pushes one off its right edge - so the labels give up
   a little type, tracking and padding instead. The clamp only bites below
   400: at 400 the type is still the 12px it is everywhere else. */
@media (max-width: 400px) {
  .link-row { gap: 8px; }
  .link-row a { font-size: clamp(9px, 3vw, 12px); letter-spacing: 0.03em; padding: 12px 6px; }
  .link-row .tag { font-size: 9.5px; letter-spacing: 0.06em; padding: 2px 4px; }
  .link-row .fav-btn { padding-inline: 10px; }
}

/* ============================================================
   THE PRODUCT FILTER
   One menu per criterion, every one built and dressed the same:
   a button that opens a panel of boxes you can tick as many of
   as you like. Driven by assets/js/product-filter.js.
   ============================================================ */
.pfil {
  /* The band's own two measurements. --pfil-band is the space it keeps
     between its content and each of its edges; --pfil-rule is the rule it
     closes with, which .pfil::before paints INSIDE the bottom edge. So the
     padding facing the rule has to carry the rule's width on top of the gap
     you want to see, or the bottom reads a rule-width tighter than the top. */
  --pfil-rule: 1.5px;
  --pfil-band: 12px;
  /* and the rule as it is LAID OUT, which is not the 1.5px it is written as:
     measured in Chromium at a device pixel ratio of 1, 2 and 3, a 1.5px
     border takes 1px, the masthead's included. This is what the padding that
     faces the rule has to carry. */
  --pfil-foot: 1px;
  position: sticky;
  top: var(--pfil-top, calc(var(--topbar-h) + 75px));
  z-index: 60;
  margin-bottom: clamp(20px, 2.6vw, 32px);
  background: var(--paper);
}
/* the ground runs the full width so cards pass cleanly underneath.
   It reaches exactly to the viewport edges, never past them: anything
   wider makes the page scroll sideways. */
.pfil::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(50% - 50vw);
  right: calc(50% - 50vw);
  z-index: -1;
  background: var(--paper);
  border-bottom: var(--pfil-rule) solid var(--line-strong);
}

/* The bar closes with a rule and opens with nothing, so whatever paper sits
   directly above it is read as part of it: the eye sees one strip running from
   the edge overhead down to the rule. Unstuck that edge is the hero's bottom
   (or, where a page has one, the lede's last line); stuck it is the masthead's
   own rule. For the space above the content to equal the space below it, the
   bar has to own every pixel between that edge and itself - so the lead that
   used to sit above it is handed back, and the bar pads itself instead. This
   is what makes the stuck and unstuck rhythms the same rhythm. */
.pi-cards:has(> .wrap > .pfil:first-child) { padding-top: 0; }
.cat-lede:has(+ .pfil) { margin-bottom: 0; }

.pfil-menus {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  gap: 10px;
  padding: 12px 0;
}
.pfil-menu { position: relative; min-width: 0; }

/* the button - identical for every criterion */
.pfil-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 12px;
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: left;
  color: var(--ink);
  background: var(--paper-lift);
  border: 1.5px solid var(--line);
  cursor: pointer;
  transition: border-color 0.16s ease, color 0.16s ease;
}
@media (hover: hover) {
  .pfil-btn:hover { border-color: var(--ink); }
}
.pfil-btn .pfil-btn-t { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.pfil-btn.has-pick { border-color: var(--ink); }
.pfil-menu.is-open .pfil-btn { border-color: var(--ink); }
.pfil-btn-n {
  display: inline-grid;
  place-items: center;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--paper);
  background: var(--red);
  flex: 0 0 auto;
}
.pfil-caret { width: 10px; height: 6px; flex: 0 0 auto; transition: transform 0.16s ease; }
.pfil-menu.is-open .pfil-caret { transform: rotate(180deg); }

/* the phone's drawer and its one button. On a wide screen neither exists:
   the drawer is display:contents, so the menus sit in the bar untouched. */
.pfil-sheet { display: contents; }
.pfil-toggle { display: none; }

/* the panel */
.pfil-panel {
  position: absolute;
  z-index: 5;
  top: calc(100% - 1.5px);
  left: 0;
  min-width: 100%;
  max-height: 320px;
  overflow-y: auto;
  padding: 6px;
  background: var(--paper-lift);
  border: 1.5px solid var(--ink);
  box-shadow: 0 14px 30px -12px rgba(46, 32, 14, 0.34);
}
/* long lists, sizes especially, pair up rather than run off the screen */
.pfil-panel.is-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 4px; min-width: 260px; }

.pfil-opt {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.03em;
  color: var(--ink);
  cursor: pointer;
}
@media (hover: hover) {
  .pfil-opt:hover { background: var(--paper-deep); }
}
.pfil-opt.is-off { opacity: 0.34; cursor: default; }
@media (hover: hover) {
  .pfil-opt.is-off:hover { background: none; }
}
.pfil-opt input {
  appearance: none;
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  margin: 0;
  flex: 0 0 auto;
  background: var(--paper);
  border: 1.5px solid var(--line);
  cursor: pointer;
}
.pfil-opt input:checked {
  background: var(--ink) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.2l2.4 2.4L9.5 4' fill='none' stroke='%23fbf9f4' stroke-width='2'/%3E%3C/svg%3E") center/11px no-repeat;
  border-color: var(--ink);
}
.pfil-opt input:focus-visible { outline: 2px solid var(--red); outline-offset: 1px; }
.pfil-opt-t { flex: 1 1 auto; white-space: nowrap; }
.pfil-opt-n { font-size: 10px; opacity: 0.55; flex: 0 0 auto; }

/* the summary line - the count, and the picks once something is picked.
   THE EQUILIBRIUM, on a desk. The band's content runs from the menus' top
   edges, 12px under the band's top, to the lowest thing in this row, and the
   space under that has to be the same 12px, measured to the rule's top: so
   the padding carries the rule (--pfil-foot). What the lowest thing IS
   changes with the state. With picks it is the frame of a pill, or Clear's
   underline, both 27px boxes. With none it is the count alone - and a line
   box of mono at 1.55 hangs 5px of empty leading under its capitals, which
   is how the band came to keep 12px over its content and 17px under it. So
   the count's box is set nearly solid (a 13px line) and, with nothing
   picked, the padding gives back what is left between its capitals and its
   foot. */
.pfil-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 16px;
  padding: 0 0 calc(var(--pfil-band) + var(--pfil-foot));
}
/* on its 13px line the count's box still keeps 2px under its capitals (measured).
   A desk only: on a phone the count row is the bar and sets its own height. */
@media (min-width: 901px) {
  .pfil:not(.has-active) .pfil-foot { padding-bottom: calc(var(--pfil-band) + var(--pfil-foot) - 2px); }
}
/* THE CENTRE LINE. Beside the count stand the pills, whose frames are what
   the eye lines things up by, and Clear. Every word in the row - the count,
   a pill's label, Clear's capitals - is put on the middle of a pill's frame,
   by ink. Measured at 1280 before this: the count and the pill labels 0.8px
   above the frames' middle, Clear's capitals 1.3px above it. The count comes
   down by being set nearly solid (a box that hugs its type centres its
   type); a pill's label and Clear's capitals come down by giving their boxes
   more padding at the head than at the foot, which keeps every box the
   height it was.
   And every one of those offsets is a whole pixel - a 27px pill and a 27px
   Clear on 17px lines, a 13px count 7px down the row - because the raster
   rounds each box on its own. Where the words sat on fractions (.56, .05,
   .30) they rounded apart by different amounts at rest and stuck, since the
   bar itself sits on a fraction at rest and on a whole pixel when stuck:
   measured at 1280, the count went from 0.2px under the frames' middle
   stuck to 0.7px over it at rest. Whole pixels move together. */
.pfil-count {
  margin: 0;
  line-height: 13px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}
/* the pills and Clear travel as one group (see product-filter.js): on a wide
   screen the group is no box at all, so they sit in the count row as before */
.pfil-picks { display: contents; }
.pfil-active { display: flex; flex-wrap: wrap; gap: 6px; flex: 1 1 auto; min-width: 0; }
/* With nothing picked this is an empty box, and on a phone its flex-basis
   still claims a line of its own - so the row-gap under the content was being
   charged for a line that draws nothing, leaving 8px of dead band below the
   button that no padding accounted for. No pills, no line. */
.pfil-active:empty { display: none; }
.pfil-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px 3px;   /* the label onto the frame's middle, see .pfil-count */
  line-height: 17px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--red);
  background: none;
  border: 1px solid var(--red);
  cursor: pointer;
  transition: color 0.16s ease, background-color 0.16s ease;
}
.pfil-pill svg { width: 9px; height: 9px; flex: 0 0 auto; }
@media (hover: hover) {
  .pfil-pill:hover { color: var(--paper); background: var(--red); }
}
.pfil-clear {
  padding: 5px 0 3px;   /* the capitals onto the row's line, see .pfil-count */
  line-height: 17px;
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  background: none;
  border: none;
  border-bottom: 2px solid var(--red);
  cursor: pointer;
  white-space: nowrap;
}
@media (hover: hover) {
  .pfil-clear:hover { color: var(--red); }
}

.pfil-none { display: block; margin: 8px 0 0; font-size: 15px; color: var(--ink-soft); }

/* every display rule in here outranks the UA rule for [hidden] - panels,
   badges and the empty-state line all set one. Say it once, last, for all. */
.pfil [hidden], .pfil-none[hidden] { display: none !important; }


@media (max-width: 900px) {
  .pfil-menus { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 700px) {
  .pfil-menus { gap: 8px; padding: 10px 0; }
  .pfil-btn { padding: 9px 10px; letter-spacing: 0.1em; }
  /* a panel that would hang off the screen opens against the other edge */
  .pfil-menu:nth-child(even) .pfil-panel { left: auto; right: 0; }
  .pfil-panel.is-grid { grid-template-columns: 1fr; min-width: 190px; }
}

/* The bar pins to the masthead's foot, so it has to follow the masthead's own
   step: the logo drops to 28px at 560 (see .masthead .logo img), taking the
   masthead from 76px to 70px. The offset used to change at 700 instead, and to
   64px rather than 70 - so between 561 and 700 the bar pinned 12px high and
   below 560 it pinned 6px high, parking it UNDER the masthead, which paints
   over it (z-index 80 against 60). The space the band kept above its content
   was being eaten by the header, which is why the stuck bar read tight on top
   and loose underneath. One breakpoint, one height, no overlap.
   And no gap either. The masthead is 69px here and 75px above 560, not 70 and
   76: its rule is written 1.5px and laid out as 1px, measured in Chromium at a
   device pixel ratio of 1, 2 and 3 alike (a 28px logo and 40px of padding
   plus 1 here, a 34px logo above 560).
   Pinned at +70 the bar stood 1px clear of the masthead's foot, and the grid
   scrolled past in that 1px slit - a card's frame or photograph drawn as a
   broken second line under the masthead's rule - and the space over the
   stuck bar's content read 1px more than the space over it at rest, so the
   band could not be even in both states at once. Flush, it is. Where an
   engine keeps more of the 1.5px, the masthead covers that fraction of the
   bar's top, which is empty paper; a slit is never opened. */
@media (max-width: 560px) {
  .pfil { --pfil-top: calc(var(--topbar-h) + 69px); }
}

/* ── the phone: the bar is the button ─────────────────────────────────
   Five boxes stacked down a phone push the first floor off the screen, so
   below the width where the bar still runs as one row the menus ride in a
   drawer under it. And the bar itself is the thing you tap. It used to be a
   count on the left and a boxed FILTER button on the right, 67.5px of band
   for one line of type and one button, and two things that read as two.
   Now it is one row built from the phone menu's own parts: the word on the
   left in the masthead's capitals, the phone menu's red arrow on the right,
   the running count in mono just inside the arrow, and the whole band - rule
   included, gutter to gutter and past them to the screen's edges - is the
   button. 45px tall, so a thumb gets more than the 44px a target needs.
   It says what it is doing without anything being added to it. While the
   drawer is open the word reads CLOSE, as the burger does while its sheet is
   up; the arrow turns a quarter to point down into the drawer, as the Trade
   row's arrow does when it opens what it holds; and the word wears the red
   underline an opened masthead menu wears (.masthead nav a.open), laid over
   the bar's own rule exactly where the rule already runs, so it neither
   doubles the rule nor sits in the band as extra ink. The red badge beside
   the word is the whole tally of ticked boxes, and steps aside while the
   drawer is open, since the drawer shows the ticks themselves.
   THE EQUILIBRIUM. The band's content is one line, and every mark on it -
   the capitals of both faces, the badge, the arrow - is centred on one line
   halfway between the band's top edge and the top of its rule. The band's
   top edge is the hero's foot at rest and the masthead's rule when stuck,
   and the bar pins flush under the masthead (see --pfil-top), so the two
   states measure the same. The padding that faces the rule carries the rule.
   The type needs no nudge, and that is measured, not assumed: set at
   line-height 1, a box of either face holds its capitals on its own middle -
   from the fonts' own metrics, Archivo's stand 0.11px above the middle of a
   12px box and Spline Sans Mono's exactly on it - so centring the boxes
   centres the ink. (The old count carried 4.6px of padding to lift it onto
   the button's label line, and still read 2.5px high of it.) ── */
@media (max-width: 900px) {
  /* --pfil-h is the band, rule and all. --pfil-foot is the rule as it is
     laid out: written 1.5px, it takes 1px (measured, as the masthead's does),
     so the paper above it is 44px and its middle is 22px down - a whole
     pixel, which is the point. Every mark on the bar is centred on that
     middle, and a middle on a half pixel (46px of band, 45 of paper) is one
     that a 16px badge or arrow cannot sit on: it is painted half a pixel off
     it, one way at rest and the other way stuck. */
  .pfil { --pfil-h: 45px; }
  .pfil-foot {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: center;
    gap: 0;
    padding: 0;
  }
  /* the count and the button share the one cell; the count is drawn over
     the button and lets every tap fall through to it */
  .pfil-foot > .pfil-count,
  .pfil-foot > .pfil-toggle { grid-area: 1 / 1; }
  .pfil-toggle {
    position: relative;
    display: flex;
    align-items: stretch;
    gap: 10px;
    height: var(--pfil-h);
    /* out to the screen's edges, so the whole band is the target */
    margin-inline: calc(-1 * var(--gutter));
    padding: 0 var(--gutter) var(--pfil-foot);
    font-family: var(--font-ui);
    font-size: 12px;
    font-weight: 800;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-align: left;
    color: var(--ink);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .pfil-toggle:focus-visible { outline: 2px solid var(--red); outline-offset: -2px; }
  /* the word's box runs the height of the band so its underline can reach the
     rule; the capitals are centred inside it */
  .pfil-toggle-t {
    position: relative;
    display: flex;
    align-items: center;
    line-height: 1;
    white-space: nowrap;
    transition: color 0.16s ease;
  }
  .pfil-toggle .pfil-btn-n { align-self: center; min-width: 16px; height: 16px; padding: 0 3px; }
  .pfil-chev {
    display: flex;
    align-items: center;
    margin-left: auto;
    color: var(--red);
    transition: transform 0.2s ease;
  }
  .pfil-chev svg { display: block; width: 16px; height: 16px; }
  @media (hover: hover) {
    .pfil-toggle:hover .pfil-toggle-t { color: var(--red); }
  }
  /* open: CLOSE, the arrow down, the red underline on the rule */
  .pfil.sheet-open .pfil-chev { transform: rotate(90deg); }
  .pfil.sheet-open .pfil-toggle .pfil-btn-n { display: none; }
  .pfil.sheet-open .pfil-toggle-t::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(-1 * var(--pfil-foot));
    border-bottom: var(--pfil-rule) solid var(--red);
  }
  @media (prefers-reduced-motion: reduce) {
    .pfil-chev { transition: none; }
  }
  /* the count: right-aligned just inside the arrow, never under the word,
     and centred on the paper above the rule as the button's contents are
     (the margin-bottom).
     It reads "product families" (owner ruling, 2026-09-23), which in 12px
     mono capitals is 168px in English and 210 in Spanish: wider than one
     line can be beside FILTER and its badge on the narrowest phones (144
     free at 320). So where one line will not hold it, it WRAPS, onto two
     balanced lines set right against the arrow. The type is not shrunk.
     110px keeps the widest word (FILTRAR, 65px), a two-digit badge (18) and
     17px of air clear of it, and lets the Spanish line stay whole from 390 up.
     The 14px line box keeps whole pixels: one line is 15px with its margin,
     15 down a 45px bar, two are 29, 8 down, and one line's ink sits where it
     did at line-height 1 (the half-leading is equal above and below;
     measured, count against FILTER: -0.17px at DPR 3, 0.00 at DPR 2). */
  .pfil-count {
    position: relative;
    z-index: 1;
    justify-self: end;
    min-width: 0;
    max-width: calc(100% - 26px - 110px);
    margin: 0 26px var(--pfil-foot) 0;   /* the arrow's 16 and the 10 between */
    line-height: 14px;
    white-space: normal;
    text-wrap: balance;
    text-align: right;
    overflow: hidden;
    text-overflow: ellipsis;
    pointer-events: none;
  }

  /* the drawer - full-bleed like the bar's own ground, dropped under it.
     It SLIDES, down and up, and the slide is run from product-filter.js:
     the height and the foot padding are animated, the box is clipped while
     they move, and the drawer's own bottom edge travels over the cards and
     back off them. The curve and the duration are the masthead's, not a new
     pair - see the note in the script. Nothing here fades: the drawer paints
     an opaque paper ground, so no card is ever seen through it, and it is out
     of flow, so the grid under it never moves and the sticky bar is never
     asked to lay out again.
     Two states, and the second is the reason the closing slide can be seen at
     all: .sheet-open is the drawer in use, .sheet-shut is the drawer on its
     way out - still on screen, taking no pointer, so the cards beneath are
     live the instant it is dismissed. The script drops .sheet-shut when the
     animation lands, and with no animation (a wide screen, reduced motion, no
     Web Animations) it is never worn and the drawer simply appears, which is
     what it did before.
     Its height is capped at two thirds of the screen, or at whatever the
     topbar, the masthead and the bar leave under them (36 + 69 + 45 = 150),
     whichever is less. */
  .pfil-sheet {
    display: block;
    position: absolute;
    top: 100%;
    left: calc(50% - 50vw);
    right: calc(50% - 50vw);
    max-height: 66vh;
    max-height: min(66dvh, calc(100dvh - 150px));
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding: 0 var(--gutter) 16px;
    background: var(--paper);
    border-bottom: 1.5px solid var(--line-strong);
    box-shadow: 0 20px 34px -18px rgba(46, 32, 14, 0.45);
    visibility: hidden;
    pointer-events: none;
  }
  .pfil.sheet-open .pfil-sheet,
  .pfil.sheet-shut .pfil-sheet { visibility: visible; }
  .pfil.sheet-open .pfil-sheet { pointer-events: auto; }

  /* the picks, at the drawer's foot under the menus they summarise: a Remove
     pill per ticked box and Clear all at the end of the line, 16px under the
     last menu as the first menu stands 16px under the bar */
  .pfil-sheet .pfil-picks {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 6px;
    padding: 16px 0 0;
  }
  .pfil-sheet .pfil-active { flex: 0 1 auto; }
  .pfil-sheet .pfil-clear { margin-left: auto; }

  /* inside the drawer the menus stack and share their rules, and a panel
     opens in the flow instead of floating over the menu below it */
  .pfil-menus { grid-template-columns: 1fr; gap: 0; padding: 16px 0 0; }
  .pfil-menu + .pfil-menu { margin-top: -1.5px; }
  .pfil-btn { padding: 13px 12px; letter-spacing: 0.12em; }
  .pfil-panel {
    position: static;
    max-height: none;
    min-width: 0;
    margin-top: -1.5px;
    padding: 4px 4px 8px;
    border-color: var(--line);
    border-top: none;
    box-shadow: none;
  }
  .pfil-panel.is-grid { grid-template-columns: 1fr 1fr; min-width: 0; }
  .pfil-opt { padding: 9px 8px; }
}

/* ============================================================
   THE PRODUCT CARD
   Shared: the catalogue ships these in the HTML, the wishlist and
   the search page build them from the catalogue index.
   ============================================================ */
/* the decided product card, from _prototype/product-cards */
/* overflow-x: clip keeps the filter's full-bleed ground from widening the
   page by a scrollbar's width; clip is not a scroll container, so the
   sticky filter still sticks. */
.pi-cards { padding: clamp(28px, 3.6vw, 48px) 0 clamp(40px, 5vw, 72px); overflow-x: clip; }
.pcards { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(28px, 3vw, 40px) clamp(18px, 2.4vw, 34px); }
.pcard { text-decoration: none; color: var(--ink); display: block; position: relative; }
.pcard-link { display: block; color: inherit; text-decoration: none; }
.pcard-swatches { display: flex; gap: 7px; margin-top: 10px; }
/* a swatch is a link in the HTML and a radio button once the script has it:
   the colourway is tried on the card rather than opened. Both wear the same
   square, and the button wears nothing of its own. */
.pcard-swatches a,
.pcard-swatches button { display: block; }
.pcard-swatches button {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.pcard-swatches img { width: 22px; height: 22px; object-fit: cover; border: 1px solid var(--ink); display: block; }
.pcard-swatches a.current img,
.pcard-swatches button.current img { outline: 2px solid var(--ink); outline-offset: 2px; }
.pcard-swatches button:focus-visible { outline: none; }
.pcard-swatches button:focus-visible img { outline: 2px solid var(--red); outline-offset: 3px; }
@media (hover: hover) {
  .pcard-swatches a:hover img,
  .pcard-swatches button:hover img { outline: 2px solid var(--red); outline-offset: 2px; }
}
.pcard-media {
  display: block;
  position: relative;
  aspect-ratio: 1 / 1;
  border: 1.5px solid var(--ink);
  overflow: hidden;
  background: var(--paper-deep);
}
.pcard-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* an image the storage host refused for good, after every retry in chrome.js:
   hidden rather than left as a broken square, so the card shows its ground and
   the name and specs under it stay the whole of what is read */
img[data-img-lost] { visibility: hidden; }
.pcard-media img.scene { opacity: 0; transition: opacity 0.35s ease; }
@media (hover: hover) {
  .pcard:hover .pcard-media img.scene { opacity: 1; }
}
/* a card with no face image has only the room to show: left hidden it would be
   a blank square. The hover form carries the weight to outlast the touch rule
   further down, which otherwise pins the room back to nothing after a tap. */
.pcard-media img.scene:only-child { opacity: 1; }
@media (hover: hover) {
  .pcard:hover .pcard-media img.scene:only-child { opacity: 1; }
}
.card-fav {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 2;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--paper);
  border: 1.5px solid var(--ink);
}
.card-fav svg { width: 16px; height: 16px; display: block; }
@media (hover: hover) {
  .card-fav:hover { color: var(--red); border-color: var(--red); }
}
.card-fav.on { color: var(--red); border-color: var(--red); }
.card-fav.on svg { fill: currentColor; }
.pcard-name {
  display: block;
  margin: 12px 0 3px;
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: 96%;
  text-transform: uppercase;
  font-size: 20px;
  line-height: 1.05;
}
.pcard-name em { font-style: normal; color: var(--red); }
.pcard-meta {
  display: block;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  line-height: 1.5;
}
@media (hover: hover) {
  .pcard:hover .pcard-name { color: var(--red); }
  .pcard:hover .pcard-name em { color: var(--ink); }
}

@media (max-width: 1080px) {
  .pcards { grid-template-columns: repeat(2, 1fr); }
}
/* two to a row on a phone. One floor per screen turns a catalogue into a
   scroll; at two-up a thumb passes a dozen without lifting. The face stays
   square - only the words under it come down a size to fit half a screen. */
@media (max-width: 560px) {
  .pcards,
  .pms-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 12px; }
  .pms-grid--coll { grid-template-columns: minmax(0, 1fr); gap: 32px 12px; }
  .pcard { min-width: 0; }
  .pcard-name { margin: 9px 0 2px; font-size: 16px; }
  .pcard-meta { font-size: 10.5px; letter-spacing: 0.02em; line-height: 1.45; }
  .pcard-swatches { gap: 5px; margin-top: 8px; }
  .pcard-swatches img { width: 20px; height: 20px; }
  .card-fav { top: 7px; right: 7px; width: 30px; height: 30px; }
  .card-fav svg { width: 14px; height: 14px; }
}

/* the header heart carries its own state: outline when the list is empty,
   filled the moment something is on it */
.nav-fav svg, .mm-fav svg { transition: fill 0.16s ease, color 0.16s ease; }
.nav-fav.has svg, .mm-fav.has svg { fill: var(--red); color: var(--red); }

/* a finger has no hover to take back: the tap fires one, it never lifts, and the
   lift, the scale and the crossfade latch on and stay. so on touch nothing moves.

   colour is answered further down, in THE PRESS: every :hover on this sheet now
   sits behind @media (hover: hover), so on a phone it never fires at all, and
   the colour is given back on :active instead, for as long as the finger is down.
   What stays here is the motion half - still needed on the odd device that
   reports a hover and a coarse pointer at once, where the hover rules do run. */
@media (hover: none), (pointer: coarse) {
  .pms-card:hover .pms-card__image img,
  .pms-coll:hover .pms-coll__media img,
  .bd-chip:hover,
  .face-grid button:hover,
  .btn:hover .arrow,
  .family-card:hover .strip img { transform: none; }

  /* the chip rises out of nothing - there is no resting shadow to put back */
  .swatch:not(.current):hover .chip { transform: none; box-shadow: none; }

  /* these already float at rest, so the shadow returns to --float-shadow */
  .hero-stack img:hover,
  .variety-row img:hover,
  .hero-grid img:hover,
  .grain-row figure:hover img,
  .family-card:hover .strip { transform: none; box-shadow: var(--float-shadow); }

  .face-grid button:hover img { box-shadow: 0 3px 10px rgba(46, 32, 14, 0.18); }

  /* reveals that would otherwise never close again */
  .grain-row figure:hover figcaption { opacity: 0; transform: translateY(4px); }
  .pcard:hover .pcard-media img.scene { opacity: 0; }
  .size-opt:not(.active):hover .face,
  .ls-opt:not(.active):hover .face { filter: grayscale(35%) opacity(0.55); }

  /* the row keeps its red and loses the slide */
  .doc-list a:hover { padding-left: 2px; }
}

/* ============================================================
   THE GUIDE'S OWN PDF
   ------------------------------------------------------------
   Appended block. One link, in the same slot on all 36 guide pages:
   the last thing in the guide header, under the lede. Mono, uppercase,
   rule underneath - the small-link idiom the guides already use for a
   stamp or a kicker, so it reads as part of the header and not as a
   button bolted onto it.

   All three header shells (.gh-inner, .fc-head-in, .gt-head-in) are the
   same two-column grid, headline left and lede right, so the link is told
   to span the grid. Without that it would become a third grid item and
   tuck itself under the headline in column one.
   ============================================================ */
.guide-pdf {
  grid-column: 1 / -1;
  justify-self: start;
  align-self: start;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-top: clamp(16px, 2vw, 26px);
  padding-bottom: 4px;
  border-bottom: 1.5px solid var(--line-strong);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  transition: color 0.18s ease, border-bottom-color 0.18s ease;
}
.guide-pdf svg { width: 15px; height: 15px; flex: none; }
@media (hover: hover) {
  .guide-pdf:hover { color: var(--red); border-bottom-color: var(--red); }
}
.guide-pdf:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; }

/* ============================================================
   PRINT - the guide PDFs
   ------------------------------------------------------------
   Appended block. Everything below lives inside one @media print
   and touches nothing on screen. It is what `tools/build_guide_pdfs.py`
   renders through: Chrome headless prints a guide page and these rules
   strip the site chrome so the page arrives as a document.

   Rules carry !important on purpose - each guide page ships its own
   <style> block after this stylesheet, so specificity alone loses.
   ============================================================ */
@media print {

  /* 1. the sheet. Letter, generous outer margin, no browser header or
        footer (the script also passes --no-pdf-header-footer). */
  @page {
    size: Letter;
    margin: 14mm 8mm 16mm;
  }

  /* 2. print the design, not a bleached copy of it: the ink index card,
        the crimson rules and the banded tables all carry their colour. */
  html, body {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
    background: #fff !important;
  }

  /* the fixed fractal-noise grain is a 891x1179 bitmap that Chrome
     rasterises onto every single sheet - 3 MB a page, invisible on paper. */
  body::after { display: none !important; }

  /* 3. the chrome goes. Topbar, masthead, the mega panels under it, the
        mobile menu, the search sheet, the skip link, the footer's grain,
        and the footer index, which is a list of links no one can follow
        on paper. What stays is the row that carries the two addresses,
        and the base: that is what a printed guide should end on. */
  .topbar,
  .masthead,
  .mega,
  .mobile-menu,
  .pms,
  #search-overlay,
  .skip-link,
  .site-foot .ft-index,
  .site-foot .ft-grain,
  .site-foot .ft-scrim,
  .site-foot .ft-cut,
  .site-foot img.ft-photo { display: none !important; }

  /* and the download link itself - it points at the file you are holding */
  .guide-pdf { display: none !important; }

  .site-foot { background: #fff !important; color: #141110 !important; box-shadow: none !important; }
  .site-foot .ft-say,
  .site-foot .ft-num,
  .site-foot .ft-city,
  .site-foot .ft-addr,
  .site-foot .ft-hours,
  .site-foot .ft-dir,
  .site-foot .ft-mail,
  .site-foot .ft-legal,
  .site-foot .ft-lang { color: #141110 !important; text-shadow: none !important; }
  .site-foot img.ft-word { filter: none !important; }
  .site-foot .ft-dir { border-bottom-color: rgba(20, 17, 16, 0.4) !important; }
  .site-foot .ft-num { border-bottom-color: rgba(20, 17, 16, 0.4) !important; }
  .site-foot .ft-lang .is-on { border-bottom-color: rgba(20, 17, 16, 0.4) !important; }
  .site-foot .ft-base { border-top-color: rgba(20, 17, 16, 0.22) !important; }
  /* on paper the band is not a band: the statement takes its own line
     above the two addresses, with a rule instead of a photograph. */
  .site-foot .ft-top { display: block !important; padding-block: 0 !important; }
  .site-foot .ft-say {
    border-top: 1.5px solid #141110 !important;
    font-size: 24px !important;
    padding-block: 12px 4px !important;
  }
  .site-foot .ft-offices {
    break-inside: avoid !important;   /* the contact block is one block or none */
    display: flex !important;
    padding-block: 8px 10px !important;
    gap: 24px !important;
  }
  .site-foot .ft-num { padding-bottom: 6px !important; }
  .site-foot .ft-base { padding-block: 10px !important; }

  /* 4. no video on paper */
  video, iframe { display: none !important; }

  /* 5. reveal-on-scroll never runs in a headless print: the observer only
        fires for what is in the first viewport, so everything below it
        would print blank. Land every reveal in its finished state. */
  .reveal, .reveal.in-view {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }

  /* 6. sticky is meaningless on paper and repeats the rail on every sheet */
  .guide-toc, .specs .left { position: static !important; top: auto !important; }
  .guide-toc { break-inside: avoid; }

  /* 7. panes that scroll sideways on screen would be clipped to one
        screenful on paper. Let them show what they hold. */
  .ps-pane, .ps-scroll, .pei-scale,
  .fc-more-grid .strip, .gt-more-grid .strip { overflow: visible !important; }
  .ps-inner, .ps-track { transform: none !important; }

  /* the controls that drive them are dead ink on paper */
  .ps-nav, .ps-btn, .car-nav, .car-btn { display: none !important; }

  /* 8. <details> FAQs are closed by default, so the answers would simply
        not be in the file. Open every one. ::details-content is the
        UA slot Chrome hides; unhiding it is the whole trick. */
  details > *:not(summary) { display: block !important; }
  details::details-content {
    content-visibility: visible !important;
    block-size: auto !important;
    overflow: visible !important;
  }
  .faq-a { height: auto !important; overflow: visible !important; }
  .faq-ic { display: none !important; }
  summary { list-style: none !important; }
  summary::-webkit-details-marker { display: none !important; }

  /* 9. the page width is the paper width, so the two-column guide grid
        would squeeze the body into a gutter. One column reads better. */
  .guide-grid { display: block !important; }
  .guide-head .gh-inner { display: block !important; }
  .guide-head h1, .fc-head-in h1, .gt-head-in h1 {
    font-size: 34pt !important;
    line-height: 1.02 !important;
  }
  .guide-lede, .fc-head-in > p, .gt-head-in > p { max-width: none !important; }

  /* the wrap keeps a gutter of its own. The @page margin alone would do for
     plain text, but the ink bands run to the edge of the sheet and their
     contents would then start on the trim line. 6mm inside the band. */
  .wrap { max-width: none !important; padding-inline: 6mm !important; }

  /* a portrait scene photo will otherwise take a whole sheet on its own.
     Cap the height; contain keeps the aspect ratio rather than squashing it. */
  img { max-height: 13cm !important; object-fit: contain !important; object-position: left center; }

  /* 10. where the page breaks. Headings stay with what follows them;
         tables, figures and cards are not cut in half. */
  h1, h2, h3, h4 { break-after: avoid-page; break-inside: avoid; }
  table, figure, blockquote, .faq-item, .card, .fig { break-inside: avoid; }
  thead { display: table-header-group; }
  tr, img, svg { break-inside: avoid; }
  .guide-body > section { break-before: auto; }

  /* 11. shadows and hover lifts are noise in CMYK */
  * { box-shadow: none !important; }

  /* 12. type scale - 17px screen body is too loose for a printed column */
  body { font-size: 10.5pt !important; line-height: 1.5 !important; }

  /* 13. links print as text, not as blue-underlined web furniture */
  a { text-decoration: none !important; color: inherit !important; }
}

/* ------------------------------------------------------------
   THE PRESS
   ------------------------------------------------------------
   Every :hover above now lives behind @media (hover: hover), so a finger
   never lights anything just by resting on it or sliding across. What a
   press deserves is still feedback, so the colour hover used to give is
   given again here - but on :active, which lasts exactly as long as the
   finger is down. Generated by tools/gate_hover.py --emit-active.
   ------------------------------------------------------------ */
@media (hover: none), (pointer: coarse) {
  /* our own colour is the only one the press should show */
  a, button, summary, label, [role="button"] { -webkit-tap-highlight-color: transparent; }

  .topbar a:active { color: var(--red); }
  .topbar .lang-opt:not(.active):active { color: var(--red); }
  .lang-opt:not(.active):active { color: var(--red); }
  .masthead nav a:active { border-bottom-color: var(--red); }
  .masthead .mega a:active { color: var(--red); border-bottom-color: var(--line); }
  .masthead .mega a.mega-room:active .mr-copy { color: var(--ink-soft); }
  .pms-back:active { color: var(--red); }
  .pms-clear:active { color: var(--red); }
  .pms-item:active { color: var(--red); border-bottom-color: var(--red); }
  .pms-card__save:active { color: var(--red); }
  .pms-card__title a:active { color: var(--red); }
  .pms-more:active { background: var(--ink); color: var(--paper); }
  .pms-coll__link:active { color: var(--red); }
  .mm-links button.mm-back:active { color: var(--ink); }
  .mm-links button.mm-back:active .mm-arrow { color: var(--red); }
  .mm-links button:active { color: var(--red); }
  .mm-links .mm-sub2 > a:active { color: var(--red); }
  .fav-btn:active { background: var(--ink); color: var(--paper); }
  .fav-btn.on:active { background: var(--red); color: #fff; }
  .mm-close:active { background: var(--red); border-color: var(--red); color: #fff; }
  .mm-links a:active { color: var(--red); }
  .mm-contact a:active { color: var(--red); }
  .crumbs a:active { color: var(--red); }
  .mode-toggle button:not(.active):active { color: var(--red); }
  .scene-nav button:active { background: var(--ink); color: var(--paper); }
  .swatch:not(.current):active .swatch-label { color: var(--red); }
  .acc-card .copy-btn:active { color: var(--red); border-color: var(--red); }
  .fin-seg .fin-opt:not(.active):active { color: var(--red); }
  .stage-caption a:active { color: var(--red); }
  .order-code .copy-btn:active { color: var(--red); border-color: var(--red); }
  .fact-link:active .v,
  .fact-link:active .k { color: var(--red); }
  .btn:active .btn-badge { background: var(--red); color: #fff; }
  .btn:active { background: var(--ink); border-color: var(--ink); }
  .btn:active .arrow { color: var(--red); }
  .link-row a:active { background: var(--ink); color: var(--paper); }
  .link-row a:active .tag { background: var(--paper); color: var(--ink); }
  .perf-guide:active { background: var(--ink); color: var(--paper); }
  .perf-guide:active .arrow { color: #c93a4b; }
  .doc-list a:active { color: var(--red); padding-left: 8px; }
  .doc-list a:active .tag { background: var(--red); }
  .family-card:active .meta { border-top-color: var(--ink); }
  .site-foot a:active { color: var(--ft-paper); text-decoration: underline; text-underline-offset: 5px; text-decoration-thickness: 1px; }
  .site-foot .ft-dir:active { border-bottom-color: var(--ft-paper); }
  .site-foot .ft-num:active { border-bottom-color: var(--ft-paper); text-decoration: none; }
  .lightbox .lb-close:active { background: var(--red); border-color: var(--red); }
  .lightbox .lb-arrow:active { background: var(--red); border-color: var(--red); }
  .pfil-btn:active { border-color: var(--ink); }
  .pfil-opt:active { background: var(--paper-deep); }
  .pfil-opt.is-off:active { background: none; }
  .pfil-pill:active { color: var(--paper); background: var(--red); }
  .pfil-clear:active { color: var(--red); }
  /* the bar, pressed: its word goes red, as a phone menu row's does */
  @media (max-width: 900px) {
    .pfil-toggle:active .pfil-toggle-t { color: var(--red); }
  }
  .pcard-swatches a:active img,
  .pcard-swatches button:active img { outline: 2px solid var(--red); outline-offset: 2px; }
  .card-fav:active { color: var(--red); border-color: var(--red); }
  .pcard:active .pcard-name { color: var(--red); }
  .pcard:active .pcard-name em { color: var(--ink); }
  .guide-pdf:active { color: var(--red); border-bottom-color: var(--red); }
}
