/* ==========================================================================
   HAZ MED — Component library
   Requires tokens.css then base.css.

   Every component here is written with logical properties. If you add a
   component, it mirrors for free under dir="rtl" — do not write an RTL
   stylesheet fork.

   There are exactly FOUR categories of legitimate [dir="rtl"] override in this
   file. Anything outside them is a bug:

     1. TRANSFORMS. translateX / scaleX / transform-origin have no logical
        form. Use the --flip token (1 in LTR, -1 in RTL).
     2. BACKGROUND-POSITION. The select chevron; the table scroll-fade.
     3. GRADIENT DIRECTION. `to inline-end` is NOT valid CSS and silently drops
        the whole declaration, so a real `to right` / `to left` pair is needed.
        (Hero scrim, table edge fades.)
     4. RE-ANCHORING TEXT INSIDE A direction:ltr ISLAND. Any element forced to
        `direction: ltr` for bidi isolation — numeric table cells, stat values,
        the 404 code — resolves `text-align: start` to LEFT regardless of page
        direction. Those need an explicit `text-align: end` in RTL so the value
        still sits on the row's reading edge.

   Current count: 6 override blocks, all in those four categories.
   ========================================================================== */

/* ==========================================================================
   BUTTONS
   Minimum target 48px block-size — these are read on phones in facilities,
   sometimes gloved.
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  /* min-block-size, never a fixed height: Arabic has taller vertical metrics
     and a fixed 48px button clips its descenders and diacritics. */
  min-block-size: 3rem;
  padding-block: var(--space-3);
  padding-inline: var(--space-6);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font-family: var(--font-body);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  line-height: 1.3;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

.btn:active { transform: translateY(1px); }

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}

/* Primary. Brand cyan fill with INK text — 5.60:1.
   White on brand cyan is 2.75:1 and fails AA, so it is not offered.
   The cyan-600 edge gives the control a 3.83:1 boundary against white
   (WCAG 1.4.11), which a bare cyan fill at 2.75:1 would not. */
.btn--primary {
  background-color: var(--action-fill);
  border-color: var(--action-edge);
  color: var(--action-on-fill);
}

.btn--primary:hover {
  background-color: var(--action-fill-hover);
  border-color: var(--cyan-500);
  color: var(--action-on-fill);
}

.btn--primary:active { background-color: var(--action-fill-active); color: var(--neutral-0); }

/* Ink. The alternative primary — use where a cyan fill would sit on a cyan
   or tinted ground and lose its edge. */
.btn--ink {
  background-color: var(--ink);
  border-color: var(--ink);
  color: var(--text-inverse);
}

.btn--ink:hover {
  background-color: var(--ink-2);
  border-color: var(--ink-2);
  color: var(--text-inverse);
}

/* Secondary — outline on light grounds */
.btn--secondary {
  background-color: transparent;
  border-color: var(--border-control);
  color: var(--text-strong);
}

.btn--secondary:hover {
  border-color: var(--ink);
  background-color: var(--surface-sunken);
  color: var(--text-strong);
}

/* Outline on ink grounds */
.btn--inverse {
  background-color: transparent;
  border-color: rgba(255, 255, 255, 0.4);
  color: var(--text-inverse);
}

.btn--inverse:hover {
  background-color: rgba(255, 255, 255, 0.1);
  border-color: var(--text-inverse);
  color: var(--text-inverse);
}

.btn--ghost {
  background-color: transparent;
  border-color: transparent;
  color: var(--text-accent);
  padding-inline: var(--space-3);
}

.btn--ghost:hover {
  background-color: var(--surface-tint);
  color: var(--text-accent-hover);
}

.btn--sm {
  min-block-size: 2.5rem;
  padding-inline: var(--space-4);
  font-size: var(--fs-sm);
}

.btn--lg {
  min-block-size: 3.5rem;
  padding-inline: var(--space-8);
  font-size: var(--fs-lg);
}

.btn--block { display: flex; inline-size: 100%; }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}

@media (max-width: 479px) {
  .btn-row > .btn { inline-size: 100%; }
}

/* ==========================================================================
   TAG / BADGE
   ========================================================================== */
.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-block-size: 1.75rem;
  padding-block: var(--space-1);
  padding-inline: var(--space-3);
  border-radius: var(--radius-pill);
  background-color: var(--surface-tint);
  border: 1px solid var(--cyan-200);
  color: var(--text-accent);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  line-height: 1.4;
}

.tag--neutral {
  background-color: var(--surface-sunken);
  border-color: var(--border-card);
  color: var(--text-muted);
}

.tag--inverse {
  background-color: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.24);
  color: var(--text-inverse);
}

.tag--placeholder {
  background-color: transparent;
  border-style: dashed;
  border-color: var(--border-card);
  color: var(--text-muted);
}

/* ==========================================================================
   SITE HEADER + NAVIGATION
   ========================================================================== */
.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-header);
  background-color: var(--surface);
  border-block-end: 1px solid var(--border-decorative);
  transition: box-shadow var(--dur-base) var(--ease-out);
}

.site-header.is-stuck { box-shadow: var(--shadow-sm); }

.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  min-block-size: var(--header-h-compact);
  max-inline-size: var(--container-wide);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

@media (min-width: 1024px) {
  .site-header__inner { min-block-size: var(--header-h); }
}

.site-header__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  flex: none;
  text-decoration: none;
  color: var(--text-strong);
}

.site-header__logo {
  block-size: 2rem;
  inline-size: auto;
}

@media (min-width: 1024px) {
  .site-header__logo { block-size: 2.5rem; }
}

.site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-inline-start: auto; /* mirrors: sits at the trailing edge in both dirs */
  min-inline-size: 0;
}

/* The header CTA does not fit alongside the brand, the language switcher and
   the menu button on a phone — three competing controls in 375px is a Hick's
   law problem as well as an overflow one. Below 768px the quote CTA lives in
   the drawer as a full-width button instead, which is also a larger target. */
.site-header__cta { display: none; }

@media (min-width: 768px) {
  .site-header__cta { display: inline-flex; }
}

/* Below 400px the switcher collapses to its two-letter code. The full
   language name stays in the accessible name via the visually-hidden span,
   so nothing is lost to a screen reader. */
.lang-switch__short { display: none; }

@media (max-width: 399px) {
  .site-header__inner { gap: var(--space-3); }

  /* The wordmark is hidden VISUALLY, not with display:none.
     The mark itself is decorative (alt=""), so the wordmark text IS the brand
     link's accessible name. display:none removes it from the accessibility
     tree and leaves the link nameless — a WCAG 2.4.4 failure that only appears
     below 400px, which is exactly where it will not be noticed.
     Hiding it this way means the name survives without relying on an author
     remembering to add a visually-hidden span to every instance. */
  .site-header__brand-text {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .lang-switch { padding-inline: var(--space-3); }
  .lang-switch .lang-switch__label { display: none; }
  .lang-switch .lang-switch__short { display: inline; }
}

/* ------ Primary nav ------------------------------------------------------ */
.nav { display: none; }

@media (min-width: 1024px) {
  .nav {
    display: block;
    /* margin-inline-start on the nav, not margin-left — mirrors for free */
    margin-inline-start: var(--space-4);
  }
}

.nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav__link,
.nav__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-block-size: 2.75rem;
  padding-inline: var(--space-3);
  border-radius: var(--radius-md);
  color: var(--text);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}

.nav__link:hover,
.nav__trigger:hover {
  color: var(--text-accent);
  background-color: var(--surface-tint);
}

.nav__link[aria-current="page"] {
  color: var(--text-accent);
  box-shadow: inset 0 -2px 0 var(--action-fill);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}

.nav__trigger .icon {
  inline-size: 0.85em;
  block-size: 0.85em;
  transition: transform var(--dur-base) var(--ease-out);
}

/* Chevron rotates on open. It is a directional icon but points DOWN, so it
   does not mirror — only rotates. */
.nav__item[data-open="true"] .nav__trigger .icon { transform: rotate(180deg); }

.nav__item[data-open="true"] .nav__trigger {
  color: var(--text-accent);
  background-color: var(--surface-tint);
}

/* ------ Mega-menu -------------------------------------------------------- */
.nav__item--mega { position: static; }

