/*! Belle Chemicals — built 2026-09-16T13:27Z. Source: src/css/. Do not edit directly. */

/* ===== 01-tokens.css ===== */
/* ==========================================================================
   Belle Chemicals — design tokens
   Direction: the Belle Chemicals logo. Two colours sampled from the 200x40
   logo file (the only artwork available): the navy of the mark and wordmark,
   #032A3B, and the blue of the drop inside the mark, #006990.
   The drop blue is the interactive accent (links, buttons); the navy is the
   dark "band" surface (hero, CTA band) — the same two roles the logo gives them.
   Token NAMES are unchanged from the Beckman fork so every other stylesheet
   keeps working; only the values moved.
   Amber stays reserved for certification marks.
   ========================================================================== */
:root {
  /* --- brand: hue ~198deg. 600 IS the logo drop #006990, 900 IS the logo navy
         #032A3B. Contrast measured (WCAG 2.x relative luminance):
           600 on white 6.13:1, on --bg 5.87:1  -> body text and buttons, AA
           500 on white 5.04:1, on --bg 4.83:1  -> AA for text
           400 on white 3.05:1                  -> large text, borders, fills only
           white on 900 (the band) 15.02:1; brand-200 on 900 10.34:1;
           brand-100 on 900 12.67:1; ink-200 on 900 9.46:1 --- */
  --brand-950: #021b26;
  --brand-900: #032a3b;   /* sampled: logo navy */
  --brand-800: #073d55;
  --brand-700: #05506f;
  --brand-600: #006990;   /* sampled: logo drop */
  --brand-500: #18779d;
  --brand-400: #4f9dbd;
  --brand-300: #86bdd4;
  --brand-200: #bddbe8;
  --brand-100: #e0eef5;
  --brand-050: #f1f7fa;

  /* --- ink: cool slate, same hue as the brand at low saturation. Each step was
         solved to the SAME luminance as the warm ink it replaces, so the
         contrast notes elsewhere in the CSS still hold: 500 is 6.05:1 on white
         and carries meta text; 400 is 3.97:1 and is NOT for small text. --- */
  --ink-950: #0a0d0e;
  --ink-900: #121619;
  --ink-800: #1e2529;
  --ink-700: #2c373d;
  --ink-600: #3c4c53;
  --ink-500: #50666f;
  --ink-400: #68848f;
  --ink-300: #96abb3;
  --ink-200: #c4cfd4;
  --ink-100: #dfe6e8;
  --ink-050: #f0f3f4;

  /* --- paper: near-white, cooled to sit with the navy. Luminance-matched. --- */
  --paper-000: #ffffff;
  --paper-050: #f9fafb;
  --paper-100: #f1f5f6;
  --paper-200: #e2eaed;
  --paper-300: #cbd9de;

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

  /* --- danger: rust. Far from the blue brand hue, so an error never reads as
         brand. 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-900);   /* the logo navy */
  --text:          var(--ink-900);
  --text-soft:     var(--ink-600);
  --text-faint:    var(--ink-500);     /* 6.05:1 on white, 5.51: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 band system: navy bands, white text (15.02:1) --- */
  --band-bg:     var(--brand-900);
  --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: navy-tinted, to match the ink ramp --- */
  --e-1: 0 1px 2px rgba(3,42,59,.06), 0 1px 1px rgba(3,42,59,.04);
  --e-2: 0 2px 4px rgba(3,42,59,.06), 0 4px 12px rgba(3,42,59,.06);
  --e-3: 0 6px 14px rgba(3,42,59,.08), 0 14px 34px rgba(3,42,59,.09);
  --e-4: 0 18px 48px rgba(3,42,59,.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. Accent text lightens to brand-300
   (9.51:1 on --bg); the band stays navy one step lighter than the page. */
:root[data-theme="dark"] {
  --bg:            var(--ink-950);
  --bg-sunken:     #060809;
  --surface:       var(--ink-900);
  --surface-raised:var(--ink-800);
  --surface-invert:var(--brand-700);
  --text:          #ecf0f2;
  --text-soft:     var(--ink-300);
  --text-faint:    #8d9ea6;   /* 6.56:1 on --surface, 5.60:1 on ink-800 */
  --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(134,189,212,.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); }
/* The brand band: navy (--band-bg), white text. */
.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; }
/* The Belle logo is a 200x40 wordmark (mark + "Belle Chemicals" lettering),
   so the image IS the lockup and there is no separate text wordmark. Shown at
   its native 40px height so it is never resampled on a 1x screen. The only
   source file is 200x40; a vector/high-res logo is on the client list. */
.header__logo img { flex: none; block-size: 2.5rem; inline-size: auto; aspect-ratio: 5 / 1; object-fit: contain; }
/* Navy lettering on a transparent PNG disappears on the dark header, so in dark
   mode it sits on a white chip, the way a printed label would. */
:root[data-theme="dark"] .header__logo img { background: #fff; border-radius: var(--r-sm); padding: 2px 6px; box-sizing: content-box; block-size: 2.25rem; }
.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 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) {
  /* The 200px wordmark plus five 44px tools overflowed a 390px screen by 15px
     (measured). The logo scales down proportionally, and the phone icon goes:
     the drawer carries the number and the WhatsApp button stays on screen. */
  .header__logo img, :root[data-theme="dark"] .header__logo img { block-size: 1.5rem; }
  .header__bar > .header__tools > .header__tel { display: none; }
  .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 hero is the navy band (--band-bg = the logo 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 */
/* No grid backdrop: the texture is the diagonal dash on .hero::before. 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%;
  /* A pale blue bloom from the logo drop colour, reading as light on the navy. */
  background: radial-gradient(circle, rgba(134,189,212,.18), 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;
  /* Band foreground at 85% on the navy band: well above 4.5:1 in both themes. */
  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;
}

/* ========================= Belle home: COA stat + sister company ========================= */
/* The COA item in the stat ribbon is a link to /coa; it keeps the stat look. */
.stat--link { display: block; color: inherit; text-decoration: none; transition: background var(--d-fast) var(--ease-out); }
.stat--link:hover { background: var(--surface); }
.stat--link .stat__n { color: var(--accent); }
.stat--link .stat__n svg { inline-size: .9em; block-size: .9em; }
.stat--link:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

/* "Part of the Beckman Egypt group": one quiet row, mark / text / link. */
.group {
  display: grid; gap: var(--s-5); align-items: center;
  grid-template-columns: auto 1fr;
  padding-block: var(--s-6); border-block: 1px solid var(--border);
}
.group > .btn { grid-column: 1 / -1; justify-self: start; }
@media (min-width: 48rem) {
  .group { grid-template-columns: auto 1fr auto; }
  .group > .btn { grid-column: auto; }
}
.group__mark { display: block; inline-size: 3rem; color: var(--brand-900); }
:root[data-theme="dark"] .group__mark { color: var(--brand-300); }
.group__mark svg { display: block; inline-size: 100%; block-size: auto; fill: currentColor; }   /* 02-base sets svg { fill:none } */
.group__title { margin-block-start: var(--s-2); font-size: var(--t-xl); }
.group__lede { margin-block-start: var(--s-2); max-width: 62ch; }
/* Lot fields are mono and LTR (codes), but the Arabic placeholder is prose: in
   the mono stack its letters rendered unjoined. Applies to /coa as well. */
html[dir="rtl"] .coa__input::placeholder { font-family: var(--font-ar); letter-spacing: 0; }

/* ===== 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; position: relative; }
.slider__media { display: block; aspect-ratio: 3 / 1; }
.slider__media img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; }

/* ---- text over the image ------------------------------------------------
   Below 768px: the banner is too short (~117px on a phone), so the words sit in a panel BELOW it.
   Wider: drawn over the image's left side on a pale scrim. The generated
   photos are bright with open space on the left, so the text is dark ink and
   the colours are pinned rather than themed — the image does not change with
   dark mode, so neither may the text on it. It stays on the physical left in
   Arabic too: mirroring would put it over the subject. */
.slider__cap { padding: var(--s-5); display: grid; gap: var(--s-2); justify-items: start; background: var(--surface); }
.slider__eyebrow { margin: 0; font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: .12em; text-transform: uppercase; color: var(--accent); }
html[dir="rtl"] .slider__eyebrow { letter-spacing: 0; font-family: var(--font-ar); font-size: var(--t-xs); }
.slider__title { margin: 0; font-family: var(--font-display); font-size: var(--t-xl); line-height: 1.12; letter-spacing: -.02em; color: var(--text); text-wrap: balance; }
html[dir="rtl"] .slider__title { font-family: var(--font-ar); letter-spacing: 0; line-height: 1.35; }
.slider__text { margin: 0; font-size: var(--t-sm); color: var(--text-soft); max-inline-size: 42ch; }
.slider__btn { margin-block-start: var(--s-2); }

@media (min-width: 48rem) {
  .slider__cap {
    position: absolute; inset-block: 0; left: 0; inline-size: min(48%, 34rem);
    align-content: center; padding: var(--s-6) var(--s-7);
    background: linear-gradient(90deg, rgb(250 249 246 / .94) 0%, rgb(250 249 246 / .86) 62%, rgb(250 249 246 / 0) 100%);
    padding-inline-end: var(--s-9);
  }
  /* Arabic text is right-aligned, so its fade-side padding is on the physical right. */
  html[dir="rtl"] .slider__cap { padding-left: var(--s-7); padding-right: var(--s-9); }
  .slider__eyebrow { color: #006990; }
  .slider__title { color: #100b0c; font-size: var(--t-2xl); }
  .slider__text { color: #3b3536; }
  /* Beat the dark-theme primary button rule (0,5,0): this image never goes dark. */
  :root .slider-sec .slider__cap .slider__btn.btn.btn { --btn-bg: #006990; --btn-bd: #006990; --btn-fg: #fff; color: #fff; }
  :root .slider-sec .slider__cap .slider__btn.btn.btn:hover { --btn-bg: #05506f; --btn-bd: #05506f; color: #fff; }
}
@media (min-width: 48rem) and (max-width: 64rem) {
  .slider__text { display: none; }           /* banner is ~250px tall here */
  .slider__cap { padding-block: var(--s-4); }
  .slider__title { font-size: var(--t-xl); }
}
@media (min-width: 64rem) { .slider__title { font-size: clamp(1.5rem, .8rem + 1.5vw, 2.375rem); } }

.slider__nav {
  /* Top-right corner of the image, both arrows together: the left side
     belongs to the text panel. Physical right in Arabic too, same reason.
     Top, not bottom — .slider also holds the dots, so bottom is below the image. */
  position: absolute; top: var(--s-4);
  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 { right: calc(var(--s-4) + 3.25rem); }
.slider__nav--next { right: var(--s-4); }
html[dir="rtl"] .slider__nav--prev { right: var(--s-4); }
html[dir="rtl"] .slider__nav--next { right: calc(var(--s-4) + 3.25rem); }
.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; } }

/* ===== 11-chemicals.css ===== */
/* ==========================================================================
   Chemistry catalogue: identity block, GHS pictograms, pack picker, SDS docs.
   Logical properties throughout, so RTL needs no overrides beyond the few
   LTR-only values (CAS, formula) that the markup pins with dir="ltr".
   ========================================================================== */

/* --- identity block (product page) -------------------------------------- */
.chem {
  margin-block-start: var(--s-5);
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
}
.chem__list {
  display: grid; gap: 0;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  margin: var(--s-3) 0 0;
}
.chem__row {
  padding-block: var(--s-2);
  border-block-start: 1px solid var(--border-soft);
}
.chem__row dt {
  font-family: var(--font-mono); font-size: var(--t-micro);
  letter-spacing: .06em; text-transform: uppercase; color: var(--text-faint);
}
html[dir="rtl"] .chem__row dt { font-family: var(--font-ar); letter-spacing: 0; }
.chem__row dd { margin: 2px 0 0; font-size: var(--t-base); font-weight: 600; color: var(--text); }
/* An LTR value inside an RTL row still has to sit at the row's start edge. */
html[dir="rtl"] .chem__row dd[dir="ltr"] { text-align: right; }
.chem__row sub { font-size: .72em; }
.chem__sub {
  margin-block-start: var(--s-4);
  font-size: var(--t-sm); font-weight: 600; color: var(--text-soft);
}

/* --- GHS pictograms ------------------------------------------------------ */
.ghs { display: flex; flex-wrap: wrap; gap: var(--s-3); list-style: none; margin: var(--s-3) 0 0; padding: 0; }
.ghs__i {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-1);
  inline-size: 5.5rem; text-align: center;
  font-size: var(--t-micro); line-height: var(--lh-snug); color: var(--text-soft);
}
.ghs__i img { inline-size: 3.5rem; block-size: 3.5rem; }
/* The pictogram is a red diamond on WHITE by regulation; on a dark surface it
   keeps its white ground rather than floating as a bare red outline. */
:root[data-theme="dark"] .ghs__i img { background: var(--paper-000); border-radius: var(--r-sm); padding: 2px; }

/* --- pack-size picker ---------------------------------------------------- */
.packs { margin: var(--s-5) 0 0; padding: 0; border: 0; min-inline-size: 0; }
.packs__list { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-block-start: var(--s-3); }
.packs__i {
  position: relative;
  display: inline-flex; align-items: baseline; gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); color: var(--text);
  font-size: var(--t-sm); font-weight: 600; cursor: pointer;
  transition: border-color var(--d-fast) var(--ease-out), box-shadow var(--d-fast) var(--ease-out);
}
/* The native radio stays in the tab order and the accessibility tree; it is
   only visually replaced by the chip's own selected state. */
.packs__i input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.packs__i:hover { border-color: var(--border-strong); }
.packs__i:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-weak); }
.packs__i:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }
.packs__sku { font-family: var(--font-mono); font-size: var(--t-micro); font-weight: 400; color: var(--text-faint); }
.packs__hint { margin-block-start: var(--s-2); font-size: var(--t-sm); color: var(--text-faint); }
:root[data-theme="dark"] .packs__i:has(input:checked) { box-shadow: 0 0 0 2px var(--brand-800); }

/* --- product card -------------------------------------------------------- */
.card__chem {
  display: flex; flex-wrap: wrap; gap: var(--s-1) var(--s-3);
  font-family: var(--font-mono); font-size: var(--t-micro); color: var(--text-faint);
}

/* --- documents: SDS called out ------------------------------------------ */
.doclink .doclink__kind {
  display: inline-block; margin-block-end: 2px;
  font-style: normal; font-weight: 600; color: var(--text-soft);
}
.doclink--sds { border-color: var(--border-strong); border-inline-start: 3px solid var(--accent); }
.doclink--sds .doclink__kind { color: var(--accent); }

/* --- quote form pack select --------------------------------------------- */
.quote__pack { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-block-start: var(--s-2); }
.quote__pack label { font-size: var(--t-sm); color: var(--text-soft); }
.quote__pack .select { inline-size: auto; min-block-size: 2.25rem; padding-block: var(--s-1); font-size: var(--t-sm); }

/* --- search hint --------------------------------------------------------- */
.chem-search__hint { margin-block-start: var(--s-2); font-size: var(--t-sm); color: var(--text-faint); }

/* ===== 12-coa.css ===== */
/* ==========================================================================
   Certificate of analysis — /coa lookup and result pages.
   Tokens only, logical properties only, so RTL and dark mode come for free.
   ========================================================================== */
.coa { display: grid; gap: var(--s-5); margin-block: var(--s-7) var(--s-9); }

.coa__form {
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}
.coa__row { display: flex; flex-wrap: wrap; gap: var(--s-3); }
/* Lot numbers are codes: mono, upper-case as typed, left-to-right even on the
   Arabic page, where a mixed "2403A117" would otherwise reorder visually. */
.coa__input {
  flex: 1 1 14rem;
  min-inline-size: 0;
  font-family: var(--font-mono);
  font-size: var(--t-lg);
  letter-spacing: .04em;
  text-transform: uppercase;
}
.coa__input::placeholder { text-transform: none; letter-spacing: 0; }
.coa__row .btn { flex: 0 0 auto; }

.coa__card { display: grid; gap: var(--s-5); }
.coa__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); }

.coa__note { display: flex; gap: var(--s-2); align-items: flex-start; font-size: var(--t-sm); max-width: 60ch; }
.coa__note svg { inline-size: 1.1em; block-size: 1.1em; flex: 0 0 auto; margin-block-start: .15em; color: var(--accent); }
.coa .alert a { color: inherit; text-decoration: underline; }

@media (max-width: 40rem) {
  .coa__row .btn { inline-size: 100%; }
}
