/* ==========================================================================
   JMV Water Solutions — Global design system
   Tokens, base, layout primitives and reusable components.
   Breakpoint-specific overrides live in responsive.css

   HOUSE RULES — keep these true and the site stays consistent:

   1. Width.    Every page's content sits in `.shell` (max `--container`,
                padded by `--gutter`). Nothing sets its own page width.
   2. Rhythm.   Vertical space between bands comes from `.section`
                (`--section-y`) or `.section--tight` (`--section-y-tight`).
                A band that paints its own padding uses the same two tokens.
   3. Cards.    Radius, padding, border, shadow and hover lift come from the
                `--card-*` and `--sh-card*` tokens. Content cards take
                `--card-r`; compact tiles take `--card-r-sm`. No component
                invents its own shadow or radius.
   4. Type.     Sizes are the `--fs-*` scale only. Card titles are `--fs-h5`,
                card copy `--fs-base`, meta lines `--fs-sm`, labels
                `.eyebrow`. Nothing hard-codes a font size.
   5. Grids.    `.grid--cards` for a known set of cards (3 / 2 / 1 across);
                the auto-fit grids for collections whose length varies.
   6. Even rows. Card copy is clamped (`.clamp-2`, `.clamp-3`) so cards in a
                row share one height instead of tracking the copy length.
   7. Images.   Ratios come from the `.media--*` variants; nothing sets an
                image height. See DEPLOYMENT.md § 6 for the sizes.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------------- */