.megamenu {
  position: absolute;
  inset-inline: 0;
  inset-block-start: 100%;
  z-index: var(--z-megamenu);
  background-color: var(--surface);
  border-block-start: 1px solid var(--border-decorative);
  border-block-end: 1px solid var(--border-decorative);
  box-shadow: var(--shadow-menu);
  /* Closed state. `visibility` (not display:none) so the panel is removed
     from the tab order but can still transition. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition:
    opacity var(--dur-base) var(--ease-out),
    transform var(--dur-base) var(--ease-out),
    visibility 0s linear var(--dur-base);
}

.nav__item[data-open="true"] > .megamenu,
/* No-JS / no-pointer fallback: focus-within alone opens it, so the menu is
   fully keyboard-operable even if main.js fails to load. */
.no-js .nav__item--mega:hover > .megamenu,
.no-js .nav__item--mega:focus-within > .megamenu {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}

.megamenu__inner {
  display: grid;
  gap: var(--grid-gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  max-inline-size: var(--container-wide);
  margin-inline: auto;
  padding: var(--space-10) var(--gutter);
}

.megamenu__col { min-inline-size: 0; }

.megamenu__heading {
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: var(--track-caps);
  color: var(--text-muted);
  padding-block-end: var(--space-3);
  margin-block-end: var(--space-3);
  border-block-end: 1px solid var(--border-decorative);
}

.megamenu__heading:lang(ar) { text-transform: none; letter-spacing: 0; font-size: var(--fs-xs); }

.megamenu__list { list-style: none; margin: 0; padding: 0; }

.megamenu__link {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding-block: var(--space-2);
  padding-inline: var(--space-3);
  margin-inline: calc(var(--space-3) * -1);
  border-radius: var(--radius-md);
  color: var(--text);
  text-decoration: none;
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.megamenu__link:hover {
  background-color: var(--surface-tint);
  color: var(--text-accent);
}

.megamenu__link .icon {
  color: var(--action-fill);
  margin-block-start: 0.1em;
}

.megamenu__link-title { font-weight: var(--fw-medium); display: block; }
.megamenu__link-desc { color: var(--text-muted); font-size: var(--fs-xs); display: block; }

.megamenu__feature {
  padding: var(--space-6);
  background-color: var(--surface-tint);
  border-radius: var(--radius-lg);
}

/* ------ Language switcher ------------------------------------------------
   It is a real <a href> to the mirrored URL, so it works with JS off.
   main.js only adds the localStorage persistence and the dir/lang swap for
   the single-document styleguide case.
   ------------------------------------------------------------------------- */
.lang-switch {
  display: inline-flex;
  align-items: center;
  /* The styleguide specimen faked this with an inline margin on the icon.
     A real gap, because the header is now reproduced on 36 pages and an
     inline style is 36 chances to drop it. */
  gap: var(--space-2);
  min-block-size: 2.75rem;
  padding-inline: var(--space-4);
  border: 1px solid var(--border-control);
  border-radius: var(--radius-md);
  color: var(--text-strong);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  text-decoration: none;
  white-space: nowrap;
  transition: border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}

.lang-switch:hover {
  border-color: var(--action-edge);
  background-color: var(--surface-tint);
  color: var(--text-accent);
}

/* The target-language label is always set in its OWN script and direction —
   "العربية" must render RTL even inside an English page, and "English" must
   render LTR inside an Arabic page. */
.lang-switch__label {
  unicode-bidi: isolate;
}

.lang-switch__label:lang(ar) {
  font-family: var(--font-arabic);
  direction: rtl;
}

.lang-switch__label:lang(en) {
  font-family: var(--font-latin);
  direction: ltr;
}

/* ------ Mobile nav ------------------------------------------------------- */
.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  border: 1px solid var(--border-control);
  border-radius: var(--radius-md);
  color: var(--text-strong);
}

@media (min-width: 1024px) { .nav-toggle { display: none; } }

.nav-toggle:hover { border-color: var(--action-edge); color: var(--text-accent); }

/* With JS off the drawer is a plain expanded list in the flow — navigable,
   just not collapsible. With JS on it becomes a panel. */
.mobile-nav {
  border-block-start: 1px solid var(--border-decorative);
  background-color: var(--surface);
}

.js .mobile-nav {
  position: fixed;
  inset-block-start: var(--header-h-compact);
  inset-inline: 0;
  inset-block-end: 0;
  z-index: var(--z-drawer);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-6) var(--gutter) var(--space-16);
  transform: translateX(calc(100% * var(--flip)));
  visibility: hidden;
  transition:
    transform var(--dur-slow) var(--ease-out),
    visibility 0s linear var(--dur-slow);
}

.js .mobile-nav[data-open="true"] {
  transform: none;
  visibility: visible;
  transition-delay: 0s;
}

@media (min-width: 1024px) {
  .mobile-nav { display: none; }
}

.mobile-nav__list { list-style: none; margin: 0; padding: 0; }

.mobile-nav__item + .mobile-nav__item {
  border-block-start: 1px solid var(--border-decorative);
}

.mobile-nav__link,
.mobile-nav__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  inline-size: 100%;
  min-block-size: 3.25rem;
  padding-block: var(--space-3);
  color: var(--text-strong);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  text-decoration: none;
  text-align: start;
}

.mobile-nav__trigger .icon { transition: transform var(--dur-base) var(--ease-out); }
.mobile-nav__item[data-open="true"] .mobile-nav__trigger .icon { transform: rotate(180deg); }

.mobile-nav__sub {
  list-style: none;
  margin: 0;
  padding-block-end: var(--space-4);
  padding-inline-start: var(--space-4);
  border-inline-start: 2px solid var(--cyan-200);
}

.js .mobile-nav__sub[hidden] { display: none; }

.mobile-nav__sub a {
  display: block;
  padding-block: var(--space-2);
  color: var(--text);
  text-decoration: none;
  font-size: var(--fs-base);
}

.mobile-nav__sub a:hover { color: var(--text-accent); }

.nav-scrim {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background-color: rgba(14, 40, 48, 0.5);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-base) var(--ease-out),
              visibility 0s linear var(--dur-base);
}

.nav-scrim[data-open="true"] {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

/* ==========================================================================
   BREADCRUMB
   Separator is a directional glyph: it must mirror.
   ========================================================================== */
.breadcrumb {
  padding-block: var(--space-4);
  font-size: var(--fs-sm);
}

.breadcrumb__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.breadcrumb__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-muted);
}

.breadcrumb__item:not(:last-child)::after {
  content: "";
  inline-size: 0.5rem;
  block-size: 0.5rem;
  /* PHYSICAL borders here, deliberately — one of the very few places in this
     codebase where that is correct.
     A logical `border-inline-end` mirrors the shape by itself; adding scaleX
     mirrors it a second time and the two cancel into a chevron pointing DOWN.
     The shape must be direction-independent so that the single scaleX does
     all the mirroring. */
  border-top: 1.5px solid var(--neutral-400);
  border-right: 1.5px solid var(--neutral-400);
  /* Order matters: transforms apply right-to-left, so this rotates FIRST and
     then mirrors. `rotate(45deg) scaleX(-1)` — the other order — mirrors first
     and yields a chevron pointing UP. */
  transform: scaleX(var(--flip)) rotate(45deg);
  transform-origin: center;
  flex: none;
}

.breadcrumb__item a { color: var(--text-muted); text-decoration: none; }
.breadcrumb__item a:hover { color: var(--text-accent); text-decoration: underline; }
.breadcrumb__item[aria-current="page"] { color: var(--text-strong); font-weight: var(--fw-medium); }

/* ==========================================================================
   HERO
   Full-bleed, dark ground, copy aligned to the reading edge (left in LTR,
   right in RTL) via text-align:start and a logical grid — no [dir] override.
   ========================================================================== */
.hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-block-size: clamp(30rem, 22rem + 26vw, 42rem);
  background-color: var(--ink);
  color: var(--text-inverse);
  overflow: hidden;
  isolation: isolate;
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
}

.hero__media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  /* The hero ground is composed with its quiet half on the reading edge, so
     the headline never sits over brand cyan. That composition has to swap
     sides in Arabic. It is purely abstract geometry — no pictogram, no text,
     nothing with real-world meaning — so mirroring it is correct, and a CSS
     flip costs nothing where a second file would cost another download.
     This is exception 1 in DESIGN-SYSTEM.md §3: transforms have no logical
     form, so --flip does the work. Directional ARTWORK that contains
     pictograms is handled the other way, by shipping a *-rtl.svg twin. */
  transform: scaleX(var(--flip));
}

/* The overlay is a functional legibility scrim, not decoration. It runs from
   the reading edge so the copy side is darkest.
   NOTE: linear-gradient has NO logical direction keywords — `to inline-end`
   is invalid CSS and silently drops the whole declaration. Gradient direction
   is therefore the THIRD and last documented [dir="rtl"] exception in this
   codebase, alongside transforms and background-position. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(
      to bottom,
      rgba(14, 40, 48, 0.35) 0%,
      rgba(14, 40, 48, 0.72) 55%,
      rgba(14, 40, 48, 0.92) 100%
    );
}

@media (min-width: 768px) {
  .hero { align-items: center; }

  .hero__scrim {
    background:
      linear-gradient(
        to bottom,
        rgba(14, 40, 48, 0.28) 0%,
        rgba(14, 40, 48, 0.62) 100%
      ),
      linear-gradient(
        to right,
        rgba(14, 40, 48, 0.92) 0%,
        rgba(14, 40, 48, 0.72) 42%,
        rgba(14, 40, 48, 0.18) 78%
      );
  }

  [dir="rtl"] .hero__scrim {
    background:
      linear-gradient(
        to bottom,
        rgba(14, 40, 48, 0.28) 0%,
        rgba(14, 40, 48, 0.62) 100%
      ),
      linear-gradient(
        to left,
        rgba(14, 40, 48, 0.92) 0%,
        rgba(14, 40, 48, 0.72) 42%,
        rgba(14, 40, 48, 0.18) 78%
      );
  }
}

.hero__inner {
  inline-size: 100%;
  max-inline-size: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
  padding-block: var(--band-y);
}

.hero__content {
  max-inline-size: 40rem;
  text-align: start;
}

.hero__title {
  font-size: var(--fs-5xl);
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  color: var(--text-inverse);
  margin-block-end: var(--space-5);
}

.hero__lead {
  font-size: var(--fs-lg);
  line-height: var(--lh-relaxed);
  color: var(--text-inverse-muted);
  max-inline-size: var(--measure-narrow);
  margin-block-end: var(--space-8);
}

/* Thin cyan rule on the reading edge — the one piece of brand chrome in the
   hero. Uses border-inline-start so it flips sides in Arabic. */
