/*! Beckman Egypt — built 2026-09-16T10:56Z. Source: src/css/. Do not edit directly. */

/* ===== 01-tokens.css ===== */
/* ==========================================================================
   Beckman Egypt — design tokens
   Direction: the 2026 print catalogue. Crimson #AA1218 sampled from the cover
   and confirmed identical on every inner page. The catalogue alternates
   full-bleed crimson bands with white photo bands; the site does the same.
   Ink is a warm near-black, not the old cool navy — navy fights crimson.
   Amber stays reserved for certification marks (FM / CE / OIML), which the
   catalogue also does.
   ========================================================================== */
:root {
  /* --- brand: the catalogue crimson. --brand-600 IS #AA1218, the sampled value.
         Measured on white: 600 = 7.48:1, 500 = 5.73:1 — both clear AA for body
         text, so unlike the cyan this replaces, no darkened text-only variant
         is needed. 400 is 3.84:1: large text, borders and fills only. --- */
  --brand-950: #2b0406;
  --brand-900: #490a0d;
  --brand-800: #6b0e13;
  --brand-700: #8a0f15;
  --brand-600: #aa1218;   /* sampled from the catalogue */
  --brand-500: #c22a30;
  --brand-400: #d9575c;
  --brand-300: #e98f93;
  --brand-200: #f5c6c8;
  --brand-100: #fbe5e6;
  --brand-050: #fdf3f3;

  /* --- ink: warm neutral near-black. Retuned from the old cool navy, which
         clashed with crimson. 500 is 6.04:1 on white and carries meta text;
         400 is 4.00:1 and is therefore NOT for small text. --- */
  --ink-950: #100b0c;
  --ink-900: #1a1416;
  --ink-800: #2a2225;
  --ink-700: #3d3336;
  --ink-600: #544749;
  --ink-500: #6e5f62;
  --ink-400: #8c7b7e;
  --ink-300: #b3a4a6;
  --ink-200: #d6cbcc;
  --ink-100: #eae3e3;
  --ink-050: #f5f1f1;

  /* --- paper: the catalogue's white bands. Kept warm. --- */
  --paper-000: #ffffff;
  --paper-050: #fbfaf7;
  --paper-100: #f6f4ef;
  --paper-200: #ece8e0;
  --paper-300: #dcd6ca;

  /* --- amber: certification / approval only (FM, CE, ISO, OIML) --- */
  --cert-600: #8a5a08;
  --cert-500: #b8791a;
  --cert-400: #dba033;
  --cert-100: #fbeed2;

  /* --- danger: rust, not red. The brand is crimson now, so the old #c92a2a
         sat 2° of hue from it and error states read as brand. This is 20°
         away and visibly separate. Error states still carry text and an icon,
         never hue alone. --- */
  --danger-600: #9a3412;
  --danger-500: #c2410c;
  --danger-100: #fdebd9;
  --ok-600:     #1c6b45;
  --ok-500:     #248a59;
  --ok-100:     #d8f0e3;

  /* --- semantic: light is the default. Labs are bright rooms. --- */
  --bg:            var(--paper-050);
  --bg-sunken:     var(--paper-100);
  --surface:       var(--paper-000);
  --surface-raised:var(--paper-000);
  --surface-invert:var(--brand-600);   /* the catalogue's dark surface is red */
  --text:          var(--ink-900);
  --text-soft:     var(--ink-600);
  --text-faint:    var(--ink-500);     /* 6.04:1 on white, 5.79:1 on sunken */
  --text-invert:   var(--paper-000);
  --border:        var(--paper-300);
  --border-soft:   var(--paper-200);
  --border-strong: var(--ink-200);
  --accent:        var(--brand-600);
  --accent-hover:  var(--brand-700);
  --accent-weak:   var(--brand-100);
  --focus:         var(--brand-500);

  /* --- the catalogue's band system --- */
  --band-bg:     var(--brand-600);
  --band-fg:     #ffffff;
  --band-rule:   #ffffff;            /* the short heavy rule under a heading */
  --band-chip:   #ffffff;            /* white rounded chip holding a logo */
  --band-dash:   rgba(255,255,255,.055);  /* diagonal circuit-dash texture */
  --band-swoosh: var(--paper-050);   /* must equal --bg: the curve cuts INTO it */
  --rule-w:      5.5rem;
  --rule-h:      6px;

  /* --- type. IBM Plex stays for prose and data. The catalogue's headings are
         a heavy geometric sans with a script accent word; both are loaded in
         02-base.css and both fall back to the Plex stack. The script is
         decorative and never carries meaning on its own. --- */
  --font-sans: "IBM Plex Sans", "IBM Plex Sans Arabic", ui-sans-serif, system-ui,
               -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-ar:   "IBM Plex Sans Arabic", "IBM Plex Sans", "Noto Sans Arabic",
               ui-sans-serif, system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --font-display: "Archivo", "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif;
  --font-script:  "Kaushan Script", "Segoe Script", cursive;

  /* fluid scale, 1.24 ratio at the top end */
  --t-micro: 0.75rem;    /* 12px. Was 11px, which is under the readable floor
                            and this token carries every eyebrow and meta line. */
  --t-xs:    0.75rem;
  --t-sm:    0.8125rem;
  --t-base:  clamp(0.9375rem, 0.9rem + 0.18vw, 1.0625rem);
  --t-lg:    clamp(1.0625rem, 1rem + 0.3vw,   1.1875rem);
  --t-xl:    clamp(1.25rem,  1.15rem + 0.5vw, 1.5rem);
  --t-2xl:   clamp(1.5rem,   1.3rem + 1vw,    2.125rem);
  --t-3xl:   clamp(1.875rem, 1.5rem + 1.9vw,  3rem);
  --t-4xl:   clamp(2.25rem,  1.6rem + 3.2vw,  4.25rem);
  --t-hero:  clamp(2.5rem,   1.4rem + 5.4vw,  6.5rem);

  --lh-tight: 1.08;
  --lh-snug:  1.25;
  --lh-body:  1.62;
  --track-eyebrow: 0.14em;
  --track-tight:  -0.022em;

  /* --- space: 4pt rhythm, deliberate section tiers (not uniform padding) --- */
  --s-1: 0.25rem; --s-2: 0.5rem;  --s-3: 0.75rem; --s-4: 1rem;
  --s-5: 1.5rem;  --s-6: 2rem;    --s-7: 3rem;    --s-8: 4rem;
  --s-9: 6rem;    --s-10: 8rem;
  --section-y:  clamp(3.5rem, 2rem + 6vw, 8rem);
  --gutter:     clamp(1.125rem, 0.5rem + 3vw, 3.5rem);
  --measure:    68ch;
  --container:  84rem;
  --container-narrow: 52rem;
  /* The header bar gets its own, wider cap. Its contents are fixed-width
     (logo + seven nav links + five controls) and measured at 1251px, which does
     not fit the 1232px content box that --container leaves after gutters — the
     nav links then overflow their flex box silently. 90rem gives 1328px. */
  --container-header: 90rem;

  /* --- radii: deliberately tight. Instruments have small chamfers.
         --r-chip is the exception: the catalogue's logo chips are properly
         rounded, and that shape is a brand signature. --- */
  --r-xs: 2px; --r-sm: 3px; --r-md: 5px; --r-lg: 8px; --r-pill: 999px;
  --r-chip: 14px;

  /* --- elevation: warm-tinted now, to match the ink ramp --- */
  --e-1: 0 1px 2px rgba(26,20,22,.06), 0 1px 1px rgba(26,20,22,.04);
  --e-2: 0 2px 4px rgba(26,20,22,.06), 0 4px 12px rgba(26,20,22,.06);
  --e-3: 0 6px 14px rgba(26,20,22,.08), 0 14px 34px rgba(26,20,22,.09);
  --e-4: 0 18px 48px rgba(26,20,22,.16);
  --e-inset: inset 0 1px 0 rgba(255,255,255,.7);

  /* --- motion: one rhythm for the whole site --- */
  --d-fast: 140ms; --d-norm: 240ms; --d-slow: 420ms;
  --ease-out:  cubic-bezier(.16,1,.3,1);
  --ease-in:   cubic-bezier(.7,0,.84,0);
  --ease-both: cubic-bezier(.65,0,.35,1);

  --z-base: 0; --z-raised: 10; --z-sticky: 40; --z-nav: 60; --z-overlay: 80; --z-modal: 100;
}

/* Dark: tonal shift, not inversion. The band inverts to near-black so crimson
   stays a highlight rather than glowing at full saturation on a dark ground. */
:root[data-theme="dark"] {
  --bg:            var(--ink-950);
  --bg-sunken:     #080506;
  --surface:       var(--ink-900);
  --surface-raised:var(--ink-800);
  --surface-invert:var(--brand-700);
  --text:          #f4eeef;
  --text-soft:     var(--ink-300);
  --text-faint:    #a8989b;   /* 5.9:1 on --surface; --ink-400 is 4.2:1 there */
  --text-invert:   var(--ink-950);
  --border:        var(--ink-700);
  --border-soft:   var(--ink-800);
  --border-strong: var(--ink-600);
  --accent:        var(--brand-300);
  --accent-hover:  var(--brand-200);
  --accent-weak:   rgba(233,143,147,.14);
  --focus:         var(--brand-300);
  --band-bg:       var(--brand-800);
  --band-swoosh:   var(--ink-950);
  --band-dash:     rgba(255,255,255,.045);
  --cert-400:      #e8bb5c;
  --danger-500:    #e2643a;
  --danger-600:    #f0906c;
  --danger-100:    rgba(226,100,58,.15);
  --e-1: 0 1px 2px rgba(0,0,0,.5);
  --e-2: 0 2px 6px rgba(0,0,0,.5), 0 8px 20px rgba(0,0,0,.4);
  --e-3: 0 8px 20px rgba(0,0,0,.55), 0 20px 44px rgba(0,0,0,.45);
  --e-4: 0 24px 60px rgba(0,0,0,.6);
  --e-inset: inset 0 1px 0 rgba(255,255,255,.04);
}

/* Light is the default and stays the default.
   The OS `prefers-color-scheme: dark` auto-switch is deliberately NOT honoured:
   labs are bright rooms, the brand reads as a light-paper system, and a
   dark-by-surprise page was the wrong first impression. Dark is opt-in only,
   via the header toggle, which sets data-theme="dark" (see the block above).
   To restore automatic switching, re-add:
     @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { ...dark tokens... } }
   using exactly the token list from the [data-theme="dark"] block. */

/* ===== 02-base.css ===== */
/* ========================= reset + base ========================= */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* offset for the sticky header when jumping to #anchors */
  scroll-padding-top: 6rem;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--t-base);
  line-height: var(--lh-body);
  font-feature-settings: "kern" 1, "liga" 1, "cv05" 1;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100dvh;
  overflow-x: hidden;           /* belt: no horizontal scroll from motion transforms */
}
html[dir="rtl"] body { font-family: var(--font-ar); }

