/* ═══════════════════════════════════════════════════════════════════════════
   SALAH ALDEEN
   ═══════════════════════════════════════════════════════════════════════════

   The page is a wall section. A heat strip along the very top edge, the real
   material below it, cool ground where the reading happens. That strip doubles
   as the scroll indicator: dimmed ahead of you, saturated behind you.

   Bands alternate paper -> mint -> ink -> green, so green carries the page and
   WHITE IS RESERVED FOR CARDS, which then lift off the ground.

   ── Three things that break if you forget them ─────────────────────────────

   Arabic is cursive: NEVER give it positive letter-spacing. It pulls joined
   letters apart and the word stops reading as one. All tracking goes through
   --tr-wide / --tr-mid / --tr-sm, which collapse to 0 under html[lang="ar"].

   IBM Plex Mono has NO ARABIC GLYPHS. Any mono-styled label containing Arabic
   falls through to whatever monospace the visitor's OS has. Two guards: the
   Arabic text face sits inside --f-mono, AND every tracked label is listed in
   the one html[lang="ar"] :is(...) rule below, which switches it to the text
   face. ADD NEW LABELS TO THAT LIST.

   In RTL a flex row ALREADY puts the first child on the right. Do not "help"
   it with flex-direction: row-reverse — that double-flips. It once put the
   calculator's 50 degree label over the cold end of a mirrored gradient.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ═══ FONTS — self-hosted since 2026-09-17 ══════════════════════════════════
   Built by `py _src/mkfonts.py` from the files Google Fonts served, all four
   SIL OFL. No request leaves this domain for type any more. The Arabic files
   are subset (the letters, harakat, digits and punctuation this site uses, plus
   the Kurdish/Persian letters); unicode-range keeps an English page from ever
   downloading them, and an Arabic page from downloading the Latin display face
   unless a heading actually contains Latin. */
/* THE ORDER AND THE RANGES ARE LOAD-BEARING (measured 2026-09-17).
   The Arabic files are built from the COMPLETE fonts and carry basic Latin,
   the punctuation and the dashes as well as Arabic (_src/mkfonts.py, whose
   RANGE this unicode-range mirrors exactly). They are declared AFTER the Latin
   faces of the same family, because where two faces' ranges overlap the one
   declared later wins. Before this, every space, full stop and dash inside an
   Arabic sentence was drawn from a Latin file that is not preloaded and arrived
   about a second after first paint; when it landed the line re-wrapped and the
   hero moved (CLS 0.036 home and 0.056 on the 404, in Arabic). */