:root {
  /* ---- Pure Water Droplet theme --------------------------------------
     Canonical palette. The --jmv-* names are the design-system source of
     truth; the shorter aliases below them are what every component uses,
     so re-theming happens here and nowhere else. */
  /* Brand — the navy gradient carries the top bar, dark bands and footer */
  --jmv-blue-700: #113087;        /* brand navy gradient, start */
  --jmv-navy-900: #102E7B;        /* brand navy gradient, end   */
  --jmv-navy-800: #0E295B;        /* deep navy — headings, ink  */
  --jmv-blue: #1068E8;            /* primary blue               */
  --jmv-blue-hover: #0B57C6;      /* primary blue, hover        */

  /* Accents */
  --jmv-cyan: #29C1DF;
  --jmv-cyan-soft: #8FD8EA;
  --jmv-drop-top: #9FD8EE;        /* droplet gradient, start    */
  --jmv-drop-mid: #BEDFF3;        /* droplet mid-tone           */
  --jmv-drop-sub: #4E86D6;        /* droplet sub-label          */

  /* Text */
  --jmv-text: #0E295B;            /* headings          */
  --jmv-text-muted: #5B6478;      /* body              */
  --jmv-text-soft: #7B849A;       /* secondary         */
  --jmv-text-label: #9AA5BC;      /* labels, placeholders */
  --jmv-text-meta: #8A93A8;       /* meta              */

  /* Surfaces */
  --jmv-surface: #F7FAFF;
  --jmv-surface-2: #F2F7FF;
  --jmv-ice: #EAF3FF;
  --jmv-ice-deep: #E7EFFD;

  /* Borders */
  --jmv-border-soft: #EDF2FA;
  --jmv-border: #E7EEF9;
  --jmv-border-mid: #E1E9F6;
  --jmv-border-strong: #DCE6F5;
  --jmv-border-deep: #D5DFEF;
  --jmv-border-deepest: #CBD8EC;

  /* Navy scale (dark emphasis bands, footer, ink) */
  --navy-900: var(--jmv-navy-900);
  --navy-850: #0A2350;
  --navy-800: #0C2755;
  --navy-700: var(--jmv-navy-800);
  --navy-600: var(--jmv-blue-700);
  --navy-500: #14357A;
  --navy-400: #1A3FA8;

  /* Blue / cyan */
  --blue-700: var(--jmv-blue-hover);
  --blue-600: var(--jmv-blue);
  --blue-500: #3B86F0;
  --blue-100: #D7E7FE;
  --blue-50:  var(--jmv-ice);
  --cyan-600: var(--jmv-cyan);
  --cyan-500: var(--jmv-cyan);
  --cyan-300: var(--jmv-cyan-soft);

  /* Neutrals */
  --white: #ffffff;
  --bg: var(--jmv-surface);
  --bg-tint: var(--jmv-surface-2);
  --bg-alt: var(--jmv-ice);
  --bg-deep: var(--jmv-ice-deep);
  --border-soft: var(--jmv-border-soft);
  --border: var(--jmv-border);
  --border-mid: var(--jmv-border-mid);
  --border-strong: var(--jmv-border-strong);
  --border-deep: var(--jmv-border-deep);
  --border-deepest: var(--jmv-border-deepest);
  --ink: var(--jmv-text);
  --body: var(--jmv-text-muted);
  --muted: var(--jmv-text-soft);
  --label: var(--jmv-text-label);
  --meta: var(--jmv-text-meta);

  /* Status */
  --success: #0F9C6C;
  --success-bg: #E6F7F1;
  --danger: #C0392B;
  --danger-bg: #FDECEB;
  --warning: #B7791F;

  /* Semantic surfaces */
  --surface: var(--white);
  --surface-sunken: var(--bg);
  --on-navy: rgba(234, 243, 255, .86);
  --on-navy-dim: rgba(203, 219, 245, .76);

  /* Typography */
  --font-display: "Plus Jakarta Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-body: "Plus Jakarta Sans", "Segoe UI", system-ui, -apple-system, sans-serif;

  --fs-eyebrow: .8125rem;   /* 13px */
  --fs-xs: .75rem;          /* 12px */
  --fs-sm: .875rem;         /* 14px */
  --fs-base: 1rem;          /* 16px */
  --fs-md: 1.0625rem;       /* 17px */
  --fs-lg: 1.125rem;        /* 18px */
  --fs-h6: 1.0625rem;
  --fs-h5: clamp(1.0625rem, .98rem + .35vw, 1.25rem);
  --fs-h4: clamp(1.1875rem, 1.05rem + .6vw, 1.5rem);
  --fs-h3: clamp(1.375rem, 1.15rem + 1vw, 1.875rem);
  --fs-h2: clamp(1.75rem, 1.35rem + 1.85vw, 2.875rem);
  --fs-h1: clamp(2rem, 1.4rem + 2.9vw, 3.5rem);
  --fs-hero: clamp(2.25rem, 1.3rem + 4.4vw, 4.625rem);
  --fs-stat: clamp(1.625rem, 1.3rem + 1.6vw, 2.25rem);

  --lh-tight: 1.05;
  --lh-head: 1.14;
  --lh-snug: 1.35;
  --lh-body: 1.65;

  --ls-tight: -.028em;
  --ls-eyebrow: .12em;

  /* Spacing scale (4px base) */
  --sp-1: .25rem;
  --sp-2: .5rem;
  --sp-3: .75rem;
  --sp-4: 1rem;
  --sp-5: 1.25rem;
  --sp-6: 1.5rem;
  --sp-7: 2rem;
  --sp-8: 2.5rem;
  --sp-9: 3rem;
  --sp-10: 4rem;
  --sp-11: 5rem;
  --sp-12: 6rem;

  --section-y: clamp(3.25rem, 2rem + 4vw, 5.75rem);
  --section-y-tight: clamp(2.5rem, 1.75rem + 2.6vw, 4rem);

  /* Containers */
  --container: 1200px;
  --gutter: clamp(1rem, .55rem + 1.6vw, 1.5rem);

  /* Radius */
  --r-xs: 8px;
  --r-sm: 10px;
  --r-md: 14px;    /* buttons, inputs */
  --r-lg: 18px;    /* cards, photos */
  --r-xl: 22px;    /* large photos, feature cards */
  --r-2xl: 28px;
  --r-pill: 999px;

  /* ---- Card system ----------------------------------------------------
     One set of values for every card-like surface on the site. A component
     chooses the content tier or the compact tier and takes nothing else, so
     padding, radius, border, shadow and hover lift stay in step everywhere.
     Content tier: product, family, article, feature, project, industry.
     Compact tier: metric, logo, FAQ row, spec row, form field group. */
  --card-r: var(--r-xl);
  --card-r-sm: var(--r-lg);
  --card-pad: var(--sp-6);
  --card-pad-sm: var(--sp-5);
  --card-lift: -4px;

  /* The site's reading measure. Articles and the legal pages now run on the
     page container instead (see `.article`), so this is kept as the
     documented narrow measure rather than applied by default. */
  --measure-px: 760px;

  /* ---- Droplet geometry (single source of truth) ----------------------
     One silhouette for every droplet on the site: pointed top, round base.
     The polygon is the design's reference shape; the border-radius below is
     only the fallback for the rare element that cannot be clipped. The
     ratio makes the bulb perfectly circular: bulb width 68% of the box,
     bulb height 51.5% of the box, so height = width x 1.32. */
  --clip-drop: polygon(
    50% 0%,
    79.1% 48.5%, 82.8% 57.2%, 84% 66%, 82.8% 74.8%, 79.4% 83%,
    74% 90%, 67% 95.4%, 58.8% 98.8%,
    50% 100%,
    41.2% 98.8%, 33% 95.4%, 26% 90%,
    20.6% 83%, 17.2% 74.8%, 16% 66%, 17.2% 57.2%, 20.9% 48.5%
  );
  --shape-drop: 50% 50% 50% 50% / 62% 62% 38% 38%;
  --shape-drop-soft: 50% 50% 50% 50% / 58% 58% 42% 42%;
  --drop-ratio: 1 / 1.32;

  /* Shadows */
  --sh-xs: 0 1px 2px rgba(14, 41, 91, .05);
  --sh-sm: 0 4px 14px rgba(14, 41, 91, .06);
  --sh-md: 0 12px 30px rgba(14, 41, 91, .09);
  --sh-lg: 0 24px 60px rgba(14, 41, 91, .14);
  --sh-blue: 0 12px 28px rgba(16, 104, 232, .26);
  --sh-navy: 0 18px 44px rgba(8, 30, 70, .24);
  /* Every card raises onto the same shadow — no per-component values */
  --sh-card: var(--sh-xs);
  --sh-card-hover: 0 22px 46px rgba(14, 41, 91, .10);

  /* Motion */
  --t-fast: 140ms cubic-bezier(.2, .6, .3, 1);
  --t-base: 240ms cubic-bezier(.2, .6, .3, 1);
  --t-slow: 420ms cubic-bezier(.2, .6, .3, 1);

  /* Layered UI */
  --z-rail: 60;
  --z-header: 100;
  --z-menu: 200;
  --z-fab: 150;

  --tap: 48px;

  /* Floating action stack (bottom right) */
  --fab-size: 56px;
  --fab-right: clamp(.75rem, .3rem + 1.2vw, 1.25rem);
  --fab-bottom: clamp(1rem, .5rem + 1.6vw, 1.75rem);
  --fab-gap: .75rem;
  --header-h: 86px;   /* sticky header height — sticky sub-bars offset by this */

  /* Gradients */
  /* Light, airy hero wash */
  --grad-hero:
    radial-gradient(1100px 700px at 78% 20%, var(--jmv-ice) 0%, var(--jmv-surface) 45%, #ffffff 78%),
    radial-gradient(760px 520px at 6% 88%, #EFF6FF 0%, rgba(239, 246, 255, 0) 66%);
  /* Dark emphasis bands */
  --grad-band: linear-gradient(180deg, var(--jmv-blue-700) 0%, var(--jmv-navy-900) 100%);
  --grad-accent: linear-gradient(135deg, var(--jmv-blue) 0%, var(--jmv-cyan) 100%);
  /* Droplet fills — the design's two droplet treatments, nothing else.
     `--grad-drop` is the large 3D droplet; `--grad-drop-bullet` is the small
     solid bullet used for list markers and separators. */
  --grad-drop: radial-gradient(circle at 36% 30%, #FFFFFF, var(--jmv-drop-mid) 60%, var(--jmv-blue));
  --grad-drop-bullet: linear-gradient(180deg, var(--jmv-drop-top), var(--jmv-blue));
  --grad-drop-dark: radial-gradient(circle at 34% 26%, rgba(255, 255, 255, .95) 0%, rgba(234, 243, 255, .74) 26%, rgba(143, 216, 234, .34) 66%, rgba(41, 193, 223, .18) 100%);
  /* Inner modelling that gives the 3D droplet its volume. Inset shadows are
     painted inside the box, so they survive the clip path. */
  --sh-drop: inset -8px -12px 22px rgba(255, 255, 255, .9),
             inset 8px 14px 24px rgba(41, 120, 220, .14);
  /* Blue keyline. A clipped element cannot take a border, so the outline is
     drawn as four offset drop-shadows of the clipped silhouette. */
  --drop-outline: drop-shadow(2px 0 0 var(--jmv-blue))
                  drop-shadow(-2px 0 0 var(--jmv-blue))
                  drop-shadow(0 2px 0 var(--jmv-blue))
                  drop-shadow(0 -2px 0 var(--jmv-blue));
  /* Glassy orb ornament — a perfect sphere rather than a teardrop */
  --grad-orb: radial-gradient(circle at 34% 30%, rgba(255, 255, 255, .95) 0%, rgba(190, 225, 255, .55) 55%, rgba(41, 193, 223, .3) 100%);
  --crest-color: var(--white);

  /* Shared shape / motion aliases used across components */
  --jmv-r-img: 16px;
  /* Height reserved for the header chrome before components.js renders it.
     Topbar (34px) + header bar (87px). See `#site-header:empty`. */
  --chrome-h: 121px;
  --jmv-ease: cubic-bezier(.22, .61, .36, 1);
  --t-water: 900ms var(--jmv-ease);
}

/* --------------------------------------------------------------------------
   2. Reset / base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 140px;
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--fs-md);
  line-height: var(--lh-body);
  color: var(--body);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg, video { display: block; max-width: 100%; height: auto; }

/* The `hidden` attribute is how every script here shows and hides a block
   (product filters, the article's "more from the blog" row, the technology
   detail sections). A component that sets its own `display` beats the user
   agent's `[hidden] { display: none }`, because an author rule always wins
   over the UA sheet — which is why `.family { display: grid }` was leaving
   filtered-out product families on the page. Stated once, here. */
[hidden] { display: none !important; }

h1, h2, h3, h4, h5, h6 {
  margin: 0 0 var(--sp-4);
  font-family: var(--font-display);
  font-weight: 800;
  line-height: var(--lh-head);
  letter-spacing: var(--ls-tight);
  color: var(--ink);
  text-wrap: balance;
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
h4 { font-size: var(--fs-h4); }
h5 { font-size: var(--fs-h5); font-weight: 700; }
h6 { font-size: var(--fs-h6); font-weight: 700; }

p { margin: 0 0 var(--sp-4); }
p:last-child { margin-bottom: 0; }

/* The browser's own margins on these elements do not belong to any
   component, and they were pushing description lists, figures and quotes
   out of line with the cards beside them. Spacing is set by the component. */
figure, dl, dd, blockquote { margin: 0; }

a { color: var(--blue-600); text-decoration: none; transition: color var(--t-fast); }
a:hover { color: var(--blue-700); }

ul, ol { margin: 0 0 var(--sp-4); padding-left: 1.15rem; }
li { margin-bottom: var(--sp-2); }

strong, b { font-weight: 700; color: var(--ink); }
small { font-size: var(--fs-sm); }

hr { height: 1px; border: 0; background: var(--border); margin: var(--sp-8) 0; }

button { font: inherit; color: inherit; }

:focus-visible {
  outline: 3px solid var(--blue-500);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

::selection { background: var(--cyan-500); color: var(--navy-900); }

/* Scrollbar — slim, grey-blue, unobtrusive. Firefox first, then WebKit. */
html {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border: 3px solid var(--bg);   /* keeps the visible thumb slim */
  border-radius: var(--r-pill);
}
::-webkit-scrollbar-thumb:hover { background: #a9b5c6; }
::-webkit-scrollbar-corner { background: var(--bg); }

/* Scrollable areas on dark panels keep the same slim treatment */
.section--navy ::-webkit-scrollbar-track,
.section--band ::-webkit-scrollbar-track,
.site-footer ::-webkit-scrollbar-track { background: transparent; }

.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: -100px;
  z-index: 999;
  padding: var(--sp-3) var(--sp-5);
  background: var(--blue-600);
  color: #fff;
  font-weight: 700;
  border-radius: var(--r-sm);
  transition: top var(--t-fast);
}
.skip-link:focus { top: var(--sp-4); color: #fff; }

.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   3. Layout primitives
   -------------------------------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-y); }
.section--tight { padding-block: var(--section-y-tight); }
.section--sunken { background: var(--bg); }
.section--navy { background: var(--navy-700); color: var(--on-navy); }
.section--navy h1, .section--navy h2, .section--navy h3,
.section--navy h4, .section--navy h5, .section--navy h6 { color: #fff; }
/* The technology band carries the page title on its own pages */
.section--band { background: var(--grad-band); color: var(--on-navy); }
.section--band h1, .section--band h2, .section--band h3,
.section--band h4, .section--band h5, .section--band h6 { color: #fff; }

.grid { display: grid; gap: var(--sp-6); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr)); }
.grid--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--triple { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* Card grids: every row is the same height, so a second row of cards lines
   up with the first instead of tracking its own longest card. Grids that
   hold whole page columns rather than cards are left out on purpose. */
.grid--3, .grid--4, .grid--pair, .grid--triple, .grid--cards,
.industry-grid, .variant-grid, .stats-strip__card { grid-auto-rows: 1fr; }
/* A fixed card grid — three across, two on tablet, one on phones. Used
   where the number of cards is known and a ragged last row would show:
   the product range, the technology list, the article grid. Auto-fit is
   kept for collections whose length varies. */
.grid--cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }


/* A short quoted statement — vision, mission, key takeaway */
.statement + .statement { margin-top: var(--sp-4); }
.statement {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h5);
  font-weight: 500;
  line-height: 1.5;
  color: var(--ink);
}
/* Two equal columns from the large breakpoint up (replaces the Bootstrap row) */
.grid--pair-lg { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.flow-between {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-4) var(--sp-6);
}

/* --------------------------------------------------------------------------
   4. Type helpers
   -------------------------------------------------------------------------- */
.eyebrow {
  display: block;
  margin: 0 0 var(--sp-3);
  font-family: var(--font-body);
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--blue-600);
}
.eyebrow--cyan { color: var(--cyan-500); }
.eyebrow--muted { color: var(--muted); }

.lead {
  font-size: var(--fs-lg);
  line-height: var(--lh-body);
  color: var(--body);
  max-width: 62ch;
}
.section--navy .lead, .section--band .lead { color: var(--on-navy); }

.fine-print { font-size: var(--fs-sm); color: var(--meta); }

.section-head { max-width: 46ch; }
.section-head > :last-child { margin-bottom: 0; }
.section-head .lead { margin-top: var(--sp-4); }
/* A section head that carries a supporting paragraph, not just a heading.
   The 46ch cap is a headline measure; applied to a paragraph it wraps the
   copy a third of the way across the container. `--wide` lifts the cap off
   the head AND off every child, so heading and copy both run the full
   container width — a cap left on the heading alone still wraps it early. */
.section-head--wide,
.section-head--wide > h1,
.section-head--wide > h2,
.section-head--wide > h3,
.section-head--wide .lead { max-width: none; }

/* In-body section titles (product detail, technology detail, articles) */
.section-title-sm { font-size: var(--fs-h3); }
/* Heading-size utilities, so a sub-heading can take the right visual weight
   without changing its level in the document outline */
.h4 { font-size: var(--fs-h4); }

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */
.btn-jmv {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: .8rem 1.4rem;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  font-family: var(--font-body);
  font-size: var(--fs-base);
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast),
              box-shadow var(--t-base), transform var(--t-fast);
}
.btn-jmv:disabled, .btn-jmv[aria-disabled="true"] { opacity: .6; cursor: not-allowed; }
.btn-jmv:not(:disabled):hover { transform: translateY(-1px); }
.btn-jmv:not(:disabled):active { transform: translateY(0); }

.btn-primary-jmv { background: var(--blue-600); color: #fff; box-shadow: var(--sh-blue); }
.btn-primary-jmv:hover { background: var(--blue-700); color: #fff; box-shadow: 0 16px 34px rgba(16, 104, 232, .32); }

/* Painted with the header band's own gradient, so a navy button and the bar
   at the top of the page are the same navy. */
.btn-navy-jmv { background: var(--grad-band); color: #fff; box-shadow: var(--sh-navy); }
.btn-navy-jmv:hover { background: var(--jmv-blue-700); color: #fff; }

.btn-outline-jmv {
  background: var(--white);
  color: var(--ink);
  border-color: var(--border-strong);
}
.btn-outline-jmv:hover { background: var(--blue-50); border-color: var(--blue-500); color: var(--ink); }

/* Secondary action — a white pill that works on both light and dark grounds */
.btn-ghost-navy {
  background: var(--white);
  color: var(--ink);
  border-color: var(--border-strong);
  box-shadow: var(--sh-xs);
}
.btn-ghost-navy:hover {
  background: var(--white);
  color: var(--blue-700);
  border-color: var(--blue-500);
  box-shadow: var(--sh-sm);
}

.btn-white-jmv { background: #fff; color: var(--ink); }
.btn-white-jmv:hover { background: var(--blue-50); color: var(--ink); }

/* Two button sizes and no more. `--lg` is the hero and closing CTAs, the
   default is everything else. */
.btn-jmv--lg { min-height: 52px; padding: .85rem 2.15rem; }
.btn-jmv--block { width: 100%; }

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  min-height: 44px;
  font-weight: 700;
  color: var(--blue-600);
}
.link-arrow::after {
  content: "→";
  transition: transform var(--t-fast);
}
.link-arrow:hover::after { transform: translateX(3px); }
.link-arrow--light { color: var(--cyan-500); }

/* --------------------------------------------------------------------------
   6. Pills, chips, badges
   -------------------------------------------------------------------------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .45rem .85rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  background: var(--white);
  font-size: var(--fs-sm);
  color: var(--body);
  white-space: nowrap;
}
.chip--sunken { background: var(--white); border-color: var(--border-strong); }

.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding: 0;
  margin: 0;
  list-style: none;
}
.chip-list li { margin: 0; }

.badge-jmv {
  display: inline-flex;
  align-items: center;
  padding: .3rem .6rem;
  border-radius: var(--r-xs);
  background: var(--blue-50);
  color: var(--blue-600);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.badge-jmv--neutral { background: var(--bg); color: var(--body); letter-spacing: 0; text-transform: none; font-weight: 500; font-size: var(--fs-sm); }

.badge-square {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  padding-top: 11px;
  filter: drop-shadow(0 6px 12px rgba(14, 41, 91, .16));
  color: var(--jmv-navy-800);
  font-family: var(--font-display);
  font-size: .5625rem;
  font-weight: 800;
  letter-spacing: .02em;
  line-height: 1;
  flex: none;
  align-self: flex-start;
}
/* A droplet with no content — the decoration on its own. Declared after the
   base rule so it wins on the properties it resets. */
.badge-square--plain { padding-top: 0; font-size: 0; }

/* Counters are droplets — the signature ornament of the theme */
.num-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  padding-top: 10px;
  filter: drop-shadow(0 6px 12px rgba(14, 41, 91, .16));
  color: var(--jmv-navy-800);
  font-family: var(--font-display);
  font-size: .625rem;
  font-weight: 800;
  line-height: 1;
  align-self: flex-start;
  flex: none;
}
.num-badge--soft { color: var(--jmv-navy-800); }

/* Filter pill bar (products, blog) */
.filter-bar {
  position: sticky;
  top: var(--header-h);
  z-index: 40;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
.filter-bar__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: center;
  padding-block: var(--sp-5);
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.filter-bar__inner::-webkit-scrollbar { display: none; }

.filter-pill {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: .5rem 1.05rem;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: var(--white);
  color: var(--ink);
  font-size: .9375rem;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}
.filter-pill:hover { border-color: var(--blue-500); color: var(--blue-700); }
.filter-pill[aria-selected="true"], .filter-pill.is-active {
  background: var(--blue-600);
  border-color: var(--blue-600);
  color: #fff;
  font-weight: 700;
  box-shadow: var(--sh-blue);
}

/* --------------------------------------------------------------------------
   7. Cards & media
   -------------------------------------------------------------------------- */
.card-jmv {
  position: relative;
  display: flex;
  flex-direction: column;
  background: linear-gradient(180deg, var(--white) 0%, var(--jmv-surface) 100%);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
  box-shadow: var(--sh-card);
  overflow: hidden;
  transition: box-shadow var(--t-base), transform var(--t-base), border-color var(--t-base);
}
.card-jmv--hover:hover, .card-jmv--hover:focus-within {
  transform: translateY(var(--card-lift));
  box-shadow: var(--sh-card-hover);
  border-color: var(--border-strong);
}
/* Photos inside a card keep the frame inset, matching the reference */
.card-jmv > .media,
.card-jmv__media > .media { margin: var(--sp-3) var(--sp-3) 0; border-radius: var(--r-lg); }
.card-jmv__media { display: block; }
.card-jmv__body { padding: var(--card-pad); display: flex; flex-direction: column; gap: var(--sp-3); flex: 1 1 auto; }
.card-jmv__title { margin: 0; font-size: var(--fs-h5); }
.card-jmv__title, .variant-card__title, .blog-card__title, .blog-row__title { overflow-wrap: anywhere; }
/* Inside a card grid the bands line up across the row: the title reserves
   its two lines so the summary, chips and footer of every card in the row
   start at the same height. */
.grid > .card-jmv .card-jmv__title,
.grid > .card-jmv .blog-card__title { min-height: calc(2em * var(--lh-head)); }
.card-jmv__title a, .variant-card__title a, .family__title a, .blog-card__title a { color: var(--ink); }
.card-link { position: static; }
.card-link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}
/* Anything that must stay clickable sits above the stretched link */
.card-jmv__foot a, .card-jmv .chip-list, .card-jmv__body a:not(.card-link) { position: relative; z-index: 2; }
.card-jmv__title a:hover, .variant-card__title a:hover,
.family__title a:hover, .blog-card__title a:hover { color: var(--blue-700); }
.card-jmv__text { margin: 0; font-size: var(--fs-base); }
.card-jmv__foot { margin-top: auto; padding-top: var(--sp-3); }

/* Line clamp — the one place the site limits copy to a fixed number of
   lines. Applied to card text so grids stay even; never to page copy. */
.clamp-2, .clamp-3 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }
.clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }

.card-soft {
  background: linear-gradient(180deg, var(--white) 0%, var(--jmv-surface) 100%);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
  padding: var(--card-pad);
  box-shadow: var(--sh-card);
}
.card-navy {
  position: relative;
  background: var(--grad-band);
  color: var(--on-navy);
  border-radius: var(--card-r);
  padding: var(--card-pad);
  box-shadow: var(--sh-navy);
  overflow: hidden;
}
.card-navy h2, .card-navy h3, .card-navy h4, .card-navy strong,
.card-navy .statement,
.card-navy .testimonial__quote { color: #fff; }

/* Closing company lockup (About page) */
.signature {
  position: relative;
  display: grid;
  justify-items: center;
  gap: .35rem;
  padding: clamp(2.25rem, 1.5rem + 2.4vw, 3.5rem) var(--sp-6);
  text-align: center;
  background: linear-gradient(180deg, var(--white) 0%, var(--jmv-surface) 100%);
  border: 1px solid var(--border);
  border-radius: var(--r-2xl);
  overflow: hidden;
  isolation: isolate;
}
/* The signature droplet, from the shared droplet system */
.signature::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -24px;
  z-index: -1;
  width: clamp(96px, 10vw, 140px);
  aspect-ratio: var(--drop-ratio);
  translate: 0 -50%;
  clip-path: var(--clip-drop);
  border-radius: var(--shape-drop);
  background: var(--grad-drop);
  box-shadow: var(--sh-drop);
  opacity: .26;
  pointer-events: none;
  animation: drop-float 13s ease-in-out infinite alternate;
}
.signature__name {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 800;
  letter-spacing: var(--ls-tight);
  line-height: 1.2;
  color: var(--ink);
}
.signature__line { font-size: var(--fs-base); color: var(--body); }
.signature__strapline {
  margin-top: var(--sp-4);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--blue-600);
}

.card-accent {
  position: relative;
  background: var(--grad-accent);
  color: #fff;
  border-radius: var(--card-r);
  padding: var(--card-pad);
  overflow: hidden;
  box-shadow: var(--sh-blue);
}
/* Glass droplet ornament inside the accent card */
.card-accent::after {
  content: "";
  position: absolute;
  right: -12%;
  top: -18%;
  width: 46%;
  aspect-ratio: 1;
  border-radius: var(--shape-drop);
  background: radial-gradient(circle at 34% 28%, rgba(255, 255, 255, .5) 0%, rgba(255, 255, 255, .12) 55%, transparent 70%);
  pointer-events: none;
  animation: drop-float 11s ease-in-out infinite alternate;
}
.card-accent h3, .card-accent h4 { color: #fff; }

/* Heading followed directly by copy inside a card */
.card-soft > h2 + p, .card-soft > h3 + p,
.card-navy > h2 + p, .card-navy > h3 + p,
.card-accent > h2 + p, .card-accent > h3 + p { margin-top: var(--sp-3); }

/* Numbered feature card, e.g. "Why choose JMV" */
.feature-card {
  background: linear-gradient(180deg, var(--white) 0%, var(--jmv-surface) 100%);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
  padding: var(--card-pad);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  box-shadow: var(--sh-card);
  transition: box-shadow var(--t-base), transform var(--t-base), border-color var(--t-base);
}
.feature-card:hover {
  box-shadow: var(--sh-card-hover);
  transform: translateY(var(--card-lift));
  border-color: var(--border-strong);
}
.feature-card__title { margin: 0; font-size: var(--fs-h5); }
.feature-card__text { margin: 0; font-size: var(--fs-base); color: var(--body); }

/* About — lifecycle list item (numbered card with accent edge) */

/* Media frame — mirrors the navy image blocks of the approved design.
   Real images sit on top; the label remains visible while assets are pending. */
.media {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--r-lg);
  background: linear-gradient(170deg, var(--white) 0%, var(--jmv-ice) 100%);
  box-shadow: inset 0 0 0 1px var(--border);
  color: var(--muted);
  aspect-ratio: 4 / 3;
}
/* Pending photo: a dashed well, so the slot reads as intentional */
.media:not(.has-image)::after {
  content: "";
  position: absolute;
  inset: 10px;
  border: 1px dashed var(--border-strong);
  border-radius: calc(var(--r-lg) - 6px);
  pointer-events: none;
}
/* A real photograph is always a clean rectangle */
.media.has-image { box-shadow: inset 0 0 0 1px var(--border-strong); }
.media--wide { aspect-ratio: 16 / 9; }
.media--card { aspect-ratio: 5 / 4; }
.media--landscape { aspect-ratio: 16 / 10; }
/* Artwork rather than photography: no frame, and the edges feather out so
   the graphic sits on the page instead of in a box. An infographic must
   never be cropped, so the frame follows the image rather than a ratio. */
.media--blend, .media--blend.has-image {
  background: none;
  box-shadow: none;
  border-radius: 0;
}
.media--blend.has-image { aspect-ratio: auto; }
.media--blend::after { content: none; }
/* .media img sets position/height, so this needs the extra class to win */
.media.media--blend img {
  position: relative;
  inset: auto;
  height: auto;
  object-fit: contain;
  -webkit-mask-image: radial-gradient(125% 125% at 50% 50%, #000 58%, transparent 100%);
  mask-image: radial-gradient(125% 125% at 50% 50%, #000 58%, transparent 100%);
}
.media--blend .media__label { position: relative; inset: auto; aspect-ratio: 16 / 9; }

.media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-slow), opacity var(--t-base);
}
.media img[data-missing] { opacity: 0; }

/* ---- Product images ---------------------------------------------------
   Product photography is supplied as transparent cut-outs on a 5:4 canvas,
   not as rectangular photographs. Cropping one to a photographic frame
   slices the top and bottom off the unit — 22% of it in the family rail,
   30% in a related-product card, 46% in a mobile row — so a product image
   is *contained* instead: the whole unit, centred, never cropped and never
   stretched, with a margin so it never touches the frame edge. One
   treatment, applied wherever a product image appears. */
.media--product { aspect-ratio: 5 / 4; }
.media.media--product img {
  object-fit: contain;
  object-position: center;
  /* Percentage padding resolves against the frame's own width, so the
     margin stays proportional from a 112px thumbnail to a 600px hero. */
  padding: 7%;
}
/* A cut-out sits on the panel rather than behind a photograph's keyline */
.media.media--product.has-image { box-shadow: inset 0 0 0 1px var(--border); }

.media__label {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .35rem;
  padding: var(--sp-4);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  color: var(--label);
  text-align: center;
  pointer-events: none;
}
.media__label svg { width: 26px; height: 26px; opacity: .65; }
.media.has-image .media__label { display: none; }

.card-jmv--hover:hover .media img:not([data-missing]) { transform: scale(1.04); }

/* Overlay media card (industries, gallery) */
.overlay-card {
  position: relative;
  /* Two rows: the photo well, then the caption, and it is the *caption*
     that absorbs any extra height. The tiles sit in a grid with
     `grid-auto-rows: 1fr`, so every card in a row is as tall as the
     tallest one; when the photo well took the surplus instead, a caption
     that wrapped to two lines on one tile stretched its neighbours' photo
     wells past 4:3, and `object-fit: cover` then cropped each photo by a
     different amount. On a phone, where "Food Industry" wraps and
     "Hotels" does not, five tiles were being shown at five different
     aspect ratios — measured at 1.33, 1.06 and 0.90 side by side.
     The column is explicitly shrinkable — an `auto` column takes its
     width from the longest word in the caption and would push the photo
     past the tile. */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
  overflow: hidden;
  background: linear-gradient(180deg, var(--white) 0%, var(--jmv-surface) 100%);
  color: var(--ink);
  box-shadow: var(--sh-card);
  isolation: isolate;
  transition: box-shadow var(--t-base), transform var(--t-base), border-color var(--t-base);
}
/* The well fixes the photo height at 4:3, at every width */
.overlay-card::before {
  content: "";
  grid-area: 1 / 1;
  aspect-ratio: 4 / 3;
  border-radius: var(--r-lg);
  background: linear-gradient(170deg, var(--white) 0%, var(--jmv-ice) 100%);
  box-shadow: inset 0 0 0 1px var(--border);
}
.overlay-card img {
  grid-area: 1 / 1;
  position: relative;
  inset: auto;
  width: 100%;
  /* The photo carries the same 4/3 the well does, rather than filling
     whatever height the row happens to be. Every tile is then cropped
     identically, on every screen. The `height: auto` matters: a percentage
     height inside an `auto` row resolves to the content height, which for
     a landscape photo in a narrow column is not 4:3. */
  height: auto;
  aspect-ratio: 4 / 3;
  border-radius: var(--r-lg);
  object-fit: cover;
  transition: transform var(--t-slow);
}
/* Soft fade at the foot of the photo */
.overlay-card::after {
  content: "";
  grid-area: 1 / 1;
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, rgba(14, 41, 91, 0) 52%, rgba(14, 41, 91, .12) 100%);
  pointer-events: none;
}
.overlay-card__content {
  grid-area: 2 / 1;
  position: static;
  display: flex;
  flex-direction: column;
  gap: .2rem;
  padding: var(--sp-4) var(--sp-3);
  text-align: center;
}
.overlay-card__title {
  margin: 0;
  overflow-wrap: anywhere;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--fs-h5);
  font-weight: 800;
  letter-spacing: -.015em;
}
.overlay-card__meta { margin: 0; font-size: var(--fs-sm); color: var(--muted); }
.overlay-card img[data-missing] { opacity: 0; }
.overlay-card__placeholder {
  grid-area: 1 / 1;
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .35rem;
  font-size: var(--fs-sm);
  color: var(--muted);
  text-align: center;
  pointer-events: none;
}
.overlay-card__placeholder::after {
  content: "";
  position: absolute;
  inset: 10px;
  border: 1px dashed var(--border-strong);
  border-radius: calc(var(--r-lg) - 6px);
}
.overlay-card__placeholder svg { width: 26px; height: 26px; opacity: .55; }
.overlay-card.has-image .overlay-card__placeholder { display: none; }
/* The industry tiles are a showcase, not links, so the hover stays a gentle
   lift of the photo rather than a click affordance. */