img, picture, svg, video { display: block; max-width: 100%; height: auto; }
svg { fill: none; }
input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
textarea { resize: vertical; }

h1, h2, h3, h4 {
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: var(--track-tight);
  text-wrap: balance;
}
h1 { font-size: var(--t-3xl); }
h2 { font-size: var(--t-2xl); }
h3 { font-size: var(--t-xl); }
h4 { font-size: var(--t-lg); }
p  { text-wrap: pretty; }
p, li { max-width: var(--measure); }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: .18em; }
a:hover { color: var(--accent-hover); }

/* Focus: never removed. Visible on every theme. */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: var(--accent); color: var(--paper-000); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.skip-link {
  position: fixed; top: var(--s-2); inset-inline-start: var(--s-2);
  z-index: var(--z-modal);
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-4); box-shadow: var(--e-3);
  transform: translateY(-200%);
  transition: transform var(--d-fast) var(--ease-out);
}
.skip-link:focus { transform: translateY(0); }

/* ========================= layout ========================= */
.wrap { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.wrap--narrow { max-width: var(--container-narrow); }
.section { padding-block: var(--section-y); }
.section--sunken { background: var(--bg-sunken); }
/* Renamed from .section--ink: this surface is the catalogue's crimson band
   now, not a navy one, and a class called "ink" painting red is a trap. */
.section--band { background: var(--band-bg); color: var(--band-fg); }
.section--band h1, .section--band h2, .section--band h3 { color: var(--band-fg); }

.stack > * + * { margin-block-start: var(--s-4); }
.stack-lg > * + * { margin-block-start: var(--s-6); }

.grid { display: grid; gap: var(--s-5); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fill, minmax(min(100%, 13rem), 1fr)); }

/* ========================= type helpers ========================= */
/* Eyebrow doubles as the "measurement label" motif: tick + tracked caps */
.eyebrow {
  /* Also used on an <h2> (the product benefits section), so the heading's own
     font-size and margins have to be overridden rather than inherited. */
  margin: 0;
  display: flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--text-faint);
}
.eyebrow::before {
  content: ""; inline-size: var(--s-5); block-size: 1px;
  background: currentColor; opacity: .5; flex: none;
}
.eyebrow--bare::before { display: none; }

.lede { font-size: var(--t-lg); color: var(--text-soft); max-width: 60ch; }
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
/* every number that sits in a column gets tabular figures */
.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums lining-nums; }
.text-soft { color: var(--text-soft); }
.text-faint { color: var(--text-faint); }

/* Rich text coming out of the DB / admin editor */
.prose { max-width: var(--measure); }
.prose > * + * { margin-block-start: var(--s-4); }
.prose h2 { font-size: var(--t-xl); margin-block-start: var(--s-7); }
.prose h3 { font-size: var(--t-lg); margin-block-start: var(--s-6); }
.prose ul, .prose ol { padding-inline-start: var(--s-5); }
.prose li + li { margin-block-start: var(--s-2); }
.prose a { text-decoration: underline; }
.prose img { border-radius: var(--r-md); border: 1px solid var(--border-soft); margin-block: var(--s-5); }
/* vendor HTML arrives with inline styles and hotlinked images — neutralise the worst */
.prose [style*="font-family"] { font-family: inherit !important; }
.prose [style*="font-size"]   { font-size: inherit !important; }
.prose [style*="background"]  { background: transparent !important; }
.prose table {
  inline-size: 100%; border-collapse: collapse; font-size: var(--t-sm);
  display: block; overflow-x: auto;         /* wide tables scroll, page never does */
}
.prose th, .prose td {
  border-block-end: 1px solid var(--border-soft);
  padding: var(--s-2) var(--s-3); text-align: start; vertical-align: top;
}
.prose th { font-weight: 600; color: var(--text); white-space: nowrap; }
.prose td { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--text-soft); }

/* ===== 03-components.css ===== */
/* ========================= buttons ========================= */
.btn {
  --btn-bg: var(--accent); --btn-fg: #fff; --btn-bd: var(--accent);
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s-2);
  min-block-size: 2.875rem;            /* 46px — clears the 44px touch minimum */
  padding: var(--s-3) var(--s-5);
  background: var(--btn-bg); color: var(--btn-fg);
  border: 1px solid var(--btn-bd); border-radius: var(--r-sm);
  font-size: var(--t-sm); font-weight: 600;
  letter-spacing: .01em; text-decoration: none; white-space: nowrap;
  cursor: pointer;
  transition: background var(--d-fast) var(--ease-out),
              border-color var(--d-fast) var(--ease-out),
              color var(--d-fast) var(--ease-out),
              box-shadow var(--d-fast) var(--ease-out),
              transform var(--d-fast) var(--ease-out);
}
.btn:hover  { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; box-shadow: var(--e-2); }
/* press feedback that does NOT move surrounding content */
.btn:active { transform: translateY(1px); box-shadow: none; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; transform: none; box-shadow: none; }

.btn--ghost { --btn-bg: transparent; --btn-fg: var(--text); --btn-bd: var(--border-strong); }
.btn--ghost:hover { background: var(--accent-weak); color: var(--accent-hover); border-color: var(--accent); box-shadow: none; }
.btn--quiet { --btn-bg: transparent; --btn-fg: var(--text-soft); --btn-bd: transparent; padding-inline: var(--s-3); }
.btn--quiet:hover { background: var(--bg-sunken); color: var(--text); box-shadow: none; }
.btn--onInk { --btn-bg: var(--paper-050); --btn-fg: var(--ink-900); --btn-bd: var(--paper-050); }
.btn--onInk:hover { background: #fff; color: var(--ink-950); border-color: #fff; }
/* Dark mode: the solid button's fill is --accent, which dark mode lightens to
   brand-300 (#e98f93) so accent TEXT stays readable on near-black. White on that
   fill measured 2.38:1 -- every primary button on the site, in dark mode, failed
   (Add to quote list, Send request, Search, Contact). Ink on it is ~8:1, and on
   the brand-200 hover fill ~13:1. Scoped with :not() because the variants set
   their own --btn-fg and hover colour, and this rule out-ranks them. */
:root[data-theme="dark"] .btn:not(.btn--ghost):not(.btn--quiet):not(.btn--onInk) { --btn-fg: var(--ink-950); }
:root[data-theme="dark"] .btn:not(.btn--ghost):not(.btn--quiet):not(.btn--onInk):hover { color: var(--ink-950); }
.btn--lg { min-block-size: 3.25rem; padding-inline: var(--s-6); font-size: var(--t-base); }
.btn--block { inline-size: 100%; }
.btn__icon { inline-size: 1.125em; block-size: 1.125em; flex: none; stroke: currentColor; stroke-width: 1.75; }

/* directional arrow flips itself in RTL */
html[dir="rtl"] .btn__icon--arrow { transform: scaleX(-1); }

/* ========================= badges ========================= */
.badge {
  display: inline-flex; align-items: center; gap: var(--s-1);
  padding: 0.1875rem var(--s-2);
  font-family: var(--font-mono); font-size: var(--t-micro);
  font-weight: 500; letter-spacing: .06em; text-transform: uppercase;
  border: 1px solid var(--border); border-radius: var(--r-xs);
  color: var(--text-soft); background: var(--surface);
}
/* certification badges are the ONE place amber appears */
.badge--cert { color: var(--cert-600); border-color: var(--cert-400); background: var(--cert-100); }
.badge--brand { color: var(--text); border-color: var(--border-strong); }
.badge--doc  { color: var(--accent); border-color: var(--accent); background: var(--accent-weak); }
:root[data-theme="dark"] .badge--cert { color: var(--cert-400); background: rgba(219,160,51,.12); }

/* ========================= forms ========================= */
.field { display: block; }
.field + .field { margin-block-start: var(--s-5); }
.field__label {
  display: block; margin-block-end: var(--s-2);
  font-size: var(--t-sm); font-weight: 600; color: var(--text);
}
.field__req { color: var(--danger-500); margin-inline-start: 2px; }
.field__hint { display: block; margin-block-start: var(--s-2); font-size: var(--t-xs); color: var(--text-faint); }
.field__error {
  display: flex; align-items: center; gap: var(--s-2);
  margin-block-start: var(--s-2); font-size: var(--t-xs); color: var(--danger-600); font-weight: 500;
}
.input, .textarea, .select {
  inline-size: 100%;
  min-block-size: 2.875rem;
  padding: var(--s-3) var(--s-4);
  /* 16px on mobile stops iOS zooming the viewport on focus */
  font-size: max(1rem, var(--t-base));
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  transition: border-color var(--d-fast) var(--ease-out), box-shadow var(--d-fast) var(--ease-out);
}
.textarea { min-block-size: 8rem; line-height: var(--lh-body); }
.input::placeholder, .textarea::placeholder { color: var(--text-faint); }
.input:hover, .textarea:hover, .select:hover { border-color: var(--ink-300); }
.input:focus, .textarea:focus, .select:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-weak);
}
.input[aria-invalid="true"], .textarea[aria-invalid="true"] {
  border-color: var(--danger-500); box-shadow: 0 0 0 3px var(--danger-100);
}
.select {
  appearance: none; padding-inline-end: var(--s-7);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%236b7fa6' stroke-width='1.6'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-size: 1rem;
  background-position: right var(--s-4) center;
}
html[dir="rtl"] .select { background-position: left var(--s-4) center; }
.field--readonly .input { background: var(--bg-sunken); color: var(--text-soft); }

/* honeypot — must be unreachable, not just invisible */
/* Spam honeypot. Hidden by clipping, NOT by left: -9999px — on an RTL page
   that adds 9,900px of horizontal scroll, because negative inline offsets are
   scrollable in the other direction. The contact and quote pages could both be
   dragged sideways into empty space in Arabic. Clipping is direction-agnostic
   and creates no scrollable area at all. */
.hp {
  position: absolute !important;
  inline-size: 1px !important; block-size: 1px !important;
  margin: -1px !important; padding: 0 !important; border: 0 !important;
  overflow: hidden !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;
  opacity: 0; pointer-events: none;
}