@font-face { font-family: 'Archivo'; font-weight: 700; font-stretch: 112%; font-display: swap;
  src: url('fonts/archivo-exp-latin-700.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215; }
@font-face { font-family: 'IBM Plex Sans Arabic'; font-weight: 400; font-display: swap;
  src: url('fonts/plex-ar-latin-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215; }
@font-face { font-family: 'IBM Plex Sans Arabic'; font-weight: 500 700; font-display: swap;
  src: url('fonts/plex-ar-latin-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215; }
@font-face { font-family: 'IBM Plex Mono'; font-weight: 400; font-display: swap;
  src: url('fonts/plex-mono-latin-400.woff2') format('woff2'); }
@font-face { font-family: 'IBM Plex Mono'; font-weight: 500 700; font-display: swap;
  src: url('fonts/plex-mono-latin-600.woff2') format('woff2'); }
@font-face { font-family: 'Noto Kufi Arabic'; font-weight: 700; font-display: swap;
  src: url('fonts/kufi-arabic-700.woff2') format('woff2');
  unicode-range: U+0020-007E, U+00A0-00BF, U+00D7, U+00F7, U+060C, U+061B, U+061F, U+0621-063A, U+0640-0652, U+0660-066D, U+0670, U+067E, U+0686, U+0695, U+0698, U+06A4, U+06A9, U+06AF, U+06B5, U+06C6, U+06CC, U+06CE, U+06D5, U+200C-200F, U+2010-2015, U+2018-201A, U+201C-201E, U+2022, U+2026, U+2212; }
@font-face { font-family: 'IBM Plex Sans Arabic'; font-weight: 400; font-display: swap;
  src: url('fonts/plex-ar-arabic-400.woff2') format('woff2');
  unicode-range: U+0020-007E, U+00A0-00BF, U+00D7, U+00F7, U+060C, U+061B, U+061F, U+0621-063A, U+0640-0652, U+0660-066D, U+0670, U+067E, U+0686, U+0695, U+0698, U+06A4, U+06A9, U+06AF, U+06B5, U+06C6, U+06CC, U+06CE, U+06D5, U+200C-200F, U+2010-2015, U+2018-201A, U+201C-201E, U+2022, U+2026, U+2212; }
@font-face { font-family: 'IBM Plex Sans Arabic'; font-weight: 500 700; font-display: swap;
  src: url('fonts/plex-ar-arabic-600.woff2') format('woff2');
  unicode-range: U+0020-007E, U+00A0-00BF, U+00D7, U+00F7, U+060C, U+061B, U+061F, U+0621-063A, U+0640-0652, U+0660-066D, U+0670, U+067E, U+0686, U+0695, U+0698, U+06A4, U+06A9, U+06AF, U+06B5, U+06C6, U+06CC, U+06CE, U+06D5, U+200C-200F, U+2010-2015, U+2018-201A, U+201C-201E, U+2022, U+2026, U+2212; }

:root {
  /* ── the palette is MEASURED off the company logo, not chosen ─────────── */
  --green:      #08613A;   /* the brand, the header, every action. 7.5:1 on paper */
  --green-dk:   #054A2C;
  --green-lt:   #0A7546;
  --green-br:   #17A468;   /* the bright edge — emphasis figures on the INK ground */
  --green-pale: #A9F0CD;   /* emphasis figures on the GREEN field. green-br is 2.4:1 there */

  --steel:      #88918E;   /* the logo grey. 3.2:1 — RULES, ICONS, LARGE TEXT ONLY */
  --steel-ink:  #4E5A57;   /* body-size meta text. 5.3:1 on paper */

  --heat:       #E4551F;   /* ONLY where heat is the subject. Never decorative */
  --heat-dk:    #C3410F;   /* the same warm as a FIELD under white text */
  --heat-ink:   #A83609;   /* the same warm as SMALL TEXT on paper */
  --heat-lt:    #FF7A45;   /* the same warm as text on the ink ground */

  --ink:        #0C1A14;   /* near-black carrying the green undertone */
  --ink-2:      #142219;   /* a panel lifted off the ink ground */
  --paper:      #E7F0EA;   /* the ground is green-tinted, not white */
  --mint:       #D6E7DC;   /* a deeper green ground, for alternating bands */
  --line:       #C6DACD;   /* hairlines, green-tinted to match */
  --white:      #FFFFFF;   /* sheets: tables, the instruments, the message */
  --ink-soft:   #33413C;   /* body copy on paper and mint. 10:1 on paper */
  --studio:     #DADDD6;   /* the ground the product photographs were shot on */

  /* Text on a dark ground. The floor for anything under 24px is .82 —
     .66 measures 3.6:1 and fails. Hierarchy comes from size and weight. */
  --on-1:       #F2F7F4;
  --on-2:       rgba(231, 240, 234, .82);
  --on-3:       rgba(231, 240, 234, .6);   /* large text and rules ONLY */

  /* ── local colour context ──────────────────────────────────────────────
     A white card inside a green band is its own context. Without these,
     band-level "on dark" rules leak into it and paint paper on white.
     Every surface that changes ground re-declares the whole set. */
  --lc-key:     var(--steel-ink);
  --lc-rule:    var(--line);
  --lc-mark:    var(--green);
  --lc-link:    var(--green);
  --lc-heavy:   var(--ink);          /* the 2px rule that heads a sheet or a column */
  --lc-body:    var(--ink-soft);     /* running copy */

  /* ── measure ──────────────────────────────────────────────────────────── */
  --wrap:       1200px;
  --gut:        clamp(20px, 4vw, 40px);
  --head-h:     70px;          /* FIXED. See the header note. */
  /* Drawing-office corners: a sheet, a slab and a chip are cut, not moulded.
     Nothing on the site is rounder than this except the WhatsApp message. */
  --r:          2px;
  --r-sm:       2px;

  /* ── tracking. collapses to 0 for Arabic — see the trap note ──────────── */
  --tr-wide:    .14em;
  --tr-mid:     .07em;
  --tr-sm:      .02em;

  /* ── type ─────────────────────────────────────────────────────────────── */
  --f-display:  'Noto Kufi Arabic', 'Archivo', system-ui, sans-serif;
  --f-latin:    'Archivo', 'Noto Kufi Arabic', system-ui, sans-serif;
  --f-text:     'IBM Plex Sans Arabic', system-ui, sans-serif;
  /* the Arabic text face sits INSIDE the mono stack — guard one of two */
  --f-mono:     'IBM Plex Mono', 'IBM Plex Sans Arabic', ui-monospace, monospace;

  /* --ease is the workhorse: hovers, colour, small state changes.
     --ease-out is the ENTRANCE curve — it dumps almost all of its distance in
     the first third and settles, so a panel or a card arrives already legible
     instead of drifting into place. Use it for anything that appears.
     --ease-in is for something leaving, where the opposite is true. */
  --ease:       cubic-bezier(.2, .7, .3, 1);
  --ease-out:   cubic-bezier(.16, 1, .3, 1);
  --ease-in:    cubic-bezier(.7, 0, .84, 0);
  /* Elevation is for things that float OVER the page — the products menu, the
     phone drawer, the toast. Nothing that sits IN the page casts a shadow. */
  --shadow:     0 1px 2px rgba(12, 26, 20, .06), 0 12px 28px -18px rgba(12, 26, 20, .34);
  --shadow-lg:  0 2px 4px rgba(12, 26, 20, .08), 0 22px 44px -22px rgba(12, 26, 20, .42);
}

html[lang="ar"] {
  --tr-wide: 0;
  --tr-mid: 0;
  --tr-sm: 0;
}

/* GUARD TWO of the Arabic-in-mono trap: every tracked or mono-styled label that
   can contain Arabic is switched to the text face under Arabic. ADD NEW ONES. */
html[lang="ar"] :is(
  .label, .card__no, .pitem__n, .rcard__spec, .phero__spec,
  .chan__k, .specs__k, .slider__scale, .sizer__cap, .board__hd span,
  .spec thead th, .readout__item dt, .readout__item dd i, .tag, .wall__legend
) {
  font-family: var(--f-text);
  letter-spacing: 0;
}

/* ═══ RESET ═══════════════════════════════════════════════════════════════ */

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

/* A component's own `display` beats the user-agent [hidden] rule, and a hidden
   menu then renders over the page. This is the one !important that is policy. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;   /* every control has its own :active state */
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--head-h) + 20px);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Same-origin page changes and the Arabic/English switch share one quiet
   transition. Browsers without the View Transition API ignore this block and
   keep the site's normal instant navigation. The movement is deliberately
   shorter than a card reveal: it connects two pages without making the
   visitor wait for either one. */
@view-transition { navigation: auto; }
::view-transition-old(root) {
  animation: page-out .16s var(--ease-in) both;
}
::view-transition-new(root) {
  animation: page-in .32s var(--ease-out) both;
}
@keyframes page-out {
  to { opacity: 0; transform: translateY(-4px); }
}
@keyframes page-in {
  from { opacity: 0; transform: translateY(6px); }
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

/* set by app.js while the mobile drawer is open. The drawer is full height and
   scrolls itself; without this the page scrolls underneath it, so closing the
   menu drops you somewhere you never chose to be. */
html.is-locked { overflow: hidden; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-kerning: normal;
  font-family: var(--f-text);
  font-size: clamp(15.5px, .5vw + 14px, 17px);
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;

  /* the fixed header is out of flow; body carries the space it used to hold */
  padding-block-start: var(--head-h);

  /* `clip` rather than `hidden`. Both stop a stray horizontal overflow, but
     `hidden` makes this element a scroll container, which is what put the
     sticky header at the mercy of the engine's viewport-propagation rules.
     `clip` never creates one. The fallback below matters only for engines
     older than Safari 16 / Chrome 90. */
  overflow-x: clip;
}
@supports not (overflow: clip) {
  body { overflow-x: hidden; }
}

img, svg { max-width: 100%; display: block; }
a { color: inherit; }
button, input, select, textarea { font: inherit; color: inherit; }
h1, h2, h3, h4 { margin: 0; font-weight: 700; }
p, ul, ol, dl, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }

:focus-visible { outline: 3px solid var(--heat); outline-offset: 3px; }
a { text-underline-offset: .22em; text-decoration-thickness: 1px; }
a:hover { text-decoration-thickness: 2px; }

/* ── the surfaces the browser draws, not us ───────────────────────────────
   Selection, the caret, the scrollbar and the native slider all ship with
   defaults that belong to no design system — a blue highlight and a grey bar
   on a page measured off a company's own green. They are three lines and they
   are the difference between a page that was built and one that was
   assembled. */
:root { color-scheme: light; accent-color: var(--green); }

::selection { background: var(--green); color: var(--white); }
/* on the ink and green bands a green-on-green selection would vanish */
.band--ink ::selection, .band--green ::selection, .foot ::selection {
  background: var(--green-pale); color: var(--ink);
}

input, textarea { caret-color: var(--green); }

* { scrollbar-width: thin; scrollbar-color: var(--steel) transparent; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--steel); border-radius: 99px;
  border: 3px solid var(--paper); background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--steel-ink); }

.skip {
  position: fixed; inset-block-start: 8px; inset-inline-start: 8px;
  z-index: 200; transform: translateY(-160%);
  background: var(--ink); color: var(--white);
  padding: 12px 20px; text-decoration: none; font-weight: 600;
  border-radius: var(--r-sm);
}
.skip:focus { transform: none; }

.matdefs { position: absolute; width: 0; height: 0; overflow: hidden; }

.wrap {
  width: 100%; max-width: var(--wrap);
  margin-inline: auto; padding-inline: var(--gut);
}

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

/* ═══ TYPE ════════════════════════════════════════════════════════════════

   One display face per script, because Arabic has no width axis to match
   Archivo's: Noto Kufi Arabic for Arabic headlines, Archivo at width 112 for
   Latin — expanded reads as MASS, which is what a block of foam is.

   Every measured value is set in mono (.num). It is what makes the site read
   like a datasheet rather than a brochure.
   ═══════════════════════════════════════════════════════════════════════════ */

.h-display {
  font-family: var(--f-display);
  font-weight: 700;
  line-height: 1.38;
}
html[lang="en"] .h-display,
html[lang="en"] .hero__h,
html[lang="en"] .shead__h,
html[lang="en"] .phero__h,
html[lang="en"] .cta__h {
  font-family: var(--f-latin);
  font-stretch: 112%;
  line-height: 1.06;
  letter-spacing: -.01em;       /* tracking tightens as the size grows */
  text-wrap: balance;
}

.num {
  font-family: var(--f-mono);
  font-variant-numeric: tabular-nums lining-nums;
  letter-spacing: 0;
}

/* A label names the control or the list under it. It is NOT an eyebrow over a
   heading: those were removed on 2026-09-17, because a small coloured line above
   every heading is the commonest mark of a machine-made page and the headings
   here carry their own weight. Sentence case, the text face, no tracking. */
.label {
  font-size: .86rem; font-weight: 600;
  color: var(--lc-key);
  margin-block-end: .8em;
}

/* ═══ THE HEAT STRIP / SCROLL INDICATOR ═══════════════════════════════════

   The whole bar is the heat gradient, dimmed by an overlay, and the read
   portion is revealed with clip-path — NOT transform: scaleX, which would
   squash the gradient inside the fill so its colours stop lining up with the
   track behind it.
   ═══════════════════════════════════════════════════════════════════════════ */

.heatline {
  position: fixed; inset-block-start: 0; inset-inline: 0;
  height: 3px; z-index: 120;
  background: linear-gradient(90deg, #F0A81E, var(--heat) 48%, var(--heat-dk));
  opacity: .28;
}
.heatline__fill {
  position: absolute; inset: 0;
  background: inherit; opacity: 1;
  clip-path: inset(0 calc((1 - var(--p, 0)) * 100%) 0 0);
}
html[dir="rtl"] .heatline__fill { clip-path: inset(0 0 0 calc((1 - var(--p, 0)) * 100%)); }

/* ═══ HEADER ══════════════════════════════════════════════════════════════

   THE HEIGHT IS FIXED AND MUST STAY FIXED. Shrinking a sticky header on scroll
   changes document height -> moves scroll position -> re-crosses the threshold
   -> the bar visibly oscillates. Only paint changes, and the stuck state has
   hysteresis (latches at 16px, releases at 4px) in app.js.
   ═══════════════════════════════════════════════════════════════════════════ */

/* FIXED, not sticky, and that is about in-app browsers.
   `position: sticky` resolves against the nearest scrolling ancestor, and
   `body` carries an overflow rule, which is enough to make some engines treat
   the body as that scroller rather than the viewport. A desktop browser
   propagates it to the viewport and the header holds; the webviews inside
   Instagram and Telegram did not, and the header scrolled away with the page —
   reported by the owner 2026-08-21. The heatline three pixels above it is
   `position: fixed` and never moved once, in the same webviews, which is the
   tell.

   `fixed` takes the header out of flow, so body carries exactly the height it
   used to occupy: same layout, none of the scroll-container guesswork. The
   height is a constant already (see the header note at --head-h), so this
   cannot drift. */
.site {
  position: fixed; inset-block-start: 0; inset-inline: 0; z-index: 100;
  height: var(--head-h);
  background: var(--green);
  transition: background .25s var(--ease), box-shadow .25s var(--ease);
}
.site.is-stuck {
  background: var(--green-dk);
  box-shadow: 0 10px 30px -18px rgba(12, 26, 20, .8);
}
.site__in { height: 100%; display: flex; align-items: center; gap: clamp(12px, 2vw, 28px); }

.brand {
  display: flex; align-items: center; gap: 11px;
  text-decoration: none; color: var(--white); flex-shrink: 0;
}
/* On the brand green the mark's own green half would disappear, so it is
   reversed to solid white. The S still reads because it is negative space. */
.brand__mark { width: 40px; height: auto; filter: brightness(0) invert(1); }
.brand__txt { display: grid; line-height: 1.2; }
.brand__name { font-family: var(--f-display); font-size: 1.12rem; font-weight: 700; letter-spacing: var(--tr-sm); }
.brand__sub { font-size: .64rem; color: rgba(255, 255, 255, .8); letter-spacing: var(--tr-mid); }
html[lang="ar"] .brand__sub { font-size: .72rem; }

.site__nav {
  display: flex; align-items: center; gap: clamp(2px, .8vw, 12px);
  margin-inline-start: auto; position: relative;
}
.site__nav > a, .drop__btn {
  display: inline-flex; align-items: center; gap: .4em;
  padding: 9px 12px;
  text-decoration: none;
  color: rgba(255, 255, 255, .88);
  font-size: .92rem; font-weight: 500;
  letter-spacing: var(--tr-sm);
  background: none; border: 0; cursor: pointer; white-space: nowrap;
  transition: color .18s var(--ease);
}
.site__nav > a:hover, .drop__btn:hover { color: var(--white); }


/* The underline marks which PAGE you are on; hovering previews where it would
   go. --x arrives from app.js ALREADY SIGNED for the current direction, because
   inset-inline-start:0 anchors to the right edge under RTL and a left-based
   offset then walks the bar the wrong way. Do not re-flip it here. */
.site__ink {
  position: absolute; inset-block-end: 13px; inset-inline-start: 0;
  height: 2px; width: var(--w, 0);
  background: var(--white);
  transform: translateX(var(--x, 0));
  opacity: 0;
  transition: transform .32s var(--ease), width .32s var(--ease), opacity .2s;
  pointer-events: none;
}
.site__nav.has-ink .site__ink { opacity: 1; }

/* ── the products dropdown ────────────────────────────────────────────────
   .drop__menu is a TRANSPARENT box whose top padding bridges the gap to the
   button; the visible panel is .drop__panel inside it. Remove the bridge and
   put the offset back on the menu and the pointer leaves .drop on the way
   down, closing the menu before anything can be clicked. There is also a
   220 ms grace period on mouseleave, in app.js.
   ───────────────────────────────────────────────────────────────────────── */

.drop { position: relative; }
/* PHYSICAL borders, not logical ones. border-inline-end resolves to the LEFT
   edge under RTL, so the same rotate() turned this into a ‹ aimed at the page
   edge in Arabic — the site's primary language — where it read as "go
   somewhere" rather than "expand me", on the one control that expands. A
   chevron is a picture of a direction and does not mirror. */
.drop__chev {
  width: 6px; height: 6px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .2s var(--ease);
}
.drop.is-open .drop__chev { transform: rotate(225deg) translate(-2px, -2px); }

/* ANCHORED TO THE BUTTON, not centred on it. It used to be centred with
   translateX(±50%), and because the panel is ~340px against a ~90px nav item
   it hung well past both sides and read as a card floating over the hero
   rather than a menu belonging to "المنتجات". inset-inline-start:0 pins its
   leading edge to the button's own leading edge — the right edge under RTL,
   the left under LTR — so it drops straight down from the word it belongs to
   and the transform is not needed in either direction. */
.drop__menu {
  position: absolute; inset-block-start: 100%; inset-inline-start: 0;
  padding-block-start: 10px;          /* the hover bridge. Do not remove. */
  min-width: 320px;
  opacity: 0; visibility: hidden;
  transition: opacity .2s var(--ease), transform .28s var(--ease-out),
              visibility .2s;
  transform: translateY(-6px);
}
.drop.is-open .drop__menu { opacity: 1; visibility: visible; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .drop__menu, .drop.is-open .drop__menu { transform: none; transition: opacity .12s linear, visibility .12s; }
}

.drop__panel {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow-lg);
  padding: 8px;
  --lc-key: var(--steel-ink);
}
.drop__panel a {
  display: grid; gap: 2px;
  padding: 10px 14px;
  text-decoration: none; color: var(--ink);
  border-radius: var(--r-sm);
  transition: background .16s var(--ease);
}
.drop__panel a:hover { background: var(--paper); }
.drop__panel b { font-size: .94rem; font-weight: 600; }
.drop__panel span { font-size: .78rem; color: var(--steel-ink); line-height: 1.5; }

.site__end { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }

.lang {
  width: 40px; height: 34px;
  background: none; border: 0; border-block-end: 2px solid rgba(255, 255, 255, .55);
  border-radius: 0;
  color: var(--white);
  font-family: var(--f-mono); font-size: .74rem; font-weight: 600;
  cursor: pointer;
  transition: border-color .18s var(--ease), background .18s var(--ease);
}
.lang:hover { border-color: var(--white); background: rgba(255, 255, 255, .1); }

.burger {
  display: none; width: 40px; height: 34px;
  background: none; border: 1px solid rgba(255, 255, 255, .4);
  border-radius: var(--r-sm);
  cursor: pointer; padding: 8px 9px;
  flex-direction: column; justify-content: space-between;
}
.burger span { display: block; height: 2px; background: var(--white); transition: transform .22s var(--ease), opacity .22s; }
.is-open .burger span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.is-open .burger span:nth-child(2) { opacity: 0; }
.is-open .burger span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* ═══ BUTTONS ═════════════════════════════════════════════════════════════

   A SLAB, not a pill. Cut corners (2px), a flat fill, and a darker 3px edge
   along the bottom that is the only thing that moves: it thins to 1px when the
   slab is pressed. Hover darkens the fill and nothing jumps.

   The WhatsApp slab carries its icon in its own darker cell and, on the large
   size, the number itself under the label, because a buyer here often wants
   the number more than the link.

   The secondary action is not a second button. It is a link with a drawn rule
   and an arrow, so a page never offers two slabs side by side.
   ═══════════════════════════════════════════════════════════════════════════ */

.btn {
  --slab: var(--green); --slab-dk: var(--green-dk); --slab-ink: var(--white);
  display: inline-flex; align-items: stretch; justify-content: center;
  min-height: 48px;
  padding: 0 22px;
  background: var(--slab); color: var(--slab-ink);
  border: 0; border-radius: var(--r-sm);
  box-shadow: inset 0 -3px 0 var(--slab-dk);
  text-decoration: none;
  font-weight: 600; font-size: .96rem; line-height: 1.3;
  cursor: pointer;
  transition: background-color .16s var(--ease), box-shadow .12s var(--ease);
}
.btn > span, .btn { align-items: center; }
.btn:hover { background-color: var(--slab-dk); }
.btn:active { box-shadow: inset 0 -1px 0 var(--slab-dk); }
.btn--lg { min-height: 56px; padding: 0 26px; font-size: 1rem; }
.btn--sm { min-height: 40px; padding: 0 14px; font-size: .88rem; }

/* the icon cell sits flush against the slab's START edge at any width, so a
   full-width slab on a phone does not float its icon in the middle */
.btn--ico { justify-content: flex-start; padding-inline-start: 0; }
.btn__ico {
  display: grid; place-items: center; flex: none; align-self: stretch;
  inline-size: 48px; margin-inline-end: 18px;
  background: var(--slab-dk);
  border-start-start-radius: var(--r-sm); border-end-start-radius: var(--r-sm);
}
.btn--lg .btn__ico { inline-size: 56px; margin-inline-end: 20px; }
.btn__txt { display: grid; gap: 1px; text-align: start; }
.btn__glyph { inline-size: 22px; block-size: 22px; }
.btn__num { font-size: .78rem; font-weight: 500; opacity: .9; direction: ltr; unicode-bidi: isolate; justify-self: start; }
html[dir="rtl"] .btn__num { justify-self: end; }

/* on the green band and in the header the slab turns white */
.band--green .btn, .site .btn {
  --slab: var(--white); --slab-dk: var(--mint); --slab-ink: var(--green);
}
.band--green .btn:hover, .site .btn:hover { --slab: var(--paper); color: var(--green-dk); }
.band--green .btn__ico, .site .btn__ico { background: var(--mint); }

/* the secondary action: a link with a drawn rule and an arrow */
.btn--ghost {
  --slab: transparent;
  min-height: 48px; padding: 0 2px;
  color: var(--ink);
  box-shadow: inset 0 -2px 0 currentColor;
  gap: .6em;
}
.btn--ghost::after {
  content: '←'; font-weight: 400;
  transition: transform .2s var(--ease);
}
html[dir="ltr"] .btn--ghost::after { content: '→'; }
.btn--ghost:hover { background-color: transparent; color: var(--green); box-shadow: inset 0 -3px 0 currentColor; }
.btn--ghost:hover::after { transform: translateX(-4px); }
html[dir="ltr"] .btn--ghost:hover::after { transform: translateX(4px); }
.btn--ghost:active { box-shadow: inset 0 -2px 0 currentColor; }
/* The ghost has to out-rank ".band--green .btn" (0,2,0), which paints every
   slab white on the green band. Written as (0,2,0) and BELOW it, so it wins on
   source order. Losing this race once rendered "all the numbers" as an empty
   white box with white text in it: an invisible link that every structural
   check passes. */
.band--ink .btn--ghost, .band--green .btn--ghost, .site .btn--ghost { --slab: transparent; color: var(--white); }
.band--ink .btn--ghost:hover, .band--green .btn--ghost:hover { color: var(--white); background: none; }

@media (prefers-reduced-motion: reduce) {
  .btn--ghost:hover::after { transform: none; }
}

/* ═══ BANDS ═══════════════════════════════════════════════════════════════ */

.band { padding-block: clamp(64px, 8vw, 120px); position: relative; }
.band--tight { padding-block: clamp(48px, 5.6vw, 80px); }

.band--paper { background: var(--paper); }
.band--mint  { background: var(--mint); }
.band--ink   { background: var(--ink); color: var(--on-1); }
.band--green { background: var(--green); color: var(--white); }

/* every dark surface re-declares the local colour context */
.band--ink, .band--green, .foot {
  --lc-key:   var(--on-2);
  --lc-rule:  rgba(231, 240, 234, .18);
  --lc-mark:  var(--green-br);
  --lc-link:  var(--white);
  --lc-heavy: var(--on-1);
  --lc-body:  var(--on-1);
}
/* running copy on the green field is white: #F2F7F4 measures 6.97:1 there and
   the body floor on a dark ground is 7 */
.band--green { --lc-mark: rgba(255, 255, 255, .86); --lc-body: var(--white); --lc-heavy: var(--white); }

/* ...and a white SHEET flips it back, wherever it is nested. Only surfaces
   that paint an opaque light background belong in this list. */
.drop__panel, .board, .msg,
.band--paper .readout, .band--mint .readout {
  --lc-key:   var(--steel-ink);
  --lc-rule:  var(--line);
  --lc-mark:  var(--green);
  --lc-link:  var(--green);
  --lc-heavy: var(--ink);
  --lc-body:  var(--ink-soft);
  color: var(--ink);
}

/* ── section heading ─────────────────────────────────────────────────── */
.shead { max-width: 62ch; margin-block-end: clamp(40px, 4.6vw, 64px); }
.shead__h {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(1.9rem, 3.5vw, 2.8rem);
  line-height: 1.4;
  text-wrap: balance;
}
.shead__p { margin-block-start: 1em; font-size: 1.04rem; color: var(--lc-body); max-width: 56ch; text-wrap: pretty; }

/* Heading on one side, lede on the other, for a head that runs the full width
   with nothing under its second half. */
.shead--split { max-width: none; display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: 0 clamp(24px, 4vw, 64px); align-items: end; }
.shead--split .shead__h { grid-column: 1; grid-row: 1; }
.shead--split .shead__p { grid-column: 2; grid-row: 1; margin-block-start: 0; max-width: 46ch; }
@media (max-width: 860px) {
  .shead--split { grid-template-columns: 1fr; }
  .shead--split .shead__p { grid-column: 1; grid-row: 2; margin-block-start: 1em; }
}

/* ═══ HERO — the wall section, drawn ═════════════════════════════════════

   The first screen is paper, and the argument is a DRAWING: a wall cut through
   the way a contractor reads one on a drawing sheet. Block hatching, the
   insulation symbol, dimension lines in centimetres, a legend and a title
   block. Heat comes in from the sun side at 50 and leaves the insulation as a
   thin line; the room is 26.

   The drawing is geometry in an SVG and every word on it is HTML laid over it,
   so it translates. The SVG is drawn for Arabic, with the outside on the right;
   English mirrors the geometry and un-mirrors the two numbers inside the SVG.
   ═══════════════════════════════════════════════════════════════════════════ */

.hero {
  background: var(--paper); color: var(--ink);
  padding-block: clamp(48px, 7vw, 104px) clamp(56px, 7vw, 112px);
  position: relative;
}
.hero__in {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr);
  gap: clamp(32px, 6vw, 96px);
  align-items: center;
}
@media (max-width: 900px) {
  .hero__in { grid-template-columns: 1fr; }
}

.hero__h {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(2.15rem, 4.3vw, 3.5rem);
  line-height: 1.38;
  max-width: 15ch;
  text-wrap: balance;
}
.hero__p { margin-block-start: 1.3em; max-width: 44ch; font-size: clamp(1.02rem, .4vw + .94rem, 1.14rem); line-height: 1.85; color: var(--lc-body); text-wrap: pretty; }
.hero__cta { display: flex; align-items: center; gap: 16px 32px; flex-wrap: wrap; margin-block-start: clamp(28px, 3.4vw, 48px); }

.wall { margin: 0; }
.wall__draw { position: relative; container-type: inline-size; }
.wall__svg { display: block; inline-size: 100%; block-size: auto; overflow: visible; }
html[dir="ltr"] .wall__svg { transform: scaleX(-1); }
html[dir="ltr"] .wall__svg text { transform: scaleX(-1); transform-box: fill-box; transform-origin: center; }

.w-cut   { stroke: var(--ink); stroke-width: 1.8; fill: none; }
.w-brk   { stroke: var(--ink); stroke-width: 1.1; fill: none; }
.w-dim   { stroke: var(--steel-ink); stroke-width: 1; fill: none; }
.w-dimt  { font-family: var(--f-mono); font-size: 14px; font-weight: 600; fill: var(--ink); text-anchor: middle; }
.w-eps   { fill: var(--white); }
.w-wave  { stroke: var(--green); stroke-width: 1.4; fill: none; }
.w-heat  { fill: var(--heat); fill-opacity: .9; }
.w-shaft { stroke: var(--steel-ink); stroke-width: 2.4; }
.w-head  { fill: var(--steel-ink); }

.wall__t {
  position: absolute; inset-block-start: 22.6%;
  display: grid; gap: 4px; margin: 0;
  font-size: clamp(11px, 2.3cqi, 13.5px); line-height: 1.4; color: var(--ink-soft);
}
.wall__t b { font-family: var(--f-mono); font-size: clamp(20px, 6.2cqi, 36px); line-height: 1; font-weight: 600; direction: ltr; unicode-bidi: isolate; }
/* each label stays inside its own zone of the drawing: the outside zone is the
   21% beyond the render, the room is the 41% before the plaster */
.wall__t--out { inset-inline-start: 1.5%; max-inline-size: 20%; }
.wall__t--in  { max-inline-size: 36%; }
.wall__t--out b { color: var(--heat-ink); }
.wall__t--in  { inset-inline-end: 3%; }
.wall__t--in b  { color: var(--green); }

.wall__cap {
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  margin-block-start: 8px;
  border-block: 2px solid var(--ink);
  border-block-end-width: 1px; border-block-end-color: var(--line);
  font-size: .82rem; line-height: 1.6;
}
.wall__legend { display: grid; gap: 8px; align-content: center; padding: 14px 0; padding-inline-end: 20px; border-inline-end: 1px solid var(--line); }
.wall__legend li { display: flex; align-items: center; gap: 10px; }
.wall__legend svg { inline-size: 28px; block-size: 14px; flex: none; }
.wall__title { display: grid; gap: 4px; align-content: center; padding: 14px 0; padding-inline-start: 20px; color: var(--ink-soft); }
.wall__title b { color: var(--ink); font-size: .9rem; }
.wall__title a { color: var(--green); font-weight: 600; }
@media (max-width: 520px) {
  .wall__cap { grid-template-columns: 1fr; }
  .wall__legend { padding-inline-end: 0; border-inline-end: 0; border-block-end: 1px solid var(--line); }
  .wall__title { padding-inline-start: 0; }
}

/* THE ONE AUTHORED MOMENT ON THE HOME PAGE. The wall assembles once, when at
   least a third of it is on screen: the dimensions draw, the insulation is laid
   in, the heat arrives and is cut down to a line, and the room temperature
   appears last, because it is the result. About 1.8s.

   app.js sets data-draw="wait" before it observes anything, so with no
   JavaScript the attribute never exists and the drawing is simply complete.
   Reduced motion never sees the start state either. */
@media (prefers-reduced-motion: no-preference) {
  .wall[data-draw] .w-dimg { opacity: 0; }
  .wall[data-draw] .w-wave { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .wall[data-draw] .w-heat { transform: scaleX(0); transform-box: fill-box; transform-origin: 100% 50%; }
  .wall[data-draw] .w-arrow { transform: scaleX(0); transform-box: fill-box; transform-origin: 100% 50%; }
  .wall[data-draw] .wall__t--in { opacity: 0; transform: translateY(6px); }

  .wall[data-draw="go"] .w-dimg { opacity: 1; transition: opacity .5s var(--ease-out); }
  .wall[data-draw="go"] .w-wave { stroke-dashoffset: 0; transition: stroke-dashoffset .9s var(--ease-out) .2s; }
  .wall[data-draw="go"] .w-heat { transform: none; transition: transform .7s var(--ease-out) .7s; }
  .wall[data-draw="go"] .w-arrow { transform: none; transition: transform .45s var(--ease-out) 1.2s; }
  .wall[data-draw="go"] .wall__t--in { opacity: 1; transform: none; transition: opacity .4s var(--ease-out) 1.5s, transform .4s var(--ease-out) 1.5s; }
}

/* ═══ PAGE HERO (interior pages) ══════════════════════════════════════════
   Paper, like the home page. The heading carries the page; a product page
   adds one line of specification in mono under the lede, the way a datasheet
   opens. No breadcrumb: the underlined nav item already says where you are. */

.phero {
  background: var(--paper); color: var(--ink);
  padding-block: clamp(44px, 6vw, 96px) clamp(48px, 6vw, 96px);
  position: relative;
}
.phero__in { position: relative; }
.phero__h {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(2rem, 4vw, 3.25rem);
  line-height: 1.38; max-width: 20ch;
  text-wrap: balance;
}
.phero__p { margin-block-start: 1.1em; max-width: 58ch; font-size: clamp(1.02rem, .4vw + .94rem, 1.12rem); line-height: 1.85; color: var(--lc-body); text-wrap: pretty; }
.phero__spec {
  display: flex; flex-wrap: wrap; gap: .4em 1.4em;
  margin-block-start: 1.6em; padding-block-start: 1em;
  border-block-start: 2px solid var(--ink);
  max-width: 58ch;
  font-family: var(--f-mono); font-size: .92rem; font-weight: 600;
  color: var(--green-dk);        /* --green is 6.5:1 on paper; this is 9:1 */
}
.phero__spec > span { unicode-bidi: isolate; }

/* ═══ COLUMNS AND THE CATALOGUE ════════════════════════════════════════════

   There are no cards. A group of related blocks is a set of COLUMNS, each
   headed by a 2px rule, the way a drawing sheet divides its notes; a product is
   a CATALOGUE ENTRY: its photograph on the studio ground it was shot on, then
   the same heavy rule, its name, one line of measured specification, and a
   sentence. Nothing lifts, nothing casts a shadow, and the colour context is
   whatever band the column sits on.
   ═══════════════════════════════════════════════════════════════════════════ */

.grid { display: grid; gap: clamp(32px, 3.4vw, 48px) clamp(24px, 3vw, 40px); }
/* min(…, 100%): a track minimum wider than a 320px phone's content box made the
   column overflow the page, and under RTL that overflow is scrollable. */
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(256px, 100%), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(212px, 100%), 1fr)); }