.hero__content--ruled {
  border-inline-start: 3px solid var(--action-fill);
  padding-inline-start: var(--space-6);
}

.hero--compact { min-block-size: clamp(18rem, 14rem + 14vw, 26rem); }
.hero--compact .hero__title { font-size: var(--fs-4xl); }

/* Page hero for interior pages — light ground, no photograph. */
.page-hero {
  background-color: var(--surface-alt);
  border-block-end: 1px solid var(--border-decorative);
  padding-block: var(--band-y-tight) var(--band-y);
}

.page-hero__title { margin-block: var(--space-3) var(--space-5); }

/* ==========================================================================
   BAND — the alternating full-width section
   ========================================================================== */
.band {
  padding-block: var(--band-y);
}

.band--tight { padding-block: var(--band-y-tight); }
.band--tall  { padding-block: var(--band-y-tall); }

.band--alt   { background-color: var(--surface-alt); }
.band--sunken { background-color: var(--surface-sunken); }
.band--tint  { background-color: var(--surface-tint); }

/* Dark sections REDEFINE the semantic text tokens rather than enumerating an
   override per component. Enumerating is how `.section-head__lead` shipped at
   2.8:1 on ink — it was simply missing from the list, and every future
   component would have had the same trap. Redefining the tokens means anything
   dropped into a dark section inherits correct inverse colour automatically.

   Surfaces are deliberately NOT redefined here: a component with its own
   background (card, chip, stat, table) needs an explicit `--ink` modifier,
   because inverting its surface silently would have a much wider blast radius.

   Applies to every dark ground in the system, not just .band--ink. */
.band--ink,
.cta-band,
.site-footer,
.hero,
.on-ink {
  --text: var(--text-inverse-muted);      /* 10.50 on ink */
  --text-strong: var(--text-inverse);     /* 15.39 on ink */
  --text-muted: var(--text-inverse-muted);/* 10.50 on ink */
  --text-accent: var(--text-inverse-accent);      /* 9.79 on ink */
  --text-accent-hover: var(--cyan-200);           /* 12.00 on ink */
  --text-display-accent: var(--text-inverse-accent);
  --border-decorative: var(--ink-line);
  --border-card: var(--ink-line);
  --border-control: var(--neutral-400);   /* 7.12 on ink — clears 1.4.11 */
  --focus-ring: var(--focus-ring-inverse);
}

.band--ink {
  background-color: var(--surface-inverse);
  color: var(--text-inverse-muted);
}

.band--ink h1, .band--ink h2, .band--ink h3,
.band--ink h4, .band--ink h5, .band--ink h6 { color: var(--text-inverse); }

.band--bordered { border-block-start: 1px solid var(--border-decorative); }

.section-head {
  max-inline-size: var(--measure);
  margin-block-end: var(--space-10);
}

.section-head--center {
  margin-inline: auto;
  text-align: center;
}

.section-head__title { margin-block-end: var(--space-4); }

.section-head__lead {
  font-size: var(--fs-lg);
  line-height: var(--lh-relaxed);
  color: var(--text-muted);
}

/* ==========================================================================
   PRODUCT FAMILY CARD GRID
   ========================================================================== */
.card-grid {
  display: grid;
  gap: var(--grid-gap);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr));
}

.card-grid--wide {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 24rem), 1fr));
}

.product-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background-color: var(--surface);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition:
    border-color var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out),
    transform var(--dur-base) var(--ease-out);
}

.product-card:hover {
  border-color: var(--action-edge);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

.product-card:focus-within {
  border-color: var(--action-edge);
  box-shadow: var(--shadow-md);
}

.product-card__media {
  --media-ratio: 4 / 3;
  border-radius: 0;
  border-block-end: 1px solid var(--border-decorative);
}

.product-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: var(--space-3);
  padding: var(--space-6);
}

.product-card__title {
  font-size: var(--fs-xl);
  line-height: var(--lh-snug);
  color: var(--text-strong);
}

/* Whole card is the click target; the <a> stretches. Keeps one tab stop per
   card instead of three, and satisfies Fitts's law on mobile. */
.product-card__title a {
  color: inherit;
  text-decoration: none;
}

.product-card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.product-card__desc {
  color: var(--text-muted);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
}

.product-card__meta {
  margin-block-start: auto;
  padding-block-start: var(--space-4);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-accent);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
}

.product-card__meta .icon { transform: scaleX(var(--flip)); }

.product-card--ink {
  background-color: var(--ink);
  border-color: var(--border-inverse);
  color: var(--text-inverse-muted);
}

.product-card--ink .product-card__title { color: var(--text-inverse); }
.product-card--ink .product-card__desc { color: var(--text-inverse-muted); }
.product-card--ink .product-card__meta { color: var(--text-inverse-accent); }

/* ==========================================================================
   KIT PAGE BLOCKS
   ========================================================================== */

/* ------ "Intended for" panel --------------------------------------------
   The qualifier block on every kit page. Cyan rule on the reading edge.
   ------------------------------------------------------------------------- */
.intended-panel {
  background-color: var(--surface-tint);
  border: 1px solid var(--border-decorative);
  border-radius: var(--radius-lg);
  padding: var(--space-8);
}

/* The panel earns its separation from ground tone, border and spacing. The brand
   accent lives on the label — small, letterspaced, cyan — not on the container edge.
   A thick coloured rule down one side of a card is the single most recognisable tell
   of a generated UI, and this panel repeats on all seven kit pages. */
.intended-panel__eyebrow {
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  color: var(--text-accent);
  margin-block-end: var(--space-2);
}

/* Caps and tracking are Latin-only — the same :not(:lang(ar)) scoping base.css
   uses everywhere. Arabic is cursive; letter-spacing breaks the joins and
   text-transform does nothing. */
.intended-panel__eyebrow:not(:lang(ar)) {
  text-transform: uppercase;
  letter-spacing: var(--track-caps);
}

.intended-panel__title {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--fs-xl);
  color: var(--text-strong);
  margin-block-end: var(--space-4);
}

.intended-panel__title .icon { color: var(--action-fill-active); }

.intended-panel__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}

@media (min-width: 768px) {
  .intended-panel__list { grid-template-columns: 1fr 1fr; gap: var(--space-3) var(--space-8); }
}

/* Flow layout with a floated icon, not a flex row — see the .callout note.
   A flex container promotes every text run between element children to its own
   item, so `gap` lands mid-sentence and each run becomes a rigid column. The
   copy decks mandate <bdi> around Latin runs in Arabic prose, so any new string
   would shatter this the moment it arrives. Float keeps the icon on the reading
   edge in both directions and leaves the text in normal flow. */
.intended-panel__list li {
  display: block;
  font-size: var(--fs-base);
  line-height: var(--lh-snug);
}

.intended-panel__list li::after { content: ""; display: block; clear: both; }

.intended-panel__list .icon {
  float: inline-start;
  margin-inline-end: var(--space-3);
  color: var(--action-fill-active);
  margin-block-start: 0.15em;
}

/* ------ Contents list ----------------------------------------------------
   The kit contents block. Each row is icon + name + why-it-matters.
   ------------------------------------------------------------------------- */
.contents-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-px);
  background-color: var(--border-decorative);
  border: 1px solid var(--border-decorative);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

@media (min-width: 900px) {
  .contents-list { grid-template-columns: 1fr 1fr; }
}

/* Flow layout with a floated icon, not a flex row — see the .callout note.
   A flex container promotes every text run between element children to its own
   item, so `gap` lands mid-sentence and each run becomes a rigid column. The
   copy decks mandate <bdi> around Latin runs in Arabic prose, so any new string
   would shatter this the moment it arrives. Float keeps the icon on the reading
   edge in both directions and leaves the text in normal flow. */
.contents-list__item {
  display: block;
  padding: var(--space-6);
  background-color: var(--surface);
}

.contents-list__item::after { content: ""; display: block; clear: both; }

.contents-list__icon {
  float: inline-start;
  margin-inline-end: var(--space-4);
  display: grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  border-radius: var(--radius-md);
  background-color: var(--surface-tint);
  color: var(--action-fill-active);
}

.contents-list__icon .icon { inline-size: 1.5rem; block-size: 1.5rem; }

.contents-list__body { min-inline-size: 0; }

.contents-list__name {
  display: block;
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
  margin-block-end: var(--space-1);
}

.contents-list__why {
  display: block;
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  color: var(--text-muted);
}

/* ------ Safety callout ---------------------------------------------------
   The ONLY place hazard-signal colour is permitted outside iconography.
   Never used for marketing emphasis.
   ------------------------------------------------------------------------- */