.alert {
  display: flex; gap: var(--s-3); align-items: flex-start;
  padding: var(--s-4); border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface);
  font-size: var(--t-sm);
}
.alert--ok    { border-color: var(--ok-500);     background: var(--ok-100);     color: var(--ok-600); }
.alert--error { border-color: var(--danger-500); background: var(--danger-100); color: var(--danger-600); }
.alert__icon { inline-size: 1.25rem; block-size: 1.25rem; flex: none; stroke: currentColor; stroke-width: 1.75; margin-block-start: 1px; }
:root[data-theme="dark"] .alert--ok    { background: rgba(36,138,89,.14);  color: #7fd3a6; }
:root[data-theme="dark"] .alert--error { background: rgba(201,42,42,.14);  color: #f0a0a0; }

/* ========================= product card ========================= */
/* Depth comes from a real raised surface + an accent rule that draws on hover,
   not from a uniform drop shadow on everything. */
.card {
  position: relative;
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-md);
  overflow: hidden;
  text-decoration: none; color: inherit;
  transition: border-color var(--d-norm) var(--ease-out),
              box-shadow var(--d-norm) var(--ease-out),
              transform var(--d-norm) var(--ease-out);
}
.card::after {                      /* the "measurement rule" that extends on hover */
  content: ""; position: absolute; inset-block-end: 0; inset-inline-start: 0;
  block-size: 2px; inline-size: 0; background: var(--accent);
  transition: inline-size var(--d-slow) var(--ease-out);
}
.card:hover { border-color: var(--border-strong); box-shadow: var(--e-3); transform: translateY(-3px); }
.card:hover::after { inline-size: 100%; }
.card:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.card__media {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--paper-100);
  display: grid; place-items: center;
  overflow: hidden;
  border-block-end: 1px solid var(--border-soft);
}
/* A light plate, not ink: the photos are white-backed cut-outs, and on a dark
   well every one showed as a bright white rectangle. --paper-300 rather than
   white keeps the plate from glaring against the dark page. */
:root[data-theme="dark"] .card__media { background: var(--paper-300); }
.card__media img {
  inline-size: 100%; block-size: 100%;
  object-fit: contain;              /* instruments are cut-outs — never crop them */
  padding: var(--s-5);
  transition: transform var(--d-slow) var(--ease-out);
}
.card:hover .card__media img { transform: scale(1.045); }
/* Product photos are cut from supplier datasheets and arrive on pure #fff,
   while the well is --paper-100, so every one rendered as a white box. Multiply
   makes white take the well's colour and leaves the instrument itself visually
   unchanged -- the same fix the brand logos already use (.band__chip img,
   .brandcard__logo img). In dark mode the well becomes a light plate (above)
   precisely so multiply can stay on -- on a dark well it would turn the whole
   photograph black. */
.card__media img { mix-blend-mode: multiply; }
.card__body { display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-4) var(--s-4) var(--s-5); flex: 1; }
.card__brand { font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: .08em; text-transform: uppercase; color: var(--text-faint); }
.card__title { font-size: var(--t-base); font-weight: 600; line-height: var(--lh-snug); color: var(--text); }
.card__meta { margin-block-start: auto; display: flex; flex-wrap: wrap; gap: var(--s-2); padding-block-start: var(--s-2); }
.card__cta {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); font-weight: 600; color: var(--accent);
}
.card__cta svg { inline-size: 1em; block-size: 1em; stroke: currentColor; stroke-width: 2; transition: transform var(--d-norm) var(--ease-out); }
.card:hover .card__cta svg { transform: translateX(3px); }
html[dir="rtl"] .card__cta svg { transform: scaleX(-1); }
html[dir="rtl"] .card:hover .card__cta svg { transform: scaleX(-1) translateX(3px); }

/* ========================= spec table ========================= */
/* This is the component the old site never had. Values are mono + tabular so
   columns of numbers line up the way they do on a real datasheet. */
.specs { border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; background: var(--surface); }
.specs__group {
  padding: var(--s-3) var(--s-4);
  background: var(--bg-sunken);
  border-block-end: 1px solid var(--border);
  font-family: var(--font-mono); font-size: var(--t-micro);
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-soft);
}
.specs__row {
  display: grid; grid-template-columns: minmax(9rem, 38%) 1fr;
  gap: var(--s-3) var(--s-5);
  padding: var(--s-3) var(--s-4);
  border-block-end: 1px solid var(--border-soft);
  font-size: var(--t-sm);
}
.specs__row:last-child { border-block-end: 0; }
.specs__row:nth-child(even of .specs__row) { background: color-mix(in oklab, var(--bg-sunken) 45%, transparent); }
.specs__k { color: var(--text-soft); }
.specs__v { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text); overflow-wrap: anywhere; }
.specs__v mark { background: var(--accent-weak); color: inherit; padding: 0 2px; border-radius: 2px; }
.specs__src {
  display: inline-block; margin-inline-start: var(--s-2);
  font-family: var(--font-sans); font-size: var(--t-micro);
  color: var(--text-faint); letter-spacing: .04em; text-transform: uppercase;
}
@media (max-width: 30rem) {
  .specs__row { grid-template-columns: 1fr; gap: var(--s-1); }
  .specs__k { font-size: var(--t-xs); }
}

/* ===== 04-blocks.css ===== */
/* ========================= scroll progress ========================= */
/* Driven by Motion's scroll(); scaleX is compositor-only. */
.progress {
  position: fixed; inset-block-start: 0; inset-inline: 0;
  block-size: 2px; z-index: var(--z-nav);
  background: var(--accent);
  transform: scaleX(0); transform-origin: left center;
  will-change: transform;
}
html[dir="rtl"] .progress { transform-origin: right center; }

/* ========================= header ========================= */
.header {
  position: sticky; inset-block-start: 0; z-index: var(--z-nav);
  background: color-mix(in oklab, var(--bg) 88%, transparent);
  backdrop-filter: blur(12px) saturate(1.4);
  border-block-end: 1px solid transparent;
  transition: border-color var(--d-norm) var(--ease-out),
              box-shadow var(--d-norm) var(--ease-out),
              background var(--d-norm) var(--ease-out);
}
@supports not (backdrop-filter: blur(1px)) { .header { background: var(--bg); } }
.header[data-stuck="true"] { border-block-end-color: var(--border); box-shadow: var(--e-1); }
.header__bar { display: flex; align-items: center; gap: var(--s-5); min-block-size: 4.5rem; max-width: var(--container-header); }
/* The lockup must never be squeezed: `flex: 0 1 auto` + `min-inline-size: 0`
   let the 40px mark collapse to a sliver when the bar ran out of room. */
.header__logo { display: flex; align-items: center; gap: var(--s-3); text-decoration: none; color: var(--text); flex: none; }
.header__logo img { flex: none; inline-size: 2.5rem; block-size: 2.5rem; object-fit: contain; }
.header__wordmark b,
.header__wordmark span { white-space: nowrap; }   /* the tagline was wrapping over the nav */
.header__logo img { block-size: 2.5rem; inline-size: auto; border-radius: var(--r-xs); }
.header__wordmark { display: flex; flex-direction: column; line-height: 1.1; }
.header__wordmark b { font-size: var(--t-base); font-weight: 600; letter-spacing: -.01em; }
.header__wordmark span { font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: .1em; text-transform: uppercase; color: var(--text-faint); }
.header__nav { display: flex; align-items: center; gap: var(--s-1); margin-inline-start: auto; min-inline-size: 0; }
.navlink {
  position: relative;
  white-space: nowrap;          /* "Dehydrated Culture Media" must stay on one line */
  /* Header labels are client-editable since the menu builder, so nav_label()'s
     `static $short` map no longer guards this width. When the total runs long
     the links now shrink and ellipsise instead of overflowing their flex box
     and running under the phone number — which is the exact failure the 88rem
     breakpoint below was added for. The full label stays in title=. */
  min-inline-size: 0; flex: 0 1 auto;
  overflow: hidden; text-overflow: ellipsis;
  padding: var(--s-2) var(--s-3);
  font-size: var(--t-sm); font-weight: 500;
  color: var(--text-soft); text-decoration: none;
  border-radius: var(--r-sm);
  transition: color var(--d-fast) var(--ease-out), background var(--d-fast) var(--ease-out);
}
.navlink:hover { color: var(--text); background: var(--bg-sunken); }
.navlink[aria-current] { color: var(--accent); }
.navlink[aria-current]::after {
  content: ""; position: absolute; inset-block-end: 2px;
  inset-inline: var(--s-3); block-size: 2px; background: var(--accent); border-radius: 1px;
}
.header__tools { display: flex; align-items: center; gap: var(--s-2); flex: none; }
.header__tel {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-mono); font-size: var(--t-sm);
  color: var(--text); text-decoration: none; white-space: nowrap;
}
.header__tel svg { inline-size: 1rem; block-size: 1rem; stroke: var(--accent); stroke-width: 1.75; flex: none; }
.header__tel:hover { color: var(--accent); }
/* 96rem, not 62rem. The 2026 taxonomy added a fourth top-level category, and
   with the number spelled out the nav still overran the tools by 34px at
   1440px. The number is the cheapest thing in this cluster to give up: the
   icon still dials, the footer carries it in full, and the WhatsApp button and
   "Request a quotation" are both right beside it. */