.card {
  border-block-start: 2px solid var(--lc-heavy);
  padding-block-start: clamp(16px, 1.8vw, 22px);
}
.card__no {
  font-size: .86rem; font-weight: 600;
  color: var(--lc-key);
  margin-block-end: .6em;
}
.card__no.num { font-family: var(--f-mono); color: var(--lc-mark); }
.card__h {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(1.1rem, 1.6vw, 1.28rem);
  line-height: 1.5;
  margin-block-end: .5em;
}
.card__p { font-size: .96rem; color: var(--lc-body); line-height: 1.8; }

/* ── the range grid ──────────────────────────────────────────────────── */

.range { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(268px, 100%), 1fr)); gap: clamp(48px, 5vw, 72px) clamp(24px, 3vw, 40px); }

/* Five entries in a three-track grid: the first spans two, so the rows read
   [boards-and-blocks, boxes] / [pallet, fan, float] with no hole. Guarded above
   the one-track breakpoint, where a span of 2 builds an implicit column. */
@media (min-width: 610px) {
  .range > :first-child { grid-column: span 2; }
}
.rcard { display: grid; }
.rcard__a {
  display: grid; align-content: start;
  text-decoration: none; color: inherit;
  height: 100%;
}
.rcard__art {
  aspect-ratio: 4 / 3;
  background: var(--studio);
  display: grid; place-items: center;
  /* The row has to be DEFINITE. Left implicit it is auto-sized, a percentage
     height inside it resolves to auto, and an <svg class="art"> then takes its
     own aspect off the frame's width and is clipped by the overflow below. */
  grid-template-rows: minmax(0, 1fr);
  padding: 14px; overflow: hidden;
}
.rcard__art svg, .rcard__art img { width: 100%; height: 100%; object-fit: contain; }
.rcard__art--square { aspect-ratio: 1 / 1; }
.rcard__art.rcard__art--product { aspect-ratio: 1 / 1; padding: 0; background: var(--studio); }