/* NOT flex. A flex container promotes every element child AND every text run
   between element children to its own flex item, so `gap` is injected
   mid-sentence and each run becomes a rigid column. Both copy decks mandate
   <bdi> around Latin runs in Arabic prose, so a flex .callout shatters the
   moment it receives a real deck string — worst on Arabic, where <bdi> appears
   most often, and observed stacking one word per line.

   Flow layout with a floated icon instead: arbitrary inline content wraps
   normally, and because the float only occupies space when an icon is present,
   the 34 icon-less callouts need no padding reservation. */
.callout {
  display: block;
  padding: var(--space-5) var(--space-6);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-card);
  background-color: var(--surface-sunken);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
}

/* Logical float, so it sits on the reading edge in both directions. */
.callout__icon {
  float: inline-start;
  margin-inline-end: var(--space-4);
  margin-block-start: 0.1em;
}

/* A float is out of flow, so it can overhang a short callout's bottom border.
   Contain it without display:flow-root's side effects on the float itself. */
.callout::after {
  content: "";
  display: block;
  clear: both;
}
.callout__title { display: block; font-weight: var(--fw-semibold); color: var(--text-strong); margin-block-end: var(--space-1); }

.callout--caution {
  background-color: var(--signal-caution-bg);
  border-color: var(--signal-caution-mark);
  color: var(--text);
}

.callout--caution .callout__icon,
.callout--caution .callout__title { color: var(--signal-caution); }

.callout--danger {
  background-color: var(--signal-danger-bg);
  border-color: var(--signal-danger);
}

.callout--danger .callout__icon,
.callout--danger .callout__title { color: var(--signal-danger); }

.callout--placeholder {
  border-style: dashed;
  background-color: var(--surface);
  color: var(--text-muted);
}

/* ==========================================================================
   RESPONSE METHODOLOGY — the 7-step sequence
   Signature component. A numbered vertical spine with a continuous rail and
   a node per step; on wide screens the step body splits into a two-column
   sub-grid so it reads as a procedure specification rather than a blog list.

   The rail sits on the reading edge via inset-inline-start, so it moves from
   the left in English to the right in Arabic without a single [dir] override.
   ========================================================================== */
.method {
  --node: 3rem;
  --rail: 2px;
  counter-reset: method;
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
}

@media (min-width: 768px) {
  .method { --node: 3.5rem; }
}

/* The continuous rail. Drawn behind the nodes, inset by half a node so it
   runs through their centres. Stops short at top and bottom so it does not
   dangle past the first and last node. */
.method::before {
  content: "";
  position: absolute;
  inset-block: calc(var(--node) / 2) calc(var(--node) / 2);
  inset-inline-start: calc(var(--node) / 2 - var(--rail) / 2);
  inline-size: var(--rail);
  /* The rail is what makes this read as a connected procedure rather than a
     numbered list — it is load-bearing, not decoration, so it uses the full
     brand cyan rather than a tint. cyan-200 tested as effectively invisible
     against white (1.31:1). */
  background-color: var(--action-fill);
}

.method__step {
  position: relative;
  display: grid;
  grid-template-columns: var(--node) 1fr;
  gap: var(--space-5);
  padding-block-end: var(--space-8);
}

.method__step:last-child { padding-block-end: 0; }

/* The numbered node. Numerals are wrapped .num in markup so they stay LTR
   and tabular in Arabic. */
.method__num {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  inline-size: var(--node);
  block-size: var(--node);
  border-radius: 50%;
  background-color: var(--surface);
  border: var(--rail) solid var(--action-fill);
  color: var(--text-accent);
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  transition: background-color var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out);
}

.method__step:hover .method__num,
.method__step:focus-within .method__num {
  background-color: var(--action-fill);
  color: var(--action-on-fill);
}

.method__body { min-inline-size: 0; padding-block-start: var(--space-1); }

.method__title {
  font-size: var(--fs-xl);
  line-height: var(--lh-snug);
  color: var(--text-strong);
  margin-block-end: var(--space-2);
}

.method__desc {
  color: var(--text-muted);
  max-inline-size: var(--measure);
}

@media (min-width: 1024px) {
  .method__step {
    grid-template-columns: var(--node) minmax(0, 18rem) minmax(0, 1fr);
    gap: var(--space-6) var(--space-8);
    align-items: start;
    padding-block-end: var(--space-10);
  }

  .method__body { display: contents; }
  .method__title { margin-block-end: 0; padding-block-start: var(--space-2); }
  .method__desc { padding-block-start: var(--space-2); }
}

/* On ink */
.band--ink .method::before { background-color: var(--cyan-700); }

.band--ink .method__num {
  background-color: var(--ink);
  border-color: var(--action-fill);
  color: var(--text-inverse-accent);
}

.band--ink .method__step:hover .method__num {
  background-color: var(--action-fill);
  color: var(--action-on-fill);
}

.band--ink .method__title { color: var(--text-inverse); }
.band--ink .method__desc { color: var(--text-inverse-muted); }

/* ------ Compact variant --------------------------------------------------
   For the homepage summary: seven chips in a wrapping row, connected by a
   rule. Reads at a glance; links through to the full methodology page.
   ------------------------------------------------------------------------- */
.method-compact {
  counter-reset: mc;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-5);
  /* Explicit column counts, not auto-fit. There are exactly seven steps, and
     auto-fit resolves to five at desktop width — leaving an orphan row of two
     with dangling connector rules. Seven is a prime, so the only clean
     desktop layout is one row of seven. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 560px) {
  .method-compact { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (min-width: 1080px) {
  .method-compact { grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-4); }
}

.method-compact__step {
  position: relative;
  padding-block-start: var(--space-6);
  border-block-start: 2px solid var(--cyan-200);
}

.method-compact__step::before {
  content: "";
  position: absolute;
  inset-block-start: -0.4375rem;
  inset-inline-start: 0;
  inline-size: 0.75rem;
  block-size: 0.75rem;
  border-radius: 50%;
  background-color: var(--action-fill);
}

.method-compact__num {
  display: block;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--text-accent);
  margin-block-end: var(--space-2);
}

.method-compact__title {
  display: block;
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--text-strong);
}

.band--ink .method-compact__step { border-block-start-color: var(--ink-line); }
.band--ink .method-compact__num { color: var(--text-inverse-accent); }
.band--ink .method-compact__title { color: var(--text-inverse); }

/* ==========================================================================
   DATA TABLE
   Must carry the HAZ TAB dilution guide: 5 columns, 7 data rows, a footnote
   marker, and stay readable on a 320px phone.

   Strategy: the table scrolls horizontally inside .table-wrap. The BODY never
   scrolls horizontally. The first column is sticky so the row label stays
   visible while the tablet-size columns scroll under it, and .table-wrap
   carries tabindex="0" + role="region" + aria-label so keyboard users can
   actually reach and scroll it (WCAG 2.1.1).
   ========================================================================== */
.table-wrap {
  position: relative;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
  /* Edge fade cueing more content. background-position must be mirrored in
     RTL — one of the two documented [dir] exceptions. */
  background-image:
    linear-gradient(to right, var(--surface) 30%, rgba(255, 255, 255, 0)),
    linear-gradient(to right, rgba(255, 255, 255, 0), var(--surface) 70%),
    linear-gradient(to right, rgba(14, 40, 48, 0.10), rgba(14, 40, 48, 0)),
    linear-gradient(to left, rgba(14, 40, 48, 0.10), rgba(14, 40, 48, 0));
  background-position: 0 0, 100% 0, 0 0, 100% 0;
  background-repeat: no-repeat;
  background-size: 3rem 100%, 3rem 100%, 0.875rem 100%, 0.875rem 100%;
  background-attachment: local, local, scroll, scroll;
}

/* RTL: the scroll origin moves to the trailing edge, so the fade stops swap.
   Documented [dir] exception #2 — background-position/gradient direction. */
[dir="rtl"] .table-wrap {
  background-image:
    linear-gradient(to left, var(--surface) 30%, rgba(255, 255, 255, 0)),
    linear-gradient(to left, rgba(255, 255, 255, 0), var(--surface) 70%),
    linear-gradient(to left, rgba(14, 40, 48, 0.10), rgba(14, 40, 48, 0)),
    linear-gradient(to right, rgba(14, 40, 48, 0.10), rgba(14, 40, 48, 0));
  background-position: 100% 0, 0 0, 100% 0, 0 0;
}

.table-wrap:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.data-table {
  inline-size: 100%;
  min-inline-size: 40rem;
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  text-align: start;
}

.data-table caption {
  caption-side: top;
  padding: var(--space-5) var(--space-6);
  text-align: start;
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
  border-block-end: 1px solid var(--border-decorative);
}

.data-table th,
.data-table td {
  padding: var(--space-4) var(--space-5);
  text-align: start;
  vertical-align: top;
  border-block-end: 1px solid var(--border-decorative);
}

.data-table thead th {
  position: sticky;
  inset-block-start: 0;
  z-index: 2;
  background-color: var(--surface-sunken);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  /* NOT uppercase. These headers carry unit symbols and chemical names, where
     case is meaning: `1.67 g` uppercases to `1.67 G` (giga, not grams) and
     `NaDCC` to `NADCC`. CSS cannot tell a label from a quantity, so the
     transform is off and emphasis comes from weight, size and colour instead. */
  letter-spacing: var(--track-caps);
  color: var(--text-muted);
  white-space: nowrap;
  border-block-end: 1px solid var(--border-card);
}