@media (max-width: 96rem) {
  /* icon-only here, so it needs a real target and the same box as its neighbours */
  .header__tel {
    display: grid; place-items: center;
    inline-size: 2.75rem; block-size: 2.75rem;
    border: 1px solid var(--border); border-radius: var(--r-sm);
    background: var(--surface);
  }
  .header__tel:hover { border-color: var(--accent); background: var(--accent-weak); }
}
.iconbtn {
  display: grid; place-items: center;
  inline-size: 2.75rem; block-size: 2.75rem;      /* 44px touch target */
  border: 1px solid var(--border); border-radius: var(--r-sm);
  color: var(--text-soft); background: var(--surface);
  transition: color var(--d-fast) var(--ease-out), border-color var(--d-fast) var(--ease-out), background var(--d-fast) var(--ease-out);
}
.iconbtn:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-weak); }
.iconbtn svg { inline-size: 1.125rem; block-size: 1.125rem; stroke: currentColor; stroke-width: 1.75; }
.langswitch { display: flex; border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; }
.langswitch a {
  padding: var(--s-2) var(--s-3);
  min-block-size: 2.75rem; min-inline-size: 2.75rem;   /* 44x44 touch target */
  display: grid; place-items: center;
  font-family: var(--font-mono); font-size: var(--t-xs); font-weight: 500;
  color: var(--text-soft); text-decoration: none; background: var(--surface);
  transition: background var(--d-fast) var(--ease-out), color var(--d-fast) var(--ease-out);
}
.langswitch a[aria-current] { background: var(--brand-600); color: #fff; }
.langswitch a:not([aria-current]):hover { background: var(--bg-sunken); color: var(--text); }
.header__burger { display: none; }
/* Raised from 62rem: the real category names are long, and they were
   wrapping the header onto three lines at 1440px. */
/* 88rem, not 80rem. The 2026 taxonomy added a fourth top-level category and at
   1440px the seven links needed 640px inside a 528px nav — they overflowed the
   container silently (white-space: nowrap, min-inline-size: 0) and ran straight
   under the phone number. Measured, not guessed: the row needs ~1400px. */
@media (max-width: 88rem) {
  .header__nav { display: none; }
  .header__burger { display: grid; }
  /* the nav carried `margin-inline-start:auto`; with it hidden the tools were
     left stranded beside the logo, 116px short of the right edge at 375px */
  .header__tools { margin-inline-start: auto; }
}
/* Below this the tools row overflowed the viewport (measured 433px of controls
   against a 375px screen). The quote CTA and theme toggle move into the drawer;
   the phone number collapses to its icon. */
/* The header tagline is gone at every width, not hidden below a breakpoint.
   The bar lives inside .wrap, so its width is capped at --container (84rem =
   1344px) no matter how wide the viewport gets — widening the screen never
   buys the header any room. Above 1536px the tagline (132px) and the phone
   digits (66px) both appeared, and 1431px of content does not fit in 1344px:
   the nav links silently overflowed their box and ran under the tools.
   The tagline is the widest purely decorative item in the lockup and it has
   now caused two separate overflow bugs, so it goes for good. The wordmark and
   the monogram still carry the brand, and site.tagline is still used in <title>
   and the meta description. */
.header__wordmark span { display: none; }
/* The digits go at the SAME breakpoint that turns .header__tel into a 44px
   icon box (96rem, above). They used to be split — box at 96rem, digits at
   84rem — so between 1344px and 1536px the number rendered inside a 44px box
   and spilled over the search button. One breakpoint, one rule. */
@media (max-width: 96rem) {
  .header__tel span { display: none; }
}
@media (max-width: 62rem) {
  .header__quote { display: none; }
  .header__theme { display: none; }
}
@media (max-width: 30rem) {
  .header__wordmark { display: none; }   /* keep the mark, drop the lockup text */
  .header__bar { gap: var(--s-3); }
  .header__tools { gap: var(--s-1); }
}
/* mobile drawer */
.drawer[hidden] { display: none !important; }
.drawer {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  background: color-mix(in oklab, var(--ink-950) 55%, transparent);
  backdrop-filter: blur(3px);
}
.drawer__panel {
  position: absolute; inset-block: 0; inset-inline-end: 0;
  inline-size: min(22rem, 88vw);
  background: var(--surface); border-inline-start: 1px solid var(--border);
  padding: var(--s-5) var(--gutter) var(--s-7);
  display: flex; flex-direction: column; gap: var(--s-2);
  overflow-y: auto; box-shadow: var(--e-4);
}
.drawer__panel a {
  padding: var(--s-3) 0; font-size: var(--t-lg); font-weight: 500;
  color: var(--text); text-decoration: none; border-block-end: 1px solid var(--border-soft);
}
.drawer__panel a:hover { color: var(--accent); }
.drawer__close { align-self: flex-end; }

/* ========================= hero ========================= */
/* Scroll story: the grid backdrop drifts, the headline scales down and the
   whole plate lifts away as you scroll past. Pinning is position:sticky so the
   browser does the work (Motion docs recommend this over JS pinning). */
/* The catalogue cover is full-bleed crimson, so the hero is too. This was the
   last dark surface on the site and the one that kept reading as "not light
   mode" — the brand's own answer is red, not navy. */
.hero { position: relative; background: var(--band-bg); color: var(--band-fg); overflow: clip; }
/* Same diagonal circuit-dash texture as .band, so the hero and the brand bands
   below it are visibly the same material. */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: repeating-linear-gradient(
    -55deg, transparent 0 22px, var(--band-dash) 22px 26px, transparent 26px 64px);
}
.hero__track { min-block-size: 150vh; }                 /* scroll distance for the pin */
.hero__pin {
  position: sticky; inset-block-start: 0;
  min-block-size: 100vh; display: grid; align-items: center;
  overflow: clip;
}
/* the measurement-grid motif: a real reference to graph paper / calibration */
/* The blueprint grid from the retired "Precision Instrument" direction is
   gone: the catalogue's texture is the diagonal dash on .hero::before, and
   running both at once just made the red muddy. The markup no longer emits
   [data-hero-grid]; app.js already null-guards it. */
.hero__glow {
  position: absolute; inset-block-start: -30%; inset-inline-end: -15%;
  inline-size: 60rem; block-size: 60rem; border-radius: 50%;
  /* Was cyan, which cast green over the crimson. A warm white bloom reads as
     light falling on the band instead of a second brand colour. */
  background: radial-gradient(circle, rgba(255,235,225,.20), transparent 62%);
  will-change: transform; pointer-events: none;
}
.hero__inner { position: relative; z-index: 1; padding-block: var(--s-9); will-change: transform, opacity; }
.hero__eyebrow { color: var(--brand-200); margin-block-end: var(--s-5); }
.hero__eyebrow::before { background: var(--brand-200); opacity: 1; }
.hero h1 {
  font-size: var(--t-hero); font-weight: 600;
  letter-spacing: -.035em; line-height: .96;
  max-width: 22ch;
}
.hero h1 em { font-style: normal; color: var(--brand-200); }
.hero__lede { margin-block-start: var(--s-5); font-size: var(--t-lg); color: var(--ink-200); max-width: 46ch; }
.hero__actions { margin-block-start: var(--s-6); display: flex; flex-wrap: wrap; gap: var(--s-3); }
/* per-word reveal targets, animated with stagger() */
.hero .word { display: inline-block; will-change: transform, opacity; }

/* scroll cue */
.hero__cue {
  position: absolute; inset-block-end: var(--s-6); inset-inline-start: 50%;
  transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: var(--s-2);
  font-family: var(--font-mono); font-size: var(--t-micro);
  letter-spacing: .14em; text-transform: uppercase;
  /* The hero is always a red band, but this was ink-400 grey: 1.87:1 on the
     light-mode red, 3.09:1 on the dark-mode red. Band foreground at 85% is
     ~5.4:1 on the lighter of the two. */
  color: color-mix(in oklab, var(--band-fg) 85%, transparent);
}
.hero__cue i { display: block; inline-size: 1px; block-size: var(--s-6); background: linear-gradient(color-mix(in oklab, var(--band-fg) 85%, transparent), transparent); }

/* ========================= stat ribbon ========================= */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); border-block: 1px solid var(--border); }
.stat { padding: var(--s-6) var(--s-5); border-inline-end: 1px solid var(--border); }
.stat:last-child { border-inline-end: 0; }
.stat__n { font-family: var(--font-mono); font-size: var(--t-3xl); font-weight: 500; font-variant-numeric: tabular-nums; color: var(--text); letter-spacing: -.03em; line-height: 1; }
.stat__n sup { font-size: .45em; vertical-align: super; color: var(--accent); margin-inline-start: 2px; }
.stat__k { margin-block-start: var(--s-3); font-size: var(--t-sm); color: var(--text-soft); }
@media (max-width: 48rem) {
  .stat { border-inline-end: 0; border-block-end: 1px solid var(--border); }
  .stat:last-child { border-block-end: 0; }
}

/* ========================= brand marquee ========================= */
/* Speed is nudged by scroll velocity via Motion's scroll() info callback. */
.marquee { position: relative; overflow: hidden; padding-block: var(--s-5); mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent); }
.marquee__row { display: flex; gap: var(--s-7); inline-size: max-content; animation: marquee 60s linear infinite; will-change: transform; }
.marquee:hover .marquee__row, .marquee:focus-within .marquee__row { animation-play-state: paused; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
html[dir="rtl"] .marquee__row { animation-direction: reverse; }
.marquee__item {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-5);
  border: 1px solid var(--border-soft); border-radius: var(--r-sm);
  background: var(--surface); white-space: nowrap; text-decoration: none;
  transition: border-color var(--d-fast) var(--ease-out), color var(--d-fast) var(--ease-out);
}
.marquee__item:hover { border-color: var(--accent); }
.marquee__item b { font-size: var(--t-sm); font-weight: 600; color: var(--text); }
.marquee__item span { font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: .08em; text-transform: uppercase; color: var(--text-faint); }
@media (prefers-reduced-motion: reduce) {
  .marquee { mask-image: none; overflow-x: auto; }
  .marquee__row { animation: none; }
}

/* ========================= sticky capability sections ========================= */
/* Left column pins while the right column's items scroll past — the
   "moving items as you scroll" pattern, done with sticky + inView. */
.pinrow { display: grid; gap: var(--s-7); align-items: start; }
@media (min-width: 62rem) { .pinrow { grid-template-columns: 0.85fr 1.15fr; gap: var(--s-9); } }
.pinrow__aside { position: sticky; inset-block-start: 7rem; }
.pinrow__list { display: grid; gap: var(--s-5); }
.pinitem {
  position: relative;
  padding: var(--s-6);
  background: var(--surface);
  border: 1px solid var(--border-soft); border-radius: var(--r-md);
  box-shadow: var(--e-1);
  will-change: transform, opacity;
}
.pinitem__n { position: absolute; inset-block-start: var(--s-5); inset-inline-end: var(--s-5); font-family: var(--font-mono); font-size: var(--t-micro); color: var(--text-faint); letter-spacing: .1em; }
.pinitem h3 { margin-block-end: var(--s-3); }
.pinitem p  { color: var(--text-soft); font-size: var(--t-sm); }

/* ========================= footer ========================= */
.footer { background: var(--ink-950); color: var(--ink-200); padding-block: var(--s-8) var(--s-6); }
.footer a { color: var(--ink-200); text-decoration: none; }
.footer a:hover { color: #fff; text-decoration: underline; }
.footer__grid { display: grid; gap: var(--s-7); grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); }
.footer__grid > div:first-child { grid-column: 1 / -1; }
@media (min-width: 62rem) { .footer__grid { grid-template-columns: 1.4fr repeat(3, 1fr); } .footer__grid > div:first-child { grid-column: auto; } }
.footer .footer__h { font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-400); margin-block-end: var(--s-4); font-weight: 500; }
.footer ul { list-style: none; padding: 0; display: grid; font-size: var(--t-sm); }
/* Links were 17px tall. Padding gives a real tap target without changing the
   visual rhythm much, so the grid gap goes and the padding provides it. */
.footer li > a { display: inline-flex; align-items: center; min-block-size: 2.75rem; }
.footer ul { gap: 0; }
.footer__bottom {
  margin-block-start: var(--s-7); padding-block-start: var(--s-5);
  border-block-start: 1px solid var(--ink-800);
  display: flex; flex-wrap: wrap; gap: var(--s-4); justify-content: space-between;
  font-size: var(--t-xs); color: var(--ink-400);
}
.footer__social { display: flex; gap: var(--s-2); }
.footer__social a { display: grid; place-items: center; inline-size: 2.75rem; block-size: 2.75rem; border: 1px solid var(--ink-800); border-radius: var(--r-sm); }
.footer__social a:hover { border-color: var(--brand-300); color: var(--brand-200); }
.footer__social svg { inline-size: 1.125rem; block-size: 1.125rem; stroke: currentColor; stroke-width: 1.75; }