/* ── the wide feature card ────────────────────────────────────────────────
   The home range promotes "boards and blocks" across two columns, so its
   frame is the one product frame a square photograph does NOT fill. The
   photograph stays CONTAINED — a board cropped off at both ends is not a
   picture of a board — and the frame carries the photograph's own ground
   instead of the mint used elsewhere, so the letterbox reads as the backdrop
   continuing rather than as two grey bars. Sampled off the file: #CBCBC7 at
   the top-left corner to #DEDFDA at the bottom-right.

   Below 610px the card stops spanning (see the .range comment above) and the
   frame goes back to square with it, where the photograph fills it exactly
   and the ground never shows. */
.rcard__art.rcard__art--wide {
  /* .rcard__art.rcard__art--product also sets a background and carries the same
     (0,2,0) weight, so this selector names two classes as well AND is written
     below it — equal weight means source order decides, and losing that is
     silent: the mint just keeps painting. Measured, not assumed.
     Corner-matched to the file: #D0D2CA top-left, #DEDFDA bottom-right. */
  background: var(--studio);
}
/* No aspect-ratio: the pair inside sets the height, so the frame hugs it and
   there is no letterbox left to colour match. The gradient below is kept only
   as the paint-flash ground while the two files decode. */
.rcard__art.rcard__art--wide { aspect-ratio: auto; padding: 0; }

/* Real product photography. Every supplied file is a 1254x1254 square shot on
   the same pale ground, so the frame is square too and the photograph is
   CONTAINED in it — a box that is 50 wide and 63 deep and one that is 30 wide
   and 37 deep must both arrive undistorted, and `cover` would crop whichever
   one did not match the frame. Intrinsic width and height are on every <img>,
   so the square is reserved before the file lands and nothing shifts.

   The multi-view gallery that used to sit above the box cards is gone
   (2026-08-25): four photographs of one box read as four products. Its CSS
   went with it — nothing referenced .product-gallery* afterwards.

   The sheen fires once as the section reveals and again on hover. It is a
   one-shot, never a loop. */
.product-media {
  position: relative; isolation: isolate; overflow: hidden;
  background-color: var(--studio);
}
.product-photo {
  display: block; inline-size: 100%; block-size: 100%;
  object-fit: contain; object-position: center;
  opacity: 1; filter: none;
  transform: scale(1.001);
  transition: opacity .46s var(--ease-out), filter .62s var(--ease-out),
              transform .9s var(--ease-out);
}

/* Product files are lazy-loaded. Fade each one in only after it has decoded
   far enough to paint; the unscoped defaults above keep every image visible
   with JavaScript disabled. */
.js .product-photo { opacity: 0; filter: blur(6px) saturate(.86); }
.js .product-photo.is-loaded { opacity: 1; filter: none; }

/* ── THE FLOAT TYPES ──────────────────────────────────────────────────────
   Two types, ONE photograph. The left assembly is type 1 and the right is
   type 2, and rather than invent a second image each card opens a window on
   to the same file.

   The window, measured off the file (1254 x 1254):

       type 1 assembly   x 306..585   type 2 assembly   x 671..999
       both              y 113..1094 / 125..1071
       window            430 x 1020, from y 90; x 230.5 (t1), x 620 (t2)

   430 is as wide as the window can be without the neighbouring assembly
   entering the frame, and 1020 clears the top of the float and the bottom of
   the threaded base with room to spare — float, arm, both pivots, the sealing
   ring and the thread are all inside it. The window sits wholly inside the
   image, so the photograph covers it edge to edge and .vcrop's own background
   never shows.

       inline-size  1254/430  = 291.63%      inset-block-start  -90/1020  =  -8.82%
       block-size   1254/1020 = 122.94%      left (t1)       -230.5/430  = -53.60%
                                             left (t2)         -620/430  = -144.19%

   Both percentages resolve to the same number of pixels, because .vcrop's
   aspect ratio IS the window's — the photograph cannot stretch.

   `left`, not `inset-inline-start`: the crop is a picture of a physical place
   in a photograph. A logical inset would anchor to the right edge under RTL
   and the Arabic page would show type 2 in the type 1 card.

   transform-origin is the window's centre in image coordinates, so the hover
   scale grows about the valve instead of about the middle of the file, which
   is empty background between the two.
   ─────────────────────────────────────────────────────────────────────────── */
.vfig { display: grid; place-items: center; margin-block-end: 16px; }
.vcrop {
  position: relative; overflow: hidden;
  inline-size: min(100%, 190px);
  aspect-ratio: 430 / 1020;
  border-radius: var(--r-sm);
  background: linear-gradient(152deg, #C8CAC3, #DCDED9);
}
.vcrop > .product-photo {
  position: absolute;
  inline-size: 291.63%; block-size: 122.94%; max-inline-size: none;
  inset-block-start: -8.82%;
  left: var(--vc-x);
  transform-origin: var(--vc-ox) 47.85%;
}
.vcrop--t1 { --vc-x: -53.60%; --vc-ox: 35.53%; }
.vcrop--t2 { --vc-x: -144.19%; --vc-ox: 66.59%; }
.vtypes { max-inline-size: 720px; margin-inline: auto; }

/* Narrower phones: the card loses its side padding to the viewport before the
   window does, so the window comes down with it rather than being clipped. */
@media (max-width: 400px) {
  .vcrop { inline-size: min(100%, 150px); }
}

/* ── the three box sizes ────────────────────────────────────────────────
   Three sizes are a SET. On the shared .grid--3 the row breaks to two-and-one
   between about 560 and 800px, and a lone Large under a Small and a Medium
   reads as a pair plus a spare rather than as one range. It holds all three
   across down to 620px — the cards are square photographs and a short mono
   size, both of which survive a 176px track — and stacks to one below that,
   which is the same rhythm every other grid on the site uses on a phone.
   These two rules must stay BELOW .grid--3: same weight, so source order is
   what decides them. */
.bxsizes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 620px) { .bxsizes { grid-template-columns: 1fr; } }

/* ── FIT: trimming the studio margin off a wide product ───────────────────
   Every approved photograph is a 1254 square, which suits a product that is
   roughly as tall as it is wide — the cool boxes fill about 74% of the frame
   each way and look right. The two polystyrene shots do not: the boards are a
   flat wide stack that fills 94% of the width and only 45% of the HEIGHT, so
   in a square frame the product came out small with half the card empty above
   and below it. The blocks have the same problem less severely, at 65%.

   The fix is a window on to the file, exactly like .vcrop on the float page:
   keep the full width, cut the dead bands off the top and bottom, and give the
   frame the window's aspect ratio. Measured off the files, product plus its
   contact shadow:

       boards   shadow y 303..959   window y 278, 1254 x 705  (16/9)  -> 94% x 80%
       blocks   shadow y 231..1063  window y 199, 1254 x 896  (7/5)   -> 93% x 91%

   Because every window keeps the FULL width, the image spans the frame edge to
   edge and the frame's own background never shows — there is nothing to colour
   match. `left`, not `inset-inline-start`: a window on to a photograph is a
   physical place and must not mirror under RTL.

   transform-origin is the PRODUCT's centre, not the window's, so the hover
   scale grows about the boards rather than about the empty ground. */
.pfit { position: relative; overflow: hidden; aspect-ratio: var(--pf-ar); }
.pfit > .product-photo {
  position: absolute; left: 0;
  inline-size: 100%; block-size: var(--pf-h); max-inline-size: none;
  inset-block-start: var(--pf-y);
  transform-origin: var(--pf-ox) var(--pf-oy);
}
.pfit--boards { --pf-ar: 16 / 9; --pf-h: 177.78%; --pf-y: -39.41%; --pf-ox: 48.13%; --pf-oy: 52.51%; }
.pfit--blocks { --pf-ar: 7 / 5;  --pf-h: 140%;    --pf-y: -22.22%; --pf-ox: 48.05%; --pf-oy: 51.04%; }

/* used as a card's art frame: drop the frame's own padding and ground, since
   the window fills it completely */
.rcard__art.pfit { padding: 0; background: none; }

/* The home range's two-column feature card carries BOTH polystyrene products,
   because that card is called "boards and blocks" and one square photograph in
   a 21:9 frame left it mostly empty. The column ratio is the two windows' own
   aspect ratios, which is what makes the two panels come out the same height —
   w/1.778 and w/1.4 are equal when the widths are in that same 1.778 : 1.4
   proportion. Stacked below the breakpoint where the card stops spanning,
   where side by side would put both products under 90px tall. */
.pfitpair { display: grid; grid-template-columns: 1fr; gap: 0; inline-size: 100%; }
@media (min-width: 610px) { .pfitpair { grid-template-columns: 1.778fr 1.4fr; align-items: center; } }

/* ── the measured size under a product name ─────────────────────────────── */
.dim {
  margin-block-start: .4em; padding-inline: 4px;
  font-family: var(--f-mono); font-size: .95rem; font-weight: 600;
  color: var(--lc-key);
  /* One bidi run. Without the isolate the surrounding Arabic can reorder the
     neutral multiplication signs and print the box back to front; inside it,
     the triple resolves in the paragraph's own direction and reads correctly
     in both. Same construction as pl.v2. */
  unicode-bidi: isolate;
}

@media (prefers-reduced-motion: reduce) {
  .product-photo { transition: none; transform: none; filter: none; }
}
.rcard__h {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(1.1rem, 1.5vw, 1.25rem); line-height: 1.5;
  margin-block-start: 16px; padding-block-start: 14px;
  border-block-start: 2px solid var(--lc-heavy);
  transition: border-color .2s var(--ease);
}
.rcard__spec {
  margin-block-start: .35em;
  font-family: var(--f-mono); font-size: .9rem; font-weight: 600;
  color: var(--green-dk);
}
.rcard__spec bdi, .phero__spec bdi { unicode-bidi: isolate; }
.rcard__p { font-size: .95rem; color: var(--lc-body); line-height: 1.8; margin-block-start: .45em; max-width: 46ch; }
.rcard__go {
  display: inline-flex; align-items: center; gap: .5em;
  margin-block-start: .9em;
  font-size: .9rem; font-weight: 600; color: var(--lc-link);
}
.rcard__go::after { content: '←'; font-weight: 400; transition: transform .2s var(--ease); }
html[lang="en"] .rcard__go::after { content: '→'; }
@media (hover: hover) and (pointer: fine) {
  a.rcard__a:hover .rcard__h { border-color: var(--green); }
  a.rcard__a:hover .product-photo { transform: scale(1.02); }
  a.rcard__a:hover .rcard__go::after { transform: translateX(-4px); }
  html[lang="en"] a.rcard__a:hover .rcard__go::after { transform: translateX(4px); }
}
@media (prefers-reduced-motion: reduce) {
  a.rcard__a:hover .product-photo, a.rcard__a:hover .rcard__go::after { transform: none; }
}

/* ═══ SPEC TABLES ═════════════════════════════════════════════════════════
   A datasheet: a white sheet under a 2px rule, hairlines between rows, no
   zebra, no radius, no shadow. Availability is a drawn tick, never a ✓. */

.board {
  background: var(--white);
  border-block-start: 2px solid var(--ink);
  overflow: hidden;
}
.board__hd {
  padding: 14px 18px;
  border-block-end: 1px solid var(--ink);
  display: flex; align-items: baseline; justify-content: space-between; gap: 6px 14px; flex-wrap: wrap;
}
.board__hd b { font-family: var(--f-display); font-size: 1.02rem; font-weight: 700; }
.board__hd span { font-family: var(--f-mono); font-size: .8rem; color: var(--steel-ink); }

.spec { width: 100%; border-collapse: collapse; font-size: .92rem; }
.spec th, .spec td {
  padding: 12px 18px; text-align: start; white-space: nowrap;
  border-block-end: 1px solid var(--line);
}
.spec thead th {
  font-size: .84rem; font-weight: 600;
  color: var(--steel-ink);
}
.spec tbody tr:last-child td { border-block-end: 0; }
.spec .c { text-align: center; }
.spec__yes { color: var(--green); font-weight: 700; }
.tick {
  display: inline-block; vertical-align: middle;
  inline-size: 16px; block-size: 16px;
  background: var(--green);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8.5l3.2 3.2L13 4.8' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='square'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8.5l3.2 3.2L13 4.8' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='square'/%3E%3C/svg%3E") center / contain no-repeat;
}
@media (forced-colors: active) { .tick { background: CanvasText; } }
.board__ft {
  padding: 12px 18px; border-block-start: 1px solid var(--line);
  font-size: .82rem; color: var(--steel-ink);
}

/* ── the thickness rail ───────────────────────────────────────────────── */

.rail { display: flex; flex-wrap: wrap; gap: 8px; }
.rail__i {
  font-family: var(--f-mono);
  display: inline-flex; align-items: center; justify-content: center;
  min-inline-size: 52px; min-block-size: 44px; padding: 0 12px;
  border: 1px solid var(--steel); border-radius: var(--r-sm);
  background: transparent;
  font-size: 1rem; font-weight: 600; color: var(--ink);
}
.band--ink .rail__i, .band--green .rail__i {
  border-color: rgba(231, 240, 234, .4);
  color: var(--on-1);
}
.rail__i--hi { background: var(--green); border-color: var(--green); color: var(--white); }
.band--ink .rail__i--hi, .band--green .rail__i--hi { background: var(--green-br); border-color: var(--green-br); color: var(--ink); }

/* ═══ SPEC ROWS ═══════════════════════════════════════════════════════════ */