.data-table thead th:lang(ar) { letter-spacing: 0; font-size: var(--fs-xs); }

.data-table tbody tr:last-child th,
.data-table tbody tr:last-child td { border-block-end: 0; }

.data-table tbody tr:hover td,
.data-table tbody tr:hover th { background-color: var(--surface-tint); }

/* Row header — sticky on the reading edge so the row label survives the
   horizontal scroll. */
.data-table tbody th[scope="row"] {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
  background-color: var(--surface);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
  white-space: nowrap;
  /* Hairline that reads as the frozen-column edge */
  box-shadow: inset calc(-1px * var(--flip)) 0 0 var(--border-decorative);
}

/* Numeric / code cells: LTR, isolated, tabular. Applied to the <td> because
   every value in the dilution guide is a Latin-numeral measurement that will
   reverse inside Arabic without it. */
.data-table td.is-num,
.data-table th.is-num {
  direction: ltr;
  unicode-bidi: isolate;
  text-align: start;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  white-space: nowrap;
}

/* In RTL the numeric column still reads LTR internally but should align to
   the row's reading edge. `text-align: start` inside a `direction: ltr` cell
   resolves to left, so re-anchor it. */
[dir="rtl"] .data-table td.is-num,
[dir="rtl"] .data-table th.is-num { text-align: end; }

.data-table .is-strong { color: var(--text-strong); font-weight: var(--fw-semibold); }

.data-table__footnote {
  padding: var(--space-4) var(--space-6);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  background-color: var(--surface-sunken);
  border-block-start: 1px solid var(--border-decorative);
}

/* The scroll affordance, announced to screen readers too. */
.table-hint {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-block-end: var(--space-3);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

@media (min-width: 900px) {
  .table-hint { display: none; }
}

/* ==========================================================================
   STAT / FEATURE BAND
   NOTE: HAZ MED has no approved statistics. Ship .stat--placeholder until the
   client supplies verified figures. Do not invent numbers for this component.
   ========================================================================== */
.stat-band {
  display: grid;
  gap: var(--space-px);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  background-color: var(--border-decorative);
  border-block: 1px solid var(--border-decorative);
}

.stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-8) var(--space-6);
  background-color: var(--surface);
  text-align: start;
}

.stat__value {
  font-size: var(--fs-3xl);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  color: var(--text-display-accent); /* 3.83 — permitted, this is display size */
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
}

[dir="rtl"] .stat__value { text-align: end; }

.stat__label {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  line-height: var(--lh-snug);
}

.stat--placeholder .stat__value {
  color: var(--text-muted);
  font-size: var(--fs-xl);
  border: 1px dashed var(--border-card);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  align-self: start;
}

.band--ink .stat-band { background-color: var(--border-inverse); border-color: var(--border-inverse); }
.band--ink .stat { background-color: var(--ink); }
.band--ink .stat__value { color: var(--text-inverse-accent); }
.band--ink .stat__label { color: var(--text-inverse-muted); }

/* ------ Feature band (media + copy, alternating side) --------------------
   `direction: rtl` on the wrapper flips the visual column order without
   touching the source order — so the copy is still read first by a screen
   reader. This is the correct use of a direction override: it is scoped to
   layout and the inner content re-asserts its own direction.
   ------------------------------------------------------------------------- */
.feature-band {
  display: grid;
  gap: var(--grid-gap);
  align-items: center;
}

@media (min-width: 900px) {
  .feature-band {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    gap: clamp(2rem, 1rem + 5vw, 5rem);
  }

  /* Media leading instead of trailing. Uses grid ordering, not direction, so
     it is safe in both LTR and RTL. */
  .feature-band--reverse .feature-band__media { order: -1; }
}

.feature-band__body { min-inline-size: 0; }
.feature-band__media { min-inline-size: 0; }

.feature-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-5);
}

/* Flow layout with a floated icon, not a flex row — see the .callout note.
   A flex container promotes every text run between element children to its own
   item, so `gap` lands mid-sentence and each run becomes a rigid column. The
   copy decks mandate <bdi> around Latin runs in Arabic prose, so any new string
   would shatter this the moment it arrives. Float keeps the icon on the reading
   edge in both directions and leaves the text in normal flow. */
.feature-list__item {
  display: block;
}

.feature-list__item::after { content: ""; display: block; clear: both; }

.feature-list__icon {
  float: inline-start;
  margin-inline-end: var(--space-4);
  display: grid;
  place-items: center;
  inline-size: 2.5rem;
  block-size: 2.5rem;
  border-radius: var(--radius-md);
  background-color: var(--surface-tint);
  color: var(--action-fill-active);
}

.band--ink .feature-list__icon {
  background-color: rgba(9, 171, 197, 0.16);
  color: var(--text-inverse-accent);
}

/* These are <span>s inside a <div>. Without display:block they render inline
   and the title runs straight into the description with no space —
   "Bilingual by designArabic and English across kit design". */
.feature-list__title {
  display: block;
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
  margin-block-end: var(--space-1);
}

.band--ink .feature-list__title { color: var(--text-inverse); }

.feature-list__desc { display: block; color: var(--text-muted); font-size: var(--fs-base); line-height: var(--lh-body); }
.band--ink .feature-list__desc { color: var(--text-inverse-muted); }

/* ==========================================================================
   CTA BAND
   ========================================================================== */
.cta-band {
  background-color: var(--surface-inverse);
  color: var(--text-inverse-muted);
  padding-block: var(--band-y);
  position: relative;
  overflow: hidden;
}

/* Single restrained brand device: a large low-opacity diamond echoing the
   logo mark, anchored to the trailing edge. Decorative, aria-hidden in
   markup, and it does not read as a gradient. */
.cta-band::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-end: -6rem;
  inline-size: 22rem;
  block-size: 22rem;
  border: 2px solid rgba(9, 171, 197, 0.18);
  border-radius: 3rem;
  transform: translateY(-50%) rotate(45deg);
  pointer-events: none;
}

@media (max-width: 767px) { .cta-band::after { display: none; } }

.cta-band__inner {
  position: relative;
  z-index: 1;
  display: grid;
  gap: var(--space-8);
  align-items: center;
}

@media (min-width: 900px) {
  .cta-band__inner { grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-12); }
}

.cta-band__title { color: var(--text-inverse); margin-block-end: var(--space-3); }
.cta-band__lead { color: var(--text-inverse-muted); max-inline-size: var(--measure); }

/* ==========================================================================
   QUOTE REQUEST FORM
   Single column at every width. Every field is justified — this is the
   primary conversion and each added field costs completion.
   ========================================================================== */
.form {
  max-inline-size: 38rem;
}

.form--wide { max-inline-size: 52rem; }

.form__grid {
  display: grid;
  gap: var(--space-5);
}

@media (min-width: 640px) {
  .form--wide .form__grid { grid-template-columns: 1fr 1fr; }
  .form--wide .form__field--full { grid-column: 1 / -1; }
}

.form__fieldset + .form__fieldset { margin-block-start: var(--space-10); }

.form__legend {
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
  padding-block-end: var(--space-2);
  margin-block-end: var(--space-5);
  border-block-end: 1px solid var(--border-decorative);
  inline-size: 100%;
}

.form__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-inline-size: 0;
}

.form__label {
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--text-strong);
}

.form__req {
  color: var(--signal-danger);
  margin-inline-start: var(--space-1);
}

.form__hint {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.form__control {
  inline-size: 100%;
  min-block-size: 3rem;
  padding-block: var(--space-3);
  padding-inline: var(--space-4);
  /* border-control is 3.51:1 — WCAG 1.4.11 for a form boundary. Do not
     downgrade this to a decorative grey. */
  border: 1px solid var(--border-control);
  border-radius: var(--radius-md);
  background-color: var(--surface);
  color: var(--text-strong);
  /* 16px minimum prevents the iOS zoom-on-focus jump. */
  font-size: max(1rem, var(--fs-base));
  line-height: var(--lh-snug);
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}

.form__control::placeholder { color: var(--neutral-500); }

.form__control:hover { border-color: var(--neutral-600); }

.form__control:focus {
  outline: none;
  border-color: var(--action-edge);
  box-shadow: 0 0 0 var(--focus-width) rgba(12, 143, 164, 0.28);
}

textarea.form__control {
  min-block-size: 8rem;
  resize: vertical;
  line-height: var(--lh-body);
}

/* Native select chevron. background-position must be mirrored — the second
   documented [dir] exception. */
select.form__control {
  appearance: none;
  padding-inline-end: var(--space-10);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%23616B6B'%3E%3Cpath d='M5.5 7.5 10 12l4.5-4.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: 1.25rem;
  background-position: right var(--space-4) center;
}

[dir="rtl"] select.form__control {
  background-position: left var(--space-4) center;
}

/* Validation. State is never carried by colour alone: the field gets a
   border change, an icon, and a text message tied by aria-describedby. */
.form__control[aria-invalid="true"] {
  border-color: var(--signal-danger);
  background-color: var(--signal-danger-bg);
}

.form__control[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 var(--focus-width) rgba(179, 38, 30, 0.24);
}

.form__error {
  display: none;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--signal-danger);
}