.overlay-card:hover { border-color: var(--border-strong); }
.overlay-card:hover img { transform: scale(1.04); }

/* --------------------------------------------------------------------------
   8. Top bar + header + navigation
   -------------------------------------------------------------------------- */
.topbar {
  background: var(--grad-band);
  color: rgba(255, 255, 255, .82);
  font-size: var(--fs-sm);
}
.topbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: 34px;
  padding-block: .3rem;
}
.topbar__tagline { color: #6BA5F2; font-weight: 600; letter-spacing: -.005em; }
.topbar__meta {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  flex-wrap: wrap;
}
.topbar__meta a { color: rgba(255, 255, 255, .86); }
.topbar__meta a:hover { color: #fff; }
.topbar__sep { color: rgba(255, 255, 255, .28); }

/* The mount point must not box the header, or sticky would only work inside
   the wrapper's own height. */
/* The chrome is rendered by components.js into this placeholder, and the
   placeholder is `display: contents` so the topbar and the header itself sit
   in the page flow exactly as if they had been written there by hand.

   While it is still empty it has no box at all, which meant the first paint
   put the page at the very top of the viewport and the arriving header then
   pushed everything down by the full height of the chrome — a 121px jump on
   desktop, and the single largest layout shift on every page of the site.
   `:empty` therefore gives it that height to hold until the markup lands.
   `--chrome-h` is the topbar plus the header bar, measured at each
   breakpoint; the value steps down in responsive.css alongside them. */
#site-header:empty {
  display: block;
  height: var(--chrome-h);
}
#site-header { display: contents; }

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: rgba(255, 255, 255, .97);
  backdrop-filter: saturate(140%) blur(8px);
  border-bottom: 1px solid var(--border-soft);
  transition: box-shadow var(--t-base);
}
.site-header.is-scrolled { box-shadow: 0 10px 30px rgba(14, 41, 91, .07); }
.site-header__inner {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-height: var(--header-h);
  padding-block: var(--sp-3);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  flex: none;
  color: var(--ink);
}
.brand:hover { color: var(--ink); }

/* The brand lockup is the supplied logo file — assets/logos/favicon.svg —
   used at every size. Height drives the width; the aspect ratio is the
   file's own. On the navy footer the same file is knocked out to white so
   there is still only one logo asset in the project. */
.brand__logo {
  display: block;
  width: auto;
  height: 56px;
  flex: none;
}
.brand__logo--sm { height: 34px; }
/* The footer lockup is the same coloured artwork as the header's. It used to
   be knocked out to a white silhouette, which threw away the droplet and the
   two-tone wordmark; but the logo is navy on transparent and the footer is
   navy, so it needs a ground of its own to sit on. Hence the plate: white,
   softly rounded, sized so the whole brand row keeps the height it had —
   `box-sizing: border-box` means the padding comes out of the 56px rather
   than adding to it, so nothing in the footer moves. */
.brand--light .brand__logo {
  padding: .4rem .6rem;
  background: #fff;
  border-radius: var(--r-md, 10px);
  box-shadow: 0 4px 14px rgba(3, 14, 33, .22);
}

.primary-nav { margin-left: var(--sp-8); margin-right: auto; }
.primary-nav__list {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.primary-nav__list > li { margin: 0; }
.primary-nav__link {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  min-height: 44px;
  padding: .5rem .8rem;
  border-radius: var(--r-sm);
  color: var(--ink);
  font-size: var(--fs-md);
  font-weight: 600;
  letter-spacing: -.012em;
  white-space: nowrap;
  transition: color var(--t-fast), background var(--t-fast);
}
.primary-nav__link:hover { color: var(--blue-600); background: var(--blue-50); }
.has-dropdown[data-open="true"] > .primary-nav__link { color: var(--blue-600); background: var(--blue-50); }
.primary-nav__link[aria-current="page"], .primary-nav__link.is-active { font-weight: 700; color: var(--blue-600); }
.primary-nav__caret { font-size: .5rem; color: var(--muted); transition: transform var(--t-fast); }
.has-dropdown[data-open="true"] .primary-nav__caret { transform: rotate(180deg); }

/* Mega panel — a detached rounded card sitting just under the header bar.
   It is positioned against the header shell so it always clears the full
   header height; components.js sets --mega-x so the panel centres on its
   own trigger and stays inside the container. */
.mega {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 5;
  width: min(1060px, calc(100vw - var(--gutter) * 2));
  padding: clamp(1.5rem, 1rem + 1.2vw, 2.25rem) clamp(1.5rem, 1rem + 1.2vw, 2.25rem) var(--sp-5);
  background: var(--white);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-2xl);
  box-shadow: 0 30px 60px rgba(11, 36, 71, .16);
  opacity: 0;
  visibility: hidden;
  transform: translate3d(var(--mega-x, 0px), -12px, 0);
  /* visibility switches instantly so the panel is clickable as soon as it
     opens; only opacity and transform animate. */
  transition: opacity var(--t-base), transform var(--t-base);
}
.mega--sm { width: min(820px, calc(100vw - var(--gutter) * 2)); }
.has-dropdown[data-open="true"] > .mega {
  opacity: 1;
  visibility: visible;
  transform: translate3d(var(--mega-x, 0px), 0, 0);
}

.mega__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(1.25rem, .8rem + 1.4vw, 2.5rem);
  align-items: start;
}
.mega--cards-only .mega__inner,
.mega--tech .mega__inner { grid-template-columns: minmax(0, 1fr); }

 /* Two rows filled column by column, so each column pairs two families —
    the approved arrangement. Extra families create further 1fr columns
    inside the panel instead of overflowing it. */
.mega__cols {
  display: grid;
  grid-template-rows: repeat(2, auto);
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: clamp(1.25rem, .9rem + 1vw, 1.75rem) clamp(1.25rem, .8rem + 1.4vw, 2.25rem);
  align-items: start;
}
.mega__group { min-width: 0; }

/* The group heading is a label, not a link — the products under it are */
.mega__group-title {
  display: block;
  margin-bottom: .5rem;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--label);
}

.mega__list { list-style: none; margin: 0; padding: 0; }
.mega__list li { margin: 0; }
.mega__link {
  padding: .3rem 0;
  min-height: 34px;
  border-radius: var(--r-xs);
  color: var(--ink);
  font-size: var(--fs-base);
  line-height: 1.35;
}
.mega__link:hover { background: none; color: var(--blue-600); }

/* Products panel — the family rail on the left, the selected family's
   product groups on the right. components.js switches the panel on hover
   and on keyboard focus, and keeps the foot label in step. */
.mega--split { width: min(1180px, calc(100vw - var(--gutter) * 2)); }
.mega--split .mega__inner {
  grid-template-columns: minmax(0, 288px) minmax(0, 1fr);
  gap: clamp(1.25rem, .8rem + 1.4vw, 2.25rem);
}

.mega__rail {
  display: grid;
  gap: .15rem;
  align-content: start;
  margin: 0;
  padding: 0 clamp(1rem, .6rem + 1vw, 1.5rem) 0 0;
  list-style: none;
  border-right: 1px solid var(--border-soft);
}
.mega__rail li { margin: 0; }
.mega__rail-link {
  justify-content: space-between;
  gap: var(--sp-3);
  padding: .65rem .9rem;
  min-height: 46px;
  border-radius: var(--r-lg);
  color: var(--ink);
  font-size: var(--fs-md);
  font-weight: 600;
  letter-spacing: -.012em;
}
.mega__rail-link:hover,
.mega__rail-link.is-active { background: var(--blue-50); color: var(--blue-600); }
.mega__rail-caret {
  flex: none;
  color: var(--label);
  font-size: 1.125rem;
  line-height: 1;
  transition: transform var(--t-fast), color var(--t-fast);
}
.mega__rail-link:hover .mega__rail-caret,
.mega__rail-link.is-active .mega__rail-caret {
  color: var(--blue-600);
  transform: translateX(2px);
}

.mega__panels { min-width: 0; }
.mega__panel { min-width: 0; animation: mega-panel var(--t-base) both; }
@keyframes mega-panel {
  from { opacity: 0; transform: translate3d(8px, 0, 0); }
  to   { opacity: 1; transform: none; }
}

/* Product groups sit three across and wrap onto further rows */
.mega--split .mega__cols {
  grid-template-rows: none;
  grid-auto-flow: row;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.5rem, 1rem + 1.2vw, 2.25rem) clamp(1.25rem, .8rem + 1.4vw, 2.25rem);
}

/* Technology panel — a droplet, the technology name and what it does */
.mega-tech {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.25rem, .8rem + 1.2vw, 2rem) clamp(1.25rem, .8rem + 1.4vw, 2.5rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
.mega-tech li { margin: 0; min-width: 0; }
.mega-tech__link {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: .25rem 0;
  min-height: 48px;
}
.mega-tech__drop {
  display: grid;
  place-items: center;
  /* Sized for the technology logo it carries: at 46px the artwork had about
     30px to work with and read as an empty droplet. */
  width: 58px;
  aspect-ratio: var(--drop-ratio);
  padding-top: 14px;
  color: var(--jmv-navy-800);
  font-family: var(--font-display);
  font-size: .4375rem;
  font-weight: 800;
  letter-spacing: -.01em;
  line-height: 1;
  flex: none;
  transition: transform var(--t-base), filter var(--t-base);
}
.mega-tech__link:hover .mega-tech__drop { transform: translateY(-3px) scale(1.06); }
.mega-tech__body { display: grid; gap: .1rem; min-width: 0; }
.mega-tech__body strong {
  font-family: var(--font-display);
  font-size: var(--fs-h6);
  font-weight: 800;
  letter-spacing: -.015em;
  color: var(--ink);
}
.mega-tech__body span { font-size: var(--fs-sm); color: var(--body); }
.mega-tech__link:hover .mega-tech__body strong { color: var(--blue-600); }

.mega__cards { display: grid; gap: var(--sp-3); align-content: start; min-width: 190px; }
.mega--cards-only .mega__cards {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  min-width: 0;
}
.mega__card {
  display: flex;
  flex-direction: column;
  gap: .1rem;
  align-items: flex-start;
  justify-content: center;
  min-height: 62px;
  padding: .85rem 1.25rem;
  border: 1px solid var(--border-soft);
  border-radius: var(--r-lg);
  background: var(--jmv-surface-2);
  transition: background var(--t-fast), border-color var(--t-fast), transform var(--t-fast);
}
.mega__card strong {
  font-family: var(--font-display);
  font-size: var(--fs-h6);
  font-weight: 800;
  letter-spacing: -.015em;
  color: var(--ink);
}
.mega__card span { font-size: var(--fs-sm); color: var(--body); }
.mega__card:hover {
  background: var(--bg-deep);
  border-color: var(--border-deep);
  transform: translateY(-1px);
}
.mega__card:hover strong { color: var(--blue-700); }

.mega__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
}
.mega__kicker {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}
.mega__all {
  padding: 0;
  min-height: 40px;
  color: var(--blue-600);
  font-weight: 700;
  background: none;
}
.mega__all:hover { background: none; color: var(--blue-700); }

.dropdown__link {
  display: flex;
  align-items: center;
  padding: .6rem .75rem;
  border-radius: var(--r-sm);
  color: var(--ink);
  font-size: var(--fs-base);
  min-height: 44px;
}
.dropdown__link:hover { background: var(--bg); color: var(--blue-700); }

/* Card links carry both classes — keep the card's own alignment */
.mega .mega__card { align-items: flex-start; }

@media (pointer: coarse) {
  .mega__link, .mega__all { min-height: 44px; }
}

.header-actions { display: flex; align-items: center; gap: var(--sp-3); flex: none; }
.header-actions .btn-outline-jmv { border-color: var(--border-strong); box-shadow: var(--sh-xs); }

.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--white);
  cursor: pointer;
}
.nav-toggle__bars, .nav-toggle__bars::before, .nav-toggle__bars::after {
  display: block;
  width: 20px;
  height: 2px;
  background: var(--ink);
  border-radius: 2px;
  content: "";
  position: relative;
}
.nav-toggle__bars::before { position: absolute; top: -6px; }
.nav-toggle__bars::after { position: absolute; top: 6px; }

/* Page scroll lock while the menu is open. `overflow: hidden` on its own
   still lets a touch drag move the page underneath on iOS, so the body is
   also pinned at the offset it was scrolled to; components.js writes the
   `top` and restores the position on close. */
.is-nav-open, .is-nav-open body { overflow: hidden; }
.is-nav-open body {
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
}

/* Mobile menu panel
   The overlay is exactly one viewport tall and the panel fills it, so the
   only thing that can ever grow is the nav list — and that is the one box
   allowed to scroll. `dvh` is what keeps the footer CTAs clear of the
   browser's own collapsing toolbar on a phone; `vh` is the fallback. */
.mobile-menu {
  position: fixed;
  inset: 0;
  height: 100vh;
  height: 100dvh;
  z-index: var(--z-menu);
  pointer-events: none;
}
.mobile-menu__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(8, 30, 70, .55);
  opacity: 0;
  transition: opacity var(--t-base);
}
.mobile-menu__panel {
  position: absolute;
  inset-block: 0;
  right: 0;
  width: min(420px, 100%);
  background: var(--white);
  display: flex;
  flex-direction: column;
  /* A column flex container gives every item an automatic min-content
     minimum height. Without the `min-height: 0` on the body below, a long
     submenu makes the list refuse to shrink: it pushes the footer off the
     panel and nothing scrolls. */
  min-height: 0;
  transform: translateX(100%);
  transition: transform var(--t-base);
  box-shadow: var(--sh-lg);
}
.mobile-menu.is-open { pointer-events: auto; }
.mobile-menu.is-open .mobile-menu__backdrop { opacity: 1; }
.mobile-menu.is-open .mobile-menu__panel { transform: translateX(0); }

.mobile-menu__head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
}
.mobile-menu__title {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  font-family: var(--font-display);
  font-size: var(--fs-h5);
  font-weight: 800;
  color: var(--ink);
}
.mobile-menu__close {
  display: grid;
  place-items: center;
  width: var(--tap);
  height: var(--tap);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--white);
  font-size: 1.25rem;
  line-height: 1;
  color: var(--ink);
  cursor: pointer;
}
/* The single scroll container: whatever the open submenus add up to, the
   list scrolls inside the panel while the head and foot stay put.
   `overscroll-behavior` stops a flick at either end reaching the page. */
.mobile-menu__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.mobile-menu__foot {
  flex: 0 0 auto;
  padding: var(--sp-4) var(--sp-5) calc(var(--sp-5) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--border);
  display: grid;
  gap: var(--sp-3);
}

.m-nav { list-style: none; margin: 0; padding: 0; }
.m-nav li { margin: 0; }
.m-nav__link,
.m-nav__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  width: 100%;
  min-height: 56px;
  padding: var(--sp-4) var(--sp-5);
  border: 0;
  border-bottom: 1px solid var(--border);
  background: none;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.m-nav__toggle[aria-expanded="true"] { color: var(--blue-600); }
.m-nav__sign { font-size: 1.25rem; font-weight: 500; color: var(--muted); line-height: 1; }
.m-nav__toggle[aria-expanded="true"] .m-nav__sign { color: var(--blue-600); }