/* Catalogue pager. The public grid had none — fine while the catalogue was 29
   products, not once it reached 130. Centred rather than edge-aligned because
   it sits under a symmetrical grid, and it reads correctly in RTL without a
   direction rule: flex follows the document's own direction. */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-4);
  margin-block: var(--s-8) var(--s-10);
  flex-wrap: wrap;
}
.pager__at {
  font-size: var(--t-sm);
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
}

/* ===== 05-motion.css ===== */
/* ==========================================================================
   Motion support classes.
   Rule: content is VISIBLE by default. Hidden start-states apply only when
   JS is confirmed running (html.js, set by an inline script in <head>), so a
   JS failure or a crawler never gets a blank page.
   ========================================================================== */
.js [data-reveal] { opacity: 0; will-change: transform, opacity; }
.js [data-reveal="up"]    { transform: translateY(24px); }
.js [data-reveal="down"]  { transform: translateY(-18px); }
.js [data-reveal="left"]  { transform: translateX(28px); }
.js [data-reveal="right"] { transform: translateX(-28px); }
.js [data-reveal="scale"] { transform: scale(.965); }
.js [data-reveal="clip"]  { opacity: 1; clip-path: inset(0 0 100% 0); }

/* Motion sets these inline once revealed; this is the resting state it lands on. */
[data-revealed="true"] { opacity: 1 !important; transform: none !important; clip-path: inset(0 0 0 0) !important; }

/* counters animate their textContent, so reserve width to avoid reflow */
.stat__n[data-count] { display: inline-block; min-inline-size: 3ch; }

/* Elements whose transform Motion drives on scroll must not also transition it. */
[data-scroll-parallax], .hero__grid, .hero__glow, .progress { transition: none !important; }

@media (prefers-reduced-motion: reduce) {
  /* Show everything immediately, kill all decorative movement. */
  .js [data-reveal] { opacity: 1 !important; transform: none !important; clip-path: none !important; }
  .hero__track { min-block-size: auto; }
  .hero__pin { position: static; min-block-size: auto; }
  .hero__cue { display: none; }
  .progress { display: none; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ===== 06-pages.css ===== */
/* ========================= breadcrumb ========================= */
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); font-size: var(--t-xs); color: var(--text-faint); padding-block: var(--s-5); }
.crumbs a {
  color: var(--text-soft); text-decoration: none;
  /* measured 19px tall on mobile — pad out to a usable tap target */
  display: inline-flex; align-items: center; min-block-size: 2.25rem;
}
.crumbs a:hover { color: var(--accent); text-decoration: underline; }
.crumbs li { list-style: none; display: flex; align-items: center; gap: var(--s-2); }
.crumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); padding: 0; margin: 0; }
.crumbs__sep { opacity: .5; }
html[dir="rtl"] .crumbs__sep { transform: scaleX(-1); }

/* ========================= page head ========================= */
.pagehead { padding-block: var(--s-6) var(--s-7); border-block-end: 1px solid var(--border); }
.pagehead h1 { max-width: 32ch; }
.pagehead .lede { margin-block-start: var(--s-4); }

/* ========================= listing toolbar ========================= */
.toolbar {
  display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center;
  padding-block: var(--s-5);
  border-block-end: 1px solid var(--border-soft);
}
.toolbar__count { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--text-faint); letter-spacing: .06em; text-transform: uppercase; }
.toolbar__spacer { margin-inline-start: auto; }
.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip {
  display: inline-flex; align-items: center; gap: var(--s-2);
  min-block-size: 2.75rem;               /* 44px touch target, was 36px */
  padding: var(--s-2) var(--s-4);
  font-size: var(--t-sm); color: var(--text-soft); text-decoration: none;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface);
  transition: all var(--d-fast) var(--ease-out);
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip[aria-current] { background: var(--ink-900); border-color: var(--ink-900); color: var(--paper-050); }
:root[data-theme="dark"] .chip[aria-current] { background: var(--brand-200); border-color: var(--brand-200); color: var(--ink-950); }

/* ========================= product detail ========================= */
.pdp { display: grid; gap: var(--s-7); padding-block: var(--s-6) var(--s-8); }
@media (min-width: 62rem) { .pdp { grid-template-columns: 1fr 1fr; gap: var(--s-9); align-items: start; } }
.pdp__gallery { position: sticky; inset-block-start: 7rem; }
@media (max-width: 61.99rem) { .pdp__gallery { position: static; } }
.pdp__stage {
  position: relative; aspect-ratio: 1;
  background: var(--paper-100); border: 1px solid var(--border-soft); border-radius: var(--r-md);
  display: grid; place-items: center; overflow: hidden;
}
:root[data-theme="dark"] .pdp__stage { background: var(--paper-300); }   /* light plate: see .card__media */
.pdp__stage img { inline-size: 100%; block-size: 100%; object-fit: contain; padding: var(--s-7); }
.pdp__thumbs { display: flex; gap: var(--s-3); margin-block-start: var(--s-3); flex-wrap: wrap; }
.pdp__thumb {
  inline-size: 4.5rem; block-size: 4.5rem; flex: none;
  background: var(--paper-100); border: 1px solid var(--border); border-radius: var(--r-sm);
  overflow: hidden; padding: 0; cursor: pointer;
  transition: border-color var(--d-fast) var(--ease-out);
}
:root[data-theme="dark"] .pdp__thumb { background: var(--paper-300); }
.pdp__thumb img { inline-size: 100%; block-size: 100%; object-fit: contain; padding: var(--s-2); }
.pdp__thumb[aria-current="true"] { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-weak); }
.pdp__thumb:hover { border-color: var(--ink-300); }
/* Same white-box fix as .card__media img -- see 03-components.css. */
.pdp__stage img, .pdp__thumb img { mix-blend-mode: multiply; }

.pdp__head { display: flex; flex-direction: column; gap: var(--s-3); }
.pdp__brandline { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); }
.pdp h1 { font-size: var(--t-2xl); }
.pdp__sku { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--text-faint); letter-spacing: .06em; }
.pdp__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-block: var(--s-6); }
.pdp__block { margin-block-start: var(--s-7); }
.pdp__block > h2 { font-size: var(--t-lg); margin-block-end: var(--s-4); padding-block-end: var(--s-3); border-block-end: 1px solid var(--border); }

.docs { display: grid; gap: var(--s-3); }
.doclink {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-4); text-decoration: none;
  border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--surface);
  transition: border-color var(--d-fast) var(--ease-out), box-shadow var(--d-fast) var(--ease-out);
}
.doclink:hover { border-color: var(--accent); box-shadow: var(--e-1); }
.doclink svg { inline-size: 1.5rem; block-size: 1.5rem; flex: none; stroke: var(--accent); stroke-width: 1.5; }
.doclink b { display: block; font-size: var(--t-sm); font-weight: 600; color: var(--text); }
.doclink span { font-family: var(--font-mono); font-size: var(--t-micro); color: var(--text-faint); text-transform: uppercase; letter-spacing: .06em; }

/* "we're still documenting this" — honest, not a dead end */
.thin-notice {
  padding: var(--s-5);
  border: 1px dashed var(--border-strong); border-radius: var(--r-md);
  background: var(--bg-sunken); font-size: var(--t-sm); color: var(--text-soft);
}
.thin-notice b { color: var(--text); }

/* ========================= quote tray ========================= */
/* A persistent RFQ basket. The thing a B2B lab site needs and this one lacked. */
.tray {
  position: fixed; inset-block-end: 0; inset-inline: 0; z-index: var(--z-sticky);
  background: var(--ink-900); color: var(--paper-050);
  border-block-start: 1px solid var(--ink-700);
  box-shadow: 0 -8px 30px rgba(26,20,22,.34);
  transform: translateY(100%);
  transition: transform var(--d-norm) var(--ease-out);
}
.tray[data-open="true"] { transform: translateY(0); }
/* Reserve room so the fixed tray never covers page content. */
/* One number for the tray's height: the body padding below and the WhatsApp
   button in 09-whatsapp.css both read it, so they cannot drift apart. */
:root { --tray-h: 5.5rem; }
@media (max-width: 40rem) { :root { --tray-h: 8rem; } }
body.has-tray { padding-block-end: var(--tray-h); }
.tray__bar { display: flex; align-items: center; gap: var(--s-4); padding-block: var(--s-4); flex-wrap: wrap; }
@media (max-width: 34rem) {
  /* At 375px this wrapped onto three rows and ate a third of the viewport.
     Count and label on one line, actions on the next. */
  .tray__bar { gap: var(--s-2) var(--s-3); padding-block: var(--s-3); }
  .tray__label { flex: 1 1 auto; font-size: var(--t-xs); }
  .tray__actions { inline-size: 100%; margin-inline-start: 0; }
  .tray__actions .btn { flex: 1 1 auto; justify-content: center; }
  .tray__n { inline-size: 2rem; block-size: 2rem; }
}
.tray__n {
  display: grid; place-items: center; flex: none;
  inline-size: 2.25rem; block-size: 2.25rem; border-radius: var(--r-pill);
  background: var(--brand-300); color: var(--ink-950);
  font-family: var(--font-mono); font-weight: 600; font-size: var(--t-sm);
}
.tray__label { font-size: var(--t-sm); color: var(--ink-200); }
.tray__actions { margin-inline-start: auto; display: flex; gap: var(--s-2); flex-wrap: wrap; }

/* ========================= empty / 404 ========================= */
.empty { text-align: center; padding-block: var(--s-9); }
.empty__icon { inline-size: 3rem; block-size: 3rem; margin-inline: auto; margin-block-end: var(--s-5); stroke: var(--text-faint); stroke-width: 1.25; }
.empty h2 { margin-block-end: var(--s-3); }
.empty p { margin-inline: auto; }