.form__error[data-visible="true"] { display: flex; }

.form__error .icon { flex: none; margin-block-start: 0.12em; }

/* Checkbox / radio */
.form__check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: var(--fs-base);
  line-height: var(--lh-snug);
  cursor: pointer;
}

.form__check input {
  flex: none;
  inline-size: 1.25rem;
  block-size: 1.25rem;
  margin-block-start: 0.15em;
  accent-color: var(--action-fill-active);
}

.form__checks {
  display: grid;
  gap: var(--space-3);
}

@media (min-width: 640px) {
  .form__checks--2 { grid-template-columns: 1fr 1fr; }
}

.form__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin-block-start: var(--space-8);
  padding-block-start: var(--space-6);
  border-block-start: 1px solid var(--border-decorative);
}

/* Honeypot. Off-screen, not display:none — some bots skip hidden fields. */
.form__trap {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Submission status — a live region. */
.form__status {
  margin-block-start: var(--space-5);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  font-size: var(--fs-base);
  display: none;
}

.form__status[data-state="error"] {
  display: block;
  background-color: var(--signal-danger-bg);
  border: 1px solid var(--signal-danger);
  color: var(--signal-danger);
}

.form__status[data-state="ok"] {
  display: block;
  background-color: var(--signal-ok-bg);
  border: 1px solid var(--signal-ok);
  color: var(--signal-ok);
}

/* ==========================================================================
   ACCORDION
   Built on <details>/<summary> so it opens, closes and is keyboard operable
   with zero JavaScript.
   ========================================================================== */
.accordion {
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background-color: var(--surface);
}

.accordion__item + .accordion__item {
  border-block-start: 1px solid var(--border-decorative);
}

.accordion__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-block-size: 3.5rem;
  padding: var(--space-4) var(--space-6);
  cursor: pointer;
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
  list-style: none;
}

.accordion__summary::-webkit-details-marker { display: none; }
.accordion__summary:hover { background-color: var(--surface-tint); color: var(--text-accent); }

.accordion__summary .icon {
  flex: none;
  color: var(--action-fill-active);
  transition: transform var(--dur-base) var(--ease-out);
}

.accordion__item[open] > .accordion__summary .icon { transform: rotate(180deg); }
.accordion__item[open] > .accordion__summary { color: var(--text-accent); }

.accordion__panel {
  padding: 0 var(--space-6) var(--space-6);
  color: var(--text-muted);
  max-inline-size: var(--measure);
}

/* ==========================================================================
   FOOTER
   ========================================================================== */
.site-footer {
  background-color: var(--surface-inverse);
  color: var(--text-inverse-muted);
  padding-block: var(--band-y) var(--space-8);
  font-size: var(--fs-sm);
}

.site-footer__grid {
  display: grid;
  gap: var(--space-10);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}

@media (min-width: 900px) {
  .site-footer__grid { grid-template-columns: 1.6fr repeat(3, 1fr); gap: var(--space-12); }
}

.site-footer__brand { max-inline-size: 24rem; }

.site-footer__logo {
  block-size: 2.75rem;
  inline-size: auto;
  margin-block-end: var(--space-5);
}

.site-footer__tagline {
  color: var(--text-inverse-muted);
  line-height: var(--lh-relaxed);
}

.site-footer__heading {
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: var(--track-caps);
  color: var(--text-inverse);
  margin-block-end: var(--space-4);
}

.site-footer__heading:lang(ar) { text-transform: none; letter-spacing: 0; font-size: var(--fs-xs); }

.site-footer__list { list-style: none; margin: 0; padding: 0; }
.site-footer__list li + li { margin-block-start: var(--space-3); }

.site-footer__list a {
  color: var(--text-inverse-muted);
  text-decoration: none;
}

.site-footer__list a:hover { color: var(--text-inverse-accent); text-decoration: underline; }

.site-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-8);
  margin-block-start: var(--space-12);
  padding-block-start: var(--space-6);
  border-block-start: 1px solid var(--border-inverse);
  font-size: var(--fs-xs);
  color: var(--neutral-400);
}

.site-footer__legal { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-inline-start: auto; }
.site-footer__legal a { color: var(--neutral-400); text-decoration: none; }
.site-footer__legal a:hover { color: var(--text-inverse); }

/* Contact details are UNKNOWN. Every one ships in this wrapper until the
   client supplies them; it is visually obvious and greppable. */
.placeholder-value {
  display: inline-block;
  padding-inline: var(--space-2);
  /* Explicit muted token, NOT `opacity`. The stub previously inherited
     --text-muted and then dimmed it to 85%, which computed to #798181 —
     3.98:1 on white, a WCAG 1.4.3 AA failure caught by axe on the contact
     panels. --text-muted is 5.49:1 on white, and dark bands redefine the
     token, so this reads correctly on ink without a second rule. */
  color: var(--text-muted);
  border: 1px dashed currentColor;
  border-radius: var(--radius-xs);
  font-size: 0.94em;
  unicode-bidi: isolate;
}

/* ==========================================================================
   404
   ========================================================================== */
.error-page {
  display: flex;
  align-items: center;
  min-block-size: 70vh;
  padding-block: var(--band-y);
  background-color: var(--surface-alt);
}

.error-page__inner {
  max-inline-size: 40rem;
  text-align: start;
}

.error-page__code {
  display: block;
  font-size: clamp(4rem, 2rem + 12vw, 8rem);
  font-weight: var(--fw-semibold);
  line-height: 1;
  letter-spacing: var(--track-display);
  /* cyan-200 tested at 1.24:1 on the alt band — it failed even the 3:1
     large-text threshold. The error code is content, not ornament. */
  color: var(--text-display-accent);   /* cyan-600, 3.63:1 at 128px */
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
  margin-block-end: var(--space-4);
}

[dir="rtl"] .error-page__code { text-align: end; }

.error-page__title { margin-block-end: var(--space-4); }
.error-page__lead { color: var(--text-muted); font-size: var(--fs-lg); margin-block-end: var(--space-8); }

.error-page__links {
  list-style: none;
  margin: var(--space-10) 0 0;
  padding: var(--space-6) 0 0;
  border-block-start: 1px solid var(--border-decorative);
  display: grid;
  gap: var(--space-3);
}

/* ==========================================================================
   SECTOR / RELATED chips
   ========================================================================== */
.chip-grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}

.chip {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-block-size: 3.5rem;
  padding: var(--space-3) var(--space-5);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-md);
  background-color: var(--surface);
  color: var(--text-strong);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}

.chip:hover { border-color: var(--action-edge); background-color: var(--surface-tint); color: var(--text-accent); }
.chip .icon { color: var(--action-fill-active); flex: none; }

.band--ink .chip {
  background-color: var(--ink-2);
  border-color: var(--border-inverse);
  color: var(--text-inverse);
}

.band--ink .chip:hover { border-color: var(--action-fill); background-color: rgba(9, 171, 197, 0.14); color: var(--text-inverse); }
.band--ink .chip .icon { color: var(--text-inverse-accent); }


/* ==========================================================================
   WAVE 2A ADDITIONS
   Components the page assembly required that the Wave 1 library did not have,
   plus one repair. Added here rather than as page-level <style> blocks so
   Waves 2B and 2C inherit them. Same rules apply: logical properties only,
   tokens only, no second hue, no fixed heights on anything holding text.
   ========================================================================== */

/* ------ REPAIR: footer grid was built for four columns, the copy deck has five
   The deck defines a brand block plus four link columns (Products, Protection
   and disinfection, Company, Get in touch). The original
   `1.6fr repeat(3, 1fr)` silently wrapped the fifth column onto a second row
   at exactly the width where the footer is widest and the wrap is ugliest.
   Three steps now: stacked, brand-over-four, then brand-beside-four.
   ------------------------------------------------------------------------- */
@media (min-width: 900px) {
  .site-footer__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .site-footer__brand { grid-column: 1 / -1; max-inline-size: 44rem; }
}

@media (min-width: 1200px) {
  .site-footer__grid { grid-template-columns: 1.5fr repeat(4, minmax(0, 1fr)); }
  .site-footer__brand { grid-column: auto; max-inline-size: 24rem; }
}

/* ------ Compact methodology: step description ----------------------------
   The copy deck supplies a one-line body for every step of the homepage
   methodology summary and every step of the contact page's "what happens
   next". Without this the copy had nowhere to go. Deliberately --fs-xs and
   muted: at the seven-column desktop layout each column is ~150px, and the
   description is supporting detail under a title that carries the scan.
   ------------------------------------------------------------------------- */
.method-compact__desc {
  display: block;
  margin-block-start: var(--space-2);
  font-size: var(--fs-xs);
  line-height: var(--lh-body);
  color: var(--text-muted);
}

.band--ink .method-compact__desc { color: var(--text-inverse-muted); }

/* Four-step variant. The seven-column rule at 1080px is correct for the
   seven-step methodology and wrong for anything else — four steps in a
   seven-column grid leaves three dead columns and three dangling rules.
   Any sequence that is not seven steps long uses this modifier. */