.specs { border-block-start: 1px solid var(--lc-rule); }
.specs__r {
  display: flex; align-items: baseline; gap: 16px;
  padding: 13px 0; border-block-end: 1px solid var(--lc-rule);
}
.specs__k { font-size: .88rem; color: var(--lc-key); min-width: 12ch; }
.specs__v { font-family: var(--f-display); font-weight: 700; font-size: 1.06rem; margin-inline-start: auto; text-align: end; }
.specs__v.num { font-family: var(--f-mono); }

/* ═══ LISTS, TAGS, QUOTES, THE MESSAGE ═════════════════════════════════════ */

.ticks { display: grid; gap: .7em; }
.ticks li { display: flex; gap: .8em; align-items: flex-start; font-size: .98rem; color: var(--lc-body); line-height: 1.75; }
.ticks li::before {
  content: ''; flex: none;
  inline-size: 12px; block-size: 2px; margin-block-start: .85em;
  background: var(--lc-mark);
}

.tag { font-size: .8rem; font-weight: 600; color: var(--lc-key); font-style: normal; }

/* The manufacturer's own words. No side border: the quotation marks are in
   the text, the display face says it is a statement, and the attribution sits
   under a short rule. */
.quote {
  margin: 0;
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(1.12rem, 1.9vw, 1.4rem); line-height: 1.75;
}
.quote cite {
  display: block; margin-block-start: 1em; padding-block-start: .8em;
  border-block-start: 2px solid var(--lc-heavy); inline-size: fit-content;
  font-family: var(--f-text); font-weight: 400;
  font-size: .86rem; font-style: normal; color: var(--lc-key); line-height: 1.7;
}

/* THE MESSAGE. What a good enquiry looks like, shown as the thing it is: a
   sent WhatsApp message, on the side a sent message sits, with its tail. */
.msg {
  position: relative; margin: 0;
  inline-size: fit-content; max-inline-size: 40ch;
  padding: 14px 18px 16px;
  background: var(--white);
  border-radius: 12px; border-start-end-radius: 2px;
  box-shadow: 0 1px 1px rgba(12, 26, 20, .14);
  font-size: 1.02rem; line-height: 1.8; color: var(--ink);
}
.msg::after {
  content: ''; position: absolute; inset-block-start: 0; inset-inline-end: -9px;
  inline-size: 10px; block-size: 12px; background: var(--white);
  clip-path: polygon(0 0, 100% 0, 0 100%);
}
html[dir="rtl"] .msg::after { clip-path: polygon(0 0, 100% 0, 100% 100%); }
.msg__note { margin-block-start: 1em; font-size: .92rem; color: var(--lc-body); max-inline-size: 44ch; line-height: 1.75; }

.askbox {
  background: var(--white); border-block-start: 2px solid var(--ink);
  padding: clamp(18px, 2.4vw, 26px);
}
.askbox__h { font-family: var(--f-display); font-weight: 700; font-size: 1.1rem; margin-block-end: .4em; }
.askbox__p { font-size: .92rem; color: var(--steel-ink); }

/* ═══ CONTACT ROWS ════════════════════════════════════════════════════════ */

.chan { display: grid; border-block-start: 1px solid var(--lc-rule); }
.chan__i { display: flex; align-items: center; gap: 12px; border-block-end: 1px solid var(--lc-rule); }
.chan__a {
  flex: 1; display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  padding: 16px 4px; text-decoration: none;
  transition: transform .2s var(--ease);
}
.chan__a:hover { transform: translateX(-8px); }
html[dir="ltr"] .chan__a:hover { transform: translateX(8px); }
@media (prefers-reduced-motion: reduce) { .chan__a:hover { transform: none; } }
.chan__k { font-size: .9rem; color: var(--lc-key); min-width: 17ch; }
.chan__v { font-family: var(--f-mono); font-size: clamp(1.02rem, 1.9vw, 1.3rem); font-weight: 600; direction: ltr; }
.chan__role { font-size: .82rem; color: var(--lc-key); }
.chan__copy {
  background: none; border: 1px solid var(--lc-rule); border-radius: var(--r-sm);
  width: 36px; height: 36px; flex-shrink: 0;
  display: grid; place-items: center; cursor: pointer;
  transition: background .16s var(--ease), border-color .16s var(--ease);
}
.chan__copy svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; }
.chan__copy:hover { background: rgba(231, 240, 234, .1); border-color: currentColor; }
.band--paper .chan__copy:hover, .band--mint .chan__copy:hover { background: var(--white); }
.chan__copy.is-done { background: var(--green); border-color: var(--green); color: var(--white); }

/* ═══ CTA ═════════════════════════════════════════════════════════════════ */

.cta__h { font-family: var(--f-display); font-weight: 700; font-size: clamp(1.9rem, 3.6vw, 2.8rem); line-height: 1.4; max-width: 18ch; text-wrap: balance; }
.cta__row { display: flex; align-items: center; gap: 16px 32px; flex-wrap: wrap; margin-block-start: 2em; }

/* ═══ FOOTER ══════════════════════════════════════════════════════════════ */

.foot {
  background: var(--ink); color: var(--on-2);
  padding-block: clamp(40px, 5vw, 64px) 26px;
  font-size: .89rem;
}
.foot__top {
  display: flex; gap: clamp(24px, 4vw, 56px); flex-wrap: wrap; justify-content: space-between;
  padding-block-end: 26px; border-block-end: 1px solid rgba(231, 240, 234, .16);
}
.foot__brand { display: flex; gap: 13px; align-items: flex-start; max-width: 42ch; }
.foot__brand img { width: 40px; height: auto; flex-shrink: 0; }
.foot__brand b { display: block; color: var(--on-1); font-weight: 600; font-size: .94rem; line-height: 1.45; }
.foot__brand > span > span { display: block; margin-block-start: .35em; font-size: .82rem; }
.foot__nav { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: .5em clamp(20px, 3vw, 40px); align-content: start; }
.foot__nav a { text-decoration: none; transition: color .16s var(--ease); }
.foot__nav a:hover { color: var(--on-1); }
.foot__meta { margin-block-start: 22px; font-size: .78rem; color: var(--on-2); }

/* ═══ TOAST ═══════════════════════════════════════════════════════════════ */

.toast {
  position: fixed; inset-block-end: 24px; inset-inline-start: 50%;
  transform: translate(-50%, 130%);
  opacity: 0; visibility: hidden;
  background: var(--ink); color: var(--on-1);
  border: 1px solid var(--green-br); border-radius: var(--r-sm);
  padding: 11px 20px; font-size: .88rem; font-weight: 600;
  z-index: 150; pointer-events: none;
  transition: transform .3s var(--ease), opacity .2s var(--ease), visibility .3s;
}
.toast.is-on { transform: translate(-50%, 0); opacity: 1; visibility: visible; }

/* ═══ REVEAL ══════════════════════════════════════════════════════════════

   .rise and .up are hidden ONLY under html.js, and .rise carries a 1.5 s
   failsafe in app.js that reveals everything if IntersectionObserver produced
   nothing. KEEP BOTH — without them a script error or a background tab renders
   whole sections permanently invisible.
   ═══════════════════════════════════════════════════════════════════════════ */

/* --ease-out, not --ease. --ease is a hover curve: it accelerates a little
   before it settles, which on a 16px entrance reads as the block drifting into
   place. --ease-out spends most of its distance immediately and lands soft, so
   the section is legible almost at once and the movement is the tail of it
   rather than the substance. Same duration, and it feels considerably faster. */
/* Only a ROW OF PRODUCTS enters. Sections do not fade in as they arrive: an
   entrance on every block is what makes a page feel busy, and it hid content
   behind a script. .rise without [data-stagger] around it is inert now; the
   class stays on the markup so app.js's observer and its failsafe need no
   change. */
.js [data-stagger] > .rise { opacity: 0; transform: translateY(16px); transition: opacity .46s var(--ease-out), transform .5s var(--ease-out); }
.js [data-stagger] > .rise.in { opacity: 1; transform: none; }
.js [data-stagger] > .rise:nth-child(2) { transition-delay: .07s; }
.js [data-stagger] > .rise:nth-child(3) { transition-delay: .14s; }
.js [data-stagger] > .rise:nth-child(4) { transition-delay: .21s; }
.js [data-stagger] > .rise:nth-child(5) { transition-delay: .28s; }
@media (max-width: 720px) {
  .js [data-stagger] > .rise { transition-delay: 0s; }
}

/* The first screen arrives by MOVING, never by fading: LCP does not credit an
   element at opacity 0, and the headline is the LCP. Transform only, starting
   at first paint. */