/* ========================= print ========================= */
@media print {
  .header, .footer, .tray, .progress, .hero__cue, .pdp__thumbs, .skip-link { display: none !important; }
  body { background: #fff; color: #000; font-size: 10.5pt; }
  .pdp { grid-template-columns: 1fr; }
  .pdp__gallery { position: static; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 8pt; color: #555; }
  .specs, .card { break-inside: avoid; }
}

/* ===== 07-product.css ===== */
/* ==========================================================================
   Product page: selling blocks.
   Replaces the wall-of-text "Overview" the migrated vendor copy produced.
   ========================================================================== */

/* --- headline + positioning ------------------------------------------- */
.pdp__headline {
  font-size: var(--t-lg);
  font-weight: 500;
  line-height: var(--lh-snug);
  color: var(--accent);
  letter-spacing: -.01em;
  max-width: 34ch;
}
.pdp__positioning { color: var(--text-soft); max-width: 54ch; }

/* --- key-spec strip: the 4-6 numbers a buyer decides on ---------------- */
/* Sits directly under the title so the decision data is above the fold,
   instead of buried in a 30-row table. */
/* Borders live on the cells, not on a background showing through a 1px gap.
   The gap trick paints any EMPTY grid cell too, so 6 specs in a 4-column grid
   rendered a stray filled box. */
.keyspecs {
  display: grid; gap: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 8.5rem), 1fr));
  border-radius: var(--r-md);
  overflow: hidden;
  border: 1px solid var(--border-soft);
}
.keyspec {
  background: var(--surface);
  padding: var(--s-4);
  border-inline-end: 1px solid var(--border-soft);
  border-block-end: 1px solid var(--border-soft);
  margin-block-end: -1px;   /* collapse the final row's rule into the container */
  margin-inline-end: -1px;
}
.keyspec__k {
  display: block;
  font-family: var(--font-mono); font-size: var(--t-micro);
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-faint);
  margin-block-end: var(--s-2);
}
.keyspec__v {
  display: block;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--t-base); font-weight: 500; color: var(--text);
  line-height: var(--lh-snug); overflow-wrap: anywhere;
}

/* --- benefit boxes ----------------------------------------------------- */
.benefits {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}
.benefit {
  position: relative;
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-md);
  transition: border-color var(--d-norm) var(--ease-out), box-shadow var(--d-norm) var(--ease-out);
}
.benefit:hover { border-color: var(--border-strong); box-shadow: var(--e-1); }
.benefit__icon {
  display: grid; place-items: center;
  inline-size: 2.5rem; block-size: 2.5rem;
  margin-block-end: var(--s-4);
  border-radius: var(--r-sm);
  background: var(--accent-weak); color: var(--accent);
}
.benefit__icon svg { inline-size: 1.25rem; block-size: 1.25rem; stroke: currentColor; stroke-width: 1.75; }
.benefit h3 { font-size: var(--t-base); font-weight: 600; margin-block-end: var(--s-2); }
.benefit p  { font-size: var(--t-sm); color: var(--text-soft); max-width: none; }

/* --- applications ------------------------------------------------------ */
.applications { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.application {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  font-size: var(--t-sm); color: var(--text-soft);
  background: var(--bg-sunken);
  border: 1px solid var(--border-soft); border-radius: var(--r-pill);
}
.application svg { inline-size: .875rem; block-size: .875rem; stroke: var(--accent); stroke-width: 2.25; flex: none; }

/* --- range items (chemicals / culture media) --------------------------- */
/* These pages were a 30-item list rendered as paragraphs. As a dense grid
   it becomes scannable and looks like a range, not a wall. */
.range {
  display: grid; gap: var(--s-2);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr));
}
.range__item {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  font-size: var(--t-sm); color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-soft); border-radius: var(--r-sm);
}
.range__item::before {
  content: ""; flex: none;
  inline-size: 4px; block-size: 4px; border-radius: 50%;
  background: var(--accent);
}
.range__note { margin-block-start: var(--s-4); font-size: var(--t-sm); color: var(--text-faint); }

/* --- full specifications ------------------------------------------------ */
/* Open by default: on a catalogue with no prices, the spec table IS the
   product. <details> is kept only so a long table can be folded away once
   read — never to hide it on arrival.
   Layout: groups become labelled sections; inside a group the rows form a
   two-column grid on wide screens, which roughly halves the scroll length of
   a 30-row KERN datasheet. */
.specsheet {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  overflow: hidden;
}
.specsheet > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-4) var(--s-5);
  background: var(--bg-sunken);
  border-block-end: 1px solid var(--border);
  font-size: var(--t-sm); font-weight: 600; color: var(--text);
  cursor: pointer; list-style: none;
}
.specsheet > summary::-webkit-details-marker { display: none; }
.specsheet > summary:hover { background: var(--paper-200); }
:root[data-theme="dark"] .specsheet > summary:hover { background: var(--ink-700); }
.specsheet > summary .count {
  font-family: var(--font-mono); font-weight: 400; font-size: var(--t-micro);
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-faint);
  margin-inline-start: var(--s-2);
}
.specsheet__chev { inline-size: 1rem; block-size: 1rem; stroke: var(--text-faint); stroke-width: 2; flex: none; transition: transform var(--d-norm) var(--ease-out); }
.specsheet[open] .specsheet__chev { transform: rotate(90deg); }
html[dir="rtl"] .specsheet__chev { transform: scaleX(-1); }
html[dir="rtl"] .specsheet[open] .specsheet__chev { transform: scaleX(-1) rotate(-90deg); }

.specgroup + .specgroup { border-block-start: 1px solid var(--border); }
.specgroup__label {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-4) var(--s-5) var(--s-2);
  font-family: var(--font-mono); font-size: var(--t-micro);
  letter-spacing: .12em; text-transform: uppercase; color: var(--accent);
}
.specgroup__label::before {
  content: ""; inline-size: var(--s-4); block-size: 2px;
  background: var(--accent); border-radius: 1px; flex: none;
}

.specgrid {
  display: grid;
  grid-template-columns: 1fr;
  padding: 0 var(--s-3) var(--s-3);
}
@media (min-width: 52rem) { .specgrid { grid-template-columns: 1fr 1fr; column-gap: var(--s-5); } }

.specrow {
  display: grid;
  grid-template-columns: minmax(8rem, 44%) 1fr;
  gap: var(--s-4);
  align-items: baseline;
  padding: var(--s-3) var(--s-2);
  border-block-end: 1px solid var(--border-soft);
  transition: background var(--d-fast) var(--ease-out);
}
.specrow:hover { background: var(--bg-sunken); }
/* the last row of each column shouldn't carry a dangling rule */
.specrow:last-child { border-block-end: 0; }
.specrow__k {
  font-size: var(--t-sm); color: var(--text-soft); line-height: var(--lh-snug);
}
.specrow__v {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--t-sm); font-weight: 500; color: var(--text);
  line-height: var(--lh-snug); overflow-wrap: anywhere;
}
/* a yes/no field reads better as a mark than as the word */
.specrow__v .yes { color: var(--ok-600); font-weight: 600; }
.specrow__v .no  { color: var(--text-faint); }
/* ok-600 is a light-mode green: on a dark card it measured 2.8:1. */
:root[data-theme="dark"] .specrow__v .yes { color: color-mix(in oklab, var(--ok-500), #fff 45%); }
.specrow__src {
  display: inline-block; margin-inline-start: var(--s-2);
  padding: 1px 5px; border-radius: var(--r-xs);
  background: var(--accent-weak); color: var(--accent);
  font-family: var(--font-sans); font-size: 10px;
  letter-spacing: .06em; text-transform: uppercase; vertical-align: 1px;
}
@media (max-width: 32rem) {
  .specrow { grid-template-columns: 1fr; gap: var(--s-1); }
}

/* --- confidence / verification note ------------------------------------ */
.verify-note {
  display: flex; gap: var(--s-3); align-items: flex-start;
  padding: var(--s-4);
  font-size: var(--t-sm); color: var(--cert-600);
  background: var(--cert-100); border: 1px solid var(--cert-400);
  border-radius: var(--r-sm);
}
.verify-note svg { inline-size: 1.125rem; block-size: 1.125rem; flex: none; stroke: currentColor; stroke-width: 1.75; margin-block-start: 2px; }

/* --- brand logo lockups ------------------------------------------------ */
.brandmark {
  display: inline-flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface); text-decoration: none;
}
.brandmark img {
  block-size: 1.5rem; inline-size: auto; max-inline-size: 7rem;
  object-fit: contain;
}
.brandmark span { font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: .08em; text-transform: uppercase; color: var(--text-faint); }
/* Marquee: logo only, name carried as the accessible label.
   A FIXED box with object-fit:contain, not auto width — the logos arrive at
   wildly different aspect ratios (170x44 to 240x240) and auto sizing made the
   wide ones shrink to a sliver against the max-width cap. */
.marquee__item img {
  inline-size: 8rem; block-size: 2.5rem;
  object-fit: contain; object-position: center;
  /* most vendor marks are dark-on-white rasters; multiply drops the white box */
  mix-blend-mode: multiply;
}
.marquee__item { min-inline-size: 10rem; justify-content: center; }
/* Blending off alone left every white-backed vendor logo as a white box on
   the dark strip; the plate makes that white intentional. See .brandcard__logo. */
:root[data-theme="dark"] .marquee__item img,
:root[data-theme="dark"] .brandmark img {
  mix-blend-mode: normal; background: var(--paper-000);
  padding: 4px 8px; border-radius: var(--r-xs);
}

/* --- range items are selectable, not decoration --------------------------- */
/* Each entry in a chemical/media range is something a lab actually orders, so
   it gets a checkbox and feeds the quote list. */
.range__hint { font-size: var(--t-sm); color: var(--text-soft); margin-block-start: var(--s-3); }
.range__actions { margin-block-start: var(--s-4); display: flex; gap: var(--s-2); flex-wrap: wrap; }

.range__item {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3);
  font-size: var(--t-sm); color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-soft); border-radius: var(--r-sm);
  cursor: pointer;
  transition: border-color var(--d-fast) var(--ease-out),
              background var(--d-fast) var(--ease-out),
              box-shadow var(--d-fast) var(--ease-out);
}
.range__item::before { content: none; }        /* the old bullet */
.range__item:hover { border-color: var(--ink-300); }
.range__item input {
  inline-size: 1.125rem; block-size: 1.125rem; flex: none;
  accent-color: var(--accent); cursor: pointer; margin: 0;
}
.range__item:focus-within { outline: 2px solid var(--focus); outline-offset: 2px; }
.range__item:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-weak);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.range__item:has(input:checked) span { font-weight: 500; }
/* :has() is unsupported on older Safari/Firefox — the checkbox itself still
   shows state there, so selection remains perfectly usable. */
.quote__picks {
  margin-block-start: var(--s-2); padding-inline-start: var(--s-4);
  font-size: var(--t-xs); color: var(--text-soft);
}
.quote__picks li { margin-block-start: 2px; }

/* ===== 08-catalogue.css ===== */
/* ==========================================================================
   The catalogue band system
   The 2026 print catalogue alternates a full-bleed crimson band (heading +
   short heavy rule + line list + a white chip holding the brand logo) with a
   white band (product imagery). Every brand page is a stack of these.

   The print piece is ~100% red-saturated. A web page is read for minutes, not
   seconds, so the white bands carry the reading and crimson is reserved for
   section openers and CTAs — roughly 30% coverage, not 100%.
   ========================================================================== */