.m-nav__panel { display: none; background: var(--bg); }
.m-nav__panel.is-open { display: block; }
.m-nav__group-title {
  padding: var(--sp-3) var(--sp-5) .25rem;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.m-nav__sub {
  list-style: none;
  margin: 0;
  padding: 0;
}
.m-nav__sub .m-nav__link,
.m-nav__sub .m-nav__toggle {
  padding-left: calc(var(--sp-5) + var(--sp-3));
  font-size: var(--fs-base);
  font-weight: 600;
  border-bottom: 1px solid rgba(229, 234, 241, .8);
  background: none;
}
.m-nav__sub .m-nav__sub .m-nav__link {
  padding-left: calc(var(--sp-5) + var(--sp-7));
  font-family: var(--font-body);
  font-weight: 500;
  color: var(--body);
  min-height: 48px;
}

/* --------------------------------------------------------------------------
   9. Hero
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  background: var(--grad-hero);
  color: var(--body);
  overflow: hidden;
  isolation: isolate;
}
.hero h1, .hero h2 { color: var(--ink); }

/* Decorative water layer — droplets, ripples and a slow flow line.
   Animates transform/opacity only, so it stays off the main thread. */
.hero-fx {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.hero-fx__ripples,
.hero-fx__drops { position: absolute; inset: 0; display: block; }

/* Glassy water orbs floating behind the copy. Perfect spheres — the teardrop
   silhouette is reserved for badges, bullets and the falling droplets. */
.hero-fx__ripples > span {
  position: absolute;
  animation: drop-float 13s ease-in-out infinite alternate;
}
/* Positions trace the approved composition: one large orb behind the kicker,
   two small satellites, and a half-visible orb at the foot of the copy. */
.hero-fx__ripples > span:nth-child(1) { left: 4.8%; top: 7.9%;  width: 148px; aspect-ratio: 1; opacity: .85; }
.hero-fx__ripples > span:nth-child(2) { left: 22.1%; top: 23.8%; width: 18px;  aspect-ratio: 1; opacity: .9; animation-duration: 9s; animation-delay: -3s; }
.hero-fx__ripples > span:nth-child(3) { left: 14.1%; top: 48.8%; width: 56px;  aspect-ratio: 1; opacity: .8; animation-duration: 17s; animation-delay: -6s; }
.hero-fx__ripples > span:nth-child(4) { left: 3.1%; top: 65%;   width: 105px; aspect-ratio: 1; opacity: .72; animation-duration: 15s; animation-delay: -9s; }

.hero-fx__drops { opacity: .4; }
.hero-fx__drops > span {
  position: absolute;
  bottom: -12%;
  width: 22px;
  height: 22px;
  background: var(--grad-drop);
  box-shadow: var(--sh-drop);
  opacity: 0;
  animation: hero-drop 16s linear infinite;
}
/* Varied sizes keep the rise natural rather than uniform */
.hero-fx__drops > span:nth-child(1)  { left: 5%;  width: 26px; height: 26px; animation-duration: 19s; animation-delay: 0s; }
.hero-fx__drops > span:nth-child(2)  { left: 14%; width: 16px; height: 16px; animation-duration: 23s; animation-delay: 2.5s; }
.hero-fx__drops > span:nth-child(3)  { left: 23%; width: 36px; height: 36px; animation-duration: 17s; animation-delay: 5s; }
.hero-fx__drops > span:nth-child(4)  { left: 33%; width: 21px; height: 21px; animation-duration: 25s; animation-delay: 1.5s; }
.hero-fx__drops > span:nth-child(5)  { left: 43%; width: 46px; height: 46px; animation-duration: 21s; animation-delay: 7s; }
.hero-fx__drops > span:nth-child(6)  { left: 54%; width: 18px; height: 18px; animation-duration: 20s; animation-delay: 3.5s; }
.hero-fx__drops > span:nth-child(7)  { left: 64%; width: 31px; height: 31px; animation-duration: 18s; animation-delay: 9s; }
.hero-fx__drops > span:nth-child(8)  { left: 74%; width: 24px; height: 24px; animation-duration: 26s; animation-delay: 6s; }
.hero-fx__drops > span:nth-child(9)  { left: 84%; width: 40px; height: 40px; animation-duration: 22s; animation-delay: 11s; }
.hero-fx__drops > span:nth-child(10) { left: 93%; width: 17px; height: 17px; animation-duration: 24s; animation-delay: 4s; }

@keyframes hero-drop {
  0%   { transform: translate3d(0, 0, 0) scale(.55); opacity: 0; }
  14%  { opacity: .45; }
  70%  { opacity: .28; }
  100% { transform: translate3d(18px, -80vh, 0) scale(1); opacity: 0; }
}

/* --------------------------------------------------------------------------
   Right-hand hero visual — an image stack blended into the light wash.
   No frame, no hard edge: a soft radial glow sits behind it and the picture
   itself is masked away at the top and bottom so it dissolves into the page.
   Drop photos at assets/images/hero/hero-1.jpg … hero-5.jpg (one per slide).
   -------------------------------------------------------------------------- */
.hero-visual {
  position: absolute;
  top: 52%;
  /* Aligned to the page container, not the viewport edge */
  right: max(var(--gutter), calc((100% - var(--container)) / 2 + var(--gutter)));
  z-index: 1;
  width: min(36%, 560px);
  transform: translateY(-50%);
  pointer-events: none;
  isolation: isolate;
}

/* Diffuse halo behind the hero illustration */
.hero-visual__frame {
  position: relative;
  display: block;
  margin: 0;
  aspect-ratio: 7 / 8;
}
.hero-visual__frame::before {
  content: "";
  position: absolute;
  inset: -14%;
  z-index: -1;
  pointer-events: none;
  background:
    /* the original diffuse halo */
    radial-gradient(60% 55% at 50% 45%, rgba(180, 222, 255, .55), rgba(180, 222, 255, 0) 72%),
    /* light thrown out to either side by the splash */
    radial-gradient(34% 26% at 11% 42%, rgba(41, 193, 223, .22), rgba(41, 193, 223, 0) 72%),
    radial-gradient(34% 26% at 89% 46%, rgba(16, 104, 232, .16), rgba(16, 104, 232, 0) 72%),
    /* a slow sideways sheen, so the water is never quite still */
    linear-gradient(100deg,
      rgba(234, 243, 255, 0) 0%, rgba(143, 216, 234, .16) 26%,
      rgba(234, 243, 255, 0) 50%, rgba(41, 193, 223, .12) 74%,
      rgba(234, 243, 255, 0) 100%);
  background-size: 100% 100%, 100% 100%, 100% 100%, 220% 100%;
  background-position: 50% 50%, 50% 50%, 50% 50%, 0% 50%;
  background-repeat: no-repeat;
  /* Every layer above is painted into a rectangle, and the sideways sheen
     reaches the box edge at full strength — which drew a pale blue panel
     with a hard vertical edge down the left of the hero, exactly the square
     container the visual is meant not to have. The radial mask fades the
     whole pseudo-element out to nothing at all four sides, so the glow can
     never carry an edge of its own whatever is painted into it. */
  -webkit-mask-image: radial-gradient(closest-side, #000 0%, #000 42%,
                        rgba(0, 0, 0, .45) 70%, transparent 100%);
  mask-image: radial-gradient(closest-side, #000 0%, #000 42%,
                rgba(0, 0, 0, .45) 70%, transparent 100%);
  animation: hero-water-drift 26s linear infinite;
}

/* Vector splash behind the banner photograph (markup in index.html).
   Sits above the gradient glow and below the cut-out, so the unit always
   reads as standing in the water rather than in front of a picture of it. */
.hero-splash {
  position: absolute;
  /* Reaches past the frame on both sides so the spray is not clipped to the
     photograph's own box. */
  inset: -8% -16% -5%;
  z-index: 0;
  display: block;
  pointer-events: none;
}
.hero-splash svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}
/* The two sheets of water breathe very slowly, in opposite phase, which
   reads as movement without anything visibly looping. */
.hero-splash__sheets { animation: hero-splash-swell 11s ease-in-out infinite alternate; }
.hero-splash__sheets path:nth-child(2),
.hero-splash__sheets path:nth-child(4) { animation: hero-splash-swell 9s ease-in-out -4s infinite alternate; }
/* Droplets drift upward and fade, staggered so they never move as a block */
.hero-splash__drops circle { animation: hero-drop-rise 7s ease-in-out infinite; }
.hero-splash__drops circle:nth-child(2)  { animation-duration: 9s;    animation-delay: -1.2s; }
.hero-splash__drops circle:nth-child(3)  { animation-duration: 8.2s;  animation-delay: -3.4s; }
.hero-splash__drops circle:nth-child(4)  { animation-duration: 6.4s;  animation-delay: -2.1s; }
.hero-splash__drops circle:nth-child(5)  { animation-duration: 10s;   animation-delay: -5.6s; }
.hero-splash__drops circle:nth-child(6)  { animation-duration: 8.8s;  animation-delay: -0.8s; }
.hero-splash__drops circle:nth-child(7)  { animation-duration: 7.6s;  animation-delay: -4.2s; }
.hero-splash__drops circle:nth-child(8)  { animation-duration: 9.4s;  animation-delay: -6.1s; }
.hero-splash__drops circle:nth-child(9)  { animation-duration: 6.8s;  animation-delay: -2.7s; }
.hero-splash__drops circle:nth-child(10) { animation-duration: 10.6s; animation-delay: -7.3s; }
.hero-splash__drops circle:nth-child(11) { animation-duration: 8s;    animation-delay: -1.9s; }
.hero-splash__drops circle:nth-child(12) { animation-duration: 9.8s;  animation-delay: -5s; }
.hero-splash__drops circle:nth-child(13) { animation-duration: 7.2s;  animation-delay: -3.1s; }

/* A ring spreading out from the base, as though a drop had just landed */
.hero-visual__frame::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 8%;
  z-index: 0;
  width: 46%;
  aspect-ratio: 3 / 1;
  translate: -50% 0;
  border: 1.5px solid rgba(41, 193, 223, .3);
  border-radius: 50%;
  pointer-events: none;
  animation: hero-ripple 8s ease-out infinite;
}

@keyframes hero-water-drift {
  to { background-position: 50% 50%, 50% 50%, 50% 50%, 100% 50%; }
}
@keyframes hero-splash-swell {
  from { transform: translate3d(0, 4px, 0) scale(.985); opacity: .82; }
  to   { transform: translate3d(0, -4px, 0) scale(1.015); opacity: 1; }
}
@keyframes hero-drop-rise {
  0%   { transform: translateY(6px);   opacity: 0; }
  25%  { opacity: 1; }
  100% { transform: translateY(-26px); opacity: 0; }
}
@keyframes hero-ripple {
  0%   { transform: scale(.5);  opacity: 0; }
  22%  { opacity: .8; }
  100% { transform: scale(1.5); opacity: 0; }
}
/* Inline illustration of the treatment unit — the default hero visual.
   The wrapper carries the per-banner settle so the float below is left
   alone; two animations on one element would fight over `transform`. */
.hero-unit__wrap { position: relative; z-index: 1; display: block; width: 100%; height: 100%; }
.hero-unit__wrap.is-swap { animation: hero-settle var(--t-water) both; }
@keyframes hero-settle {
  from { opacity: .25; transform: translate3d(0, 22px, 0) scale(.96); }
  to   { opacity: 1;   transform: none; }
}
.hero-unit {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  filter: drop-shadow(0 30px 46px rgba(20, 60, 130, .12));
  animation: drop-float 14s ease-in-out infinite alternate;
}
/* One supplied photograph per banner. They crossfade with the copy; the
   illustration above shows through until the photos are dropped in. */
.hero-visual__img {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  /* The supplied banners are transparent cut-outs on a 7:8 canvas, the same
     ratio as the frame — so `contain` and `cover` are identical here, and
     `contain` keeps it that way if a banner is ever supplied at a different
     ratio, or the frame changes shape at a breakpoint. A cut-out has no
     edges to crop to, so cropping one only clips the unit itself. */
  object-fit: contain;
  object-position: center;
  opacity: 0;
  transform: scale(1.05);
  transition: opacity var(--t-water), transform 1.6s var(--jmv-ease);
}
/* Per banner, not per frame: a photo shows only once its own file has
   loaded, and the illustrated unit keeps standing in on any banner whose
   photo has not been supplied yet. So the four hero images can arrive one
   at a time instead of all or nothing. Without `:has()` the rule is
   dropped and the illustration simply stays behind the photo, which the
   absolutely positioned image paints over. */
.hero-visual__img.is-active[data-loaded] { opacity: 1; transform: none; }
.hero-visual:has(.hero-visual__img.is-active[data-loaded]) .hero-unit__wrap { display: none; }

/* Placeholder host — the illustration stands in for a missing photo, so this
   stays empty and hidden. Kept for the shared media fallback contract. */
.hero-visual__pending { display: none; }

/* Cursor water-ripple canvas — sits above the wash, below the copy */
.jmv-ripple-canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Home hero (slider) */
.hero-home__inner {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 0;
  padding-block: clamp(2.5rem, 1.4rem + 5.4vw, 7rem) clamp(2rem, 1.2rem + 3.6vw, 4rem);
  min-height: min(660px, 78vh);
}
/* Banners are stacked, so the copy crossfades in place instead of the
   column collapsing and re-expanding between slides. */
.hero-slide {
  grid-area: 1 / 1;
  max-width: 640px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(18px);
  transition: opacity var(--t-water), transform var(--t-water), visibility 0s linear .9s;
}
.hero-slide.is-active {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity var(--t-water), transform var(--t-water), visibility 0s;
}
.hero-slides {
  display: grid;
  align-items: start;
}
.hero-pill {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  height: 36px;
  padding: 0 1.25rem 0 .75rem;
  margin-bottom: var(--sp-6);
  background: rgba(255, 255, 255, .78);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, .9);
  border-radius: var(--r-pill);
  box-shadow: 0 10px 26px rgba(14, 41, 91, .08);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--blue-600);
}
/* Droplet bullet on the kicker */
.hero-pill::before {
  content: "";
  width: 12px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  flex: none;
  background: var(--grad-drop-bullet);
}
/* Two-line display heading: navy statement, blue continuation */
.hero-title {
  margin: 0 0 var(--sp-6);
  font-family: var(--font-display);
  font-size: var(--fs-hero);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -.03em;
  color: var(--ink);
}
.hero-title span { display: block; color: var(--blue-600); }
.hero-text {
  margin: 0 0 var(--sp-9);
  max-width: 520px;
  font-size: 1.1875rem;
  line-height: 1.62;
  color: var(--body);
}
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--sp-5); }

.hero-segments {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3) 14px;
  margin: var(--sp-7) 0 0;
  padding: 0;
  list-style: none;
  font-size: 1.0625rem;
  color: var(--body);
}
.hero-segments li { margin: 0; display: flex; align-items: center; gap: 14px; }
/* Cyan droplet separators, alternating towards blue down the row */
.hero-segments li + li::before {
  content: "";
  width: 9px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  flex: none;
  background: var(--grad-drop-bullet);
}

.hero-controls {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  margin-top: auto;
  padding-top: var(--sp-8);
}
/* Banner switcher. Drawn as brand-coloured rules on the hero wash rather
   than as a control sitting on top of it: no panel, no border, no shadow —
   only the bars themselves carry ink, so the row belongs to the hero. */
.hero-dots {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.hero-dots li { margin: 0; display: flex; }
.hero-dot {
  display: flex;
  align-items: center;
  width: 34px;
  height: 34px;               /* invisible tap target around a 4px rule */
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transition: width var(--t-base);
}
.hero-dot::after {
  content: "";
  display: block;
  width: 100%;
  height: 4px;
  border-radius: var(--r-pill);
  background: rgba(16, 104, 232, .2);
  transition: background var(--t-base), height var(--t-base);
}
.hero-dot:hover::after { background: rgba(16, 104, 232, .38); }
.hero-dot[aria-selected="true"] { width: 68px; }
.hero-dot[aria-selected="true"]::after {
  height: 5px;
  background: var(--grad-accent);
}
.hero-dot:focus-visible { outline-offset: 6px; }
/* Slide counter — balances the row and states position without a panel */
.hero-count {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.hero-count b { color: var(--ink); font-weight: 800; }

/* Metrics row directly under the hero — four separate light cards */
.stats-strip {
  position: relative;
  z-index: 3;
  background: var(--white);
  padding-block: var(--section-y-tight) 0;
}
.stats-strip__card {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-8);
  align-items: stretch;
}
/* Light surface, subtle border, premium hover shadow */
.stat {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .35rem;
  min-height: 116px;
  padding: var(--sp-5) var(--card-pad);
  background: var(--bg-tint);
  border: 1px solid var(--border-mid);
  border-radius: var(--card-r-sm);
  transition: background var(--t-base), box-shadow var(--t-base), border-color var(--t-base);
}
.stat:hover {
  background: var(--white);
  border-color: var(--border-strong);
  box-shadow: var(--sh-card-hover);
}
/* Soft glass orb tucked into the top-right corner of every metric */
.stat::after {
  content: "";
  position: absolute;
  top: -34px;
  right: -14px;
  width: 92px;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--grad-orb);
  opacity: .55;
  pointer-events: none;
  animation: drop-float 12s ease-in-out infinite alternate;
}
.stat:nth-child(2n)::after { animation-delay: -4s; }
.stat:nth-child(3n)::after { animation-delay: -8s; }
.stat > * { position: relative; z-index: 1; }
.stat__value {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-stat);
  font-weight: 800;
  letter-spacing: var(--ls-tight);
  line-height: 1.1;
  color: var(--ink);
}
.stat__label { display: block; font-size: 1rem; color: var(--muted); }

/* Page hero (inner pages) */
.page-hero {
  position: relative;
  background: var(--grad-hero);
  color: var(--body);
  overflow: hidden;
  isolation: isolate;
}
/* Glass orbs drifting behind the inner-page headline */
.page-hero::after,
.page-hero::before {
  content: "";
  position: absolute;
  z-index: 0;
  border-radius: 50%;
  background: var(--grad-orb);
  box-shadow: inset -8px -12px 26px rgba(255, 255, 255, .9),
              inset 8px 14px 30px rgba(41, 120, 220, .18),
              0 26px 50px rgba(20, 60, 130, .12);
  pointer-events: none;
  animation: drop-float 12s ease-in-out infinite alternate;
}
.page-hero::after  { top: 18%;  left: 2%;  width: clamp(90px, 9vw, 140px);  aspect-ratio: 1; }
.page-hero::before { top: 58%;  left: 4%;  width: clamp(40px, 4vw, 62px);   aspect-ratio: 1; animation-duration: 16s; animation-delay: -5s; }

.page-hero__inner {
  position: relative;
  z-index: 1;
  padding-block: var(--sp-6) var(--section-y-tight);
}
.page-hero h1 {
  color: var(--ink);
  margin-bottom: var(--sp-5);
  max-width: 20ch;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -.028em;
}
/* Accent half of a two-tone inner-page headline */
.page-hero h1 span { color: var(--blue-600); }
.page-hero p { color: var(--body); font-size: var(--fs-lg); max-width: 54ch; }
/* A hero whose copy runs the full container width, as the design draws the
   product and listing headers. Both the headline and the supporting line
   take the container — capping either one wrapped the copy a third of the
   way in from the right. `.page-hero--wide` is one class, so it ties with
   `.page-hero p` on specificity and has to be declared after it. */