.js .up { animation: upin .55s var(--ease-out) both; }
.js .up--1 { animation-delay: .05s; }
.js .up--2 { animation-delay: .1s; }
.js .up--3 { animation-delay: .15s; }
@keyframes upin { from { transform: translateY(10px); } to { transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .js [data-stagger] > .rise, .js [data-stagger] > .rise.in { opacity: 1 !important; transform: none !important; transition: none !important; }
  .js .up { animation: none !important; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ── the scrim under the mobile menu ──────────────────────────────────────
   The piece that lets the menu be a short sheet rather than a full-height
   slab: a panel that stops partway down only reads as unfinished while the
   page behind it is still bright and apparently live. Dim that, and the same
   panel reads as deliberate.

   `hidden` is removed by app.js on first open and never re-added — the fade
   out has to have something to fade. It sits under the header (z-index 100)
   and over everything else. */
.scrim {
  position: fixed; inset: var(--head-h) 0 0 0; z-index: 90;
  background: rgba(4, 16, 11, .5);
  opacity: 0; pointer-events: none;
  transition: opacity .34s var(--ease-in);
  -webkit-backdrop-filter: blur(1.5px); backdrop-filter: blur(1.5px);
}
.site.is-open ~ .scrim {
  opacity: 1; pointer-events: auto;
  transition: opacity .4s var(--ease-out);
}
@media (min-width: 901px) { .scrim { display: none; } }
@media (prefers-reduced-motion: reduce) { .scrim { transition: opacity .12s linear; } }

/* ═══ RESPONSIVE ══════════════════════════════════════════════════════════ */

@media (max-width: 900px) {
  .burger { display: flex; }
  /* A SHEET, sized to its own contents, over a dimmed page.

     Three shapes have been tried here. Sized-to-content with nothing behind it
     ended in a hard edge mid-screen and read as a menu that failed to finish
     drawing. Full height fixed that but felt, in the owner's words, "static and
     stuck" — a slab of green with a dead middle that had to be filled with
     something to justify itself. What was missing both times was the SCRIM:
     dim the page and a short panel reads as deliberate, because the dimming is
     what explains the rest of the screen.

     So it is short again, it is light, and it moves. */
  .site__nav {
    position: fixed; inset-block-start: var(--head-h); inset-inline: 0;
    max-block-size: calc(100dvh - var(--head-h) - 24px);
    background: var(--green-dk);
    border-radius: 0 0 var(--r) var(--r);
    box-shadow: 0 26px 50px -26px rgba(4, 22, 13, .85);
    flex-direction: column; align-items: stretch; gap: 0;
    padding: 4px var(--gut) calc(14px + env(safe-area-inset-bottom));
    overflow-y: auto; overscroll-behavior: contain;

    /* Off-screen by its own height, not by a fixed multiple: -140% of a short
       panel left it hanging in view on a tall phone for the first frames. */
    transform: translateY(-101%);
    /* visibility, not just a transform. A panel parked off-screen with a
       transform keeps every link inside it in the TAB ORDER, so a keyboard
       user fell into seven invisible links before ever reaching the burger.
       visibility:hidden removes them; the delay makes it wait for the slide
       to finish so the animation still plays on close. */
    visibility: hidden;
    transition: transform .34s var(--ease-in), visibility 0s linear .34s;
    margin-inline-start: 0;
  }
  .site.is-open .site__nav {
    transform: none;
    visibility: visible;
    /* the entrance is longer and lands softly; the exit is quick and gets out
       of the way. Same panel, two curves — that is most of what "fluid" is. */
    transition: transform .46s var(--ease-out), visibility 0s linear 0s;
  }

  .site__nav > a, .drop__btn {
    padding: 15px 2px; width: 100%; justify-content: space-between;
    border-block-end: 1px solid rgba(255, 255, 255, .14);
  }
  /* the list ends at its last item, not with a rule hanging under it */
  .site__nav > a.nav-contact { border-block-end: 0; }
  .site__ink { display: none; }
  .drop { width: 100%; }

  /* grid-template-rows 0fr -> 1fr animates to the panel's REAL height */
  .drop__menu {
    position: static; transform: none; opacity: 1;
    padding-block-start: 0; min-width: 0;
    display: grid; grid-template-rows: 0fr;
    transition: grid-template-rows .38s var(--ease-out);
    /* NOT visibility:visible. This used to re-declare it, which overrode the
       hidden parent panel and left the five product links focusable even with
       the whole menu shut. Visible only when BOTH the menu and this accordion
       are open — a child's `visible` beats an ancestor's `hidden`, so the
       condition has to name both. */
    visibility: hidden;
  }
  html[dir="rtl"] .drop__menu { transform: none; }
  .drop.is-open .drop__menu { grid-template-rows: 1fr; }
  .site.is-open .drop.is-open .drop__menu { visibility: visible; }
  /* the four product pages read as a group under "المنتجات": indented off a
     hairline, so the accordion looks like a branch of the list rather than
     four more top-level items that happen to be shorter */
  .drop__panel {
    border: 0; box-shadow: none; background: transparent;
    padding: 2px 0 10px;
    margin-inline-start: 8px;
    border-inline-start: 1px solid rgba(255, 255, 255, .18);
    min-height: 0; overflow: hidden;
  }
  .drop__panel a {
    color: rgba(255, 255, 255, .92);
    padding-inline-start: 14px; padding-block: 11px;
    border-radius: 0 var(--r-sm) var(--r-sm) 0;
  }
  html[dir="rtl"] .drop__panel a { border-radius: var(--r-sm) 0 0 var(--r-sm); }
  .drop__panel a:hover, .drop__panel a:active { background: rgba(255, 255, 255, .1); }
  .drop__panel span { color: rgba(255, 255, 255, .68); }

  /* nothing hovers on a phone, so the only feedback a tap can get is its own
     press state — without one every nav row feels dead for the 300ms before
     the next page paints */
  .site__nav > a:active, .drop__btn:active { background: rgba(255, 255, 255, .08); }
}

@media (max-width: 620px) {
  :root { --head-h: 62px; }
  .brand__sub { display: none; }
  .specs__r { flex-wrap: wrap; gap: 2px 16px; }
  .specs__v { margin-inline-start: 0; text-align: start; }
  .foot__top { flex-direction: column; }
}

/* ── the company's own statement of what it does ──────────────────────────
   One sentence, set larger than body and narrower than the band, with the
   source under it. Not a card: a card would file it with the specification
   blocks, and this is the one piece of copy on the site written by the
   company rather than for it. */
.stmt { max-width: 52ch; }

.stmt__src {
  margin-block-start: 1.15em;
  font-size: .82rem; color: var(--lc-key, var(--steel-ink));
}
.stmt__src a { color: var(--lc-link); font-weight: 600; }
.stmt__src a:hover { text-decoration-thickness: 2px; }

/* ═══ THE PHONE ═══════════════════════════════════════════════════════════
   Most of this site's visitors are on a phone, in Mosul, in summer. Every
   hover state above is invisible to them: what a finger gets back is a press
   state, and if there isn't one the control feels dead for the 300ms before
   the next page paints. Gated on (hover: none) so a mouse keeps its own
   language and nothing here fires on a desktop.
   ═══════════════════════════════════════════════════════════════════════ */

@media (hover: none) {
  .btn:active { box-shadow: inset 0 -1px 0 var(--slab-dk); background-color: var(--slab-dk); }
  a.rcard__a:active .rcard__h { border-color: var(--green); }
  a.rail__i:active { background: var(--green); border-color: var(--green); color: var(--white); }
  .band--ink a.rail__i:active, .band--green a.rail__i:active {
    background: var(--green-br); border-color: var(--green-br); color: var(--ink);
  }
  .lang:active, .burger:active, .chan__copy:active { background: rgba(255, 255, 255, .16); }
}

/* The two hero actions shared one line at 390px, which squeezed the ghost
   button to a stub and put both of them under a comfortable thumb width. One
   each below 430px — the primary action first, which on a phone is the whole
   point of the page. */
@media (max-width: 430px) {
  .hero__cta, .cta__row { flex-direction: column; align-items: stretch; }
  .hero__cta .btn:not(.btn--ghost), .cta__row .btn:not(.btn--ghost) { width: 100%; }
  .hero__cta .btn--ghost, .cta__row .btn--ghost { align-self: flex-start; }
}

/* The range cards are drawn at 4:3, which is right when three sit side by side
   and wrong when five sit stacked: at 390px the art alone ran 238px of a
   433px card, so the five product lines cost well over two screens of
   scrolling in one unvarying rhythm. 16:9 on a phone keeps the drawing large
   enough to read and gets a whole card back onto one screen with its heading
   and its description. */
@media (max-width: 620px) {
  .rcard__art:not(.rcard__art--product):not(.pfit) { aspect-ratio: 16 / 9; }
}

/* THE DRAWER'S ONE AUTHORED MOMENT. The sheet slides down; its rows follow
   just behind it, 35ms apart, so the menu reads as one gesture rather than a
   block of text appearing. Deliberately the only staggered entrance on the
   site — scattering this over every section is what makes a page feel busy
   rather than considered.

   The steps are short on purpose. An earlier pass used 50ms from a 100ms
   start, which put the last row a full 400ms behind the tap: individually
   smooth, collectively slow, which is how motion ends up feeling heavy. */
@keyframes navrow {
  from { opacity: 0; transform: translateY(-7px); }
  to   { opacity: 1; transform: none; }
}
@media (max-width: 900px) {
  .site.is-open .site__nav > * { animation: navrow .34s var(--ease-out) both; }
  .site.is-open .site__nav > *:nth-child(1) { animation-delay: .06s; }
  .site.is-open .site__nav > *:nth-child(2) { animation-delay: .095s; }
  .site.is-open .site__nav > *:nth-child(3) { animation-delay: .13s; }
  .site.is-open .site__nav > *:nth-child(4) { animation-delay: .165s; }
  .site.is-open .site__nav > *:nth-child(5) { animation-delay: .20s; }
  .site.is-open .site__nav > *:nth-child(6) { animation-delay: .235s; }
}

@media (prefers-reduced-motion: reduce) {
  .site.is-open .site__nav > * { animation: none; }
}

/* ═══ PRINT ═══════════════════════════════════════════════════════════════ */

@media print {
  .site, .heatline, .foot__nav, .toast, .btn { display: none; }
  body { background: #fff; color: #000; }
  .band, .hero, .phero { background: #fff !important; color: #000 !important; padding-block: 12px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE INSTRUMENTS — sizer, calculator, exploded diagram
   ═══════════════════════════════════════════════════════════════════════════

   RANGE INPUTS ARE ALREADY MIRRORED BY THE BROWSER UNDER RTL. Do not add
   direction or transform overrides — that double-flips, and it once put the
   calculator's 50 degree label over the cold end of a mirrored gradient.
   ═══════════════════════════════════════════════════════════════════════════ */

.sizer, .calc {
  display: grid;
  grid-template-columns: 1fr minmax(280px, 380px);
  gap: clamp(20px, 3vw, 40px);
  align-items: start;
  background: rgba(231, 240, 234, .04);
  border: 1px solid rgba(231, 240, 234, .16);
  border-block-start: 2px solid var(--on-1);
  padding: clamp(18px, 2.6vw, 32px);
}
.band--paper .sizer, .band--mint .sizer,
.band--paper .calc, .band--mint .calc {
  background: var(--white); border-color: var(--line); border-block-start-color: var(--ink);
}
/* minmax(0, …): a bare 1fr track cannot shrink below its content, and at 320px
   that pushed the cut-to-size instrument 5px past the page edge (in the
   baseline too, before the 2026-09-17 pass) */
@media (max-width: 900px) { .sizer, .calc { grid-template-columns: minmax(0, 1fr); } }

.sizer__ctl, .calc__ctl { display: grid; gap: clamp(18px, 2.2vw, 24px); align-content: start; }

/* ── sliders ──────────────────────────────────────────────────────────── */

.slider__lab { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-block-end: .5em; }
.slider__lab > span:first-child { font-size: .84rem; color: var(--lc-key); }
.slider__out { font-family: var(--f-mono); font-size: 1.24rem; font-weight: 600; color: var(--on-1); }
.band--paper .slider__out, .band--mint .slider__out { color: var(--ink); }

.slider input[type="range"] {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 22px; background: transparent; cursor: pointer; margin: 0;
}
.slider input[type="range"]::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px; background: var(--green);
}
.slider input[type="range"]::-moz-range-track {
  height: 4px; border-radius: 2px; background: var(--green);
}
.slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; border-radius: 50%; margin-block-start: -7px;
  background: var(--white); border: 2px solid var(--green);
  box-shadow: 0 1px 3px rgba(12, 26, 20, .4); cursor: grab;
}
.slider input[type="range"]::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--white); border: 2px solid var(--green);
  box-shadow: 0 1px 3px rgba(12, 26, 20, .4); cursor: grab;
}
.slider input[type="range"]:active::-webkit-slider-thumb { cursor: grabbing; }

.slider__scale {
  display: flex; justify-content: space-between;
  font-family: var(--f-mono); font-size: .68rem; color: var(--lc-key);
  margin-block-start: .35em;
}
.slider__help { font-size: .78rem; color: var(--lc-key); margin-block-start: .5em; }

/* ── segmented radios ─────────────────────────────────────────────────── */

.seg { border: 0; padding: 0; margin: 0; min-width: 0; }
.seg legend { font-size: .84rem; color: var(--lc-key); padding: 0; margin-block-end: .55em; }
.seg__row { display: flex; flex-wrap: wrap; gap: 6px; }
.seg__row label { position: relative; }
.seg__row input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.seg__row span {
  display: block; padding: 8px 14px;
  border: 1px solid var(--lc-rule); border-radius: var(--r-sm);
  font-size: .86rem; font-weight: 600;
  transition: background .16s var(--ease), border-color .16s var(--ease), color .16s var(--ease);
}
.seg__row input:checked + span { background: var(--green); color: var(--white); border-color: var(--green); }
.seg__row input:focus-visible + span { outline: 3px solid var(--heat); outline-offset: 2px; }
.seg__row label:hover span { border-color: currentColor; }

/* ── readouts ─────────────────────────────────────────────────────────── */

.readout {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(126px, 1fr));
  border: 1px solid var(--lc-rule); border-radius: var(--r);
  overflow: hidden; margin-block-start: clamp(16px, 2vw, 22px);
}
.readout__item {
  padding: 14px 16px;
  border-inline-end: 1px solid var(--lc-rule);
  border-block-end: 1px solid var(--lc-rule);
}
.readout__item dt { font-family: var(--f-mono); font-size: .7rem; color: var(--lc-key); margin-block-end: .35em; }
.readout__item dd { margin: 0; display: flex; align-items: baseline; gap: .3em; }
.readout__item dd b { font-family: var(--f-mono); font-size: 1.55rem; font-weight: 600; line-height: 1; }
.readout__item dd i { font-family: var(--f-mono); font-style: normal; font-size: .72rem; color: var(--lc-key); }
.readout__item--big { grid-column: span 2; }
.readout__item--big dd b { font-size: 2.1rem; color: var(--green-br); }
.band--paper .readout__item--big dd b,
.band--mint .readout__item--big dd b { color: var(--green); }
.band--green .readout__item--big dd b { color: var(--green-pale); }
@media (max-width: 520px) { .readout__item--big { grid-column: span 1; } }

/* ── the drawn sheet ──────────────────────────────────────────────────── */

.sizer__stage {
  position: relative; min-height: 290px;
  display: grid; place-items: center;
  border: 1px solid var(--lc-rule); border-radius: var(--r-sm);
  /* The height callout hangs outside the sheet on the inline-end side and needs
     ~68px (14px offset + its own width). With a flat 30px it was cut to
     "100 c" wherever the sheet came close to filling the stage — at 1440, where
     this sits in a two-column band, and at every width ≤390. The drawing is
     scale-computed rather than stretched to fit, so the wider end padding costs
     it no size at any measured width; it just moves the sheet off-centre the
     way a dimensioned drawing already is. Logical, so RTL mirrors it. */
  padding-block: 30px; padding-inline: 30px 72px;
  overflow: hidden;
  background-color: rgba(12, 26, 20, .3);
  background-image:
    linear-gradient(rgba(231, 240, 234, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(231, 240, 234, .05) 1px, transparent 1px);
  background-size: 22px 22px;
}
.band--paper .sizer__stage, .band--mint .sizer__stage {
  background-color: var(--paper);
  background-image:
    linear-gradient(rgba(12, 26, 20, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(12, 26, 20, .05) 1px, transparent 1px);
}
.sizer__board {
  position: relative;
  width: var(--bw, 200px); height: var(--bh, 100px);
  background: var(--white);
  background-image: url('media/foam-card.webp');
  background-size: cover; background-position: center;
  border: 1px solid rgba(12, 26, 20, .3);
  box-shadow: 0 var(--bt, 8px) 0 #B9C6BD, 0 var(--bt, 8px) 0 1px rgba(12, 26, 20, .3);
  transition: width .2s var(--ease), height .2s var(--ease), box-shadow .2s var(--ease);
}
.sizer__dimW, .sizer__dimH, .sizer__edge {
  position: absolute;
  font-family: var(--f-mono); font-size: .7rem; font-weight: 600;
  color: #F2F7F4; white-space: nowrap;
  background: #16241C;
  padding: 2px 7px; border-radius: 3px;
}
.sizer__dimW { inset-block-start: -20px; inset-inline-start: 50%; transform: translateX(-50%); }
.sizer__dimH { inset-block-start: 50%; inset-inline-end: -14px; transform: translate(100%, -50%); }
.sizer__edge { inset-block-end: -24px; inset-inline-start: 2px; }
.sizer__cap { position: absolute; inset-block-end: 10px; inset-inline-end: 14px; font-family: var(--f-mono); font-size: .68rem; color: var(--lc-key); }
.sizer__note { font-size: .86rem; color: var(--lc-body); margin-block-start: 1em; line-height: 1.7; }
.sizer__note a { color: var(--lc-link); font-weight: 600; }

/* ── the calculator's wall section ────────────────────────────────────── */

.calc__viz { display: grid; gap: 8px; }
.calc__scale { display: flex; justify-content: space-between; font-family: var(--f-mono); font-size: .82rem; font-weight: 600; }
.calc__hot { color: var(--heat-lt); }
.calc__cold { color: var(--green-br); }
.band--paper .calc__hot, .band--mint .calc__hot { color: var(--heat-ink); }
.band--paper .calc__cold, .band--mint .calc__cold { color: var(--green); }

.calc__bar {
  position: relative; height: clamp(120px, 15vw, 180px);
  border-radius: var(--r-sm); overflow: hidden;
  border: 1px solid var(--lc-rule);
}
.calc__fill {
  position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--heat) 0%, var(--heat) var(--edge, 30%), var(--cool, #2E7D55) 100%);
}
html[dir="rtl"] .calc__fill {
  background: linear-gradient(270deg, var(--heat) 0%, var(--heat) var(--edge, 30%), var(--cool, #2E7D55) 100%);
}
.calc__eps {
  position: absolute; inset-block: 0;
  inset-inline-start: var(--edge, 30%);
  width: var(--w, 34%);
  background: var(--white);
  background-image: url('media/foam-band.webp');
  background-size: cover;
  border-inline: 1px solid rgba(12, 26, 20, .35);
  display: grid; place-items: center;
  transition: width .25s var(--ease);
}
.calc__epslab {
  font-family: var(--f-mono); font-size: .76rem; font-weight: 700; color: var(--ink);
  background: rgba(255, 255, 255, .9); padding: 3px 8px; border-radius: 4px; white-space: nowrap;
}
.calc__assume { font-size: .78rem; color: var(--lc-key); margin-block-start: 1.1em; line-height: 1.6; }

/* Two columns: the readout column spans both rows, and the notes take the space
   the control column was leaving empty underneath itself. Only .calc — .sizer
   shares the grid rule above but has no notes block. */
@media (min-width: 901px) {
  .calc__out   { grid-column: 2; grid-row: 1 / span 2; }
  .calc__notes { grid-column: 1; grid-row: 2; align-self: end; }
  .calc__notes .calc__assume:first-child { margin-block-start: 0; }
  .calc__notes {
    margin-block-start: clamp(20px, 2.4vw, 30px);
    padding-block-start: clamp(14px, 1.6vw, 20px);
    border-block-start: 1px solid rgba(231, 240, 234, .14);
  }
}
.band--paper .calc__notes, .band--mint .calc__notes { border-block-start-color: var(--line); }

/* ── the method block ─────────────────────────────────────────────────── */

.method__eq {
  font-family: var(--f-mono); font-size: clamp(.94rem, 1.6vw, 1.16rem);
  background: var(--white); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: clamp(14px, 2vw, 20px);
  direction: ltr; text-align: center; overflow-x: auto;
}
.band--ink .method__eq, .band--green .method__eq {
  background: rgba(231, 240, 234, .06); color: var(--on-1); border-color: rgba(231, 240, 234, .18);
}
.method__key { display: grid; gap: .5em; margin-block-start: 1.2em; font-size: .88rem; color: var(--lc-key); }
.method__key b { font-family: var(--f-mono); font-weight: 600; color: var(--lc-mark); }

/* ══ ORPHANED BY THE FIVE-PRODUCT RANGE, 2026-08-21 ═══════════════════════
   Everything from here to the end of "the exploded cooler drawing" styles two
   pages that no longer exist: coolers.html and pumps.html. The owner cut the
   range to five products on 2026-08-21 and those two came out.

   It is KEPT, not deleted, on purpose. The plant still makes cooler
   COMPONENTS — the float is one of the five — so a cooler page is a plausible
   thing to want back, and this is a whole drawing system with an interactive
   exploded diagram behind it. About 400 bytes gzipped is a cheap option to
   hold. `initExplode()` in js/app.js returns immediately when #partList is
   absent, so it costs a visitor nothing at runtime.

   .art__volute is NOT dead — the float drawing uses it for the ball.

   To bring the pages back:
       git show HEAD:_src/pages/coolers.html > _src/pages/coolers.html
       git show HEAD:_src/pages/pumps.html   > _src/pages/pumps.html
   then restore their cl.* / pm.* strings from HEAD:js/translations.js and add
   the names back to ORDER in _src/build.py and to PRODUCT_PAGES in js/app.js.

   To delete it instead: remove this comment down to the "tables" section, plus
   .art__louvre / .art__pad / .art__padline / .art__ring / .art__tankband /
   .art__motor / .art__port / .art__term, and the .askbox / .use blocks.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── the exploded cooler ──────────────────────────────────────────────── */

.explode { display: grid; grid-template-columns: 1fr minmax(250px, 340px); gap: clamp(20px, 3vw, 36px); align-items: start; }
@media (max-width: 880px) { .explode { grid-template-columns: 1fr; } }
.explode__fig {
  background: rgba(231, 240, 234, .04);
  border: 1px solid rgba(231, 240, 234, .14);
  border-radius: var(--r);
  padding: clamp(16px, 2.4vw, 28px);
  min-height: 320px; display: grid; place-items: center;
}
.band--paper .explode__fig, .band--mint .explode__fig { background: var(--white); border-color: var(--line); }

.cool__part { transition: opacity .2s var(--ease); cursor: pointer; }
.explode__fig:hover .cool__part:not(.is-lit) { opacity: .3; }
.cool__part.is-lit { opacity: 1; }

.explode__list { border-block-start: 1px solid var(--lc-rule); }
.pitem {
  display: flex; align-items: baseline; gap: 10px;
  padding: 11px 10px;
  border-block-end: 1px solid var(--lc-rule);
  cursor: pointer; border-radius: var(--r-sm);
  transition: background .16s var(--ease), transform .16s var(--ease);
}
.pitem:hover, .pitem.is-lit { background: rgba(231, 240, 234, .08); transform: translateX(-6px); }
html[dir="ltr"] .pitem:hover, html[dir="ltr"] .pitem.is-lit { transform: translateX(6px); }
.band--paper .pitem:hover, .band--paper .pitem.is-lit,
.band--mint .pitem:hover, .band--mint .pitem.is-lit { background: var(--white); }
@media (prefers-reduced-motion: reduce) { .pitem:hover, .pitem.is-lit { transform: none; } }
.pitem__n { font-family: var(--f-mono); font-size: .72rem; font-weight: 600; color: var(--lc-mark); min-width: 2em; }
.pitem__h { font-weight: 600; font-size: .93rem; }
.pitem__d { font-size: .8rem; color: var(--lc-key); margin-inline-start: auto; text-align: end; }

/* ── application photos ───────────────────────────────────────────────── */

.use { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: clamp(14px, 1.8vw, 22px); }
.use__i { background: var(--white); border: 1px solid var(--line); overflow: hidden; }
.use__media { aspect-ratio: 3 / 2; overflow: hidden; background: var(--mint); position: relative; background-size: cover; background-position: center; }
.use__media img { width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .5s var(--ease); }
.use__media img.is-loaded { opacity: 1; }
.use__b { padding: 14px 16px 18px; }
.use__b h3 { font-family: var(--f-display); font-weight: 700; font-size: 1rem; margin-block-end: .35em; }
.use__b p { font-size: .84rem; color: var(--steel-ink); line-height: 1.6; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE DRAWN PRODUCTS
   ═══════════════════════════════════════════════════════════════════════════

   Every product is a shaded three-quarter render sharing one set of materials
   defined once per page in .matdefs. The art lives in _src/partials/art_*.html
   and is INLINED by the build — never <use>d from a <symbol>, because document
   CSS cannot reach inside a <use> shadow tree and the drawings have to animate.
   ═══════════════════════════════════════════════════════════════════════════ */

.art {
  width: 100%; height: auto; overflow: visible;
  stroke: #2F3D36; stroke-width: 1.6;
  stroke-linejoin: round; stroke-linecap: round;
  fill: none;
}

.art__shadow { fill: url(#mDrop); stroke: none; }
.art__bead { fill: url(#mBead); stroke: none; }

/* foam bodies */
/* art_packbox.html names its faces art__foamTop / foamL / foamR; art_coolbox.html
   names the same faces art__top / front / side. Both sets are listed, because a
   class in the markup with no rule anywhere paints NOTHING: the packing-box
   drawing shipped as unfilled outlines until 2026-09-17. */
.art__top, .art__lid, .art__lid2, .art__foamTop { fill: url(#mFoamTop); }
.art__front, .art__wall, .art__foamL { fill: url(#mFoamL); }
.art__side, .art__foamR { fill: url(#mFoamR); }
.art__rim { fill: url(#mFoamTop); }
.art__cavity { fill: #BFCCC3; }
.art__seam, .art__grip { fill: none; }
.art__cold { fill: var(--green-br); }

/* steel and housings */
.art__plate, .art__stem, .art__cap, .art__fix { fill: url(#mSteel); }
.art__ceil { stroke-width: 2.4; }
.art__ceilhatch { stroke-width: 1.2; opacity: .5; }
.art__bezel { fill: #EDF2EF; }
.art__throat { fill: #1E2E25; }
.art__guard { stroke-width: 1.2; opacity: .85; }
.art__hub { fill: url(#mSteelD); }
.art__air { stroke: var(--green); stroke-width: 2; opacity: .85; }

/* blades */
.art__blade { fill: url(#mGreen); }
.art__blades { transform-origin: center; transform-box: fill-box; }

/* cooler */
.art__louvre { fill: #EDF2EF; }
.art__pad { fill: url(#mGreen); }
.art__padline { stroke: #06442A; stroke-width: 1.1; opacity: .6; }
.art__ring { fill: #1E2E25; }
.art__knob { fill: var(--heat); }
.art__leg, .art__feet { fill: url(#mSteelD); }
.art__tankband { fill: url(#mGreenD); }
.art__water { stroke: var(--green-br); stroke-width: 2; }

/* pump */
.art__volute { fill: url(#mGreen); }
.art__motor { fill: url(#mSteel); }
.art__base { fill: url(#mSteelD); }
.art__flange, .art__port { fill: url(#mSteel); }
.art__fins { stroke-width: 1.1; opacity: .6; }
.art__imp { fill: #EDF2EF; }
.art__cable, .art__term { fill: url(#mDark); }
.art__flow { stroke: var(--green-br); stroke-width: 2.2; }

/* One orchestrated behaviour per drawing, all of it off under reduced motion.
   The box-fan blades used to turn for ever, and the airflow lines with them.
   The fan drawing came back into the product cards on 2026-08-25 when the
   boxed-carton photograph was withdrawn, and a product card is no place for
   perpetual motion — the card's own reveal and its hover lift are the whole
   motion budget now. The blade and airflow rules are kept, scoped to the
   cooler and pump drawings, which are not on the site today; see the note
   above .explode for how those two pages come back. */
@media (prefers-reduced-motion: no-preference) {
  .art--cooler .art__blades { animation: spin 3.4s linear infinite; }
  .art__water, .art__flow { stroke-dasharray: 6 8; animation: flow 1.5s linear infinite; }
  .art--coolbox .art__lid { transition: transform .4s var(--ease); transform-box: fill-box; transform-origin: center bottom; }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .rcard__a:hover .art--cooler .art__blades { animation-duration: 1s; }
  .rcard__a:hover .art--coolbox .art__lid { transform: translateY(-7px); }
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes flow { to { stroke-dashoffset: -28; } }

/* ── the exploded cooler drawing ──────────────────────────────────────── */

.cool {
  width: 100%; height: auto; max-height: 400px;
  stroke: var(--on-1); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round; fill: none;
}
.band--paper .cool, .band--mint .cool { stroke: #2F3D36; }
.cool__pad { fill: rgba(23, 164, 104, .28); }
.cool__ring { fill: rgba(231, 240, 234, .06); }
.cool__f { fill: rgba(231, 240, 234, .2); stroke-width: 1.4; }
.cool__blades { transform-origin: 190px 124px; }
.cool__hub { fill: var(--on-1); stroke: none; }
.cool__grille rect { fill: rgba(231, 240, 234, .05); }
.cool__grille path { stroke-width: 1.5; }
.cool__motor rect { fill: rgba(231, 240, 234, .1); }
.cool__tank { fill: rgba(23, 164, 104, .16); }
.cool__pump rect { fill: rgba(231, 240, 234, .12); }
.cool__float circle { fill: var(--green-br); stroke-width: 1.6; }
.cool__hose { stroke: var(--green-br); stroke-width: 2.2; }
.cool__spray { stroke: var(--green-br); stroke-width: 1.6; opacity: .8; }
.cool__waterline { stroke: var(--green-br); stroke-width: 1.6; opacity: .5; }
.cool__part.is-lit { stroke: var(--heat-lt); }
.band--paper .cool__part.is-lit, .band--mint .cool__part.is-lit { stroke: var(--heat); }

@media (prefers-reduced-motion: no-preference) {
  .cool__blades { animation: spin 4.2s linear infinite; }
  .cool__hose, .cool__spray { stroke-dasharray: 5 7; animation: flow 1.6s linear infinite; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PHONE FIXES — 2026-08-20
   Every page used to report scrollWidth 399 on a 320-390 px viewport. The
   cause was the header row, not the content: .brand and .site__end are both
   flex-shrink:0, so their intrinsic widths (144 + 12 + 168 in Arabic, more in
   English) simply exceeded the viewport and the whole document grew with them.

   Nothing below changes the desktop design. Everything is inside a max-width
   query, and the numbers were measured, not guessed.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The three header controls are tap targets first. 44×44 is the smallest
   square a thumb reliably hits; they were 40×34. */
.lang, .burger { min-width: 44px; min-height: 44px; }
/* the header WhatsApp button is a tap target too, and it was 2px short */
.site__end .btn--sm { min-height: 44px; }
.burger { padding: 13px 11px; }

/* The header WhatsApp button carries an icon as well as its word, so it can
   drop to the icon alone on a phone without losing the action. The label stays
   in the DOM — visually hidden, still read aloud — and aria-label names the
   link either way. */
/* the header's WhatsApp slab: white on the green bar, icon then word */
.site .btn--wa { gap: .5em; min-height: 44px; padding: 0 16px; box-shadow: inset 0 -2px 0 var(--slab-dk); }
.btn__wa { width: 18px; height: 18px; flex: none; }

@media (max-width: 620px) {
  .site__in  { gap: 8px; }
  .site__end { gap: 6px; }

  /* icon-only: a 44×44 square that still reads as the brand's green button */
  .btn--wa {
    width: 44px; min-width: 44px; height: 44px;
    padding: 0; gap: 0;
  }
  .btn--wa .btn__lbl {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  .btn--wa .btn__wa { width: 20px; height: 20px; }

  /* the mark and the wordmark both give a little back, and the wordmark is
     fluid rather than fixed so it never has to be clipped or ellipsed */
  .brand { gap: 8px; min-width: 0; }
  .brand__mark { width: 32px; }
  .brand__name { font-size: clamp(.95rem, 3.4vw, 1.12rem); white-space: nowrap; }
}

@media (max-width: 400px) {
  :root { --gut: 14px; }
}

/* ── tables ────────────────────────────────────────────────────────────────
   .board__scroll already kept the spec tables from widening the page. What it
   did not do was SAY it scrolls, or let a keyboard reach it — an
   overflow:auto div is not focusable on its own. app.js measures each one,
   and only where the content genuinely overflows does it add .is-scrollable
   plus tabindex/role/aria-label. So the affordance never appears on a table
   that fits. */
.board__scroll {
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}
.board__scroll.is-scrollable {
  position: relative;
  /* a soft edge on the inline-end side, which is the correct side in both
     directions because the gradient runs along the inline axis */
  -webkit-mask-image: linear-gradient(to var(--fade-to, right),
    #000 calc(100% - 26px), transparent 100%);
  mask-image: linear-gradient(to var(--fade-to, right),
    #000 calc(100% - 26px), transparent 100%);
}
html[dir="rtl"] .board__scroll.is-scrollable { --fade-to: left; }
.board__scroll:focus-visible { outline: 2px solid var(--green-br); outline-offset: -2px; }

/* app.js adds this line only while the table actually overflows, and removes
   it again when the viewport is wide enough — so it is never a lie. */
.board__hint {
  padding: 8px 16px 12px;
  font-size: .76rem; color: var(--lc-key);
}

/* Long model names, densities and units wrap instead of being cut off, on the
   narrowest phones where even a scrolling table gets uncomfortable. */
@media (max-width: 620px) {
  .spec { font-size: .84rem; }
  .spec th, .spec td { padding: 10px 10px; }
}

/* ── staging notice ─────────────────────────────────────────────────────────
   Added 2026-08-20, and it exists only while STAGING is True in
   _src/build.py — that flag also writes the noindex meta tags and the
   X-Robots-Tag header, so the three can never disagree.

   Pinned to the BOTTOM on purpose: a client reviewing the site screenshots the
   top of a page, and this must not appear in that screenshot or sit in front
   of anything. html.has-staging pads the page so the bar cannot cover the last
   line of the footer. */
.staging {
  position: fixed; inset-inline: 0; inset-block-end: 0; z-index: 90;
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 9px calc(var(--gut) + 34px) 9px var(--gut);
  background: var(--ink); color: rgba(231, 240, 234, .9);
  border-block-start: 2px solid var(--green-br);
  font-size: .78rem; line-height: 1.45; text-align: center;
}
.staging__x {
  position: absolute; inset-inline-end: 4px; inset-block: 50%;
  translate: 0 -50%;
  width: 44px; height: 44px; padding: 13px;
  background: none; border: 0; cursor: pointer;
  color: rgba(231, 240, 234, .72);
}
.staging__x:hover { color: var(--white); }
.staging__x svg { width: 100%; height: 100%; display: block; }
.staging__x:focus-visible { outline: 2px solid var(--green-br); outline-offset: -2px; }
html.has-staging body { padding-block-end: 52px; }

@media print { .staging { display: none; } }

/* the λ provenance line, and the second half of the disclaimer */
.seg__note {
  margin-block-start: 8px;
  font-size: .76rem; line-height: 1.55; color: var(--lc-key);
}
.calc__assume--2 { margin-block-start: 6px; opacity: .82; }

/* The thickness chips are interactive now — they were <li> that looked like
   buttons and did nothing. Give them the affordances a control needs. */
a.rail__i {
  text-decoration: none; cursor: pointer;
  transition: background .16s var(--ease), border-color .16s var(--ease),
              color .16s var(--ease), transform .16s var(--ease);
}
a.rail__i:hover {
  border-color: var(--green); color: var(--green);
  transform: translateY(-1px);
}
a.rail__i--hi:hover { color: var(--white); }
.band--ink a.rail__i--hi:hover, .band--green a.rail__i--hi:hover { color: var(--ink); }
a.rail__i:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
/* a thumb needs a real target, and 9px padding on a two-character chip is not */
@media (max-width: 620px) {
  /* a one-character chip ("2", "3") came out 39px wide against a 44px floor */
  a.rail__i { min-height: 44px; min-width: 44px; align-items: center; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) { a.rail__i:hover { transform: none; } }

/* ═══ TAP TARGETS ═════════════════════════════════════════════════════════
   Found by auditing every interactive element at 320/360/375/390 in both
   languages. Each of these was a real control a thumb had to hit and each was
   under 44x44: the copy-number button was 36x36, the brand link 119x25, and
   the breadcrumb and footer links about 21-25px tall.

   The fix is padding and min-height, never a bigger font — the type sizes are
   part of the design and are left alone. On desktop nothing changes.
   ═══════════════════════════════════════════════════════════════════════ */

/* the copy-number button is a control at every width, not only on phones */
.chan__copy { width: 44px; height: 44px; }
.chan__copy svg { width: 18px; height: 18px; }

@media (max-width: 620px) {
  /* the brand is the "go home" target on every page */
  .brand { min-height: 44px; }

  /* breadcrumb and footer links: keep the type size, grow the hit area */
  .foot__nav a {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px;
    /* a short word like "Home" is only ~29px wide; a thumb needs 44 both ways */
    min-width: 44px;
  }
  /* the row would otherwise stack into a very tall block once each link is
     44px, so let them share lines and tighten the vertical rhythm */
  .foot__nav { gap: 0 18px; }
}

/* ═══ THE FAN'S POWER-CUT TRACE ═════════════════════════════════════════════
   fans.html. Two traces over time, drawn for Arabic with time running right to
   left; English mirrors the geometry. Labels are HTML. It draws once behind a
   playhead when a third of it is on screen (data-draw, see initDrawings). */

.restart { margin: 0; }
.restart__draw { position: relative; container-type: inline-size; }
.restart__svg { display: block; inline-size: 100%; block-size: auto; overflow: visible; }
html[dir="ltr"] .restart__svg { transform: scaleX(-1); }

.r-cut   { fill: var(--heat); fill-opacity: .1; }
.r-grid line { stroke: var(--lc-rule); stroke-width: 1; stroke-dasharray: 2 5; }
.r-power { fill: none; stroke: var(--ink); stroke-width: 2.4; stroke-linejoin: miter; }
.r-fan   { fill: none; stroke: var(--green); stroke-width: 3; stroke-linejoin: miter; }
.r-mark  { fill: var(--paper); stroke: var(--green); stroke-width: 2.4; }
.r-axis line { stroke: var(--steel-ink); stroke-width: 1.2; }
.r-axis polygon { fill: var(--steel-ink); }
.r-head  { stroke: var(--heat); stroke-width: 1.4; opacity: 0; }

.restart__row, .restart__cut, .restart__back, .restart__time {
  position: absolute; font-size: clamp(11px, 2.4cqi, 14px); line-height: 1.3;
  white-space: nowrap; color: var(--lc-body);
}
.restart__row { inset-inline-start: 0; font-weight: 600; transform: translateY(-50%); }
.restart__row--power { inset-block-start: 18.5%; }
.restart__row--fan   { inset-block-start: 59.3%; color: var(--green); }
.restart__cut  { inset-block-start: 0; inset-inline-start: 50.4%; transform: translate(50%, -40%); color: var(--heat-ink); font-weight: 600; }
html[dir="ltr"] .restart__cut { transform: translate(-50%, -40%); }
/* under the fan trace, on the far side of the restart marker, where the trace
   runs at its upper level: it may wrap, never spill past the figure */
.restart__back { inset-block-start: 65%; inset-inline-end: 0; max-inline-size: 29%; white-space: normal; font-weight: 600; color: var(--green); }
.restart__time { inset-block-end: -2%; inset-inline-end: 0; color: var(--steel-ink); font-size: clamp(10px, 2cqi, 12.5px); }

@media (prefers-reduced-motion: no-preference) {
  .restart[data-draw] .r-power, .restart[data-draw] .r-fan { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .restart[data-draw] .r-mark, .restart[data-draw] .restart__back { opacity: 0; }
  .restart[data-draw] .r-head { opacity: 1; transform: translateX(0); }

  .restart[data-draw="go"] .r-power, .restart[data-draw="go"] .r-fan {
    stroke-dashoffset: 0; transition: stroke-dashoffset 2.2s cubic-bezier(.45, .05, .55, .95);
  }
  .restart[data-draw="go"] .r-head {
    transform: translateX(-410px); opacity: 0;
    transition: transform 2.2s cubic-bezier(.45, .05, .55, .95), opacity .3s linear 2.1s;
  }
  .restart[data-draw="go"] .r-mark, .restart[data-draw="go"] .restart__back {
    opacity: 1; transition: opacity .4s var(--ease-out) 1.5s;
  }
}

/* ═══ HOW EPS IS MADE ══════════════════════════════════════════════════════
   plant.html. A real sequence, so it is numbered; each step has a small
   drawing of the material at that stage. Drawn right to left for Arabic. */

/* A PROCESS LINE, not a row of cards: one continuous rule runs under the four
   drawings and every step hangs a square node on it, so the sequence reads as
   one thing moving from bead to board. Stacked on a phone, the rule turns
   vertical and runs down the start edge. */
.steps {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0 clamp(24px, 3vw, 40px);
  position: relative;
}
.steps::before {
  content: ''; position: absolute; inset-inline: 0; inset-block-start: 80px;
  block-size: 2px; background: var(--lc-heavy);
}
.step { position: relative; padding-block-start: 104px; }
.step::before {
  content: ''; position: absolute; inset-inline-start: 0; inset-block-start: 75px;
  inline-size: 12px; block-size: 12px; background: var(--green); border: 2px solid var(--lc-heavy);
}
.step__art { position: absolute; inset-block-start: 0; inset-inline-start: 0; display: block; inline-size: 96px; block-size: 64px; overflow: visible; }
.step__no { font-family: var(--f-mono); font-size: .86rem; font-weight: 600; color: var(--lc-mark); margin-block-end: .5em; }
.step__h { font-family: var(--f-display); font-weight: 700; font-size: clamp(1.1rem, 1.6vw, 1.28rem); line-height: 1.5; margin-block-end: .45em; }
.step__p { font-size: .96rem; color: var(--lc-body); line-height: 1.8; }
@media (max-width: 760px) {
  .steps { grid-template-columns: minmax(0, 1fr); gap: 40px; padding-inline-start: 28px; }
  .steps::before { inset-block: 0; inset-inline-start: 5px; inset-inline-end: auto; block-size: auto; inline-size: 2px; }
  .step { padding-block-start: 80px; }
  .step::before { inset-inline-start: -28px; inset-block-start: 26px; }
}
html[dir="ltr"] .step__art { transform: scaleX(-1); }
.s-seed, .s-bead { fill: rgba(255, 255, 255, .92); stroke: var(--white); stroke-width: 1.2; }
.s-line { fill: none; stroke: var(--on-1); stroke-width: 1.6; }
.s-block { fill: rgba(255, 255, 255, .14); stroke: var(--white); stroke-width: 1.6; }
.s-dot { fill: none; stroke: rgba(255, 255, 255, .7); stroke-width: 1.2; }
.s-wire { stroke: var(--heat-lt); stroke-width: 2; stroke-dasharray: 4 3; }
.step__why { margin-block-start: clamp(40px, 4.6vw, 64px); max-width: 72ch; }

@media (prefers-reduced-motion: no-preference) {
  .steps[data-draw] .s-grow { transform: scale(.22); transform-box: fill-box; transform-origin: center; }
  .steps[data-draw="go"] .s-grow { transform: none; transition: transform 1.2s var(--ease-out) .3s; }
}

/* ═══ SMALL PIECES ══════════════════════════════════════════════════════════ */

/* the sizer hands its numbers to WhatsApp */
.sizer__send { margin-block-start: 1.2em; }

/* the method key: symbol, then what it means — no dash between them */
.method__key > div { display: flex; gap: .9em; align-items: baseline; }
.method__key b { min-inline-size: 6ch; }

/* the CTA's worked example sits a little lower than the ask beside it */
.cta__eg .label { color: rgba(255, 255, 255, .86); }
.cta__eg .msg__note { color: var(--white); }

.stmt__q {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(1.2rem, 2.2vw, 1.6rem);
  line-height: 1.7;
}