/* ========================= the band ========================= */
.band {
  position: relative;
  background: var(--band-bg);
  color: var(--band-fg);
  padding-block: clamp(2.5rem, 1.5rem + 4vw, 5rem);
  /* isolate: the swoosh and monogram are positioned children, and without a
     stacking context they escape over a following sticky element. */
  isolation: isolate;
  overflow: hidden;
}
/* The diagonal circuit-dash texture from the red fields. A repeating gradient,
   not an image — it costs nothing and scales to any band height. */
.band::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: repeating-linear-gradient(
    -55deg,
    transparent 0 22px,
    var(--band-dash) 22px 26px,
    transparent 26px 64px);
}
.band > .wrap { position: relative; z-index: 1; }

/* A band that opens a page sits directly under the sticky header, so it needs
   the header's height back as padding or the heading tucks under it. */
.band--opener { padding-block-start: clamp(3rem, 2rem + 4vw, 6rem); }

/* The white band. Not strictly needed (it is just --bg), but naming it makes
   the alternation legible in the markup and gives the rhythm one place to change. */
.band--paper {
  background: var(--bg);
  color: var(--text);
  padding-block: clamp(2.5rem, 1.5rem + 4vw, 5rem);
}
.band--paper::before { content: none; }

/* ========================= the swoosh ========================= */
/* The curved divider between the header field and the body. One element, an
   inline SVG in a data URI would need re-encoding per theme, so this is a
   radial-gradient ellipse in the page background colour instead — it recolours
   with --band-swoosh and costs one pseudo-element.
   aria-hidden by construction: it is a ::after, so AT never sees it. */
.band__swoosh {
  position: absolute; inset-inline: -5%; z-index: 0;
  block-size: clamp(1.75rem, 3vw, 3.25rem);
  pointer-events: none;
  background: var(--band-swoosh);
}
.band__swoosh--bottom { inset-block-end:  -1px; border-start-start-radius: 50% 100%; border-start-end-radius: 50% 100%; }

/* ========================= heading + rule ========================= */
.band__head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--s-5) var(--s-6); }
.band__headtext { flex: 1 1 22rem; min-inline-size: 0; }

.band__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--t-4xl);
  line-height: var(--lh-tight);
  letter-spacing: var(--track-tight);
  text-transform: uppercase;
  margin: 0;
  /* Long single-token brand names ("PHARADNIA") must not push the band wide. */
  overflow-wrap: break-word;
}
/* The script accent word. Decorative: it always sits beside real text, never
   replaces it, and it is never the only thing carrying a meaning. */
.band__title .script {
  font-family: var(--font-script);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  /* Kaushan sits small against Archivo 800 at the same px, and its baseline
     runs high. Both corrections are optical, not structural. */
  font-size: 1.15em;
  line-height: 1;
  display: inline-block;
  transform: translateY(.06em) rotate(-2deg);
}
html[dir="rtl"] .band__title .script { transform: translateY(.06em) rotate(2deg); }

/* The short heavy rule under every catalogue heading. */
.band__rule {
  display: block; inline-size: var(--rule-w); block-size: var(--rule-h);
  background: var(--band-rule); border: 0; border-radius: 1px;
  margin-block: var(--s-4) var(--s-5); margin-inline: 0;
}
.band--paper .band__rule { background: var(--brand-600); }

.band__tagline {
  font-size: var(--t-lg); font-weight: 500;
  margin: 0 0 var(--s-5); max-inline-size: var(--measure);
  color: color-mix(in oklab, var(--band-fg) 88%, transparent);
}
.band--paper .band__tagline { color: var(--text-soft); }

/* ========================= the logo chip ========================= */
/* The white rounded card the brand mark sits in on every catalogue page. */
.band__chip {
  flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--band-chip);
  border-radius: var(--r-chip);
  padding: var(--s-4) var(--s-6);
  min-inline-size: 12rem; min-block-size: 5.5rem;
  box-shadow: var(--e-2);
}
.band__chip img {
  max-inline-size: 100%; max-block-size: 3.25rem;
  inline-size: auto; block-size: auto;
  object-fit: contain;
  /* Vendor logos arrive with baked-in white boxes; multiply drops them onto
     the chip cleanly. The chip is always white, so this never darkens a mark. */
  mix-blend-mode: multiply;
}
/* No logo yet (several of the 2026 brands ship without one). Fall back to the
   name set in the display face rather than an empty chip or a broken image. */
.band__chip--text {
  font-family: var(--font-display); font-weight: 800;
  font-size: var(--t-lg); text-transform: uppercase;
  letter-spacing: var(--track-tight); color: var(--brand-600);
  text-align: center;
}

/* ========================= the line list ========================= */
/* The catalogue's bullet columns. Lines are quotable entities, so each row is
   interactive — but a row with no destination stays a plain <li>. */
.lines {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--s-2);
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
.lines__i { min-inline-size: 0; }
/* Optional one-line description under a catalogue line. Empty on every line
   today, so this costs nothing until the client writes one. */
.lines__note {
  margin-block: 2px 0;
  padding-inline-start: var(--s-3);
  font-size: var(--t-sm);
  color: var(--text-soft);
}
.lines__l {
  display: flex; align-items: baseline; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  min-block-size: 2.75rem;            /* 44px touch target */
  border-radius: var(--r-sm);
  color: inherit; text-decoration: none;
  font-size: var(--t-base); line-height: var(--lh-snug);
  transition: background var(--d-fast) var(--ease-out),
              transform  var(--d-fast) var(--ease-out);
}
/* The catalogue's dash bullet. */
.lines__l::before {
  content: ""; flex: none;
  inline-size: .75rem; block-size: 2px; border-radius: 1px;
  background: currentColor; opacity: .65;
  transform: translateY(-.28em);
}
a.lines__l:hover,
a.lines__l:focus-visible {
  /* color: inherit is load-bearing. The base `a:hover` paints links in
     --accent-hover, which is RED, and it out-ranks `.lines__l { color: inherit }`
     (element + pseudo-class beats a lone class). This rule never set a colour,
     so on the red opener band the line name turned red on red and vanished,
     taking its count badge (drawn in currentColor) with it. Inheriting keeps
     each band's own text colour: white on the opener, ink on paper. */
  color: inherit;
  background: color-mix(in oklab, var(--band-fg) 14%, transparent);
  transform: translateX(2px);
}
html[dir="rtl"] a.lines__l:hover,
html[dir="rtl"] a.lines__l:focus-visible { transform: translateX(-2px); }
.band--paper a.lines__l:hover,
.band--paper a.lines__l:focus-visible { background: var(--brand-050); }

/* A line that resolves to a real SKU says so. Lines without one are still
   quotable — they just have no page to link to. */
.lines__badge {
  margin-inline-start: auto; flex: none;
  font-family: var(--font-mono); font-size: var(--t-micro);
  padding: 1px 6px; border-radius: var(--r-pill);
  border: 1px solid color-mix(in oklab, currentColor 40%, transparent);
  opacity: .8;
}