.page-hero--wide h1,
.page-hero--wide p { max-width: none; }

/* Split hero: copy on the left, a picture on the right */
.page-hero--split .page-hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .9fr);
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
  align-items: center;
}
.page-hero--split .breadcrumbs,
.page-hero--split > .page-hero__crumbs,
.page-hero__inner > .page-hero__crumbs { grid-column: 1 / -1; }
/* Product and technology pages mount their breadcrumbs from script, unlike
   every other page, which writes them into the HTML. So this one row starts
   with no height and pushes the hero down the moment it arrives. Held here
   at exactly one line of breadcrumb text plus the trail's own bottom margin,
   using the same tokens the built trail uses — a short trail then costs
   nothing, and a trail that wraps to two lines on a phone still settles by
   one line rather than by the whole row. */
.page-hero__crumbs:empty {
  min-height: calc(var(--fs-sm) * var(--lh-body));
  margin-bottom: var(--sp-5);
}

/* The technology hero is a two-cell row — the copy and the droplet family —
   and both cells are mounted from script, so the row had no height at all
   at first paint and the whole page grew around it. Each cell holds its own
   measured floor; the row is the taller of the two, exactly as it is once
   built.

   The copy: 298px, the shortest of the six technologies (768px wide).
   The droplets: always the same six marks in a three-column grid, so the
   height depends only on the width — never below 391.7px between 320px and
   991px, and never below 410.6px above that. */
.td-hero__pending {
  display: grid;
  place-content: center;
  min-height: 290px;
}
#td-circles:empty { min-height: 390px; }

/* Same idea for the other rows that arrive from script above the fold.

   The technology row on technologies.html and the filter pills on
   products.html both hold a fixed set — six marks, ten pills — so their
   built height depends only on how they wrap at a given width, and the
   floors below were measured across the whole of each band.

   The About hero's photograph needs no measuring at all: its frame is a
   5:4 `.media`, so giving the empty container that same ratio reserves
   exactly the height the image will occupy, at every width. */
#tech-circles:empty { min-height: 185px; }
#product-filters:empty { min-height: 135px; }
.page-hero__media:empty { aspect-ratio: 5 / 4; }

/* ---- Space held for the rest of the script-built blocks ---------------
   Every page of this site writes its chrome and most of its body from the
   data files, into containers that start empty. At first paint those
   containers are flat, so the document is far shorter than it ends up: on
   the catalogue and article pages the footer sat inside the opening
   viewport and was then shoved several thousand pixels down the page.

   Each floor below is the *smallest* height that block was measured at,
   across every breakpoint from 320px to 1920px and, where the content
   varies, across every product, technology and article in the data. Taking
   the floor rather than an average matters: a reservation that came out
   larger than the real block would make the page jump back *up* when the
   content arrived, which is no better than jumping down.

   Where a block is always taller than the screen — the catalogue, a
   specification sheet, an article body — the reservation is simply a
   viewport, capped at the measured floor so the cap still holds on an
   unusually tall window. That keeps those three free of a number that
   would need revisiting every time a product or a post is added.

   All of them are `:empty`, so the reservation lifts the moment the real
   markup lands and can never constrain it. */

/* Contact — the two halves of the enquiry row, side by side or stacked */
#contact-tiles:empty { min-height: 495px; }   /* floor 499px @575 */
#contact-form:empty  { min-height: 700px; }   /* floor 705px @768 */

/* Blog listing */
#blog-grid:empty { min-height: 500px; }       /* floor 512px @768 */
#blog-faqs:empty { min-height: 415px; }       /* floor 421px @1440 */

/* Article page. The heading block is now as wide as the page container, so
   a title that used to run to three lines takes two — re-measured across
   both articles at every breakpoint, and each floor is the *smallest*
   height the built block was measured at in its band, so the reservation
   is never larger than the content that replaces it. */
#bd-hero .bd-hero__pending { min-height: 224px; }        /* floor 287px @1200-1728 */
#bd-body:empty { min-height: min(100vh, 5400px); }       /* floor 5461px @991 */

/* Product detail */
#pd-overview:empty { min-height: 120px; }                /* floor 124px @768 */
#pd-features:empty { min-height: 310px; }                /* floor 318px @1920 */
#pd-specs:empty    { min-height: min(100vh, 1600px); }   /* floor 1651px @1920 */
#pd-related:empty  { min-height: 370px; }                /* floor 378px @1440 */

/* Products catalogue */
#product-families:empty { min-height: min(100vh, 9000px); } /* floor 9398px @1024 */
.page-hero__media .media { border-radius: var(--r-xl); }
.page-hero__media { position: relative; z-index: 1; }
/* The droplet family beside an inner-page headline */
.page-hero__drops { position: relative; z-index: 1; }

/* Accent words in an inner-page headline pick up the brand blue */
.page-hero .eyebrow--cyan,
.hero .eyebrow--cyan { color: var(--blue-600); }

/* Breadcrumbs sit on light now */
.page-hero .breadcrumbs a { color: var(--blue-600); }
.page-hero .breadcrumbs a:hover { color: var(--blue-700); }
.page-hero .breadcrumbs li + li::before { color: var(--border-strong); }
.page-hero .breadcrumbs [aria-current] { color: var(--ink); }

/* --------------------------------------------------------------------------
   10. Breadcrumbs
   -------------------------------------------------------------------------- */
.breadcrumbs { font-size: var(--fs-sm); }
.breadcrumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .35rem var(--sp-2);
  margin: 0 0 var(--sp-5);
  padding: 0;
  list-style: none;
}
.breadcrumbs li { margin: 0; display: flex; align-items: center; gap: var(--sp-2); }
.breadcrumbs li + li::before { content: "/"; color: rgba(255, 255, 255, .35); }
.breadcrumbs a { color: rgba(208, 228, 255, .82); }
.breadcrumbs a:hover { color: #fff; }
.breadcrumbs [aria-current] { color: #fff; font-weight: 600; }


/* --------------------------------------------------------------------------
   11. Split feature block (About / product range intro)
   -------------------------------------------------------------------------- */
.split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), 1fr));
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
  align-items: center;
}
.split--top { align-items: start; }
.split__media { position: relative; }
.split__media .media { border-radius: var(--r-xl); }
/* Glass orb breaking the right edge of the image, as the design shows */
.split__orb {
  position: absolute;
  right: clamp(-64px, -4vw, -34px);
  bottom: 16%;
  z-index: 2;
  width: clamp(76px, 8vw, 118px);
  aspect-ratio: 1;
  pointer-events: none;
  animation: drop-float 12s ease-in-out infinite alternate;
}

.pillar-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: var(--sp-6);
  margin: var(--sp-7) 0;
  padding: 0;
  list-style: none;
}
.pillar-list li {
  position: relative;
  margin: 0;
  padding-left: var(--sp-7);
}
/* Droplet bullet replaces the old rule line */
.pillar-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .2rem;
  width: 13px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  background: var(--grad-drop-bullet);
}
.pillar-list strong { display: block; font-family: var(--font-display); font-size: var(--fs-h6); color: var(--ink); margin-bottom: .25rem; }
.pillar-list span { font-size: var(--fs-base); color: var(--body); }

/* --------------------------------------------------------------------------
   12. Product cards
   -------------------------------------------------------------------------- */
.product-card__eyebrow { color: var(--blue-600); }
.product-card__variants { display: none; }
/* The variant row is one line and always occupies the same height, so a
   family with no variants still lines up with the cards beside it. The
   summary takes up the slack, which keeps the row bottom-aligned. */
.product-card .card-jmv__text { flex: 1 1 auto; }
.product-card .chip-list {
  flex-wrap: nowrap;
  min-height: 36px;
  padding-top: var(--sp-3);
}
.product-card .chip-list li { min-width: 0; }
.product-card .chip-list .chip {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Products page — family block */
/* Each solution family is one white panel: a summary column on the left and
   its variants gridded on the right. */
.family {
  display: grid;
  grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
  gap: clamp(1.5rem, 1rem + 2.4vw, 2.75rem);
  align-items: start;
  padding: clamp(1.5rem, 1rem + 1.6vw, 2.25rem);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-2xl);
  box-shadow: var(--sh-card);
}
.family + .family { margin-top: var(--sp-8); }
/* The family summary reads label → name → count → photo → description, so
   the panel names what it is before it shows it. */
.family__head { display: grid; gap: var(--sp-2); justify-items: start; }
.family__head .eyebrow { margin: 0; }
.family__title { margin: 0; font-size: var(--fs-h4); }
.family__count {
  justify-self: start;
  margin-top: var(--sp-1);
  padding: .3rem .7rem;
  border-radius: var(--r-pill);
  background: var(--jmv-ice);
  color: var(--blue-600);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
}
.family__aside { display: grid; gap: var(--sp-5); align-content: start; }
.family__text { margin: 0; font-size: var(--fs-base); }

/* Product groups inside a family, labelled the way the mega panel labels
   them so the listing and the navigation read the same */
.family__body { display: grid; gap: var(--sp-8); min-width: 0; }
.family__group { min-width: 0; }
.family__group-title {
  margin: 0 0 var(--sp-4);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--label);
}
.variant-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-5);
  align-content: start;
}
/* Variant cards sit on the family panel as tinted tiles */
.variant-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--card-pad);
  background: var(--jmv-surface);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
  transition: background var(--t-base), box-shadow var(--t-base),
              transform var(--t-base), border-color var(--t-base);
}
.variant-card:hover {
  background: var(--white);
  box-shadow: var(--sh-card-hover);
  border-color: var(--border-strong);
  transform: translateY(var(--card-lift));
}

/* Every band reserves the number of lines its clamp allows, so a product
   card is the same height and its title, summary, applications line and
   footer sit at the same y whatever the copy runs to. `grid-auto-rows: 1fr`
   only equalises one row; families hold anywhere between one and twenty-one
   products, and a one-product family (Skid RO, Heat Pump, Quencher) has no
   row to be equalised against. */
.variant-card__head { display: flex; align-items: center; gap: var(--sp-3); }
.variant-card__title {
  margin: 0;
  font-size: var(--fs-h5);
  line-height: var(--lh-head);
  min-height: calc(2em * var(--lh-head));
}
.variant-card__text {
  margin: 0;
  flex: 1 1 auto;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  min-height: calc(3em * var(--lh-body));
}
.variant-card__meta {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  min-height: calc(2em * var(--lh-body));
  color: var(--muted);
}
.variant-card__foot { margin-top: auto; }

/* Light closing panel — "Not sure which product fits your site?" */
.panel-cta {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  overflow: hidden;
  padding: clamp(1.75rem, 1.2rem + 2vw, 2.75rem);
  background: linear-gradient(180deg, var(--jmv-surface) 0%, var(--jmv-ice) 100%);
  border: 1px solid var(--border);
  border-radius: var(--r-2xl);
}
.panel-cta::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -18px;
  width: clamp(96px, 10vw, 140px);
  aspect-ratio: var(--drop-ratio);
  translate: 0 -50%;
  clip-path: var(--clip-drop);
  border-radius: var(--shape-drop);
  background: var(--grad-drop);
  box-shadow: var(--sh-drop);
  opacity: .26;
  pointer-events: none;
  animation: drop-float 13s ease-in-out infinite alternate;
}
.panel-cta__copy { position: relative; z-index: 1; max-width: 40rem; }
.panel-cta__copy h2 { margin-bottom: var(--sp-3); font-size: var(--fs-h3); }
.panel-cta__copy p { margin: 0; }
.panel-cta__actions { position: relative; z-index: 1; display: flex; flex-wrap: wrap; gap: var(--sp-3); }

/* --------------------------------------------------------------------------
   13. Technology circles
   -------------------------------------------------------------------------- */
/* Home technology band: heading centred above one row of droplets */
.tech-band__inner {
  display: grid;
  justify-items: center;
  gap: clamp(1.75rem, 1rem + 2.6vw, 3.25rem);
  padding-block: var(--section-y);
  text-align: center;
}
.tech-circles {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1rem, .5rem + 1.6vw, 2.25rem);
  justify-content: center;
  align-items: flex-start;
  margin: 0;
  padding: 0;
  list-style: none;
}
.tech-circles li { margin: 0; }
/* Technology detail hero: the family of droplets laid out as a grid.
   The cells stretch and each droplet centres itself inside its own cell —
   centring the cell instead would size it to the longest technology name
   and push the last column past the container on a phone. */
.tech-circles--grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, .6rem + 1.4vw, 2rem);
}
.tech-circles--grid li { min-width: 0; }
.tech-circles--grid .tech-circle__disc { animation-duration: 12s; }
/* On light grounds the droplets need their own shadow and label colour */
.page-hero .tech-circle__disc { filter: drop-shadow(0 14px 24px rgba(14, 41, 91, .16)); }
.page-hero .tech-circle__name { color: var(--body); }
.page-hero .tech-circle.is-active .tech-circle__name { color: var(--blue-600); font-weight: 700; }
.page-hero .tech-circle.is-active .tech-circle__disc {
  filter: var(--drop-outline);
  transform: translateY(-6px) scale(1.06);
}
.tech-circle {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  color: #fff;
  text-align: center;
}
/* The droplet itself — the 3D treatment from the droplet design system */
.tech-circle__disc {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(92px, 8vw, 124px);
  aspect-ratio: var(--drop-ratio);
  padding-top: 28px;
  color: var(--jmv-navy-800);
  transition: transform var(--t-slow), filter var(--t-slow);
  filter: drop-shadow(0 18px 30px rgba(3, 14, 33, .34));
  animation: drop-float 10s ease-in-out infinite alternate;
}
/* Staggered float and staggered rest height, so the row reads as a set of
   suspended droplets rather than a straight line of icons. */
.tech-circles li:nth-child(2n) .tech-circle__disc { animation-duration: 12s; animation-delay: -3s; }
.tech-circles li:nth-child(3n) .tech-circle__disc { animation-duration: 14s; animation-delay: -6s; }
.tech-circles li:nth-child(2n) .tech-circle { padding-top: clamp(1.5rem, 3vw, 3.25rem); }
.tech-circle:hover .tech-circle__disc,
.tech-circle:focus-visible .tech-circle__disc {
  transform: translateY(-10px) scale(1.05);
  filter: drop-shadow(0 26px 40px rgba(3, 14, 33, .46));
}
.tech-circle:hover .tech-circle__name { color: var(--cyan-500); }
.tech-circle__abbr {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(.95rem, .78rem + .42vw, 1.2rem);
  font-weight: 800;
  letter-spacing: -.03em;
  line-height: 1;
  color: var(--jmv-navy-800);
}
.tech-circle__kicker {
  display: block;
  margin-top: .2rem;
  font-size: .4375rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--jmv-drop-sub);
}
.tech-circle__name {
  font-size: var(--fs-base);
  color: rgba(255, 255, 255, .92);
  font-weight: 500;
}
/* On the blue band — the home page's Technology section and the Technologies
   page's own band — the droplet is pure white rather than the modelled
   white-to-blue teardrop used elsewhere. Two things carry the blue and both
   are switched off here: `--grad-drop` ends on `--jmv-blue`, and `--sh-drop`
   paints an inset blue at the bottom right. The silhouette, the float, the
   outer shadow that lifts it off the navy, the logo and the label are all
   untouched.

   Scoped to `.section--band` on purpose: the technology detail page lays the
   same droplets out on a light ground in `.page-hero`, where the modelling is
   what makes them read as droplets at all, and that is left alone. */
.section--band .tech-circle__disc::before {
  background: #fff;
  box-shadow: none;
}

/* Selected technology — the droplet takes the design's blue keyline */
.tech-circle.is-active .tech-circle__disc {
  filter: var(--drop-outline);
  transform: translateY(-6px) scale(1.06);
}
.tech-circle.is-active .tech-circle__name { color: var(--cyan-500); font-weight: 700; }

/* --------------------------------------------------------------------------
   14. Services / process
   -------------------------------------------------------------------------- */