@media (min-width: 1080px) {
  .method-compact--4col { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ------ Fact bar ---------------------------------------------------------
   The strip directly under the homepage hero. It exists because the strategy
   calls for a trust strip there and no verified trust asset exists — so it
   carries facts that are in the source and are not claims (assembled in
   Riyadh, bilingual on every kit, configured per facility, support for the
   life of the account) rather than certification marks nobody has supplied.
   Not `.chip-grid`: these are statements, not links, and giving them a
   card border and a hover state would misrepresent them as navigation.
   ------------------------------------------------------------------------- */
.factbar {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-4) var(--space-8);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

/* Flow layout with a floated icon, not a flex row — see the .callout note.
   A flex container promotes every text run between element children to its own
   item, so `gap` lands mid-sentence and each run becomes a rigid column. The
   copy decks mandate <bdi> around Latin runs in Arabic prose, so any new string
   would shatter this the moment it arrives. Float keeps the icon on the reading
   edge in both directions and leaves the text in normal flow. */
.factbar__item {
  display: block;
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--text);
}

.factbar__item::after { content: ""; display: block; clear: both; }

.factbar__item .icon {
  float: inline-start;
  margin-inline-end: var(--space-3);
  color: var(--action-fill-active);
  margin-block-start: 0.1em;
}

.band--ink .factbar__item { color: var(--text-inverse-muted); }
.band--ink .factbar__item .icon { color: var(--text-inverse-accent); }

/* ------ Panel ------------------------------------------------------------
   The generic content container: a titled block on a raised ground with no
   media. Used for the About values, the Sectors blocks, and the contact
   page's alternative routes and contact details.

   It is deliberately NOT `.intended-panel`. That component means one specific
   thing on a kit page ("this kit is intended for these facilities") and
   overloading it would make the kit pages harder to reason about in Wave 2B.
   Note there is no coloured edge rule here either — same reasoning as
   `.intended-panel`: ground tone, border and spacing do the separating.
   ------------------------------------------------------------------------- */
.panel {
  display: flex;
  flex-direction: column;
  background-color: var(--surface);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}

@media (min-width: 768px) {
  .panel { padding: var(--space-8); }
}

/* On white bands the panel needs a tinted ground to separate at all. */
.band .panel,
.band--tight .panel { background-color: var(--surface); }
.band--alt .panel,
.band--sunken .panel { background-color: var(--surface); }

.panel--tint { background-color: var(--surface-tint); border-color: var(--border-decorative); }

.panel--ink {
  background-color: var(--surface-inverse-2);
  border-color: var(--border-inverse);
}

.panel__eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  color: var(--text-accent);
  margin-block-end: var(--space-2);
}

.panel__eyebrow .icon { color: var(--action-fill-active); }

/* Caps and tracking are Latin-only. Arabic is cursive and joined: tracking
   breaks the joins and text-transform does nothing but advertise an
   unlocalised stylesheet. */
.panel__eyebrow:not(:lang(ar)) {
  text-transform: uppercase;
  letter-spacing: var(--track-caps);
}

.panel__title {
  font-size: var(--fs-xl);
  line-height: var(--lh-heading);
  color: var(--text-strong);
  margin-block-end: var(--space-3);
}

.panel__desc {
  color: var(--text-muted);
  margin-block-end: 0;
}

.panel__desc + .panel__links { margin-block-start: var(--space-6); }

.panel__foot { margin-block-start: auto; padding-block-start: var(--space-6); }

/* A titled list of onward links inside a panel — the "usually needs" kit
   lists on the Sectors page. Real anchors with descriptive text, per the
   internal-linking rules; never a "learn more". */
.panel__links-h {
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
  margin-block-end: var(--space-3);
}

.panel__links-h:not(:lang(ar)) {
  text-transform: uppercase;
  letter-spacing: var(--track-caps);
}

.panel__links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.panel--ink .panel__title { color: var(--text-inverse); }
.panel--ink .panel__desc { color: var(--text-inverse-muted); }
.panel--ink .panel__eyebrow { color: var(--text-inverse-accent); }

/* ------ Pull quote -------------------------------------------------------
   One line, lifted out of the About narrative. Set large enough to clear the
   3:1 large-text threshold on --text-display-accent, because at body size
   that token would fail. No quotation-mark ornament: the size and the rule
   do the lifting, and a giant decorative glyph in an Arabic RTL context is
   one more thing to mirror wrongly.
   ------------------------------------------------------------------------- */
.pullquote {
  margin: var(--space-10) 0;
  padding-block: var(--space-6);
  border-block: 1px solid var(--border-decorative);
  max-inline-size: var(--measure);
}

.pullquote p {
  margin: 0;
  font-size: var(--fs-xl);
  line-height: var(--lh-snug);
  font-weight: var(--fw-medium);
  color: var(--text-strong);
}

/* ------ Product name -----------------------------------------------------
   THE HAZ TAB SWAP POINT. The tablet product name is not cleared for
   trademark use in KSA/GCC (Guest Medical ships "Haz-Tabs" in the same
   hospital category). Every occurrence of the name in markup is written as:

     <bdi class="product-name" data-product-name="haztab">HAZ TAB</bdi>

   One element shape, one attribute, both languages — so the fallback name
   ("HAZ MED Disinfectant Tablets") is a single scripted edit rather than a
   hunt across 36 files. The exact command is in PAGE-TEMPLATE.md.

   `<bdi>` is not decoration: the name is a Latin run and reorders inside
   Arabic prose without isolation. The class re-states that explicitly so the
   isolation survives even if the element is ever changed to a span.
   ------------------------------------------------------------------------- */
.product-name {
  unicode-bidi: isolate;
}

/* ------ Shell classes the header and footer needed -----------------------
   These existed as inline `style` attributes in the styleguide specimen.
   Promoted to real classes because the header and footer are now a contract
   reproduced across 36 pages: an inline style is 36 places to get it wrong.
   ------------------------------------------------------------------------- */
.site-header__brand-text {
  font-weight: var(--fw-semibold);
  font-size: var(--fs-lg);
  line-height: 1;
  color: var(--text-strong);
}

.site-header__brand-text:not(:lang(ar)) { letter-spacing: var(--track-caps); }

/* Below 400px the wordmark is hidden VISUALLY, not with display:none. The
   brand mark is alt="" (decorative), so display:none would leave the home
   link with no accessible name at all — a WCAG 2.4.4 failure that only
   appears at a width nobody tests at. */
@media (max-width: 399px) {
  .site-header__brand-text {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

.site-footer__blurb {
  margin-block-start: var(--space-4);
  font-size: var(--fs-xs);
  line-height: var(--lh-body);
  color: var(--text-inverse-muted);
}

/* The label half of a label/value pair in the footer and in the contact
   panels. Latin-only caps and tracking, as everywhere. */
.site-footer__label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  color: var(--neutral-400);
  margin-block-end: var(--space-1);
}

.site-footer__label:not(:lang(ar)) {
  text-transform: uppercase;
  letter-spacing: var(--track-caps);
}

/* Commercial registration / VAT / Maroof. Procurement reads this row. */
.site-footer__reg {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-8);
  align-items: baseline;
  margin-block-start: var(--space-6);
  padding-block-start: var(--space-6);
  border-block-start: 1px solid var(--border-inverse);
  font-size: var(--fs-xs);
  color: var(--neutral-400);
}

.site-footer__reg .site-footer__label {
  display: inline-block;
  margin-block: 0;
  margin-inline: 0 var(--space-2);
}

/* ------ Latin runs inside Arabic pages -----------------------------------
   Found during Wave 2A assembly. `--font-arabic` is "IBM Plex Sans Arabic"
   followed by SYSTEM Arabic fallbacks — it never names "IBM Plex Sans". So on
   an Arabic page every Latin run (order codes, `10,000 ppm`, `NaDCC`, the
   product name, the English switcher label) fell through to Tahoma or Geeza
   Pro, not to Plex Latin. DESIGN-SYSTEM.md §Font loading states the opposite
   is intended: "an Arabic page downloads the Latin file only if Latin glyphs
   actually appear (they usually do — order codes and concentrations)".

   This routes them back to Plex. It works because the Plex Latin @font-face
   carries a `unicode-range` covering Latin only, so an Arabic character in
   one of these elements still resolves to Plex Arabic rather than tofu.

   Hooked on the copy deck's own isolation markers — <bdi>, .num,
   .bidi-isolate — so it costs nothing on a page of pure Arabic prose. The
   browser fetches hazmed-plex-latin-var.woff2 (40 KB) only when a Latin glyph
   is actually painted. It is not preloaded on Arabic pages and it is not the
   LCP element, so this is a bandwidth cost, not a paint-blocking one.
   ------------------------------------------------------------------------- */
html[lang="ar"] bdi,
html[lang="ar"] .num,
html[lang="ar"] .bidi-isolate {
  font-family: var(--font-latin);
}

/* .code keeps the mono stack — it is a different signal, and the mono stack
   is a system stack that downloads nothing. */
html[lang="ar"] .code { font-family: var(--font-mono); }

/* The narrow-viewport two-letter code needs the same per-script treatment as
   the full label: "ع" must be Arabic-faced and RTL, "EN" Latin and LTR. */
.lang-switch__short:lang(ar) { font-family: var(--font-arabic); direction: rtl; }
.lang-switch__short:lang(en) { font-family: var(--font-latin); direction: ltr; }