/* ========================= the monogram ========================= */
/* The BE mark as a watermark. Decorative only — aria-hidden in the markup. */
.band__mark {
  position: absolute; z-index: -1; pointer-events: none;
  inset-block-start: 50%; inset-inline-end: -2%;
  translate: 0 -50%;
  inline-size: clamp(12rem, 26vw, 26rem);
  opacity: .06;
}
.band__mark svg { inline-size: 100%; block-size: auto; display: block; fill: #fff; }

@media (max-width: 48rem) {
  /* The mark competes with the text at phone widths, where the band is narrow
     and the heading already fills it. */
  .band__mark { display: none; }
  .band__chip { min-inline-size: 0; inline-size: 100%; }
}

/* ========================= print ========================= */
@media print {
  /* Red bands at full bleed cost a cartridge and print grey on most office
     lasers. Fall back to the paper treatment. */
  .band { background: #fff !important; color: #000 !important; }
  .band::before, .band__mark, .band__swoosh { display: none !important; }
  .band__rule { background: #000 !important; }
  .band__chip { box-shadow: none; border: 1px solid #ccc; }
}

/* ==================== brand band: aside and grouping ==================== */
.band__aside {
  flex: 0 0 auto; display: grid; gap: var(--s-3); justify-items: center;
  align-content: start;
}
.band__meta {
  margin: 0; font-family: var(--font-mono); font-size: var(--t-micro);
  letter-spacing: var(--track-eyebrow); text-transform: uppercase;
  color: color-mix(in oklab, var(--band-fg) 80%, transparent);
}
.band__link {
  display: inline-flex; align-items: center; gap: var(--s-2);
  min-block-size: 2.75rem; padding-inline: var(--s-3);
  font-size: var(--t-sm); font-weight: 500;
  color: var(--band-fg); text-decoration: none;
  border-block-end: 1px solid color-mix(in oklab, var(--band-fg) 40%, transparent);
}
/* Same red-on-red trap as a.lines__l:hover: without an explicit colour the
   base a:hover turns this link red on the red band. */
.band__link:hover { color: var(--band-fg); border-block-end-color: var(--band-fg); }

/* A brand with 13 lines across five categories needs the grouping, or the
   column reads as one undifferentiated list. Single-category brands skip it. */
.band__group {
  margin: var(--s-6) 0 var(--s-3);
  font-family: var(--font-mono); font-size: var(--t-micro);
  letter-spacing: var(--track-eyebrow); text-transform: uppercase;
  color: color-mix(in oklab, var(--band-fg) 78%, transparent);
}
.band__group:first-of-type { margin-block-start: 0; }

/* Breadcrumbs sitting on the band rather than on paper. */
.crumbs--onBand { margin-block-end: var(--s-6); }
.crumbs--onBand a { color: color-mix(in oklab, var(--band-fg) 85%, transparent); }
.crumbs--onBand a:hover { color: var(--band-fg); }
.crumbs--onBand [aria-current] { color: var(--band-fg); }
.crumbs--onBand .crumbs__sep svg { stroke: color-mix(in oklab, var(--band-fg) 55%, transparent); }

@media (max-width: 48rem) {
  .band__aside { justify-items: start; inline-size: 100%; }
  .band__head  { flex-direction: column-reverse; }   /* logo first on a phone */
}

/* ==================== homepage brand cards ==================== */
/* The logo marquee below these is texture. These carry the actual proposition:
   who the manufacturer is, what they stand for, and what we supply from them. */
.brandcards {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr));
  margin-block-start: var(--s-7);
}
.brandcard {
  display: grid; gap: var(--s-4); align-content: start;
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-lg);
  text-decoration: none; color: var(--text);
  transition: border-color var(--d-norm) var(--ease-out),
              box-shadow  var(--d-norm) var(--ease-out),
              transform   var(--d-norm) var(--ease-out);
}
.brandcard:hover, .brandcard:focus-visible {
  border-color: var(--brand-200); box-shadow: var(--e-2); transform: translateY(-2px);
}
.brandcard__top { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.brandcard__logo { display: flex; align-items: center; min-block-size: 2.25rem; }
.brandcard__logo img {
  max-block-size: 2.25rem; max-inline-size: 9rem; inline-size: auto;
  object-fit: contain; mix-blend-mode: multiply;
}
/* /brands listing logo. Was an inline style, which is why a stylesheet could
   not theme it -- and it carried multiply, which crushed every logo on a dark
   card. */
.brandlogo { display: flex; align-items: center; min-block-size: 2.75rem; margin-block-end: var(--s-2); }
.brandlogo img { max-block-size: 2.5rem; max-inline-size: 9rem; object-fit: contain; mix-blend-mode: multiply; }

/* Dark mode: logos on a white plate, not blended.
   multiply against a dark card crushes a logo to near-black (measured: KERN,
   REED, DLAB and memmert all read as black on rgb(26,20,22)), and blending
   switched off shows each vendor logo's baked-in white box instead. A white
   plate is what .band__chip already does on the brand hero, and it is right in
   both cases: the baked-in white merges into it, and a dark mark stays legible. */
:root[data-theme="dark"] .brandcard__logo img,
:root[data-theme="dark"] .brandlogo img {
  mix-blend-mode: normal; background: var(--paper-000);
  padding: 4px 8px; border-radius: var(--r-xs);
}
.brandcard__logo--text {
  font-family: var(--font-display); font-weight: 800; font-size: var(--t-lg);
  text-transform: uppercase; letter-spacing: var(--track-tight);
  /* --accent, not --brand-600: identical in light mode, but brand-600 does not
     lighten for dark mode, and measured 2.43:1 on a dark card. */
  color: var(--accent);
}
.brandcard__country {
  flex: none; font-family: var(--font-mono); font-size: var(--t-micro);
  letter-spacing: var(--track-eyebrow); text-transform: uppercase; color: var(--text-faint);
}
.brandcard__tagline {
  font-size: var(--t-base); font-weight: 500; line-height: var(--lh-snug);
  color: var(--text);
}
.brandcard__lines { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.brandcard__line, .brandcard__more {
  font-size: var(--t-micro); line-height: 1.4;
  padding: 3px var(--s-3);
  border: 1px solid var(--border); border-radius: var(--r-pill);
  color: var(--text-soft);
}
.brandcard__more { border-style: dashed; color: var(--text-faint); }
.brandcard__cta {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); font-weight: 500; color: var(--accent);   /* see .brandcard__logo--text */
}
.brandcard__cta svg { inline-size: 1rem; block-size: 1rem; transition: translate var(--d-fast) var(--ease-out); }
.brandcard:hover .brandcard__cta svg { translate: 3px 0; }
html[dir="rtl"] .brandcard__cta svg { scale: -1 1; }
html[dir="rtl"] .brandcard:hover .brandcard__cta svg { translate: -3px 0; }

@media (prefers-reduced-motion: reduce) {
  .brandcard, .brandcard__cta svg { transition: none; }
  .brandcard:hover { transform: none; }
}

/* ==================== the partners wall ==================== */
/* 44 client marks in one grid. Greyscale at rest so the wall reads as one
   texture rather than 44 competing brand palettes; colour on hover rewards
   looking. The catalogue prints them in colour on white cards, which works on
   paper but is a riot at web scale. */
.partners {
  list-style: none; margin: var(--s-7) 0 0; padding: 0;
  display: grid; gap: 1px;
  /* A plain length, not min(50%, ...): a percentage inside minmax() under
     repeat(auto-fill) resolves against an indefinite track size during sizing
     and fell back to max-content, which collapsed the wall to ONE column at
     375px. Measured, not assumed. */
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  background: var(--border-soft);        /* shows through the 1px gap as rules */
  border: 1px solid var(--border-soft);
  border-radius: var(--r-md);
  overflow: hidden;
}
.partners__i {
  display: grid; place-items: center;
  padding: var(--s-5) var(--s-4);
  min-block-size: 5.5rem;
  background: var(--surface);
}
.partners__i img {
  max-inline-size: 100%; max-block-size: 2.75rem;
  inline-size: auto; block-size: auto; object-fit: contain;
  filter: grayscale(1); opacity: .62;
  transition: filter var(--d-norm) var(--ease-out), opacity var(--d-norm) var(--ease-out);
}
.partners__i:hover img { filter: grayscale(0); opacity: 1; }
/* Dark mode had no rule here: the tile went dark while the logos stayed
   grayscale at 62% opacity, so dark marks read as dark grey on near-black and
   white-backed ones as grey boxes. A light tile with multiply -- the same
   treatment as the product image wells -- merges the baked-in white into the
   tile and keeps every mark legible. */
:root[data-theme="dark"] .partners__i { background: var(--paper-300); }
:root[data-theme="dark"] .partners__i img { mix-blend-mode: multiply; }

@media (max-width: 30rem) {
  /* Below ~340px even 9rem tracks fall to a single column; two is the floor
     that still reads as a wall rather than a list. */
  .partners { grid-template-columns: repeat(2, 1fr); }
  .partners__i { padding: var(--s-4) var(--s-3); min-block-size: 4.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .partners__i img { transition: none; }
}
/* Colour is decoration here, not information: the alt text and title carry the
   name, so a viewer who never hovers loses nothing. */
@media (forced-colors: active) {
  .partners__i img { filter: none; opacity: 1; }
}
@media print { .partners { display: none; } }

/* The script accent word, used outside .band__title too. */
h1 > .script, h2 > .script, h3 > .script {
  font-family: var(--font-script); font-weight: 400;
  text-transform: none; letter-spacing: 0;
  font-size: 1.12em; line-height: 1;
  display: inline-block; transform: translateY(.06em) rotate(-2deg);
  color: var(--accent);   /* brand-600 measured 2.71:1 on the dark page */
}
html[dir="rtl"] h1 > .script,
html[dir="rtl"] h2 > .script,
html[dir="rtl"] h3 > .script { transform: translateY(.06em) rotate(2deg); }

/* The line list on a paper ground (search results, category pages) rather than
   inside a crimson band. */
.lines--paper .lines__l { color: var(--text); }
.lines--paper .lines__l::before { background: var(--brand-600); opacity: 1; }
.lines--paper .lines__badge { border-color: var(--border); color: var(--text-faint); }

/* ===== 09-whatsapp.css ===== */
/* ==========================================================================
   WhatsApp contact button
   Bottom-left, deliberately: bottom-right is where the quote tray's actions
   sit, and a floating button there competes with "Review & send".
   Physically left in both directions — a floating action button's corner is a
   physical choice, so this uses `left`, not `inset-inline-start`, and stays
   put when the page flips to RTL.
   ========================================================================== */
.wa {
  position: fixed;
  inset-block-end: var(--s-5);
  left: var(--s-5);
  z-index: var(--z-sticky);
  display: grid; place-items: center;
  inline-size: 3.5rem; block-size: 3.5rem;     /* 56px, well over the 44px floor */
  border-radius: var(--r-pill);
  background: #25d366;                          /* WhatsApp brand green */
  color: #fff;
  box-shadow: var(--e-3);
  transition: transform var(--d-norm) var(--ease-out),
              box-shadow var(--d-norm) var(--ease-out),
              inset-block-end var(--d-norm) var(--ease-out);
}
.wa svg { inline-size: 1.75rem; block-size: 1.75rem; fill: currentColor; display: block; }
.wa:hover, .wa:focus-visible { transform: scale(1.06); box-shadow: var(--e-4); }
.wa:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }

/* Lift clear of the quote tray once it has items. --tray-h is defined beside
   the body padding in 06-pages.css so the two stay in step. */
body.has-tray .wa { inset-block-end: calc(var(--tray-h) + var(--s-4)); }

@media (prefers-reduced-motion: reduce) {
  .wa { transition: none; }
  .wa:hover, .wa:focus-visible { transform: none; }
}

@media print { .wa { display: none !important; } }

/* ===== 10-slider.css ===== */
/* ==========================================================================
   Homepage banner slider (under the stat ribbon). Images are 3:1 — the admin
   asks for 2400 x 800. Sliding is native scroll-snap; app.js adds arrows,
   dots and autoplay on top.
   ========================================================================== */
.slider-sec { padding-block: var(--s-6) var(--section-y); }
.slider { position: relative; }
.slider__track {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth;
  scrollbar-width: none; border-radius: var(--r-lg); background: var(--surface);
  overscroll-behavior-x: contain;
}
.slider__track::-webkit-scrollbar { display: none; }
.slider__slide { flex: 0 0 100%; scroll-snap-align: start; aspect-ratio: 3 / 1; }
.slider__slide a { display: block; block-size: 100%; }
.slider__slide img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; }

.slider__nav {
  position: absolute; inset-block-start: 50%; translate: 0 -50%;
  inline-size: 2.75rem; block-size: 2.75rem; display: grid; place-items: center;
  border: 0; border-radius: var(--r-pill); cursor: pointer;
  background: color-mix(in oklab, var(--surface) 88%, transparent); color: var(--text);
  box-shadow: 0 2px 10px rgb(0 0 0 / .18);
}
.slider__nav:hover { background: var(--surface); color: var(--accent); }
.slider__nav--prev { inset-inline-start: var(--s-3); }
.slider__nav--next { inset-inline-end: var(--s-3); }
.slider__icon { inline-size: 1.25rem; block-size: 1.25rem; fill: none; stroke: currentColor; stroke-width: 2; }
/* chevron points right: flip it for "previous" in LTR and "next" in RTL */
.slider__nav--prev .slider__icon { transform: scaleX(-1); }
html[dir="rtl"] .slider__nav--prev .slider__icon { transform: none; }
html[dir="rtl"] .slider__nav--next .slider__icon { transform: scaleX(-1); }

.slider__dots { display: flex; justify-content: center; gap: var(--s-2); margin-block-start: var(--s-4); }
.slider__dot {
  inline-size: 2rem; block-size: 1.5rem; padding: 0; border: 0; background: none; cursor: pointer;
  display: grid; place-items: center;
}
.slider__dot::before {
  content: ""; inline-size: .5rem; block-size: .5rem; border-radius: var(--r-pill);
  background: var(--border); transition: inline-size .2s, background-color .2s;
}
.slider__dot[aria-current="true"]::before { inline-size: 1.5rem; background: var(--accent); }

@media (max-width: 40rem) { .slider__nav { display: none; } }   /* swipe on phones */
@media (prefers-reduced-motion: reduce) { .slider__track { scroll-behavior: auto; } }