.service-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--card-pad);
  background: rgba(255, 255, 255, .06);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(234, 243, 255, .16);
  border-radius: var(--card-r);
  transition: background var(--t-base), transform var(--t-base), border-color var(--t-base);
}
.service-card:hover {
  background: rgba(255, 255, 255, .1);
  border-color: rgba(143, 216, 234, .42);
  transform: translateY(var(--card-lift));
}
/* Droplet counter */
.service-card__num {
  display: inline-grid;
  place-items: center;
  width: 30px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  padding-top: 9px;
  filter: drop-shadow(0 6px 12px rgba(3, 14, 33, .3));
  font-family: var(--font-display);
  font-size: .5625rem;
  font-weight: 800;
  line-height: 1;
  color: var(--jmv-navy-800);
}
.service-card__title { margin: 0; font-size: var(--fs-h5); color: #fff; }
.service-card__text { margin: 0; font-size: var(--fs-base); color: var(--on-navy); }

/* A numbered flow: the steps in a row, a dashed connector through them and
   a droplet travelling along it from the first step to the last.

   The column count is the step count, set by the page script as `--steps`.
   It used to be `auto-fit`, which let the browser decide from the available
   width — so six steps could land as five plus an orphan on a second row
   while the connector below still ran across the first row only, and the
   droplet travelled past the orphan entirely. One row or none. */
.process {
  display: grid;
  grid-template-columns: repeat(var(--steps, 4), minmax(0, 1fr));
  gap: var(--sp-6);
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
  /* Width of the number column in the vertical mode (responsive.css), and
     the droplet diameter it has to hold. Named here so the column, the
     connector's position and the travelling droplet's position are all
     derived from one number instead of three that have to agree. */
  --flow-col: 56px;
}
.process::before {
  content: "";
  position: absolute;
  top: 32px;
  left: 8%;
  right: 8%;
  border-top: 1.5px dashed var(--border-deepest);
  z-index: 0;
}
/* A droplet travelling along the connector, left to right */
.process::after {
  content: "";
  position: absolute;
  top: 26px;
  left: 8%;
  width: 9px;
  aspect-ratio: var(--drop-ratio);
  clip-path: var(--clip-drop);
  border-radius: var(--shape-drop);
  background: var(--grad-drop-bullet);
  z-index: 0;
  pointer-events: none;
  animation: process-travel 14s ease-in-out infinite;
}
@keyframes process-travel {
  0%      { transform: translate3d(0, 0, 0);      opacity: 0; }
  6%      { opacity: 1; }
  92%     { opacity: 1; }
  100%    { transform: translate3d(76vw, 0, 0);   opacity: 0; }
}
/* The same journey down the vertical connector, used below 992px where the
   flow turns through ninety degrees. Same timings, same fade, same start
   and end points relative to the line — 8% in from each end, matching the
   inset the horizontal line uses left and right. */
@keyframes process-travel-y {
  0%      { top: 8%;   opacity: 0; }
  6%      { opacity: 1; }
  92%     { opacity: 1; }
  100%    { top: 92%;  opacity: 0; }
}
/* A numbered flow whose steps each carry a full sentence — the technology
   detail pages, against the home page's short labels. Same one-row column
   rule as `.process` above; kept as a named variant because the vertical
   mode in responsive.css gives it a little more room per step. */
.process--wide { grid-template-columns: repeat(var(--steps, 4), minmax(0, 1fr)); }

.process li { margin: 0; position: relative; z-index: 1; text-align: center; display: grid; justify-items: center; gap: var(--sp-2); }
.process__disc {
  position: relative;
  display: grid;
  place-items: center;
  width: 56px;
  aspect-ratio: var(--drop-ratio);
  padding-top: 15px;
  /* The design draws this droplet with a visible blue keyline */
  filter: var(--drop-outline);
  font-family: var(--font-display);
  font-size: var(--fs-base);
  font-weight: 800;
  color: var(--jmv-navy-800);
  animation: drop-float 9s ease-in-out infinite alternate;
}
/* Ripple ring around each step badge */
.process li::after {
  content: "";
  position: absolute;
  top: -10px;
  left: 50%;
  width: 78px;
  aspect-ratio: var(--drop-ratio);
  translate: -50% 0;
  border: 1px solid var(--cyan-300);
  border-radius: var(--shape-drop);
  opacity: 0;
  pointer-events: none;
  animation: ripple-splash 7s ease-out infinite;
}
.process li:nth-child(2n) .process__disc { animation-delay: -3s; }
.process li:nth-child(2n)::after { animation-delay: 1.4s; }
.process li:nth-child(3n) .process__disc { animation-delay: -5s; }
.process li:nth-child(3n)::after { animation-delay: 2.8s; }
.process__title { font-family: var(--font-display); font-weight: 700; color: var(--ink); font-size: var(--fs-base); }
.process__text { font-size: var(--fs-sm); color: var(--body); }

/* --------------------------------------------------------------------------
   15. Industries "also serving"
   -------------------------------------------------------------------------- */
/* Five equal industry cards, as the approved design lays them out */
.industry-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--sp-5);
}

.also-serving {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4) var(--sp-6);
  margin-top: var(--sp-8);
  padding: var(--sp-5) var(--card-pad);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
}
.also-serving h2, .also-serving h3 { flex: none; margin: 0; font-size: var(--fs-base); font-weight: 700; }
.also-serving .chip-list { flex: 1 1 auto; margin: 0; }

/* --------------------------------------------------------------------------
   16. Projects / clients / testimonials
   -------------------------------------------------------------------------- */
.project-card__tags { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; }
.project-card__meta { font-size: var(--fs-sm); color: var(--body); margin: 0; }

/* Seamless client logo marquee: the track carries the list twice, so the
   loop restarts with no visible jump. */
.logo-marquee {
  position: relative;
  overflow: hidden;
  padding-block: var(--sp-2);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.logo-marquee__track {
  display: flex;
  width: max-content;
  margin: 0;
  padding: 0;
  list-style: none;
  animation: logo-marquee 38s linear infinite;
  will-change: transform;
}
.logo-marquee:hover .logo-marquee__track,
.logo-marquee:focus-within .logo-marquee__track { animation-play-state: paused; }
.logo-marquee__track > li {
  flex: 0 0 auto;
  /* Spacing must be part of each item (not a flex gap) so the two halves of
     the track are exactly equal and the -50% loop is seamless. */
  margin: 0 var(--sp-5) 0 0;
  width: clamp(168px, 13vw, 210px);
}

@keyframes logo-marquee {
  from { transform: translate3d(-50%, 0, 0); }
  to { transform: translate3d(0, 0, 0); }
}

.client-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(45%, 150px), 1fr));
  gap: var(--sp-5);
  margin: 0;
  padding: 0;
  list-style: none;
}
.client-strip li { margin: 0; }
.client-logo {
  display: grid;
  place-items: center;
  min-height: 74px;
  padding: var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--card-r-sm);
  background: linear-gradient(180deg, var(--white) 0%, var(--jmv-surface) 100%);
  font-size: var(--fs-sm);
  color: var(--muted);
  text-align: center;
}
/* Shown in the customer's own colours, at full strength, in every state.
   A logo desaturated until it is hovered reads as disabled, and on a touch
   screen there is no hover to reveal the real thing at all. */
.client-logo img {
  max-height: 46px;
  width: auto;
  object-fit: contain;
}
.logo-marquee .client-logo {
  border-color: var(--border);
  min-height: 84px;
  padding: var(--sp-4) var(--sp-5);
}
.logo-marquee .client-logo img { width: 100%; max-height: 52px; }

.testimonial-shell {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  gap: clamp(2rem, 1rem + 4vw, 4rem);
  align-items: center;
}
.testimonial {
  position: relative;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  padding: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  box-shadow: var(--sh-lg);
  display: none;
}
/* Droplet ornament breaking the top-right corner */
.testimonial.is-active::before {
  content: "";
  position: absolute;
  top: -34px;
  right: -18px;
  width: clamp(64px, 6vw, 96px);
  aspect-ratio: 1;
  border-radius: var(--shape-drop);
  background: var(--grad-drop);
  box-shadow: inset 0 3px 8px rgba(255, 255, 255, .95), 0 16px 34px rgba(14, 41, 91, .12);
  pointer-events: none;
  animation: drop-float 10s ease-in-out infinite alternate;
}
.testimonial.is-active { display: block; }
.testimonial__mark {
  font-family: var(--font-display);
  font-size: 2.5rem;
  line-height: 1;
  color: var(--blue-500);
  margin-bottom: var(--sp-4);
}
.testimonial__quote {
  margin: 0 0 var(--sp-7);
  font-family: var(--font-display);
  font-size: var(--fs-h5);
  font-weight: 600;
  line-height: 1.5;
  color: var(--ink);
  letter-spacing: -.01em;
}
.testimonial__foot {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--border);
}
.testimonial__avatar {
  display: grid;
  place-items: center;
  width: 42px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  padding-top: 12px;
  background: var(--grad-drop);
  box-shadow: var(--sh-drop);
  color: var(--jmv-navy-800);
  /* Holds a customer logo, or the customer's initials when none was
     supplied (see home.js) — two characters, so they can sit a step larger
     than the four-character placeholder this replaced. */
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: .02em;
  flex: none;
  overflow: hidden;
}
.testimonial__avatar img { width: 100%; height: 100%; object-fit: cover; }
.testimonial__name { display: block; font-weight: 700; color: var(--ink); }
.testimonial__role { display: block; font-size: var(--fs-sm); color: var(--body); }
.testimonial__dots { display: flex; gap: var(--sp-2); margin-left: auto; list-style: none; padding: 0; }
.testimonial__dots li { margin: 0; }
.dot-btn {
  display: grid;
  place-items: center;
  width: 24px; height: 24px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.dot-btn::after {
  content: "";
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--border-strong);
  transition: background var(--t-fast);
}
.dot-btn[aria-selected="true"]::after { background: var(--blue-600); }
.slider-nav { display: flex; gap: var(--sp-3); margin-top: var(--sp-6); }

/* Square icon control — slider arrows. Same geometry as .nav-toggle so
   every small square control on the site is one size. */
.icon-btn {
  display: inline-grid;
  place-items: center;
  width: var(--tap);
  height: var(--tap);
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--white);
  color: var(--ink);
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--sh-xs);
  transition: background var(--t-fast), border-color var(--t-fast),
              color var(--t-fast), box-shadow var(--t-base);
}
.icon-btn:hover {
  background: var(--blue-50);
  border-color: var(--blue-500);
  color: var(--blue-700);
  box-shadow: var(--sh-sm);
}
.icon-btn--light { background: var(--white); }
.section--navy .icon-btn, .section--band .icon-btn {
  background: rgba(255, 255, 255, .08);
  border-color: rgba(234, 243, 255, .24);
  color: #fff;
  box-shadow: none;
}
.section--navy .icon-btn:hover, .section--band .icon-btn:hover {
  background: rgba(255, 255, 255, .16);
  border-color: var(--cyan-300);
  color: #fff;
}

/* --------------------------------------------------------------------------
   17. Blog cards
   -------------------------------------------------------------------------- */
.blog-card__meta { font-size: var(--fs-sm); color: var(--meta); margin: 0; }
.blog-card__title { margin: 0; font-size: var(--fs-h5); }

/* --------------------------------------------------------------------------
   18. FAQ accordion
   -------------------------------------------------------------------------- */
.faq { display: grid; gap: var(--sp-3); }
.faq__item {
  background: linear-gradient(180deg, var(--white) 0%, var(--jmv-surface) 100%);
  border: 1px solid var(--border);
  border-radius: var(--card-r-sm);
  overflow: hidden;
  transition: border-color var(--t-base), box-shadow var(--t-base);
}
.faq__item.is-open { border-color: var(--blue-500); box-shadow: 0 10px 26px rgba(16, 104, 232, .12); }
.faq__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  width: 100%;
  min-height: 56px;
  padding: var(--sp-5) var(--sp-6);
  border: 0;
  background: none;
  font-family: var(--font-display);
  font-size: var(--fs-h6);
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.faq__sign {
  display: grid;
  place-items: center;
  width: 17px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  flex: none;
  background: var(--grad-drop-bullet);
  opacity: .35;
  color: transparent;
  font-size: 0;
  transition: opacity var(--t-fast), transform var(--t-base);
}
.faq__item.is-open .faq__sign {
  opacity: 1;
  transform: scale(1.12);
}
.faq__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--t-base);
}
.faq__item.is-open .faq__panel { grid-template-rows: 1fr; }
.faq__panel > div { overflow: hidden; }
.faq__panel p { margin: 0; padding: 0 var(--sp-6) var(--sp-6); font-size: var(--fs-base); }

/* --------------------------------------------------------------------------
   20. Product detail
   -------------------------------------------------------------------------- */
.pd-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 1rem + 3vw, 3.5rem);
  align-items: center;
}
.pd-hero__copy { position: relative; z-index: 1; }
.pd-badges { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-5); }
.pd-badges__note { font-size: var(--fs-sm); color: var(--muted); }
.pd-title {
  margin: 0 0 var(--sp-4);
  font-size: var(--fs-h1);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -.028em;
  color: var(--ink);
}
.pd-lead { font-size: var(--fs-lg); margin-bottom: var(--sp-6); }
.pd-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 140px), 1fr));
  gap: var(--sp-4);
  margin-bottom: var(--sp-6);
  padding: 0;
  list-style: none;
}
.pd-facts > div {
  margin: 0;
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--border);
  border-radius: var(--card-r-sm);
  background: var(--white);
  box-shadow: var(--sh-card);
}
.pd-facts dt {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: .2rem;
}
.pd-facts dd { margin: 0; font-family: var(--font-display); font-weight: 700; color: var(--ink); }
/* Model range — one pill per model, the selected one filled */
.pd-models { margin-bottom: var(--sp-5); }
.pd-models__label { display: block; margin-bottom: var(--sp-3); }
.pd-models__row { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.pd-model {
  padding: .55rem 1.15rem;
  min-height: 44px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  background: var(--white);
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--fs-base);
  font-weight: 700;
  cursor: pointer;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.pd-model:hover { border-color: var(--blue-600); color: var(--blue-600); }
.pd-model[aria-selected="true"] {
  background: var(--blue-600);
  border-color: var(--blue-600);
  color: var(--white);
}

.pd-actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-6); }
.pd-note {
  padding: var(--sp-5);
  background: var(--bg);
  border-radius: var(--card-r-sm);
  font-size: var(--fs-base);
}
.pd-note p { margin: 0; }

/* The technology description runs the full container width, the way the
   product detail Overview does. Two caps have to come off for that: this
   container's, and `.lead`'s own 62ch — at this font size that resolves to
   about 772px, so lifting only the container would still stop the copy
   short of the shell. */
.tech-overview { display: grid; gap: var(--sp-5); }
.tech-overview .lead { max-width: none; margin: 0; }

/* The closing technical statement under a technology's benefits. Same
   quiet tinted block as `.pd-note`, squared off against the benefits grid
   above it. */
.tech-note {
  display: grid;
  gap: var(--sp-4);
  padding: var(--sp-5);
  background: var(--bg);
  border-radius: var(--card-r-sm);
  font-size: var(--fs-base);
}
.tech-note p { margin: 0; color: var(--body); }

.pd-gallery { display: grid; gap: var(--sp-4); }

/* Height held for the product hero while product-detail.js builds it.
   Measured, not estimated: the shortest hero in the catalogue across every
   width in each band — 529px at and above 992px (icy-hot-pro-uv at 1440),
   1099px below it (lab-q-ultra-pure at 575). The reservation is set just
   under those floors so it can never exceed a real hero and make the page
   jump back up; anything taller than the floor still settles downwards, but
   by a fraction of the 600px it used to move. */
.pd-hero__pending {
  display: grid;
  place-content: center;
  min-height: 520px;
}

/* ---- Water behind the product ----------------------------------------
   A calm pool the unit stands in, drawn from the same droplet palette as
   the rest of the site: a soft bloom under the product, two still ripple
   rings, a slow sideways drift that reads as flowing water, and one ring
   expanding as though a droplet had just landed.

   It is painted onto the frame's own background *beneath* the product, so
   the photograph's size, object-fit and blending are untouched — the only
   thing added to the image itself is a stacking order, so it stays above
   the water. Scoped to the main gallery frame; thumbnails stay plain. */
/* The gallery keeps its own frame, padding and blending. `contain` here is
   bit-identical to `cover` for a 5:4 photo in a 5:4 frame — which is every
   correctly exported product — and differs only for a file supplied at the
   wrong ratio, where it shows the whole unit instead of slicing a third off
   it. Defensive, not a change of treatment. */
.pd-gallery > .media > img,
.pd-gallery > [data-pd-main] > .media > img { object-fit: contain; }

.pd-gallery > .media.has-image,
.pd-gallery > [data-pd-main] > .media.has-image { position: relative; }

.pd-gallery > .media.has-image > img,
.pd-gallery > [data-pd-main] > .media.has-image > img { z-index: 1; }

.pd-gallery > .media.has-image::before,
.pd-gallery > [data-pd-main] > .media.has-image::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    /* two settled ripple rings where the unit meets the surface */
    radial-gradient(ellipse 46% 12% at 50% 78%, rgba(41, 193, 223, 0) 58%, rgba(41, 193, 223, .20) 66%, rgba(41, 193, 223, 0) 74%),
    radial-gradient(ellipse 62% 16% at 50% 83%, rgba(16, 104, 232, 0) 62%, rgba(16, 104, 232, .13) 70%, rgba(16, 104, 232, 0) 79%),
    /* the pool of light it stands in */
    radial-gradient(ellipse 55% 30% at 50% 85%, rgba(143, 216, 234, .32), rgba(143, 216, 234, 0) 70%),
    /* flowing water, drifting across */
    linear-gradient(100deg, rgba(234, 243, 255, 0) 0%, rgba(143, 216, 234, .18) 28%,
                    rgba(234, 243, 255, 0) 52%, rgba(41, 193, 223, .13) 76%, rgba(234, 243, 255, 0) 100%);
  background-size: 100% 100%, 100% 100%, 100% 100%, 220% 100%;
  background-position: 50% 50%, 50% 50%, 50% 50%, 0% 50%;
  background-repeat: no-repeat;
  animation: pd-water-drift 22s linear infinite;
}

.pd-gallery > .media.has-image::after,
.pd-gallery > [data-pd-main] > .media.has-image::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 11%;
  z-index: 0;
  width: 34%;
  aspect-ratio: 3 / 1;
  translate: -50% 0;
  border: 1.5px solid rgba(41, 193, 223, .32);
  border-radius: 50%;
  pointer-events: none;
  animation: pd-ripple 7s ease-out infinite;
}

@keyframes pd-water-drift {
  to { background-position: 50% 50%, 50% 50%, 50% 50%, 100% 50%; }
}
@keyframes pd-ripple {
  0%   { transform: scale(.55); opacity: 0; }
  20%  { opacity: .85; }
  100% { transform: scale(1.45); opacity: 0; }
}
.pd-thumbs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(78px, 1fr));
  gap: var(--sp-3);
  list-style: none;
  margin: 0;
  padding: 0;
}
.pd-thumbs li { margin: 0; }
.pd-thumb {
  display: block;
  width: 100%;
  padding: 0;
  border: 2px solid transparent;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: none;
  cursor: pointer;
}
.pd-thumb[aria-selected="true"] { border-color: var(--blue-600); }
.pd-thumb .media { border-radius: var(--r-xs); aspect-ratio: 1; }

/* Product detail: overview note, features grid and the specification block */

/* Overview — one stacked block: label, then the headline, then the copy.
   The old two-column split put the headline in the left half and the
   description in the right; both now run the full content width. */