/* ------ Hero image placeholder placement ---------------------------------
   The hero placeholder caption is centred by default, which put the
   photography brief directly behind the headline and lead — on a 375px
   Arabic hero the two texts overlapped into something unreadable.

   The caption must stay visible (a placeholder that is not obvious will ship),
   so it is moved rather than hidden: into the empty trailing half at desktop
   width, and to the block-start above the bottom-anchored copy on phones.
   ------------------------------------------------------------------------- */
/* Below 900px the hero copy fills the whole hero — there is no empty region
   left to put a caption in, and at 375px the Arabic lead and the brief
   overlapped into something genuinely unreadable. The caption is therefore
   clipped, not removed: the text stays in the DOM, stays greppable, is read
   by a screen reader, and is fully visible at every width where there is
   room. This applies ONLY to the hero slot. Every other .media-placeholder
   on the site shows its brief at every width. */
@media (max-width: 899px) {
  .hero__media .media-placeholder__label,
  .hero__media .media-placeholder__brief {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

@media (min-width: 900px) {
  .hero__media .media-placeholder {
    justify-content: center;
    align-items: flex-end;
    text-align: end;
    padding-inline-start: 48%;
  }

  .hero__media .media-placeholder__label { align-self: flex-end; }
  .hero__media .media-placeholder__brief { max-inline-size: 34ch; }
}


/* ==========================================================================
   MOTION LAYER
   Added after the build. Opt-in via `.js`, and switched off wholesale under
   prefers-reduced-motion at the end of this block.

   The restraint is deliberate: this site sells life-safety equipment to
   hospital infection-control leads. Motion directs attention to the response
   sequence and confirms interaction. It never decorates, never moves the page
   under a reader looking for a dilution ratio, and never delays content.

   Every selector here was checked against the shipped markup. The animation
   hangs off the existing `.is-revealed` reveal mechanism via a descendant
   selector, so it fires whether that class lands on the list or an ancestor.
   ========================================================================== */

@keyframes haz-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
@keyframes haz-settle {
  from { opacity: 0; transform: scale(1.03); }
  to   { opacity: 1; transform: none; }
}
@keyframes haz-node {
  from { opacity: 0; transform: scale(0.45); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes haz-rail-y {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

/* ---- 1. Hero entrance --------------------------------------------------
   On load, not on scroll: the hero is already in view, so a scroll trigger
   would never fire for it. Staggered so the headline lands first. */
.js .hero__content > * {
  animation: haz-rise var(--dur-reveal) var(--ease-out-firm) both;
}
.js .hero__content > *:nth-child(1) { animation-delay:  60ms; }
.js .hero__content > *:nth-child(2) { animation-delay: 130ms; }
.js .hero__content > *:nth-child(3) { animation-delay: 200ms; }
.js .hero__content > *:nth-child(4) { animation-delay: 270ms; }
.js .hero__content > *:nth-child(n+5) { animation-delay: 320ms; }

/* The hero artwork settles rather than rises — it is a ground, not content. */
.js .hero img { animation: haz-settle 700ms var(--ease-out) both; }

/* ---- 2. The response methodology --------------------------------------
   The signature moment, and the one place where sequence IS the message.

   `.method` is the vertical list: its rail is `.method::before`, an absolutely
   positioned 2px column, so it draws with scaleY from the top (flipped to the
   block-end in RTL is unnecessary — the rail runs top-to-bottom in both). */
.js .is-revealed .method::before,
.js .method.is-revealed::before {
  transform-origin: center top;
  animation: haz-rail-y 800ms var(--ease-out) both;
}

/* The numbered nodes land after the rail passes them. */
.js .is-revealed .method__num,
.js .method.is-revealed .method__num {
  animation: haz-node var(--dur-base) var(--ease-out-firm) both;
}
.js .is-revealed .method__step:nth-child(1) .method__num { animation-delay: 140ms; }
.js .is-revealed .method__step:nth-child(2) .method__num { animation-delay: 250ms; }
.js .is-revealed .method__step:nth-child(3) .method__num { animation-delay: 360ms; }
.js .is-revealed .method__step:nth-child(4) .method__num { animation-delay: 470ms; }
.js .is-revealed .method__step:nth-child(5) .method__num { animation-delay: 580ms; }
.js .is-revealed .method__step:nth-child(6) .method__num { animation-delay: 690ms; }
.js .is-revealed .method__step:nth-child(7) .method__num { animation-delay: 800ms; }

/* `.method-compact` is the horizontal variant. Its connector is a per-step
   top BORDER, which cannot be scaled, so the dots carry the sequence instead
   — which is the right read anyway: seven marks appearing in order. */
.js .is-revealed .method-compact__step::before,
.js .method-compact.is-revealed .method-compact__step::before {
  animation: haz-node var(--dur-base) var(--ease-out-firm) both;
}
.js .is-revealed .method-compact__step:nth-child(1)::before { animation-delay: 100ms; }
.js .is-revealed .method-compact__step:nth-child(2)::before { animation-delay: 190ms; }
.js .is-revealed .method-compact__step:nth-child(3)::before { animation-delay: 280ms; }
.js .is-revealed .method-compact__step:nth-child(4)::before { animation-delay: 370ms; }
.js .is-revealed .method-compact__step:nth-child(5)::before { animation-delay: 460ms; }
.js .is-revealed .method-compact__step:nth-child(6)::before { animation-delay: 550ms; }
.js .is-revealed .method-compact__step:nth-child(7)::before { animation-delay: 640ms; }

/* ---- 3. Cards ----------------------------------------------------------
   Hover only, and only where hover genuinely exists — a touch device reports
   none, so this never sticks on a phone. */
@media (hover: hover) and (pointer: fine) {
  .product-card {
    transition:
      transform var(--dur-base) var(--ease-out),
      box-shadow var(--dur-base) var(--ease-out),
      border-color var(--dur-base) var(--ease-out);
  }
  .product-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 24px rgb(11 46 79 / 0.08);
    border-color: var(--action-fill);
  }
  .product-card .media-placeholder,
  .product-card img {
    transition: transform var(--dur-slow) var(--ease-out);
  }
  .product-card:hover .media-placeholder,
  .product-card:hover img { transform: scale(1.02); }
}

/* Focus must not move the card. A keyboard user tracking the focus ring needs
   the target to be exactly where the ring says it is. */
.product-card:focus-within { transform: none; }

/* ---- 4. Header on scroll ---------------------------------------------- */
.site-header {
  transition: box-shadow var(--dur-base) var(--ease-out);
}
.site-header.is-stuck {
  box-shadow: 0 1px 0 var(--border-decorative), 0 6px 20px rgb(11 46 79 / 0.06);
}

/* ---- 5. Reduced motion — off, not faster -----------------------------
   Someone who asked the OS for less motion asked for less motion. A
   vestibular trigger at 0.01s is still a trigger. */
@media (prefers-reduced-motion: reduce) {
  .js .hero__content > *,
  .js .hero img,
  .js .is-revealed .method::before,
  .js .method.is-revealed::before,
  .js .is-revealed .method__num,
  .js .method.is-revealed .method__num,
  .js .is-revealed .method-compact__step::before,
  .js .method-compact.is-revealed .method-compact__step::before {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .product-card,
  .product-card:hover,
  .product-card .media-placeholder,
  .product-card:hover .media-placeholder,
  .product-card img,
  .product-card:hover img,
  .site-header {
    transition: none !important;
    transform: none !important;
  }
}

/* ==========================================================================
   TOUCH TARGET SIZE  (WCAG 2.5.8 Target Size Minimum, 24x24 CSS px)
   Added after live mobile testing at 390x844, which found 19 standalone links
   under 24px tall: `.link-arrow` at 22px and the footer link lists at 18px.

   Scoped to `pointer: coarse` so the desktop layout is untouched — this exists
   for thumbs, and growing the hit box on a mouse-driven page would only add
   dead space.

   `display: inline-block` rather than flex: these links contain <bdi> on the
   Arabic side, and a flex container promotes every text run between element
   children to its own item, injecting `gap` mid-phrase. That defect has
   already been fixed once in this stylesheet; it is not being reintroduced
   here.

   WCAG 2.5.8 exempts links inline within a sentence, so the two prose links
   found by the same test are deliberately left alone — padding them would
   break the line rhythm of the paragraph they sit in for no accessibility
   gain.
   ========================================================================== */
@media (pointer: coarse) {

  /* Standalone call-to-action links. 22px + 2x5px = 32px. */
  .link-arrow {
    display: inline-block;
    padding-block: var(--space-1, 4px);
  }

  /* Footer link lists: 17 links at 18px. 18px + 2x6px = 30px, and the extra
     row spacing also stops adjacent targets sharing an edge. */
  .site-footer__list li a {
    display: inline-block;
    padding-block: 6px;
  }
  .site-footer__list li + li {
    margin-block-start: var(--space-1, 4px);
  }

  /* Legal row in the footer bottom bar. */
  .site-footer__legal a {
    display: inline-block;
    padding-block: 6px;
  }

  /* Breadcrumb trail. Small type by design, so it needs the hit area rather
     than a larger font. */
  .breadcrumb__item a {
    display: inline-block;
    padding-block: 5px;
  }
}