.pd-overview { display: grid; gap: var(--sp-5); }
.pd-overview__title {
  margin: 0;
  max-width: none;
  color: var(--ink);
}
.pd-overview__body { display: grid; gap: var(--sp-4); }
/* The reading cap on .lead belongs to a column of copy beside something
   else; here the paragraph is the section, so it takes the width. */
.pd-overview__body .lead { max-width: none; margin: 0; }
.pd-overview__note { color: var(--muted); }
.pd-specs {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 1rem + 3vw, 3.5rem);
  align-items: start;
}
/* A model range needs the full width for its columns, so the applications
   and benefits cards move below the sheet instead of beside it. */
.pd-specs--wide { grid-template-columns: minmax(0, 1fr); }
.pd-specs--wide .pd-specs__aside {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: clamp(1.5rem, 1rem + 1.6vw, 2.5rem);
}
.pd-specs__aside { display: grid; gap: var(--sp-6); }
.pd-specs__aside .feature-list { grid-template-columns: 1fr; gap: var(--sp-2); }
.pd-specs__aside .feature-list li {
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
}
.card-navy .feature-list li { color: var(--on-navy); }

/* Key features and article takeaways share one droplet-bulleted list */
.spec-list,
.takeaway-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: var(--sp-5);
  list-style: none;
  margin: 0;
  padding: 0;
}
.spec-list li,
.takeaway-list li {
  position: relative;
  /* The item's own top padding. Each list variant restates it here, so the
     droplet below can be placed on the first line whatever the padding and
     however many lines the item runs to. */
  --bullet-top: var(--sp-5);
  margin: 0;
  padding: var(--sp-5) var(--card-pad) var(--sp-5) calc(var(--card-pad) + 26px);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--card-r-sm);
  font-family: var(--font-display);
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink);
}
.spec-list li::before,
.takeaway-list li::before {
  content: "";
  position: absolute;
  left: var(--sp-6);
  /* Centred on the first line box: the item's top padding plus half of that
     line. Never `top: 50%` — a two-line feature would drop its droplet into
     the middle of the card while a one-line feature kept it at the top. */
  top: calc(var(--bullet-top) + .82em);
  width: 12px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  translate: 0 -50%;
  background: var(--grad-drop-bullet);
}

/* A feature published with its own explanation stacks title over text */
.spec-list li.has-title { display: grid; gap: .25rem; }
.spec-list__title {
  font-family: var(--font-display);
  font-size: var(--fs-h6);
  font-weight: 800;
  letter-spacing: -.015em;
  color: var(--ink);
}
.spec-list__text {
  font-family: var(--font-body);
  font-size: var(--fs-base);
  font-weight: 400;
  color: var(--body);
}
/* Features published per configuration (an RO set and a UV set) */
.pd-feature-group {
  margin: 0;
  font-size: var(--fs-h5);
  font-weight: 800;
  letter-spacing: -.015em;
  color: var(--ink);
}

.feature-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  gap: var(--sp-4);
  list-style: none;
  margin: 0;
  padding: 0;
}
.feature-list li {
  display: flex;
  gap: var(--sp-3);
  margin: 0;
  padding: var(--sp-4) var(--sp-5);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--card-r-sm);
  font-size: var(--fs-base);
  box-shadow: var(--sh-card);
}
.feature-list li::before {
  content: "";
  width: 12px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  margin-top: .3rem;
  background: var(--grad-drop-bullet);
  flex: none;
}

/* Article card kicker: "SELECTION · 6 MIN READ" with a droplet separator */
.blog-card__kicker { display: flex; align-items: center; gap: var(--sp-3); }
.blog-card__dot {
  width: 7px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  flex: none;
  clip-path: var(--clip-drop);
  border-radius: var(--shape-drop);
  background: var(--grad-drop-bullet);
}
/* FAQ list running the full content width */
.faq--wide { max-width: none; }

/* ---- Article page ---- */
/* Article body and a contents rail, centred together inside the shell, so
   the page uses its width while the text keeps a proper reading measure.
   Below the desktop breakpoint the rail is dropped and the body runs on
   its own — see responsive.css. */
/* The article uses the page container, the same width every other section
   on the site gets, rather than sitting on a reading measure floated in the
   middle of a wide screen. One column by default — which is what the hero
   row above the body needs, since it carries no rail — and the contents
   rail adds its own track only once it has actually been built. Where
   `:has()` is unsupported the layout stays a single full-width column,
   which is the correct fallback rather than a broken one. */
.article-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2rem, 1rem + 3.4vw, 4rem);
  align-items: start;
}
.article-layout:has(> .article-aside:not([hidden])) {
  grid-template-columns: minmax(0, 1fr) clamp(230px, 20vw, 330px);
}
.article-column { min-width: 0; }
.page-hero--article .page-hero__inner,
.page-hero--article .shell { padding-block: var(--sp-6) 0; }
.page-hero--article::after,
.page-hero--article::before { display: none; }
.article__kicker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin: var(--sp-4) 0 var(--sp-5);
}
.article__kicker .eyebrow { margin: 0; }
.article__kicker .eyebrow:nth-of-type(n+2) { color: var(--muted); }
.article__dot {
  width: 7px;
  aspect-ratio: var(--drop-ratio);
  height: auto;
  flex: none;
  clip-path: var(--clip-drop);
  border-radius: var(--shape-drop);
  background: var(--grad-drop-bullet);
}
.page-hero--article h1 { max-width: none; font-size: var(--fs-h1); }
.article__lead { font-size: var(--fs-lg); color: var(--body); max-width: none; }

/* Contents rail — built from the article's own H2s, so it never has to be
   maintained by hand. Sticky under the header on desktop. */
.article-aside {
  position: sticky;
  top: calc(var(--header-h) + var(--sp-5));
  display: grid;
  gap: var(--sp-5);
}
.article-toc {
  padding: var(--card-pad-sm);
  background: var(--jmv-surface);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
}
.article-toc__title {
  display: block;
  margin-bottom: var(--sp-3);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--label);
}
.article-toc__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem; }
.article-toc__list li { margin: 0; }
.article-toc__list a {
  display: block;
  padding: .45rem 0 .45rem var(--sp-4);
  border-left: 2px solid var(--border-strong);
  color: var(--body);
  font-size: var(--fs-sm);
  line-height: 1.45;
  transition: color var(--t-fast), border-color var(--t-fast);
}
.article-toc__list a:hover { color: var(--blue-600); border-left-color: var(--blue-500); }

/* Closing takeaways panel */
.takeaways {
  margin: var(--sp-8) 0 0;
  padding: var(--card-pad);
  background: var(--jmv-surface);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
}
.takeaway-list--stack { grid-template-columns: 1fr; gap: var(--sp-2); }
.takeaway-list--stack li {
  --bullet-top: var(--sp-2);
  padding: var(--sp-2) 0 var(--sp-2) 26px;
  background: none;
  border: 0;
  font-weight: 500;
}
.takeaway-list--stack li::before { left: 0; }

/* "More from the blog" row cards */
.blog-row {
  display: grid;
  grid-template-columns: minmax(0, 110px) minmax(0, 1fr);
  align-items: center;
  gap: var(--sp-5);
  padding: var(--sp-5);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
  transition: box-shadow var(--t-base), transform var(--t-base), border-color var(--t-base);
}
.blog-row:hover {
  box-shadow: var(--sh-card-hover);
  border-color: var(--border-strong);
  transform: translateY(var(--card-lift));
}
.blog-row__body { display: grid; gap: var(--sp-2); }
.blog-row__title { margin: 0; font-size: var(--fs-h5); }
.blog-row__title a { color: var(--ink); }
.blog-row:hover .blog-row__title a { color: var(--blue-700); }


/* --------------------------------------------------------------------------
   21. Forms
   -------------------------------------------------------------------------- */
.form-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
  padding: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  box-shadow: var(--sh-sm);
}
.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-5);
  align-items: start;
}
.field { display: grid; gap: .4rem; align-content: start; }
.field--full { grid-column: 1 / -1; }
.field > label {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink);
}
.field__req { color: var(--danger); }

.input, .select, .textarea {
  width: 100%;
  min-height: var(--tap);
  padding: .75rem var(--sp-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--white);
  font-family: var(--font-body);
  font-size: var(--fs-base);
  color: var(--ink);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.textarea { min-height: 148px; padding-block: var(--sp-3); resize: vertical; line-height: var(--lh-snug); }
.select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%235a6472' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-4) center;
  padding-right: var(--sp-9);
}
.input::placeholder, .textarea::placeholder { color: var(--label); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--blue-600);
  box-shadow: 0 0 0 3px rgba(11, 114, 232, .16);
}
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px rgba(192, 57, 43, .12);
}
.field__error {
  display: none;
  font-size: var(--fs-sm);
  color: var(--danger);
}
.field.has-error .field__error { display: block; }

.honeypot { position: absolute !important; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-status {
  display: none;
  gap: var(--sp-3);
  align-items: flex-start;
  margin-bottom: var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--r-md);
  font-size: var(--fs-base);
}
.form-status.is-visible { display: flex; }
.form-status--success { background: var(--success-bg); color: #0d6b4c; border: 1px solid rgba(20, 153, 107, .3); }
.form-status--error { background: var(--danger-bg); color: #8c2a20; border: 1px solid rgba(192, 57, 43, .25); }
.form-status strong { color: inherit; }

.btn-jmv.is-loading { position: relative; color: transparent !important; pointer-events: none; }
.btn-jmv.is-loading::after {
  content: "";
  position: absolute;
  width: 20px; height: 20px;
  border: 2px solid rgba(255, 255, 255, .45);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Contact page info tiles */
/* Contact page: a stack of detail tiles beside the enquiry card */
.contact-layout {
  display: grid;
  grid-template-columns: minmax(0, .8fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 1rem + 2.4vw, 2.75rem);
  align-items: start;
}
.info-tiles { display: grid; gap: var(--sp-4); }
.info-tile {
  padding: var(--sp-5) var(--card-pad);
  border: 1px solid var(--border-mid);
  border-radius: var(--card-r);
  background: var(--bg-tint);
  transition: background var(--t-base), box-shadow var(--t-base), border-color var(--t-base);
}
.info-tile:hover {
  background: var(--white);
  border-color: var(--border-strong);
  box-shadow: var(--sh-card-hover);
}
.info-tile dt {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--blue-600);
  margin-bottom: .35rem;
}
.info-tile dd {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h6);
  font-weight: 700;
  color: var(--ink);
}
.info-tile dd a { color: var(--ink); }
.info-tile dd a:hover { color: var(--blue-700); }
.info-tile__note { margin: .3rem 0 0; font-size: var(--fs-sm); color: var(--muted); }

/* The enquiry card carries the same droplet ornament as the design */
.contact-layout .form-card { position: relative; }
.contact-layout .form-card::after {
  content: "";
  position: absolute;
  top: -34px;
  right: -22px;
  z-index: 0;
  width: clamp(90px, 9vw, 130px);
  aspect-ratio: var(--drop-ratio);
  clip-path: var(--clip-drop);
  border-radius: var(--shape-drop);
  background: var(--grad-drop);
  box-shadow: var(--sh-drop);
  opacity: .26;
  pointer-events: none;
  animation: drop-float 13s ease-in-out infinite alternate;
}
.contact-layout .form-card > * { position: relative; z-index: 1; }

/* Submit row: the action and its reassurance line side by side */
.form-submit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4) var(--sp-5);
}
.form-submit .btn-jmv { flex: 0 0 auto; }
.form-submit .fine-print { margin: 0; }

.map-frame {
  border: 1px dashed var(--border-strong);
  border-radius: var(--card-r);
  background: var(--bg-alt);
  overflow: hidden;
  min-height: 240px;
  display: grid;
  place-items: center;
  color: var(--muted);
  font-size: var(--fs-sm);
  text-align: center;
  padding: var(--sp-5);
}
.map-frame iframe { width: 100%; min-height: 280px; border: 0; display: block; }

/* --------------------------------------------------------------------------
   22. CTA band
   -------------------------------------------------------------------------- */
/* The closing call to action is a light ice band carrying one large glass
   droplet — the design's final, calm note before the dark footer. */
.cta-band {
  background: linear-gradient(180deg, var(--jmv-surface) 0%, var(--jmv-ice) 100%);
  color: var(--body);
}
.cta-band::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 13%;
  z-index: 0;
  width: clamp(120px, 13vw, 190px);
  aspect-ratio: var(--drop-ratio);
  translate: 0 -50%;
  clip-path: var(--clip-drop);
  border-radius: var(--shape-drop);
  background: var(--grad-drop);
  box-shadow: var(--sh-drop);
  opacity: .26;
  pointer-events: none;
  animation: drop-float 13s ease-in-out infinite alternate;
}
.cta-band__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  padding-block: var(--section-y-tight);
}
.cta-band__copy { max-width: 42ch; }
.cta-band h2 { color: var(--ink); margin-bottom: var(--sp-4); }
.cta-band p { color: var(--body); margin: 0; max-width: 42ch; }
.cta-band .eyebrow--cyan { color: var(--blue-600); }
/* The action sits over the droplet, so it stays on top */
.cta-band__actions { position: relative; z-index: 2; display: flex; flex-wrap: wrap; gap: var(--sp-3); }

/* --------------------------------------------------------------------------
   23. Footer
   -------------------------------------------------------------------------- */
.site-footer { background: var(--grad-band); color: var(--on-navy-dim); font-size: var(--fs-base); }
.site-footer::after {
  content: "";
  position: absolute;
  top: -10%;
  right: -4%;
  z-index: 0;
  width: clamp(130px, 14vw, 240px);
  aspect-ratio: 1;
  border-radius: var(--shape-drop);
  background: var(--grad-drop-dark);
  opacity: .1;
  pointer-events: none;
  animation: drop-float 16s ease-in-out infinite alternate;
}
.site-footer > * { position: relative; z-index: 1; }
/* Brand column, then the three link columns the design shows */
.site-footer__main {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
  gap: clamp(1.5rem, 1rem + 2.4vw, 3.5rem);
  /* Columns hold different numbers of links, so they top-align rather than
     stretching to the tallest one. */
  align-items: start;
  padding-block: var(--section-y-tight);
}
.site-footer h2, .site-footer h3 { color: #fff; }
.footer-brand__text { margin: var(--sp-5) 0 var(--sp-3); max-width: 34ch; font-size: var(--fs-base); }
/* Authorized Business Partner line. Sits directly under the brand
   description and reads as a credential rather than body copy. */
.footer-brand__partner {
  margin: 0 0 var(--sp-5);
  max-width: 34ch;
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--jmv-cyan-soft);
}
.footer-contact { margin: 0; padding: 0; list-style: none; display: grid; gap: .3rem; }
.footer-contact li { margin: 0; }
.footer-contact a { color: var(--on-navy-dim); }
.footer-contact a:hover { color: #fff; }

.footer-col h3 { font-size: var(--fs-lg); font-weight: 800; margin-bottom: var(--sp-4); letter-spacing: -.015em; }

.footer-links { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem; }
.footer-links li { margin: 0; }
.footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  color: var(--on-navy-dim);
}
.footer-links a:hover { color: #fff; }

/* Social row — one droplet-shaped badge per profile, carrying the brand
   mark. Wraps rather than overflowing once five of them meet a narrow
   footer column. */
.social-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-6);
  list-style: none;
  padding: 0;
}
.social-row li { margin: 0; }
.social-btn {
  display: grid;
  place-items: center;
  width: 40px; height: 44px;
  border: 1px solid rgba(234, 243, 255, .2);
  border-radius: var(--shape-drop-soft);
  color: rgba(255, 255, 255, .82);
  font-size: var(--fs-sm);
  font-weight: 600;
  transition: background var(--t-fast), border-color var(--t-fast),
              color var(--t-fast), transform var(--t-fast);
}
/* The mark takes the badge's own colour, so one hover state covers both */
.social-btn__icon {
  width: 17px;
  height: 17px;
  fill: currentColor;
  pointer-events: none;
}
a.social-btn:hover {
  background: rgba(255, 255, 255, .1);
  border-color: rgba(255, 255, 255, .4);
  color: #fff;
  transform: translateY(-2px);
}
/* A profile whose URL has not been set yet. It is not a link, so it does
   not lift on hover and is not in the tab order — but it still reads as
   part of the set rather than as a gap. */
.social-btn.is-pending { color: rgba(255, 255, 255, .55); cursor: default; }

.site-footer__bottom {
  border-top: 1px solid rgba(255, 255, 255, .1);
}
.site-footer__bottom-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-block: var(--sp-5);
  font-size: var(--fs-sm);
}
.legal-links { display: flex; flex-wrap: wrap; gap: var(--sp-6); list-style: none; margin: 0; padding: 0; }
.legal-links li { margin: 0; }
.legal-links a { color: var(--on-navy-dim); }
.legal-links a:hover { color: #fff; }

/* --------------------------------------------------------------------------
   24. Floating actions
   -------------------------------------------------------------------------- */
.wa-fab {
  position: fixed;
  right: var(--fab-right);
  bottom: var(--fab-bottom);
  z-index: var(--z-fab);
  display: grid;
  place-items: center;
  width: var(--fab-size); height: var(--fab-size);
  border-radius: 50%;
  background: #25d366;
  color: #fff;
  box-shadow: 0 10px 26px rgba(37, 211, 102, .42);
  transition: transform var(--t-base), box-shadow var(--t-base);
}
.wa-fab:hover { transform: scale(1.06); color: #fff; box-shadow: 0 14px 30px rgba(37, 211, 102, .5); }
.wa-fab svg { width: 28px; height: 28px; fill: currentColor; }

/* Sat directly above the WhatsApp button, right-aligned with it.
   Hidden as of the current design: on a desktop the header already carries a
   "Request a Quote" button, so a second one floating over the page was a
   duplicate. It is off here rather than deleted because the rule below is
   the whole of its styling — delete this one declaration to bring it back.

   This only affects widths above 767px. Below that the rail was already
   hidden and the sticky `.action-bar` at the foot of the screen carries the
   quote button instead; that is untouched, as is the WhatsApp button in
   both layouts. */
.quote-rail {
  /* The one declaration that hides it — see the note above. Restore it to
     `inline-flex` to bring the rail back; everything below is its styling,
     left intact. */
  display: none;
  position: fixed;
  right: var(--fab-right);
  bottom: calc(var(--fab-bottom) + var(--fab-size) + var(--fab-gap));
  z-index: var(--z-rail);
  align-items: center;
  min-height: 46px;
  padding: .75rem 1.25rem;
  border-radius: var(--r-pill);
  background: var(--grad-band);
  color: #fff;
  font-size: var(--fs-sm);
  font-weight: 700;
  white-space: nowrap;
  box-shadow: var(--sh-navy);
  transition: background var(--t-fast), transform var(--t-fast);
}
.quote-rail:hover { background: var(--blue-600); color: #fff; transform: translateY(-2px); }

/* Mobile sticky action bar */
.action-bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: var(--z-fab);
  display: none;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--gutter) calc(var(--sp-3) + env(safe-area-inset-bottom));
  background: rgba(255, 255, 255, .96);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--border);
}
.action-bar .btn-jmv { flex: 1 1 auto; }
.action-bar__wa {
  display: grid;
  place-items: center;
  width: var(--tap); height: var(--tap);
  border-radius: 50%;
  background: #25d366;
  color: #fff;
  flex: none;
}
.action-bar__wa svg { width: 24px; height: 24px; fill: currentColor; }

/* --------------------------------------------------------------------------
   25. Article (blog detail, legal pages)
   -------------------------------------------------------------------------- */
/* Long-form copy runs across the page container, aligned with the heading
   above it — the legal pages and the blog article read the same.
   `--measure-px` is kept as the site's documented reading measure: set this
   back to it if a narrower column is ever wanted. */
.article { max-width: none; }
.article h2 { font-size: var(--fs-h3); margin-top: var(--sp-9); }
.article h3 { font-size: var(--fs-h4); margin-top: var(--sp-7); }
.article p, .article li { font-size: var(--fs-md); }
.article ul, .article ol { padding-left: 1.35rem; margin-bottom: var(--sp-5); }
.article blockquote {
  margin: var(--sp-7) 0;
  padding: var(--sp-5) var(--sp-6);
  border-left: 4px solid var(--cyan-500);
  background: var(--bg);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  font-family: var(--font-display);
  font-size: var(--fs-h5);
  font-weight: 600;
  color: var(--ink);
}
.article img { border-radius: var(--r-md); margin-block: var(--sp-6); }
/* An image inside a media frame is spaced by the frame, not by itself */
.article .media { display: block; margin-block: var(--sp-6); }
.article .media img { margin-block: 0; border-radius: inherit; }

/* Data tables — the product specification sheet and article comparison
   tables. The wrapper scrolls on narrow screens so the page never does. */
.table-wrap {
  overflow-x: auto;
  margin-block: var(--sp-6);
  border: 1px solid var(--border);
  border-radius: var(--card-r-sm);
  background: var(--white);
  -webkit-overflow-scrolling: touch;
}
.spec-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-base);
  text-align: left;
}
.spec-table th, .spec-table td {
  padding: .85rem clamp(.85rem, .6rem + .5vw, 1.25rem);
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.spec-table tr:last-child th, .spec-table tr:last-child td { border-bottom: 0; }
.spec-table thead th {
  background: var(--bg);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--label);
  white-space: nowrap;
}
.spec-table tbody th {
  font-weight: 600;
  color: var(--ink);
}
.spec-table td { color: var(--body); }

/* Model-range sheet: a column per model, the selected one picked out, and
   a spanning subheading wherever the manufacturer groups its parameters. */
.spec-table--models th[scope="col"]:first-child { color: var(--label); }
.spec-table--models td { white-space: normal; }
.spec-table--models .is-active { background: var(--blue-50); color: var(--blue-600); font-weight: 600; }
.spec-table--models thead .is-active { color: var(--ink); }
.spec-table__group th {
  background: var(--bg);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--label);
}
/* An article table has to fit the reading measure, so it sets a tighter
   scale than the product specification sheet; .table-wrap still scrolls if
   a table is wider than the column. */
.article .table-wrap { max-width: 100%; }
.article .spec-table th, .article .spec-table td {
  padding: .7rem .8rem;
  font-size: var(--fs-sm);
}
.article .spec-table thead th { font-size: var(--fs-xs); white-space: normal; }

/* --------------------------------------------------------------------------
   26. Pure Water Droplet — shared motion + droplet primitives
   Every animated ornament on the site reuses these six keyframes.
   Transform/opacity only, so nothing touches layout or paint-heavy props.
   -------------------------------------------------------------------------- */
@keyframes drop-float {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, -14px, 0); }
}
/* Droplets flow from the top of a section to its foot. The distance comes
   from --fall, which main.js sets from the section's own height, so the
   flow always reaches the bottom edge whatever the section measures. */
@keyframes drop-fall {
  0%   { transform: translate3d(0, -40px, 0) scale(.55); opacity: 0; }
  10%  { opacity: var(--peak, .7); }
  86%  { opacity: var(--peak, .7); }
  100% { transform: translate3d(var(--drift, 12px), var(--fall, 110vh), 0) scale(1); opacity: 0; }
}
@keyframes ripple-splash {
  0%   { transform: scale(.6);  opacity: 0; }
  30%  { opacity: .32; }
  100% { transform: scale(1.5); opacity: 0; }
}
@keyframes drop-merge {
  0%   { transform: translate3d(0, 8px, 0) scale(.92); opacity: 0; }
  100% { transform: translate3d(0, 0, 0) scale(1); opacity: 1; }
}
@keyframes drop-shine {
  0%, 100% { opacity: .35; transform: translate3d(0, 0, 0) scale(1); }
  50%      { opacity: .8;  transform: translate3d(0, -3px, 0) scale(1.06); }
}
@keyframes slow-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* ---- Droplet design system --------------------------------------------
   Everything the design draws as a droplet is listed here and takes the
   same silhouette from the same token, so every droplet on the site
   belongs to one system. */
.hero-pill::before,
.hero-segments li + li::before,
.pillar-list li::before,
.feature-list li::before,
.testimonial__avatar,
.faq__sign,
.drop-fx > span,
.hero-fx__drops > span,
.spec-list li::before,
.takeaway-list li::before,
.article__dot,
.blog-card__dot {
  clip-path: var(--clip-drop);
  border-radius: var(--shape-drop);   /* fallback for un-clipped rendering */
}

/* A droplet that has to cast a shadow or carry the design's blue keyline
   cannot be clipped itself: a clip path is applied *after* a filter, so the
   filter's output would be cut straight off the edge. These droplets are an
   un-clipped host with the clipped fill on ::before, which leaves the host's
   filter working on the droplet silhouette. Content sits above the fill. */
.badge-square,
.num-badge,
.service-card__num,
.tech-circle__disc,
.process__disc,
.mega-tech__drop {
  position: relative;
  background: none;
  isolation: isolate;
}
.badge-square::before,
.num-badge::before,
.service-card__num::before,
.tech-circle__disc::before,
.process__disc::before,
.mega-tech__drop::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  clip-path: var(--clip-drop);
  border-radius: var(--shape-drop);
  background: var(--grad-drop);
  box-shadow: var(--sh-drop);
}

/* ---- A technology logo inside a droplet --------------------------------
   One rule, every place the technology droplet row appears: the home
   technology section, the Technologies band and the technology detail hero.
   Everything is a percentage of the host, so the rule is size-independent —
   it already fits the 46px header-menu droplet and the 38px card badge, and
   those two will pick it up unchanged once icon-only logo files exist (see
   DEPLOYMENT.md § 11). `has-drop-logo` scopes the treatment
   to droplets that actually hold artwork, which is what keeps it off the
   plain decorative droplet on a product card.

   The logo is an ordinary child of the host. The host is what carries the
   float animation, the hover lift and the active keyline, so the mark
   cannot drift from its droplet and needs no animation of its own. */
.drop-logo {
  /* Placed against the host rather than flowed inside it, which is what
     makes the rule size-independent: `top` and `width` resolve against the
     host's own box, so the mark tracks the fluid
     `clamp(92px, 8vw, 124px)` droplet across every breakpoint, and the
     differing text paddings the droplet hosts carry stop mattering. */
  position: absolute;
  left: 50%;
  /* A teardrop's usable area is its bulb, so the mark centres low in the
     droplet rather than in the middle of the box. */
  top: var(--drop-logo-y, 67%);
  translate: -50% -50%;
  z-index: 1;
  /* A square well, sized off the host's width, with the artwork contained
     inside it: the well is what aligns every mark on one centre, and
     `contain` keeps each logo's own proportions — never cropped, never
     stretched, whatever shape it is. `--drop-logo-scale` comes from the
     catalogue and evens out lockups that run from square to 1.75:1. */
  width: calc(var(--drop-logo-w, 68%) * var(--drop-logo-scale, 1));
  aspect-ratio: 1;
  height: auto;
  object-fit: contain;
  object-position: center;
  /* Seats the mark in the water rather than on the surface */
  filter: drop-shadow(0 1px 1.5px rgba(14, 41, 91, .22));
  pointer-events: none;
}

/* The header-menu droplet is a third the size of the row droplet, so its
   logo takes a larger share of the bulb — at 58px the mark needs every
   pixel the silhouette will spare, and it sits a touch lower where the
   teardrop is widest. */
.mega-tech__drop .drop-logo {
  --drop-logo-w: 80%;
  --drop-logo-y: 64%;
}

/* A logo supplied without an alpha channel, whose artwork sits on a white
   field. Multiplying it into the droplet drops that field away instead of
   leaving a visible box, and the mark itself survives because the water
   under it is near-white at the centre. `isolation: isolate` on the host is
   what keeps the blend inside the droplet — it composites against the water
   fill on ::before, never against the navy section behind. */
.drop-logo--blend {
  mix-blend-mode: multiply;
  /* A drop-shadow would multiply too, and print as a grey smear */
  filter: none;
}

/* The glass in front of the mark — the droplet's own specular highlight,
   painted back over the logo so the artwork reads as suspended inside the
   water. Kept off-centre and weak, so it models the droplet without
   washing the logo out. */
.has-drop-logo::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  clip-path: var(--clip-drop);
  border-radius: var(--shape-drop);
  background:
    radial-gradient(34% 22% at 33% 26%, rgba(255, 255, 255, .55), rgba(255, 255, 255, 0) 72%),
    linear-gradient(200deg, rgba(255, 255, 255, .14) 0%, rgba(255, 255, 255, 0) 46%);
  pointer-events: none;
}

/* Falling droplet layer — injected into dark bands and the services section
   by main.js, which sets the per-droplet custom properties below so the
   flow reads as natural rather than as six repeating columns. */
.drop-fx {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.drop-fx > span {
  position: absolute;
  top: 0;
  left: var(--x, 50%);
  width: var(--w, 12px);
  aspect-ratio: var(--drop-ratio);
  background: var(--grad-drop-dark);
  opacity: 0;
  animation: drop-fall var(--dur, 15s) linear var(--delay, 0s) infinite;
}
/* On light grounds the same layer uses the visible droplet fill, dialled
   back so it stays a background texture rather than foreground decoration */
.drop-fx--light { opacity: .32; }
.drop-fx--light > span { background: var(--grad-drop-bullet); }

/* Any section can host the droplet flow by declaring it in the markup */
[data-water-flow] { position: relative; overflow: hidden; isolation: isolate; }
[data-water-flow] > .shell { position: relative; z-index: 2; }

/* Glassy droplet orb ornament — the hero's decorative water spheres */
.jmv-orb,
.hero-fx__ripples > span {
  border-radius: 50%;
  background: var(--grad-orb);
  box-shadow: inset -8px -12px 26px rgba(255, 255, 255, .9),
              inset 8px 14px 30px rgba(41, 120, 220, .18),
              0 26px 50px rgba(20, 60, 130, .14);
  pointer-events: none;
  will-change: transform;
}

/* Dark bands: gradient, crest and a floating ornament, applied once */
.section--navy,
.section--band,
.cta-band,
.site-footer { position: relative; overflow: hidden; isolation: isolate; }
.section--navy,
.section--band { background: var(--grad-band); }

/* Slowly rotating ring ornament on the technology band */
.section--band::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 0;
  width: min(760px, 70vw);
  aspect-ratio: 1;
  translate: -50% -50%;
  border: 1px dashed rgba(143, 216, 234, .14);
  border-radius: 50%;
  pointer-events: none;
  animation: slow-spin 120s linear infinite;
}

main > .section--navy:not(:first-child),
main > .section--band:not(:first-child) { position: relative; }
main > .section--navy:not(:first-child)::before,
main > .section--band:not(:first-child)::before {
  content: "";
  position: absolute;
  left: -6%;
  right: -6%;
  top: -1px;
  z-index: 1;
  height: clamp(26px, 3.4vw, 58px);
  background: var(--crest-color);
  border-radius: 0 0 50% 50% / 0 0 100% 100%;
  pointer-events: none;
}
/* Content must sit above the crest and the droplet layer */
.section--navy > .shell,
.section--band > .shell,
.cta-band > .shell { position: relative; z-index: 2; }

/* A dark band that follows a sunken section needs a matching crest colour */
.section--sunken + .section--navy,
.section--sunken + .section--band { --crest-color: var(--bg); }

/* ---- Glass information card (hero stat cards) ---- */
.glass-card {
  position: absolute;
  z-index: 4;
  padding: var(--sp-5) var(--sp-6);
  background: rgba(255, 255, 255, .72);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, .9);
  border-radius: var(--card-r);
  box-shadow: var(--sh-lg);
  /* Settles in, then floats — both from the shared keyframe set */
  animation: drop-merge var(--t-slow) ease-out both,
             drop-float 10s ease-in-out .45s infinite alternate;
}
.glass-card__value {
  display: block;
  font-family: var(--font-display);
  font-size: 1.875rem;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: var(--ls-tight);
  color: var(--ink);
}
.glass-card__label {
  display: block;
  margin-top: .2rem;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}
/* Placement of the two hero cards, tied to the visual — upper right and
   lower left, both breaking its edge the way the approved design does. */
.glass-card--stat {
  top: 6.5%;
  right: -3%;
  width: 132px;
  padding: var(--sp-4) var(--sp-4);
  text-align: center;
  animation-duration: 9s;
}
.glass-card--note {
  bottom: 8.5%;
  left: -9%;
  width: 280px;
  max-width: 62%;
  animation-duration: 12s;
  animation-delay: -3s;
}
.glass-card--note .glass-card__label {
  margin: 0 0 .35rem;
  color: var(--blue-600);
}
.glass-card__text {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.1875rem;
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: -.01em;
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   27. Reveal animations
   -------------------------------------------------------------------------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(30px) scale(.985);
  transition: opacity 1s var(--jmv-ease), transform 1s var(--jmv-ease);
  will-change: opacity, transform;
}
[data-reveal].is-in { opacity: 1; transform: none; will-change: auto; }
[data-reveal-delay="1"] { transition-delay: 80ms; }
[data-reveal-delay="2"] { transition-delay: 160ms; }
[data-reveal-delay="3"] { transition-delay: 240ms; }
/* Sibling stagger, applied by initReveal from the element's own index */
[data-reveal-index="1"] { transition-delay: 80ms; }
[data-reveal-index="2"] { transition-delay: 160ms; }
[data-reveal-index="3"] { transition-delay: 240ms; }
[data-reveal-index="4"] { transition-delay: 320ms; }
[data-reveal-index="5"] { transition-delay: 400ms; }

.no-js [data-reveal] { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* Every decorative water layer stands down */
  .hero-fx,
  .drop-fx,
  .process li::after,
  .process::after,
  .cta-band::after,
  .section--band::after,
  .site-footer::after,
  .stat::after,
  .card-accent::after,
  .page-hero::after,
  .signature::after,
  .jmv-orb,
  .panel-cta::after,
  .contact-layout .form-card::after,
  .jmv-ripple-canvas,
  .testimonial.is-active::before { display: none; }
  .logo-marquee__track { animation: none; transform: none; }
  /* The splash rings rest where they read, rather than finishing the
     animation at opacity 0 and vanishing */
  .pd-gallery > .media.has-image::after,
  .pd-gallery > [data-pd-main] > .media.has-image::after {
    animation: none;
    opacity: .5;
    transform: scale(1.15);
  }
  .hero-visual__frame::after {
    animation: none;
    opacity: .45;
    transform: scale(1.2);
  }
  /* The hero splash holds the shape it was drawn in — the sheets and the
     droplets keep their authored opacity instead of ending mid-fade. */
  .hero-splash__sheets,
  .hero-splash__drops circle {
    animation: none;
    transform: none;
  }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   28. Utilities
   -------------------------------------------------------------------------- */
.u-mb-0 { margin-bottom: 0 !important; }
.u-mt-2 { margin-top: var(--sp-2); }
.u-mt-4 { margin-top: var(--sp-4); }
.u-mt-6 { margin-top: var(--sp-6); }
.u-mt-7 { margin-top: var(--sp-7); }
.u-mt-8 { margin-top: var(--sp-8); }
.u-mt-9 { margin-top: var(--sp-9); }
.u-center { text-align: center; }
.u-center-x { margin-inline: auto; }
.noscript-note {
  margin: 0;
  padding: var(--sp-4) var(--gutter);
  background: var(--danger-bg);
  color: #8c2a20;
  font-size: var(--fs-base);
  text-align: center;
}
.empty-state {
  padding: var(--sp-9) var(--card-pad);
  text-align: center;
  color: var(--body);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--card-r);
}
