/* ============================================================
   MightyLoc section + hero layer
   ============================================================
   Ported near one-to-one from the locked mockup
   (design-system-v2/MightyLoc Website Mockup.html). Uses the token bridge
   (tokens.css) so var(--color-brand-*) etc. resolve. Animations are faithful:
   meshShift, redRoam/redPulse, pulseDot, heldGloss/heldGlow, tickerScroll, seal.
   ------------------------------------------------------------ */

/* ---------- Layout helpers ---------- */
.section { padding-top: var(--space-9); padding-bottom: var(--space-9); }
.section--lg { padding-top: var(--space-10); padding-bottom: var(--space-10); }
.container { max-width: 1400px; margin: 0 auto; padding-left: 32px; padding-right: 32px; }
.container--tight { max-width: 1280px; }
@media (max-width: 768px) {
  .section, .section--lg { padding-top: var(--space-7); padding-bottom: var(--space-7); }
  .container { padding-left: 20px; padding-right: 20px; }
}

/* ---------- Reveal on scroll ---------- */
.reveal { opacity: 0; transform: translateY(12px); transition: opacity 700ms var(--ease-out), transform 700ms var(--ease-out); }
.reveal.is-in { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; } }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  font-family: var(--font-body); font-size: 14px; font-weight: 600;
  min-height: 48px; padding: 0 22px;
  border-radius: var(--radius-md); border: 1px solid transparent;
  cursor: pointer; text-decoration: none; letter-spacing: 0.01em;
  transition: background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out), border-color var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-spring);
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn--primary { background: var(--accent-primary); color: var(--text-on-brand); }
.btn--primary:hover { background: var(--color-brand-blue-mid); color: var(--text-on-brand); }
.btn--white { background: #fff; color: var(--color-brand-blue-deep); }
/* .btn--white / .btn--secondary hover = animated "bonded" gradient (defined just below) */
.btn--ghost-light { background: transparent; color: #fff; border-color: rgba(255,255,255,0.30); }
.btn--ghost-light:hover { background: rgba(255,255,255,0.08); border-color: rgba(255,255,255,0.50); color: #fff; }
.btn--secondary { background: var(--surface-elevated); color: var(--text-primary); border-color: var(--border-strong); }
/* White / secondary buttons: on hover/focus the red "bonded" gradient WIPES in
   left -> right (Leonard 2026-06-06). The old version swapped background-image on
   hover, but gradients can't be transitioned, so the red snapped in with no motion.
   Now the red lives on a z-index:-1 ::before that paints over the white surface but
   UNDER the label (CSS stacking), so the text needs no wrapper and stays on top; we
   animate only its transform (scaleX from the left). The label + arrow ease to white
   in step with the wipe so they never sit white-on-white. heldGloss shimmers the red
   once the fill lands. prefers-reduced-motion shows the fill instantly, no sweep. */
.btn--white, .btn--secondary {
  position: relative; z-index: 0; overflow: hidden; isolation: isolate;
  transition: color 400ms var(--ease-out), border-color var(--duration-fast) var(--ease-out),
    box-shadow 360ms var(--ease-out), transform var(--duration-fast) var(--ease-spring);
}
.btn--white::before, .btn--secondary::before {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background-color: var(--color-brand-red);
  background-image: linear-gradient(100deg,
    var(--color-brand-red-deep) 0%, var(--color-brand-red) 30%, #ff7a4d 50%, var(--color-brand-red) 70%, var(--color-brand-red-deep) 100%);
  background-size: 220% 100%;
  transform: scaleX(0); transform-origin: left center;
  transition: transform 420ms cubic-bezier(0.4, 0, 0.2, 1);
}
.btn--white:hover, .btn--white:focus-visible,
.btn--secondary:hover, .btn--secondary:focus-visible {
  color: #fff; border-color: transparent;
  box-shadow: 0 12px 32px -12px rgba(208,38,41,0.55), 0 2px 8px -3px rgba(208,38,41,0.50);
}
.btn--white:hover::before, .btn--white:focus-visible::before,
.btn--secondary:hover::before, .btn--secondary:focus-visible::before {
  transform: scaleX(1);
  animation: heldGloss 5s ease-in-out infinite 420ms;
}
.btn--white:hover .arrow, .btn--secondary:hover .arrow,
.btn--white:focus-visible .arrow, .btn--secondary:focus-visible .arrow { color: #fff; }
@media (prefers-reduced-motion: reduce) {
  .btn--white::before, .btn--secondary::before { transition: none; }
  .btn--white:hover::before, .btn--white:focus-visible::before,
  .btn--secondary:hover::before, .btn--secondary:focus-visible::before { transform: scaleX(1); animation: none; }
}
.btn--sm { min-height: 36px; padding: 0 14px; font-size: 13px; }
.btn .arrow { display: inline-block; transition: transform var(--duration-base) var(--ease-spring); }
.btn:hover .arrow { transform: translateX(4px); }

/* ============================================================
   Hero - animated gradient mesh
   ============================================================ */
.hero {
  position: relative; overflow: hidden;
  background: var(--color-brand-blue-deep);
  color: var(--text-on-brand);
  isolation: isolate;
}
.hero-mesh::before, .hero-mesh::after {
  content: ''; position: absolute; inset: -10%;
  pointer-events: none; z-index: 0; filter: blur(40px);
}
.hero-mesh::before {
  background:
    radial-gradient(circle at 22% 30%, color-mix(in oklab, var(--color-brand-blue) 78%, transparent) 0%, transparent 36%),
    radial-gradient(circle at 78% 24%, color-mix(in oklab, var(--color-brand-red) 32%, transparent) 0%, transparent 32%),
    radial-gradient(circle at 58% 78%, color-mix(in oklab, var(--color-brand-blue-mid) 60%, transparent) 0%, transparent 42%);
  animation: meshShift 22s ease-in-out infinite alternate;
}
.hero-mesh::after {
  background:
    radial-gradient(circle at 10% 80%, color-mix(in oklab, var(--color-brand-blue) 38%, transparent) 0%, transparent 40%),
    radial-gradient(circle at 90% 60%, color-mix(in oklab, var(--color-brand-blue-mid) 50%, transparent) 0%, transparent 36%);
  animation: meshShift 18s ease-in-out infinite alternate-reverse;
  mix-blend-mode: screen; opacity: 0.7;
}
@keyframes meshShift {
  0%   { transform: translate(0,0) scale(1); }
  50%  { transform: translate(-3%, 2%) scale(1.06); }
  100% { transform: translate(2%, -2%) scale(1.02); }
}
.hero__redglow {
  position: absolute; z-index: 0; pointer-events: none;
  width: 46%; aspect-ratio: 1; left: 50%; top: 30%; border-radius: 50%;
  background: radial-gradient(circle,
    color-mix(in oklab, var(--color-brand-red) 60%, transparent) 0%,
    color-mix(in oklab, var(--color-brand-red) 24%, transparent) 34%,
    transparent 66%);
  filter: blur(46px); mix-blend-mode: screen;
  animation: redRoam 16s cubic-bezier(0.45,0,0.55,1) infinite alternate, redPulse 5s ease-in-out infinite;
}
@keyframes redRoam {
  0%   { transform: translate(-30%, -10%) scale(0.9); }
  33%  { transform: translate(20%, 18%) scale(1.15); }
  66%  { transform: translate(-10%, -22%) scale(1.0); }
  100% { transform: translate(28%, 6%) scale(1.2); }
}
@keyframes redPulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 0.9; } }
@media (prefers-reduced-motion: reduce) {
  .hero__redglow, .hero-mesh::before, .hero-mesh::after { animation: none; }
}
.hero__grid {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(255,255,255,0.045) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,0.045) 1px, transparent 1px);
  background-size: calc(100% / 12) 80px;
}
/* Watermark removed per Leonard 2026-05-31 (rendered as a static white glow
   bottom-left). Kept as display:none so any cached markup stays hidden. */
.hero__watermark { display: none !important; }
.hero__inner {
  position: relative; z-index: 2; max-width: 1400px; margin: 0 auto;
  /* Stacked (mobile/tablet) hero: keep the 160px top clearance for the floating
     header, but trim the bottom + the copy->showcase gap (they compounded into a
     tall void above the CTA / stats ticker). Desktop keeps the mockup spec below. */
  padding: 160px 32px 64px;
  display: grid; grid-template-columns: 1fr; gap: 32px; align-items: center;
}
@media (min-width: 900px) {
  /* Hero framing (Leonard + design-review 2026-06-06): a half-cut moving-industries
     ticker reads as a bug, so the design agent chose to keep a BOLD headline and let
     the ticker reveal on scroll instead of cramming it half-visible into the first
     screen. The fixed header bottom is y=158; top padding 196 = a 38px gap to the
     first item; bottom padding 50 = a comfortable gap above the ticker (which now
     sits just below the ~880px 16:9 fold and animates in on a short scroll). The
     ticker's own top hairline (border-top below) marks it as an intentional band. */
  .hero__inner { grid-template-columns: 1.6fr 0.8fr; gap: 60px; padding: 196px 32px 50px; }
}
.hero__copy { min-width: 0; }
.hero__sigil {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 6px 12px 6px 6px;
  border: 1px solid rgba(255,255,255,0.18); border-radius: var(--radius-pill);
  margin-bottom: 32px;
  font-family: var(--font-mono); font-size: 11px; color: rgba(255,255,255,0.72); letter-spacing: 0.04em;
}
.hero__sigil .dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--color-brand-red); box-shadow: 0 0 0 4px rgba(208,38,41,0.18);
  animation: pulseDot 2400ms ease-in-out infinite;
}
@keyframes pulseDot {
  0%, 100% { box-shadow: 0 0 0 4px rgba(208,38,41,0.18); }
  50%      { box-shadow: 0 0 0 8px rgba(208,38,41,0.06); }
}
/* Selector raised to .hero .hero__h1 so the tight 0.96 line-height and display
   size always win over WordPress's global :where(h1) element styles. */
.hero .hero__h1 {
  color: #fff; max-width: 18ch; margin: 0 0 32px;
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(56px, 8.4vw, 104px); line-height: 0.96; letter-spacing: -0.035em;
}
.accent-held {
  background-image: linear-gradient(100deg,
    var(--color-brand-red-deep) 0%, var(--color-brand-red) 30%, #ff7a4d 50%, var(--color-brand-red) 70%, var(--color-brand-red-deep) 100%);
  background-size: 220% 100%; background-repeat: repeat;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  animation: heldGloss 5s ease-in-out infinite, heldGlow 3s ease-in-out infinite;
}
@keyframes heldGloss { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } }
@keyframes heldGlow {
  0%, 100% { filter: drop-shadow(0 0 6px rgba(208,38,41,0.45)); }
  50%      { filter: drop-shadow(0 0 16px rgba(255,90,60,0.85)); }
}
@media (prefers-reduced-motion: reduce) {
  .accent-held { animation: none; -webkit-text-fill-color: var(--color-brand-red); color: var(--color-brand-red); }
}
.hero__sub { color: rgba(255,255,255,0.78); font-size: 19px; line-height: 1.6; max-width: 56ch; margin: 0 0 48px; }
.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }

/* Desktop only: tighten the headline column rhythm so the whole hero -- headline
   down to the moving industries ticker -- fits a 16:9 viewport (~880px tall on a
   1080p screen after browser chrome). Leonard 2026-06-06: wanted the moving band
   visible at the bottom. Mobile keeps the base margins below (untouched). */
@media (min-width: 900px) {
  .hero__sigil { margin-bottom: 18px; }
  .hero .hero__h1 { margin-bottom: 18px; }
  .hero__sub { margin-bottom: 26px; }
}

/* ---------- Hero ticker ---------- */
.ticker {
  position: relative; z-index: 2; overflow: hidden;
  border-top: 1px solid rgba(255,255,255,0.10); border-bottom: 1px solid rgba(255,255,255,0.10);
  background: rgba(255,255,255,0.02);
}
/* gap:0 + per-item trailing margin so the duplicated track is EXACTLY 2x one
   set; translateX(-50%) then lands seamlessly (a flex `gap` adds one extra gap
   across the duplicate boundary, which made the loop jump/clip). */
.ticker__track { display: flex; gap: 0; white-space: nowrap; padding: 16px 0; animation: tickerScroll 40s linear infinite; width: max-content; }
.ticker__item { display: inline-flex; align-items: center; margin-right: 56px; font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: rgba(255,255,255,0.60); }
.ticker__item .ticker__icon { display: inline-flex; align-items: center; justify-content: center; margin-right: 12px; color: rgba(255,255,255,0.92); flex: 0 0 auto; }
.ticker__item .ticker__icon svg { width: 16px; height: 16px; display: block; }
@keyframes tickerScroll { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .ticker__track { animation: none; } }

/* ---------- Hero stats ---------- */
.hero__stats { position: relative; z-index: 2; background: var(--color-brand-blue-deep); border-bottom: 1px solid rgba(255,255,255,0.10); }
.hero__stats-inner { max-width: 1400px; margin: 0 auto; padding: 24px 32px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; }
@media (max-width: 768px) { .hero__stats-inner { grid-template-columns: repeat(2,1fr); } }
/* Divider lines removed per Leonard 2026-05-31 (the 1px borders sat on the numbers). */
.hero__stat { padding: 16px 24px 16px 0; }
@media (max-width: 768px) {
  .hero__stat:nth-child(1), .hero__stat:nth-child(2) { padding-bottom: 16px; }
}
.hero__stat .num-big { font-family: var(--font-display); font-size: 36px; font-weight: 700; letter-spacing: -0.02em; color: #fff; line-height: 1; font-variant-numeric: tabular-nums; }
.hero__stat .label { margin-top: 6px; font-family: var(--font-mono); font-size: 10px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(255,255,255,0.55); }

/* ============================================================
   Hero product showcase (weekly-rotating SKU)
   ============================================================ */
.hero-showcase {
  position: relative; background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.12); border-radius: var(--radius-lg);
  padding: 18px; overflow: hidden;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  isolation: isolate; max-width: 300px; margin-left: auto;
}
/* When the hero stacks to one column, left-align the featured card instead of
   pinning it to the right edge (margin-left:auto looked detached). Per Leonard. */
@media (max-width: 899px) { .hero-showcase { margin-left: 0; margin-right: auto; } }

/* ---------- Hero product carousel (Interactivity API; sleek bottom arrows) ---------- */
.hero-carousel { max-width: 300px; margin-left: auto; position: relative; }
@media (max-width: 899px) { .hero-carousel { margin-left: 0; margin-right: auto; } }
/* Cards stack in one grid cell so the viewport height = the tallest card (no jump
   between slides) and we crossfade between them. */
.hero-carousel__viewport { display: grid; }
/* align-self:start so each card hugs its own content instead of stretching to the
   tallest card's height (that stretch left a long empty "chin" below the button on
   shorter cards -- Leonard). The grid row still reserves the tallest card's height
   (visibility:hidden cards keep their box), so switching slides never jumps. */
.hero-carousel__viewport > .hero-showcase { grid-area: 1 / 1; align-self: start; max-width: none; width: 100%; margin: 0; opacity: 0; visibility: hidden; transform: translateY(8px); transition: opacity 480ms var(--ease-out), transform 480ms var(--ease-out); }
.hero-carousel__viewport > .hero-showcase.is-active { opacity: 1; visibility: visible; transform: none; z-index: 1; }
@media (prefers-reduced-motion: reduce) { .hero-carousel__viewport > .hero-showcase { transition: none; } }
/* In the carousel the product photo / sku is shown directly (no per-card seal-on-view). */
.hero-carousel .hero-showcase__photo, .hero-carousel .hero-showcase__skucode { opacity: 1; transform: none; }
/* Controls live in the card header (Leonard: inside the card), opposite the
   "From the range" eyebrow. Compact so they read as a quiet utility, not a CTA. */
.hero-showcase__top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.hero-carousel__nav { display: flex; align-items: center; gap: 6px; }
.hero-carousel__btn { width: 30px; height: 30px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; border: 1px solid rgba(255,255,255,0.22); background: rgba(255,255,255,0.05); color: rgba(255,255,255,0.92); cursor: pointer; transition: border-color var(--duration-fast) var(--ease-out), background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-spring); }
.hero-carousel__btn:hover { border-color: var(--color-brand-red); background: rgba(208,38,41,0.18); color: #fff; }
.hero-carousel__btn:active { transform: translateY(1px); }
.hero-carousel__btn:focus-visible { outline: 2px solid var(--color-brand-red); outline-offset: 2px; }
.hero-carousel__btn svg { width: 14px; height: 14px; display: block; }
/* Reserve enough width for the widest count ("19 / 19") + tabular figures so the
   arrows never shift as the number changes; tighter tracking + a muted slash so the
   "n / total" reads as one tidy unit, centred between the arrows (Leonard: the
   number/slash looked unaligned and messy). */
.hero-carousel__count { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.02em; color: rgba(255,255,255,0.55); font-variant-numeric: tabular-nums; min-width: 56px; text-align: center; }
.hero-carousel__count b { color: #fff; font-weight: 600; }
.hero-carousel__count-sep { opacity: 0.4; margin: 0 1px; }
@media (max-width: 768px) { .hero-carousel__btn { width: 36px; height: 36px; } .hero-carousel__btn svg { width: 16px; height: 16px; } }
.hero-showcase::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: -1;
  background:
    radial-gradient(circle at 80% 0%, color-mix(in oklab, var(--color-brand-red) 26%, transparent), transparent 50%),
    radial-gradient(circle at 0% 100%, color-mix(in oklab, var(--color-brand-blue) 30%, transparent), transparent 50%);
}
.hero-showcase__head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 6px; }
.hero-showcase__family { font-family: var(--font-display); font-size: clamp(22px, 2.4vw, 28px); font-weight: 700; line-height: 1.05; letter-spacing: -0.025em; color: #fff; margin: 0; }
.hero-showcase__chem { font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: 0.10em; text-transform: uppercase; color: rgba(255,255,255,0.62); margin: 0 0 14px; }
.hero-showcase__cure { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.10em; color: rgba(255,255,255,0.62); display: inline-flex; align-items: center; gap: 8px; margin-left: 12px; white-space: nowrap; }
.hero-showcase__cure .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--color-brand-red); animation: pulseDot 2400ms ease-in-out infinite; }
/* overflow:visible (was hidden) so the featured product's drop-shadow flows down into
   the gap above the metric instead of being clipped by a hard line at the box bottom
   (Leonard). The grid background (::before) stays bounded by inset:0, so only the
   transparent product's shadow spills. */
.hero-showcase__visual { position: relative; aspect-ratio: 16 / 10; margin: 0 -6px 14px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-md); overflow: visible; }
.hero-showcase__visual::before {
  content: ''; position: absolute; inset: 0;
  background-image: linear-gradient(to right, rgba(255,255,255,0.05) 1px, transparent 1px), linear-gradient(to bottom, rgba(255,255,255,0.05) 1px, transparent 1px);
  background-size: 24px 24px;
  -webkit-mask-image: radial-gradient(ellipse at center, #000 30%, transparent 75%);
  mask-image: radial-gradient(ellipse at center, #000 30%, transparent 75%);
}
.hero-showcase__skucode {
  position: relative; opacity: 0; transform: translateY(14px);
  font-family: var(--font-display); font-weight: 700; font-size: 30px; letter-spacing: -0.02em; color: rgba(255,255,255,0.9);
  transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out);
}
.hero-showcase.is-sealed .hero-showcase__skucode { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .hero-showcase__skucode { opacity: 1; transform: none; transition: none; } }

/* Product photo cutout treatment: spotlight + contact shadow so the transparent
   cutout sits on the card and fades in on seal (ported from the mockup). */
.hero-showcase__visual--photo::after {
  content: ''; position: absolute; z-index: 0;
  left: 50%; top: 60%; transform: translate(-50%, -50%);
  width: 94%; height: 62%;
  background: radial-gradient(ellipse at center, color-mix(in oklab, var(--color-brand-blue) 36%, transparent) 0%, transparent 68%);
  filter: blur(6px); pointer-events: none;
}
.hero-showcase__photo {
  position: relative; z-index: 1;
  max-height: 150px; width: auto; max-width: 100%; object-fit: contain;
  filter: drop-shadow(0 18px 22px rgba(0,0,0,0.50)) drop-shadow(0 2px 4px rgba(0,0,0,0.35));
  opacity: 0; transform: translateY(14px);
  transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out);
}
.hero-showcase.is-sealed .hero-showcase__photo { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .hero-showcase__photo { opacity: 1; transform: none; transition: none; } }
.hero-showcase__vmono { position: absolute; left: 14px; bottom: 10px; font-family: var(--font-mono); font-size: 9px; color: rgba(255,255,255,0.55); letter-spacing: 0.08em; }
.hero-showcase__spec { display: flex; align-items: baseline; gap: 8px; font-family: var(--font-display); font-size: clamp(30px, 3.6vw, 44px); font-weight: 700; letter-spacing: -0.03em; color: #fff; line-height: 0.95; font-variant-numeric: tabular-nums; margin-top: 4px; }
.hero-showcase__spec .unit { font-size: 20px; font-weight: 600; color: rgba(255,255,255,0.72); letter-spacing: -0.01em; }
.hero-showcase__spec-label { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(255,255,255,0.55); margin: 6px 0 12px; }
.hero-showcase__sub { color: rgba(255,255,255,0.85); font-family: var(--font-display); font-size: 16px; font-weight: 500; letter-spacing: -0.005em; line-height: 1.4; margin: 0 0 16px; }
.hero-showcase__subs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 16px; }
.hero-showcase__subs span { font-family: var(--font-body); font-size: 12px; color: rgba(255,255,255,0.82); font-weight: 500; padding: 4px 10px; border-radius: var(--radius-pill); background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.10); }

/* ============================================================
   Pathways (cursor-following gradient cards)
   ============================================================ */
.grid-bg { position: relative; }
.grid-bg::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(25,22,64,0.06) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(25,22,64,0.06) 1px, transparent 1px);
  background-size: 56px 56px;
  -webkit-mask-image: radial-gradient(ellipse at 50% 30%, #000 30%, transparent 80%);
  mask-image: radial-gradient(ellipse at 50% 30%, #000 30%, transparent 80%);
}
.pathway-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; position: relative; --mx: 50%; --my: 50%; }
/* (Compact 3-up mobile pathway rules are defined after the card detail rules
   below, so the display:none on .pathway-card__cta wins by source order.) */
.pathway-card {
  position: relative; background: var(--surface-elevated); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 32px; min-height: 280px;
  display: flex; flex-direction: column; gap: 12px; overflow: hidden; isolation: isolate;
  text-decoration: none;
  transition: border-color var(--duration-base) var(--ease-out), transform var(--duration-base) var(--ease-spring);
}
.pathway-card::before {
  content: ''; position: absolute; inset: 0; z-index: 0; opacity: 0; pointer-events: none;
  background: radial-gradient(circle 320px at var(--cx, 50%) var(--cy, 50%), color-mix(in oklab, var(--color-brand-blue) 14%, transparent), transparent 70%);
  transition: opacity var(--duration-base) var(--ease-out);
}
.pathway-card:hover { border-color: var(--border-strong); transform: translateY(-2px); text-decoration: none;
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--color-brand-blue) 16%, transparent),
              0 18px 48px -22px color-mix(in oklab, var(--color-brand-blue) 50%, transparent); }
.pathway-card:hover::before { opacity: 1; }
/* Edge-of-light glow: a border-only radial that follows the cursor. mask-composite
   leaves only the 1.5px ring lit, so light reflects along the card edges/corners
   (the "light on the wall" effect). Ported from the mockup pathways section;
   --cx/--cy are px from interactions.js. */
.pathway-card::after {
  content: ''; position: absolute; inset: 0; z-index: 0; border-radius: inherit;
  padding: 1.5px; pointer-events: none; opacity: 0;
  background: radial-gradient(circle 240px at var(--cx, 50%) var(--cy, 50%),
    color-mix(in oklab, var(--color-brand-blue) 92%, white) 0%,
    color-mix(in oklab, var(--color-brand-blue) 62%, transparent) 26%,
    transparent 62%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  transition: opacity var(--duration-base) var(--ease-out);
}
.pathway-card:hover::after { opacity: 1; }
/* Grid-wide pooled light removed: it extended 40px beyond the grid and read as
   the cursor glow "spilling out of the cards". The per-card inner glow (::before)
   and edge glow (::after) are both clipped by the card's overflow:hidden, so the
   light now stays contained within each square. */
.pathway-card > * { position: relative; z-index: 1; }
.pathway-card__num { font-family: var(--font-mono); font-size: 11px; color: var(--text-secondary); letter-spacing: 0.08em; margin-bottom: 8px; }
.pathway-card__icon { width: 32px; height: 32px; color: var(--accent-primary); margin-bottom: 16px; }
.pathway-card__title { font-family: var(--font-display); font-size: 24px; font-weight: 600; letter-spacing: -0.01em; color: var(--text-primary); margin: 0; }
.pathway-card__desc { color: var(--text-secondary); font-size: 14px; line-height: 1.6; flex: 1; margin: 0; }
.pathway-card__cta { display: inline-flex; align-items: center; gap: 6px; color: var(--accent-primary); font-family: var(--font-body); font-weight: 600; font-size: 13px; }
.pathway-card:hover .pathway-card__cta .arrow { transform: translateX(4px); }
.pathway-card__cta .arrow { transition: transform var(--duration-base) var(--ease-spring); }
/* Keep all three entry points side by side at every width (per Leonard). Below
   620px condense each card to number + icon + title so three fit on a phone
   instead of stacking one at a time. Placed after the base card rules so the
   display:none on .pathway-card__desc/.pathway-card__cta wins by source order. */
@media (max-width: 620px) {
  .pathway-grid { gap: 10px; }
  .pathway-card { padding: 14px 10px; min-height: 0; gap: 6px; }
  .pathway-card__num { font-size: 9px; margin-bottom: 2px; }
  .pathway-card__icon { width: 22px; height: 22px; margin-bottom: 4px; }
  .pathway-card__title { font-size: 12.5px; line-height: 1.2; }
  .pathway-card__desc, .pathway-card__cta { display: none; }
}

/* ============================================================
   Featured product grid (one representative per chemistry)
   ============================================================ */
.feat-grid {
  position: relative;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px;
  background: var(--border-subtle);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); overflow: hidden;
}
@media (max-width: 960px) { .feat-grid { grid-template-columns: repeat(2, 1fr); } }
/* Keep these rich product cards 2-up on phones. Previously they went 1-up below
   480px, stacking single-file and ballooning mobile page length (the catalog hit
   18,599px). A compact-card override below (after the base .feat-card rules so it
   wins on source order) shrinks photo/padding so two fit comfortably at 390px. */
@media (max-width: 360px) { .feat-grid { grid-template-columns: 1fr; } }
/* The support CTA card carries far more copy than a product card; squeezed into one
   2-up column its "Support / Technical team" header crowded together and it left an
   empty cell beside it. Give it the full row on phones (covers the catalog .ml-results
   grid and the industry feat-grid -- both are 2-up at this width). */
@media (max-width: 640px) { .feat-grid > .feat-card--cta { grid-column: 1 / -1; } }
.feat-card {
  background: var(--surface-elevated); color: var(--text-primary);
  padding: 28px 28px 24px; text-decoration: none;
  display: flex; flex-direction: column; gap: 0;
  position: relative; min-height: 400px; overflow: hidden; isolation: isolate;
  transition: background-color var(--duration-base) var(--ease-out), transform var(--duration-base) var(--ease-spring), box-shadow var(--duration-base) var(--ease-out);
}
.feat-card > * { position: relative; z-index: 1; }
.feat-card:not(.feat-card--cta):hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 32px rgba(42,61,152,0.08), 0 4px 8px rgba(42,61,152,0.06);
  text-decoration: none;
}
@media (prefers-reduced-motion: reduce) { .feat-card:hover { transform: none; } }

/* Entry stagger (revealed once JS marks the grid ready; visible without JS) */
[data-interaction~="feat-grid-stagger"].is-stagger-ready .feat-card { opacity: 0; }
[data-interaction~="feat-grid-stagger"].is-stagger-ready .feat-card.is-in { opacity: 1; animation: cardIn 640ms var(--ease-out) backwards; }
@keyframes cardIn { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  [data-interaction~="feat-grid-stagger"].is-stagger-ready .feat-card { opacity: 1; }
  [data-interaction~="feat-grid-stagger"].is-stagger-ready .feat-card.is-in { animation: none; }
}
.feat-card__head {
  display: flex; align-items: center; justify-content: space-between;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-secondary); margin-bottom: 20px;
}
.feat-card__chem { color: var(--color-brand-blue); font-weight: 500; }
.feat-card__photo {
  height: 168px; margin-bottom: 18px; border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle);
  background:
    radial-gradient(circle at 70% 20%, color-mix(in oklab, var(--color-brand-blue) 10%, #fff), transparent 60%),
    linear-gradient(135deg, #fff, var(--neutral-100));
  /* overflow:visible (was hidden) so the product's drop-shadow is NOT clipped at the
     tile's bottom edge/border -- the shadow now flows down into the card (Leonard). */
  position: relative; overflow: visible; display: flex; align-items: center; justify-content: center;
}
.feat-card__photo img {
  max-width: 84%; max-height: 86%; object-fit: contain; position: relative; z-index: 1;
  filter: drop-shadow(0 8px 16px rgba(25,22,64,0.18));
}
.feat-card__photo-fallback {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-weight: 700; font-size: 22px; letter-spacing: -0.01em;
  color: var(--color-brand-blue-deep);
  background: linear-gradient(135deg, color-mix(in oklab, var(--color-brand-blue) 12%, #fff), var(--neutral-100));
}
.feat-card__name { font-family: var(--font-display); font-size: 22px; font-weight: 600; letter-spacing: -0.015em; color: var(--text-primary); margin: 0; }
.feat-card__chem-line { font-family: var(--font-body); font-size: 13px; color: var(--text-secondary); line-height: 1.5; margin-top: 4px; }
/* Industry icons: which industries this product serves. Sits between the
   chemistry line and the technical metric (the metric stays pinned to the
   bottom via margin-top:auto), giving the requested breathing space. */
/* Industry icons: clean, borderless, larger glyphs (no square chip) so each
   industry reads clearly. Brand-navy, brightening to brand-blue on card hover. */
.feat-card__industries { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 18px; align-items: center; }
.ind-icon {
  position: relative;
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  color: var(--color-brand-blue-deep); opacity: 0.8; cursor: help; outline: none;
  transition: opacity var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out), transform var(--duration-base) var(--ease-spring);
}
.feat-card:hover .ind-icon { opacity: 1; color: var(--color-brand-blue); }
.ind-icon:hover, .ind-icon:focus-visible { transform: translateY(-2px); color: var(--color-brand-blue); opacity: 1; }
/* Clickable chip (routes to the industry filter, not the product). */
.ind-icon--link { cursor: pointer; }
.ind-icon svg { width: 26px; height: 26px; display: block; }
/* Hover/focus pop-up word (replaces the browser title tooltip; clearly readable). */
.ind-icon__label {
  position: absolute; bottom: calc(100% + 8px); left: 50%;
  transform: translate(-50%, 6px) scale(0.96);
  white-space: nowrap; pointer-events: none; z-index: 6;
  font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: #fff; background: var(--color-brand-blue-deep);
  padding: 6px 10px; border-radius: var(--radius-pill); border: 1px solid rgba(255,255,255,0.14);
  box-shadow: 0 8px 20px rgba(25,22,64,0.28);
  opacity: 0; transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-base) var(--ease-spring);
}
.ind-icon__label::after {
  content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 5px solid transparent; border-top-color: var(--color-brand-blue-deep);
}
.ind-icon:hover .ind-icon__label, .ind-icon:focus-visible .ind-icon__label { opacity: 1; transform: translate(-50%, 0) scale(1); }
@media (prefers-reduced-motion: reduce) { .ind-icon, .ind-icon__label { transition: none; } }
/* JS-portaled industry tooltip: rendered on <body> so it escapes the card AND
   grid overflow:hidden and is viewport-clamped, letting long labels show in
   full. When JS is active the inline (clippable) .ind-icon__label is hidden so
   only the portal shows; without JS the inline label remains as the fallback. */
body.js-indtip .ind-icon__label { display: none; }
.ind-tip {
  position: fixed; z-index: 200; pointer-events: none; top: 0; left: 0;
  font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: #fff; background: var(--color-brand-blue-deep); white-space: nowrap;
  padding: 6px 10px; border-radius: var(--radius-pill); border: 1px solid rgba(255,255,255,0.14);
  box-shadow: 0 8px 20px rgba(25,22,64,0.28);
  opacity: 0; transform: translateY(6px) scale(0.96);
  transition: opacity var(--duration-fast) var(--ease-out), transform var(--duration-base) var(--ease-spring);
}
.ind-tip.is-in { opacity: 1; transform: translateY(0) scale(1); }
.ind-tip::after {
  content: ''; position: absolute; top: 100%; left: var(--tip-arrow, 50%); transform: translateX(-50%);
  border: 5px solid transparent; border-top-color: var(--color-brand-blue-deep);
}
.ind-tip.ind-tip--below::after { top: auto; bottom: 100%; border-top-color: transparent; border-bottom-color: var(--color-brand-blue-deep); }
@media (prefers-reduced-motion: reduce) { .ind-tip { transition: none; } }
.feat-card__metric {
  margin-top: auto; padding-top: 16px; border-top: 1px dashed var(--border-subtle);
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
}
.feat-card__metric-num { font-family: var(--font-display); font-size: 28px; font-weight: 700; letter-spacing: -0.02em; color: var(--text-primary); font-variant-numeric: tabular-nums; text-wrap: nowrap; }
.feat-card__metric-label { font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.10em; text-transform: uppercase; color: var(--text-secondary); text-align: right; max-width: 14ch; line-height: 1.4; }

/* Consistent two-field "bottom data" on every product card (Service temp + Cure
   speed, or Foam thickness for AFT tapes). Shares the old metric's dashed footing
   but stacks two label/value rows so the data is identical across all products. */
.feat-card__specs { margin-top: auto; padding-top: 16px; border-top: 1px dashed var(--border-subtle); display: flex; flex-direction: column; gap: 8px; }
.feat-card__spec { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0; }
.feat-card__spec-label { font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.10em; text-transform: uppercase; color: var(--text-secondary); white-space: nowrap; margin: 0; }
.feat-card__spec-val { font-family: var(--font-display); font-size: 15px; font-weight: 600; letter-spacing: -0.01em; color: var(--text-primary); text-align: right; font-variant-numeric: tabular-nums; line-height: 1.35; margin: 0; }

/* CTA tile filling the final cell */
.feat-card--cta { background: var(--color-brand-blue-deep); color: #fff; justify-content: flex-end; }
.feat-card--cta .cta-glow {
  position: absolute; width: 105%; aspect-ratio: 1; border-radius: 50%;
  filter: blur(38px); z-index: 0; pointer-events: none; opacity: 0.9;
  transition: transform 760ms cubic-bezier(0.22, 1, 0.36, 1), opacity 760ms var(--ease-out);
}
.feat-card--cta .cta-glow--red { background: radial-gradient(circle, color-mix(in oklab, var(--color-brand-red) 90%, transparent) 0%, color-mix(in oklab, var(--color-brand-red) 55%, transparent) 45%, transparent 70%); right: -25%; top: -45%; }
.feat-card--cta .cta-glow--blue { background: radial-gradient(circle, color-mix(in oklab, #3a55d8 92%, transparent) 0%, color-mix(in oklab, var(--color-brand-blue) 60%, transparent) 45%, transparent 70%); left: -25%; bottom: -45%; }
/* On hover the two colour orbs drift gently toward centre and grow (they used to
   translate ~80% off the card and vanish, clipped by overflow:hidden) - Leonard
   wants to SEE both colours intensify on hover, not have them disappear. */
.feat-card--cta:hover .cta-glow--red { transform: translate(-14%, 22%) scale(1.12); opacity: 1; }
.feat-card--cta:hover .cta-glow--blue { transform: translate(14%, -22%) scale(1.12); opacity: 1; }
.feat-card--cta .feat-card__head, .feat-card--cta__body { position: relative; z-index: 2; }
@media (prefers-reduced-motion: reduce) { .feat-card--cta .cta-glow { transition: none; } }
.feat-card--cta__body { display: flex; flex-direction: column; gap: 10px; }
.feat-card--cta .feat-card__name { color: #fff; font-size: 24px; }
.feat-card--cta__link { margin-top: 8px; display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-body); font-weight: 600; font-size: 14px; color: #fff; }
.feat-card--cta__link .arrow { transition: transform var(--duration-base) var(--ease-spring); }
.feat-card--cta:hover .feat-card--cta__link .arrow { transform: translateX(5px); }
.feat-card--cta .feat-card__head { color: rgba(255,255,255,0.6); }
.feat-card--cta .feat-card__chem { color: rgba(255,255,255,0.85); }

/* Compact 2-up product card for phones (placed after the base rules so it wins on
   source order). Shrinks photo + padding + min-height so two cards fit at ~175px. */
@media (max-width: 560px) {
  .feat-card { padding: 16px 16px 18px; min-height: 0; }
  .feat-card--cta { min-height: 0; }
  .feat-card__photo { height: 124px; margin-bottom: 14px; }
  .feat-card__head { margin-bottom: 12px; }
  /* The chemistry already prints under the product name (.feat-card__chem-line),
     so on the narrow 2-up cards drop the duplicate top-right copy -- it was
     colliding with the brand / series label top-left. Keep it on the support CTA
     card, which has no chem-line of its own. */
  .feat-card:not(.feat-card--cta) .feat-card__chem { display: none; }
  .feat-card:not(.feat-card--cta) .feat-card__head { justify-content: flex-start; }
  .feat-card__name { font-size: 17px; }
  .feat-card__specs { padding-top: 12px; gap: 8px; }
  /* Stack label-over-value so long values ("15 minutes (set to touch)") get the full
     card width and stop hyphen-breaking ("min-utes") in the squeezed right column. */
  .feat-card__spec { flex-direction: column; align-items: flex-start; gap: 1px; }
  .feat-card__spec-val { font-size: 14px; text-align: left; hyphens: none; overflow-wrap: normal; line-height: 1.3; }
  .feat-card__industries { gap: 12px; margin-top: 12px; }
}

/* ============================================================
   Coverage (verified reach map; clickable country list, ported 1:1)
   ============================================================ */
.coverage-shell {
  background: var(--color-brand-blue-deep); color: var(--text-on-brand);
  border-radius: var(--radius-lg); overflow: hidden; position: relative;
  /* align-items defaults to stretch (matches the mockup): the map column stretches
     to the side's height and the .map-photo (height:auto) sits at the top, which is
     the layout the mockup's node %s were calibrated against. Safe from the old
     click-bounce because the fixed detail height keeps the side a constant height. */
  display: grid; grid-template-columns: 1fr 320px;
}
@media (max-width: 900px) { .coverage-shell { grid-template-columns: 1fr; } }
.coverage-map {
  /* Top padding trimmed (was 32px all round) so the map's northern dots sit flush to
     the top of the coverage panel -- the pins live inside .map-photo-wrap, so they
     move up WITH the photo and stay on their cities (Leonard). */
  position: relative; padding: 10px 32px 32px; min-height: 480px;
  background: radial-gradient(circle at 30% 40%, rgba(42,61,152,0.45), transparent 60%), var(--color-brand-blue-deep);
}
.coverage-map__grid {
  position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(to right, rgba(255,255,255,0.04) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,0.04) 1px, transparent 1px);
  background-size: 34px 34px;
}
/* The dotted SE-Asia map under the country markers is the EXACT design-mockup
   image (map-coverage.png, extracted from the approved mockup), shown as an <img>
   at width:100% / height:auto so it KEEPS its aspect ratio (no stretch -- the old
   background-size:100% 100% was exactly what stretched it). The map-node %s in
   front-page.html are the mockup's own values, calibrated to this image in this
   layout (padding 32, min-height 480, align-items:start), so each pin lands on the
   right city and Singapore reads as its own dot (not "inside Malaysia"). Revert:
   restore the .map-field background-image. */
.map-photo-wrap { position: relative; z-index: 1; width: 100%; }
.map-photo {
  width: 100%; height: auto; display: block;
  filter: brightness(1.12) drop-shadow(0 0 22px rgba(120, 200, 255, 0.40));
}
.map-nodes { position: absolute; inset: 0; z-index: 2; }
.map-node { position: absolute; transform: translate(-50%, -50%); width: 0; height: 0; }
/* Stable, dot-centred click target. The node box is 0x0 and the only other
   hittable parts are the 10px dot and a ring that pulses to nothing, so clicks
   on a dot were missing (and the default-active SG stayed lit). A 34px
   transparent circle, centred on the dot, makes every point reliably clickable
   on its circle. */
.map-node::before {
  content: ""; position: absolute; left: 50%; top: 50%; width: 34px; height: 34px;
  transform: translate(-50%, -50%); border-radius: 50%; z-index: 3;
}
.map-node__dot { position: absolute; left: 50%; top: 50%; width: 10px; height: 10px; border-radius: 50%; transform: translate(-50%, -50%); background: #fff; box-shadow: 0 0 10px rgba(255,255,255,0.95); }
/* HQ marker: a larger NEUTRAL (silver) dot by default; it turns red only when
   selected (.is-active, below). Singapore is is-active on load so it reads red,
   and reverts to this silver dot the instant another country is chosen, so the
   map never shows two red dots at once. Revert: set background back to
   var(--color-brand-red) and drop the .is-active .map-node__dot--hq rule. */
.map-node__dot--hq { width: 14px; height: 14px; background: #e9eef5; box-shadow: 0 0 0 3px rgba(255,255,255,0.14), 0 0 14px rgba(255,255,255,0.85); }
/* pointer-events:none so the pulse (which grows to 108px) never overlaps and
   steals clicks from a neighbouring dot (SG <-> MY are only ~32px apart). */
.map-node__ring { position: absolute; left: 50%; top: 50%; width: 14px; height: 14px; border-radius: 50%; transform: translate(-50%, -50%); border: 2px solid var(--color-brand-red); animation: nodePulse 2600ms var(--ease-out) infinite; pointer-events: none; }
@keyframes nodePulse { 0% { width: 14px; height: 14px; opacity: 0.85; } 70% { opacity: 0.25; } 100% { width: 108px; height: 108px; opacity: 0; } }
/* Phones AND tablets: the 108px ring spans half the small map and reads as a broad,
   over-bright red wash, and the dots sit close together. A smaller, fainter, slower
   ring softens the blink Leonard flagged as too abrupt on mobile + tablet. */
@keyframes nodePulseSm { 0% { width: 12px; height: 12px; opacity: 0.5; } 70% { opacity: 0.14; } 100% { width: 48px; height: 48px; opacity: 0; } }
@media (max-width: 1024px) {
  .map-node__ring { animation-name: nodePulseSm; animation-duration: 3600ms; border-width: 1.5px; }
}
/* Single column: the map photo (height:auto) is much shorter than the 480px panel,
   leaving a big empty navy band below it before the supply-network list. Let the
   panel hug the photo. */
@media (max-width: 900px) {
  .coverage-map { min-height: auto; padding-bottom: 18px; }
}
@media (prefers-reduced-motion: reduce) { .map-node__ring { animation: none; opacity: 0; } }
.map-node__label {
  position: absolute; left: 50%; bottom: calc(100% + 9px); transform: translate(-50%, 4px); white-space: nowrap;
  font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: 0.06em; color: #fff;
  padding: 4px 9px; border-radius: var(--radius-pill); background: rgba(25,22,64,0.85); border: 1px solid rgba(255,255,255,0.18);
  box-shadow: 0 6px 16px rgba(0,0,0,0.38);
  opacity: 0; pointer-events: none; z-index: 4;
  transition: opacity 220ms var(--ease-out), transform 220ms var(--ease-out);
}
.map-node.is-active .map-node__label, .map-node:hover .map-node__label { opacity: 1; transform: translate(-50%, 0); }
@media (prefers-reduced-motion: reduce) { .map-node__label { transition: none; } }
.map-node.is-active .map-node__dot { background: var(--color-brand-red); box-shadow: 0 0 0 4px rgba(208,38,41,0.25), 0 0 12px rgba(208,38,41,0.85); }
/* HQ dot keeps its larger size + a stronger red halo when it is the active one. */
.map-node.is-active .map-node__dot--hq { background: var(--color-brand-red); box-shadow: 0 0 0 4px rgba(208,38,41,0.25), 0 0 18px rgba(208,38,41,0.92); }
.coverage-side { padding: 32px; background: rgba(0,0,0,0.18); border-left: 1px solid rgba(255,255,255,0.08); display: flex; flex-direction: column; }
@media (max-width: 900px) { .coverage-side { border-left: 0; border-top: 1px solid rgba(255,255,255,0.08); max-width: 560px; margin: 0 auto; width: 100%; box-sizing: border-box; } }
.coverage-side__title { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase; color: rgba(255,255,255,0.50); margin: 0 0 20px; }
/* No scrollbar (matches the mockup): all six markets show at once. The side
   column has room for six rows, so the clean look is preserved. */
.coverage-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.coverage-list li { padding: 12px 14px; border-radius: var(--radius-sm); display: block; cursor: pointer; border: 1px solid transparent; transition: background-color var(--duration-fast) var(--ease-out), border-color var(--duration-fast) var(--ease-out); }
.coverage-list li:hover { background: rgba(255,255,255,0.04); }
.coverage-list li.is-active { background: rgba(42,61,152,0.30); border-color: rgba(255,255,255,0.18); }
.coverage-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
/* Instant toggle + FIXED open height: every country's detail occupies the same
   height, so selecting a country never changes the side height and the shell never
   resizes (Leonard's "the square expands then settles"). 240px fits the longest
   detail (Singapore ~225px). */
.coverage-detail { overflow: hidden; height: 0; }
.coverage-list li.is-active .coverage-detail { height: 240px; }
.coverage-flag { display: block; width: 46px; height: 31px; margin: 14px 0 0; border-radius: 3px; border: 1px solid rgba(255,255,255,0.20); box-shadow: 0 6px 16px rgba(0,0,0,0.35); object-fit: cover; }
.coverage-detail p { margin: 12px 0 2px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,0.10); font-family: var(--font-body); font-size: 12.5px; line-height: 1.65; color: rgba(255,255,255,0.74); max-width: 42ch; }
.coverage-detail .meta { display: block; margin-top: 8px; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.10em; text-transform: uppercase; color: rgba(255,255,255,0.62); }
.coverage-list .country { font-family: var(--font-display); font-weight: 600; font-size: 15px; color: #fff; letter-spacing: -0.005em; }
.coverage-list .native { font-family: var(--font-body); font-size: 11px; color: rgba(255,255,255,0.68); margin-top: 2px; }
.coverage-list .tier { font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.12em; text-transform: uppercase; padding: 4px 8px; border-radius: var(--radius-pill); background: rgba(255,255,255,0.06); color: rgba(255,255,255,0.75); white-space: nowrap; }
.coverage-list .tier.tier--hq { background: var(--color-brand-red); color: #fff; }
.coverage-list .tier.tier--full { background: rgba(42,61,152,0.55); color: #fff; }
/* Phones/tablets (the side column stacks under the map and each country becomes a
   full accordion row): the desktop fixed 240px open-height -- chosen so switching a
   country never resizes the side -- CLIPS the longest detail on narrow screens
   (Singapore needs ~281px at 320px wide, losing "distributor." + the address line)
   and leaves a dead gap under the short ones. Size the open panel to its content so
   it always shows in full, and tighten each segment so the list reads more compact.
   Desktop (>900px) keeps the fixed height and its no-resize behaviour. */
@media (max-width: 900px) {
  .coverage-list li.is-active .coverage-detail { height: auto; }
  .coverage-list li { padding: 9px 12px; }
  .coverage-flag { margin-top: 10px; }
  .coverage-detail p { margin-top: 10px; padding-top: 10px; }
}

/* ============================================================
   Products catalog (filter rail + results)
   ============================================================ */
.ml-catalog { display: grid; grid-template-columns: 280px 1fr; gap: var(--space-7); align-items: start; }
@media (max-width: 900px) { .ml-catalog { grid-template-columns: 1fr; } }
/* When the industry banner renders (filtered by ?industry=), it becomes the page
   header, so the generic "Catalog / Products" intro is suppressed. */
body:has(.industry-hero) .ml-catalog-intro { display: none; }
.ml-results .feat-grid { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1100px) { .ml-results .feat-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 360px) { .ml-results .feat-grid { grid-template-columns: 1fr; } }

/* Grouped catalog (default /products/ view): top 4 per chemistry + "See all". */
.ml-group { margin-bottom: var(--space-9); }
.ml-group:last-of-type { margin-bottom: 0; }
.ml-group__head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: var(--space-4); padding-bottom: var(--space-3); border-bottom: 1px solid var(--border-subtle); }
.ml-group__title { font-family: var(--font-display); font-size: clamp(22px, 2.4vw, 30px); font-weight: 700; letter-spacing: -0.02em; color: var(--text-primary); margin: 0; }
.ml-group__count { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-primary); white-space: nowrap; }
/* Grouped view: flex so a chemistry with 1-3 products shows comfortably-sized
   cards centred in the row, never narrow 1/4 cards stranded beside an empty void
   (Leonard: "empty gray space in the grid"). A full group of 4 still fills the row. */
.ml-results .ml-group__grid { display: flex; flex-wrap: wrap; gap: 16px; justify-content: center; }
.ml-results .ml-group__grid > .feat-card { flex: 1 1 240px; max-width: 340px; }
/* Phones: 2-up instead of 1-up (was max-width:none => one full-width card per row,
   the main driver of the 18,599px catalog). Two half-width cards per row; a lone
   trailing card grows to fill. Below 360px fall back to a single column. */
@media (max-width: 560px) { .ml-results .ml-group__grid > .feat-card { flex: 1 1 calc(50% - 8px); max-width: none; } }
@media (max-width: 360px) { .ml-results .ml-group__grid > .feat-card { flex-basis: 100%; } }
/* A chemistry group with a single product: left-align it (don't strand one narrow
   card centered in a wide row at tablet/desktop) and let it grow a little. On phones
   the card already fills its row, so only adjust from 561px up. */
.ml-results .ml-group__grid:has(> .feat-card:only-child) { justify-content: flex-start; }
@media (min-width: 561px) { .ml-results .ml-group__grid > .feat-card:only-child { flex-basis: 460px; max-width: 460px; } }
.ml-group__more { display: inline-flex; align-items: center; gap: 8px; margin-top: var(--space-5); font-family: var(--font-body); font-weight: 600; font-size: 14px; color: var(--accent-primary); text-decoration: none; }
.ml-group__more .arrow { transition: transform var(--duration-base) var(--ease-spring); }
.ml-group__more:hover { text-decoration: none; }
.ml-group__more:hover .arrow { transform: translateX(4px); }
/* Catalog uses a gapped, bordered card grid (not the seamless homepage grid) so a
   partial last row never paints an empty grey void; trailing empty cells blend
   with the page background. */
.ml-results .feat-grid { background: transparent; border: 0; border-radius: 0; overflow: visible; gap: 16px; }
.ml-results .feat-card { border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); }
/* The instant filter hides via the native `hidden` attribute (data-wp-bind--hidden).
   Author rules like `.feat-card{display:flex}` would otherwise beat the UA [hidden]
   rule, so force it for everything the catalog store toggles (cards, the grouped /
   flat containers, intro, empty, count). */
.ml-results [hidden] { display: none !important; }
.feat-card--cta .feat-card__chem-line { color: rgba(255, 255, 255, 0.72); }
/* Contextual intro for the active filter selection (the "section you selected"). */
.ml-results__intro { padding: 0 0 var(--space-5); margin-bottom: var(--space-5); border-bottom: 1px solid var(--border-subtle); }
.ml-results__intro-eyebrow { font-family: var(--font-mono); font-size: 10px; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent-primary); margin: 0 0 8px; }
.ml-results__intro-title { font-family: var(--font-display); font-size: clamp(24px, 3vw, 34px); font-weight: 700; letter-spacing: -0.02em; color: var(--text-primary); margin: 0; line-height: 1.1; }
.ml-results__intro-desc { color: var(--text-secondary); font-size: 15px; line-height: 1.7; margin: 12px 0 0; max-width: 60ch; }
.ml-results__count { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-secondary); margin: 0 0 var(--space-4); }
.ml-results__empty { padding: var(--space-9) var(--space-5); text-align: center; border: 1px dashed var(--border-subtle); border-radius: var(--radius-lg); }
.ml-results__empty p { margin: 0 0 var(--space-4); color: var(--text-secondary); }
.ml-results__pager { display: flex; gap: 6px; flex-wrap: wrap; margin-top: var(--space-6); }
.ml-results__pager .page-numbers { display: inline-flex; align-items: center; justify-content: center; min-width: 40px; height: 40px; padding: 0 12px; border-radius: var(--radius-md); border: 1px solid var(--border-subtle); font-family: var(--font-display); font-weight: 600; font-size: 14px; color: var(--text-primary); text-decoration: none; }
.ml-results__pager .page-numbers.current { background: var(--color-brand-blue); color: #fff; border-color: var(--color-brand-blue); }
.ml-results__pager a.page-numbers:hover { border-color: var(--border-strong); }

/* ============================================================
   Industries index (one card per industry) — /industries/
   ============================================================ */
.ml-industries { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: start; }
@media (max-width: 900px) { .ml-industries { grid-template-columns: 1fr 1fr; } }
@media (max-width: 360px) { .ml-industries { grid-template-columns: 1fr; } }
.ml-ind-card {
  display: flex; flex-direction: column; gap: 12px; min-width: 0;
  background: var(--surface-elevated); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 28px; min-height: 200px; text-decoration: none;
  transition: border-color var(--duration-base) var(--ease-out), transform var(--duration-base) var(--ease-spring), box-shadow var(--duration-base) var(--ease-out);
}
/* Phones: 2-up cards are tight, so compact padding/type and let the count drop below
   the name when cramped. min-width:0 above lets the grid columns shrink to fit (was
   overflowing at 390 because the nowrap "12 PRODUCTS" count held the column wide). */
@media (max-width: 560px) {
  .ml-ind-card { padding: 18px; min-height: 0; gap: 10px; }
  .ml-ind-card__head { flex-wrap: wrap; gap: 4px 12px; }
  .ml-ind-card__name { font-size: 18px; min-width: 0; overflow-wrap: anywhere; }
  .ml-ind-card__products { font-size: 12.5px; }
}
.ml-ind-card:hover { border-color: var(--border-strong); transform: translateY(-3px); box-shadow: 0 18px 48px -22px color-mix(in oklab, var(--color-brand-blue) 50%, transparent); text-decoration: none; }
.ml-ind-card:focus-visible { outline: 2px solid var(--accent-primary); outline-offset: 3px; border-color: var(--border-strong); transform: translateY(-3px); box-shadow: 0 18px 48px -22px color-mix(in oklab, var(--color-brand-blue) 50%, transparent); }

/* ============================================================
   Contact page: elevated office card
   ============================================================ */
.ml-contact-card {
  background: var(--surface-elevated); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-1);
  padding: var(--space-6) var(--space-7); max-width: 520px; margin-top: var(--space-5);
}
.ml-contact-card h3 { margin-top: 0; }
.ml-contact-card a { color: var(--accent-primary); }
.ml-ind-card__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.ml-ind-card__name { font-family: var(--font-display); font-size: 22px; font-weight: 600; letter-spacing: -0.015em; color: var(--text-primary); margin: 0; }
.ml-ind-card__count { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-primary); white-space: nowrap; }
.ml-ind-card__products { color: var(--text-secondary); font-size: 13px; line-height: 1.6; margin: 0; flex: 1; }
.ml-ind-card__cta { display: inline-flex; align-items: center; gap: 6px; color: var(--accent-primary); font-family: var(--font-body); font-weight: 600; font-size: 13px; margin-top: 4px; }
.ml-ind-card__cta .arrow { transition: transform var(--duration-base) var(--ease-spring); }
.ml-ind-card:hover .ml-ind-card__cta .arrow { transform: translateX(4px); }

/* ============================================================
   Shield-field background trace (curve ported from the mockup)
   A single faint shield-half curve that a small glowing DOT travels along
   (Leonard 2026-06-04: the old sweeping line beam looked too basic; he wanted a
   smaller, glowing dot moving through instead). The dot circles are created and
   positioned along the trace by interactions.js (getPointAtLength). The old beam
   paths (.shield-glow / .shield-core) are kept in the markup but hidden here.
   ============================================================ */
.shield-host { position: relative; isolation: isolate; }
.shield-deco {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 100vw; aspect-ratio: 1600 / 620;
  z-index: -1; pointer-events: none; overflow: visible;
}
.shield-deco svg { width: 100%; height: 100%; display: block; overflow: visible; }
.shield-trace { fill: none; stroke: rgba(42,61,152,0.10); stroke-width: 1.1; }
/* The old line beam is replaced by the traveling dot; hide both beam paths. */
.shield-glow, .shield-core { display: none; }
/* Traveling dot: a bright core with a soft blurred halo, glowing in brand blue.
   cx/cy are set per frame by interactions.js in SVG user units (viewBox space). */
.shield-dot { pointer-events: none; }
.shield-dot--core { fill: rgba(224,232,255,0.98); filter: drop-shadow(0 0 4px rgba(120,150,255,0.95)) drop-shadow(0 0 10px rgba(42,61,152,0.75)); }
.shield-dot--halo { fill: rgba(130,158,255,0.55); filter: blur(4px); }
@media (prefers-reduced-motion: reduce) { .shield-dot { display: none; } }
@media (max-width: 768px) { .shield-deco { width: 152vw; } .shield-trace { stroke: rgba(42,61,152,0.12); } }
/* Reusable decorative "moving line" band (mightyloc/moving-line block) for inner
   pages. Crops the full shield-deco to a slim horizontal sweep; the line animates
   via interactions.js and is hidden under reduced motion (same as the homepage). */
/* Full-bleed: break out of the constrained container so the sweep runs edge-to-edge
   across the viewport (Leonard: the line was cropped to the column and read as a
   cut-off line floating bottom-left). 100vw centred on the viewport. */
/* True full-bleed. Two things were fighting us: (1) is-layout-constrained injects
   `max-width: <contentSize>` on flow children via a :where() rule, capping
   width:100vw back to the 1100px column; (2) the layout engine rewrites the
   horizontal MARGINS of an over-wide child (it forced margin-left:0/right:-104px),
   so the prior `calc(50% - 50vw)` margin never took effect and the line sat inset
   and ran off one side. Fix: max-width:none lifts the cap, and we centre with
   left:50% + translateX(-50%) (transform can't be overridden by the margin rules).
   The container's content box is centred in the viewport, so the band lands exactly
   0->100vw -- the sweep runs corner to corner (Leonard: it emerged/ended mid-screen,
   never reaching the edges). Revert: restore `margin: var(--space-3) calc(50% - 50vw)`. */
.ml-moving-line { height: 200px; position: relative; left: 50%; transform: translateX(-50%); width: 100vw; max-width: none; margin: var(--space-3) 0; overflow: hidden; }
@media (max-width: 768px) { .ml-moving-line { height: 150px; } }
/* The static trace is barely visible globally (10% -- fine as ambient shield
   decor on the homepage). On the inner-page band the eye expects a clear line, so
   lift the resting trace here: the full edge-to-edge line stays visible between
   beam passes instead of the line seeming to vanish. Beam timing matches home. */
.ml-moving-line .shield-trace { stroke: rgba(42,61,152,0.24); }

/* ============================================================
   Page / industry hero (shared by mightyloc/industry-hero AND
   mightyloc/page-hero). Lives here (global) rather than in the industry-hero
   block stylesheet, because page-hero reuses these classes on pages where the
   industry-hero block is absent (so its block CSS would not be enqueued).
   The home hero look: navy base + blurred mesh + an animated roaming red glow
   (.hero__redglow, defined above) + faint grid. Pulls flush under the header.
   ============================================================ */
.industry-hero {
	position: relative; overflow: hidden;
	background: var(--color-brand-blue-deep); color: #fff;
	padding: var(--space-10) 0 var(--space-9);
	/* Bleeds to the very top of a hero-led page (the wrapper clearance is dropped
	   in base.css via body:has(.industry-hero)); this padding-top reserves the
	   header clearance inside the navy band so no light gap shows above it. */
	padding-top: calc(var(--space-8) + var(--header-space));
}
/* Phones: trim the hero's bottom whitespace (kept the top header clearance). */
@media (max-width: 600px) {
	.industry-hero { padding-bottom: var(--space-6); }
	.industry-hero::after { height: 90px; }
}
/* Bottom edge fades to solid navy so the roaming red glow never shows a hard clip
   at the hero's bottom (matters where a hero meets the navy About band; invisible
   on heroes that sit above white content). Sits above the glow, below content. */
.industry-hero::after {
	content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 130px; z-index: 0; pointer-events: none;
	background: linear-gradient(to bottom, transparent, var(--color-brand-blue-deep));
}
.industry-hero.hero-mesh-bg::before {
	content: ''; position: absolute; inset: -10%;
	background:
		radial-gradient(circle at 20% 30%, color-mix(in oklab, var(--color-brand-blue) 65%, transparent) 0%, transparent 38%),
		radial-gradient(circle at 85% 70%, color-mix(in oklab, var(--color-brand-red) 25%, transparent) 0%, transparent 32%);
	filter: blur(40px); pointer-events: none;
}
.industry-hero__grid {
	position: absolute; inset: 0; pointer-events: none;
	background-image:
		linear-gradient(to right, rgba(255,255,255,0.04) 1px, transparent 1px),
		linear-gradient(to bottom, rgba(255,255,255,0.04) 1px, transparent 1px);
	background-size: 56px 56px;
}
.industry-hero__inner { position: relative; z-index: 1; }
.industry-hero__eyebrow { color: rgba(255,255,255,0.65); margin: 0 0 16px; }
.industry-hero__title {
	font-family: var(--font-display); font-weight: 700;
	font-size: var(--text-display-2-size); line-height: 1.0;
	letter-spacing: var(--text-display-2-tr);
	color: #fff; max-width: 22ch; margin: 0 0 20px;
}
.industry-hero__desc {
	font-family: var(--font-body); font-size: var(--text-body-lg-size); line-height: 1.6;
	color: rgba(255,255,255,0.82); max-width: 64ch; margin: 0;
}
/* Per-industry cut-out image: text + image side by side, stacks on mobile. */
.industry-hero--has-img .industry-hero__inner { display: flex; align-items: center; gap: var(--space-8); }
.industry-hero--has-img .industry-hero__text { flex: 1 1 58%; min-width: 0; }
.industry-hero__media { flex: 0 0 38%; display: flex; justify-content: flex-end; }
.industry-hero__media img { width: 100%; max-width: 460px; height: auto; object-fit: contain; filter: drop-shadow(0 24px 48px rgba(0,0,0,0.38)); }
@media (max-width: 860px) {
	.industry-hero--has-img .industry-hero__inner { flex-direction: column; align-items: flex-start; gap: var(--space-5); }
	.industry-hero__media { width: 100%; justify-content: flex-start; }
	.industry-hero__media img { max-width: 260px; }
}
/* Catalog hero (Leonard 2026-06): show the product range BESIDE the "Products"
   heading, the same 2-column layout as every industry hero -- just a touch wider
   than a portrait cut-out because the range is a landscape strip, so the lineup
   stays legible. On mobile it stacks under the heading (inherits --has-img). */
.industry-hero--catalog .industry-hero__media { flex-basis: 44%; }
.industry-hero--catalog .industry-hero__media img { max-width: 520px; }
@media (max-width: 860px) {
	.industry-hero--catalog .industry-hero__media { justify-content: flex-start; }
	.industry-hero--catalog .industry-hero__media img { max-width: 100%; width: 100%; }
}

/* ============================================================
   About page (mightyloc/page-hero + bespoke clickable sections)
   ============================================================ */
.ml-about-showcase { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-8); align-items: center; }
@media (max-width: 860px) { .ml-about-showcase { grid-template-columns: 1fr; gap: var(--space-6); } }
.ml-about-showcase__media {
	position: relative; border-radius: var(--radius-lg); padding: var(--space-6); overflow: hidden;
	border: 1px solid var(--border-subtle);
	background:
		radial-gradient(circle at 30% 18%, color-mix(in oklab, var(--color-brand-blue) 16%, transparent), transparent 60%),
		linear-gradient(135deg, color-mix(in oklab, var(--color-brand-blue) 10%, #fff), var(--neutral-100));
}
.ml-about-showcase__media img { display: block; width: 100%; height: auto; filter: drop-shadow(0 18px 36px rgba(25,22,64,0.18)); }
.ml-about-cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; margin-top: var(--space-9); }
/* These About cards carry a full paragraph each; 2-up on phones squeezed them into
   narrow, heavily-wrapped columns. Give each the full width on phones/small tablets
   (Leonard) -- the catalog product cards stay 2-up; this is About-specific. */
@media (max-width: 700px) { .ml-about-cards { grid-template-columns: 1fr; } }
.ml-about-card {
	display: flex; flex-direction: column; gap: 10px; text-decoration: none;
	padding: var(--space-6); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
	background: var(--surface-elevated); color: var(--text-primary);
	transition: border-color var(--duration-base) var(--ease-out), transform var(--duration-base) var(--ease-spring), box-shadow var(--duration-base) var(--ease-out);
}
.ml-about-card:hover { border-color: var(--border-strong); transform: translateY(-3px); box-shadow: 0 18px 48px -22px color-mix(in oklab, var(--color-brand-blue) 50%, transparent); text-decoration: none; }
.ml-about-card:focus-visible { outline: 2px solid var(--accent-primary); outline-offset: 3px; }
.ml-about-card__title { font-family: var(--font-display); font-size: 20px; font-weight: 600; letter-spacing: -0.01em; margin: 0; color: var(--text-primary); }
.ml-about-card__desc { color: var(--text-secondary); font-size: 14px; line-height: 1.6; margin: 0; flex: 1; }
.ml-about-card__cta { display: inline-flex; align-items: center; gap: 6px; color: var(--accent-primary); font-family: var(--font-body); font-weight: 600; font-size: 13px; }
.ml-about-card .arrow { transition: transform var(--duration-base) var(--ease-spring); }
.ml-about-card:hover .arrow { transform: translateX(4px); }
/* "By the numbers" band (mightyloc/about-stats). The light-surface twin of the home
   hero stat band: same 36px display number + mono uppercase label rhythm, ink numbers
   (matching the hero's white ones, not a stray blue accent), borderless, with a single
   hairline rule and an "Established 2002" eyebrow. So the two bands read as one block. */
.ml-about-stats-band { margin-top: var(--space-9); padding-top: var(--space-6); border-top: 1px solid var(--border-subtle); }
.ml-about-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; }
.ml-about-stat { padding-right: 24px; }
.ml-about-stat__num { display: block; font-family: var(--font-display); font-size: 36px; font-weight: 700; letter-spacing: -0.02em; line-height: 1; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.ml-about-stat__label { display: block; margin-top: 6px; font-family: var(--font-mono); font-size: 10px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-secondary); }
@media (max-width: 720px) { .ml-about-stats { grid-template-columns: repeat(2, 1fr); gap: 28px 0; } }
.ml-about-facts { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-8); margin-top: var(--space-9); }
@media (max-width: 720px) { .ml-about-facts { grid-template-columns: 1fr; gap: var(--space-6); } }
.ml-about-fact__title { font-family: var(--font-display); font-size: clamp(22px, 2.6vw, 30px); font-weight: 700; letter-spacing: -0.02em; margin: 0 0 12px; color: var(--text-primary); }
.ml-about-fact__body { color: var(--text-secondary); font-size: 15px; line-height: 1.7; margin: 0; }
.ml-about-faq { margin-top: var(--space-6); }
.ml-about-faq__item { padding: var(--space-5) 0; border-top: 1px solid var(--border-subtle); }
.ml-about-faq__item:last-child { border-bottom: 1px solid var(--border-subtle); }
.ml-about-faq__q { font-family: var(--font-display); font-size: 18px; font-weight: 600; letter-spacing: -0.01em; margin: 0 0 8px; color: var(--text-primary); }
.ml-about-faq__a { color: var(--text-secondary); font-size: 15px; line-height: 1.7; margin: 0; max-width: 70ch; }

/* Closing brand-story band (Leonard: end About on a section about the brand).
   Full-bleed navy with a slow breathing red glow for warmth. */
.ml-about-brand { position: relative; overflow: hidden; background: var(--color-brand-blue-deep); color: #fff; margin-top: var(--space-9); padding: var(--space-10) var(--space-5); }
/* When the brand band sits directly under the navy hero it reads as one connected
   navy block (Leonard): no top margin, lighter top padding so the hero->band seam
   is invisible, the SAME faint grid overlay as the hero (::before) so it is clearly
   one surface, and a navy->white gradient fade at the bottom (::after) so the
   band->"What we make" transition is a gradient, not a hard line. */
.ml-about-brand--top { margin-top: 0; padding-top: var(--space-7); padding-bottom: var(--space-9); }
/* The +250px reserve is only for the desktop hero image that overlaps DOWN into this
   band. On mobile/tablet (<=860px) the image stacks inside the hero and never enters
   the band, so the reserve was a near-full-viewport of dead navy. Add it only wide. */
@media (min-width: 861px) { .ml-about-brand--top { padding-bottom: calc(var(--space-9) + 250px); } }
.ml-about-brand--top::before {
	content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
	background-image:
		linear-gradient(to right, rgba(255,255,255,0.04) 1px, transparent 1px),
		linear-gradient(to bottom, rgba(255,255,255,0.04) 1px, transparent 1px);
	background-size: 56px 56px;
}
/* Its own red orb is dropped at the top: it was clipped at the band's top edge and
   the hero's roaming glow was clipped at the hero's bottom, so the two clipped
   orbs met as a visible red line at the seam (Leonard). The hero glow now fades to
   solid navy before its bottom (.industry-hero::after), so the seam is pure navy. */
.ml-about-brand--top .ml-about-brand__glow { display: none; }
/* Smoother, blue-focused fade into "What we make" (was a harsh navy->white jump):
   navy melts through blue tints, the white only arrives at the very end. */
.ml-about-brand--top::after {
	content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 340px; z-index: 0; pointer-events: none;
	/* A long, even fade that lands on the body colour below (non-home body is
	   --surface-elevated = #fff). The blue tints thin out gradually and the lower
	   half stays near-white across a tall span, so the white emerges softly over
	   ~150px instead of snapping in over the last few pixels. */
	background: linear-gradient(to bottom,
		transparent 0%,
		color-mix(in oklab, var(--color-brand-blue) 22%, var(--surface-elevated)) 44%,
		color-mix(in oklab, var(--color-brand-blue) 11%, var(--surface-elevated)) 66%,
		color-mix(in oklab, var(--color-brand-blue) 4%, var(--surface-elevated)) 85%,
		var(--surface-elevated) 100%);
}
/* Phones/tablets: desktop reserves +250px below the content for the showcase, which
   keeps the action buttons up on the navy. Mobile had no such buffer, so the buttons
   (incl. the white "Talk to our team" ghost button) sat on the already-whitening
   gradient and went invisible, and the navy->white fade felt abrupt right under the
   text. Add a navy buffer below the buttons and keep the fade blue much longer so the
   white only emerges at the very bottom (Leonard). */
@media (max-width: 860px) {
	.ml-about-brand--top { padding-bottom: calc(var(--space-9) + 140px); }
	.ml-about-brand--top::after {
		height: 300px;
		background: linear-gradient(to bottom,
			transparent 0%,
			color-mix(in oklab, var(--color-brand-blue) 58%, var(--surface-elevated)) 48%,
			color-mix(in oklab, var(--color-brand-blue) 30%, var(--surface-elevated)) 72%,
			color-mix(in oklab, var(--color-brand-blue) 9%, var(--surface-elevated)) 89%,
			var(--surface-elevated) 100%);
	}
}
.ml-about-brand__glow {
	position: absolute; width: 80vw; max-width: 920px; aspect-ratio: 1; border-radius: 50%;
	right: -16%; top: -42%; pointer-events: none; z-index: 0; filter: blur(46px); opacity: 0.5;
	background: radial-gradient(circle, color-mix(in oklab, var(--color-brand-red) 60%, transparent) 0%, transparent 68%);
	animation: mlAboutGlow 11s ease-in-out infinite;
}
@keyframes mlAboutGlow { 0%, 100% { transform: translate(0, 0) scale(1); opacity: 0.42; } 50% { transform: translate(-4%, 6%) scale(1.12); opacity: 0.6; } }
@media (prefers-reduced-motion: reduce) { .ml-about-brand__glow { animation: none; } }
.ml-about-brand__inner { position: relative; z-index: 1; max-width: 880px; margin: 0 auto; }
.ml-about-brand__eyebrow { font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase; color: color-mix(in oklab, var(--color-brand-red) 60%, #fff); margin: 0 0 14px; }
.ml-about-brand__title { font-family: var(--font-display); font-weight: 700; font-size: clamp(28px, 4vw, 46px); line-height: 1.05; letter-spacing: -0.03em; margin: 0 0 var(--space-4); max-width: 20ch; }
.ml-about-brand__body { font-family: var(--font-body); font-size: 16px; line-height: 1.75; color: rgba(255,255,255,0.82); max-width: 62ch; margin: 0 0 var(--space-4); }
.ml-about-brand__actions { display: flex; flex-wrap: wrap; gap: 14px; margin-top: var(--space-5); }

/* ============================================================
   Privacy & Terms legal page (page-privacy.html + .ml-legal)
   ============================================================ */
.ml-legal__lead { font-size: 18px; line-height: 1.7; color: var(--text-primary); margin: 0 0 6px; }
.ml-legal__updated { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-secondary); margin: 0 0 var(--space-4); }
.ml-legal .ml-legal__part { font-family: var(--font-display); font-weight: 700; font-size: clamp(24px, 3vw, 34px); letter-spacing: -0.02em; color: var(--text-primary); margin: var(--space-8) 0 var(--space-4); padding-bottom: 12px; border-bottom: 2px solid var(--color-brand-red); }
.ml-legal h3 { font-family: var(--font-display); font-size: 19px; font-weight: 600; letter-spacing: -0.01em; color: var(--text-primary); margin: var(--space-5) 0 8px; }
.ml-legal p { font-family: var(--font-body); font-size: 15.5px; line-height: 1.75; color: var(--text-secondary); margin: 0 0 14px; max-width: 74ch; }
.ml-legal a { color: var(--accent-primary); font-weight: 500; }
.ml-legal a:hover { text-decoration: underline; }
.ml-legal__rule { border: 0; border-top: 1px solid var(--border-subtle); margin: var(--space-8) 0 0; }

/* ============================================================
   Contact page layout (details + form)
   ============================================================ */
.ml-contact-layout { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: var(--space-8); align-items: start; }
@media (max-width: 860px) { .ml-contact-layout { grid-template-columns: 1fr; gap: var(--space-6); } }
.ml-contact-details__title { font-family: var(--font-display); font-size: 22px; font-weight: 700; letter-spacing: -0.02em; margin: 0 0 var(--space-3); color: var(--text-primary); }
.ml-contact-details__line { font-family: var(--font-body); font-size: 15px; line-height: 1.7; color: var(--text-secondary); margin: 0 0 var(--space-3); }
.ml-contact-details__line a { color: var(--accent-primary); text-decoration: none; font-weight: 500; }
.ml-contact-details__line a:hover { text-decoration: underline; }
.ml-contact-details__note { font-family: var(--font-body); font-size: 13.5px; line-height: 1.65; color: var(--text-secondary); margin: var(--space-5) 0 0; padding-top: var(--space-4); border-top: 1px solid var(--border-subtle); }

/* ============================================================
   "Where we ship" drag-scroll country strip (ported 1:1)
   ============================================================ */
.country-strip-wrap { position: relative; }
.country-strip {
  display: flex; gap: 16px; overflow-x: auto; align-items: flex-start;
  cursor: grab; user-select: none; padding: 12px 6px 44px;
  scrollbar-width: none;
}
.country-strip::-webkit-scrollbar { display: none; }
.country-strip.is-dragging { cursor: grabbing; scroll-behavior: auto; }
.country-tile {
  flex: 0 0 280px; min-height: 200px;
  background: var(--surface-elevated); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 20px 22px;
  display: flex; flex-direction: column; justify-content: space-between;
  position: relative; overflow: hidden; isolation: isolate;
  transition: border-color var(--duration-base) var(--ease-out), transform var(--duration-base) var(--ease-spring), box-shadow var(--duration-base) var(--ease-out), flex-basis var(--duration-base) var(--ease-spring);
}
.country-tile:hover { border-color: var(--border-strong); transform: translateY(-3px); box-shadow: 0 18px 48px -24px color-mix(in oklab, var(--color-brand-blue) 50%, transparent); }
.country-tile--hq { background: var(--color-brand-blue-deep); color: #fff; border-color: transparent; }
.country-tile--hq:hover { border-color: rgba(255,255,255,0.18); }
.country-tile__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.country-tile__rank { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em; color: var(--text-secondary); }
.country-tile--hq .country-tile__rank { color: rgba(255,255,255,0.55); }
.country-tile__name { font-family: var(--font-display); font-size: 22px; font-weight: 600; letter-spacing: -0.01em; margin-top: 8px; }
.country-tile__native { font-family: var(--font-body); font-size: 13px; color: var(--text-secondary); margin-top: 2px; }
.country-tile--hq .country-tile__native { color: rgba(255,255,255,0.70); }
.country-tile__meta { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.10em; text-transform: uppercase; color: var(--text-secondary); }
.country-tile--hq .country-tile__meta { color: rgba(255,255,255,0.70); }
/* Where-we-ship: flag image + clickable highlight (Leonard #4). */
.country-tile__head { align-items: center; }
.country-tile__flag { width: 40px; height: 27px; border-radius: 3px; box-shadow: 0 3px 10px rgba(25,22,64,0.20); object-fit: cover; }
.country-tile--hq .country-tile__flag { box-shadow: 0 3px 10px rgba(0,0,0,0.35); }
.country-tile { cursor: pointer; }
.country-tile.is-active { border-color: var(--color-brand-red); box-shadow: 0 18px 48px -20px color-mix(in oklab, var(--color-brand-red) 55%, transparent); flex-basis: 372px; }
.country-tile:focus-visible { outline: 2px solid var(--accent-primary); outline-offset: 3px; }
/* Entry-points-style hover glow (same cursor-following inner radial + edge ring as
   the homepage pathway cards). Contained by the tile's own overflow:hidden, so it
   never spills past the strip and gets clipped (Leonard: glow "cut off below"). */
.country-tile > * { position: relative; z-index: 1; }
.country-tile::before {
  content: ''; position: absolute; inset: 0; z-index: 0; opacity: 0; pointer-events: none;
  background: radial-gradient(circle 320px at var(--cx, 50%) var(--cy, 50%), color-mix(in oklab, var(--color-brand-blue) 16%, transparent), transparent 70%);
  transition: opacity var(--duration-base) var(--ease-out);
}
.country-tile:hover::before { opacity: 1; }
.country-tile::after {
  content: ''; position: absolute; inset: 0; z-index: 0; border-radius: inherit;
  padding: 1.5px; pointer-events: none; opacity: 0;
  background: radial-gradient(circle 240px at var(--cx, 50%) var(--cy, 50%),
    color-mix(in oklab, var(--color-brand-blue) 92%, white) 0%,
    color-mix(in oklab, var(--color-brand-blue) 62%, transparent) 26%, transparent 62%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  transition: opacity var(--duration-base) var(--ease-out);
}
.country-tile:hover::after { opacity: 1; }
.country-tile--hq::before { background: radial-gradient(circle 320px at var(--cx, 50%) var(--cy, 50%), color-mix(in oklab, var(--color-brand-red) 24%, transparent), transparent 70%); }
.country-tile--hq::after { background: radial-gradient(circle 240px at var(--cx, 50%) var(--cy, 50%), color-mix(in oklab, var(--color-brand-red) 90%, white) 0%, color-mix(in oklab, var(--color-brand-red) 55%, transparent) 26%, transparent 62%); }
/* Detail reveals when the tile is selected (Leonard: expand with info on click). */
.country-tile__detail { overflow: hidden; max-height: 0; opacity: 0; transition: max-height 340ms var(--ease-out), opacity 240ms var(--ease-out), margin-top 340ms var(--ease-out); }
.country-tile.is-active .country-tile__detail { max-height: 360px; opacity: 1; margin-top: 16px; }
.country-tile__detail p { margin: 0; padding-top: 14px; border-top: 1px solid var(--border-subtle); font-family: var(--font-body); font-size: 13px; line-height: 1.65; color: var(--text-secondary); }
.country-tile--hq .country-tile__detail p { border-top-color: rgba(255,255,255,0.18); color: rgba(255,255,255,0.80); }
@media (prefers-reduced-motion: reduce) { .country-tile__detail { transition: none; } }
.strip-hint { margin-top: 16px; font-family: var(--font-mono); font-size: 11px; color: var(--text-secondary); letter-spacing: 0.06em; display: inline-flex; align-items: center; gap: 8px; text-transform: uppercase; }
.strip-hint .arr { letter-spacing: -2px; color: var(--accent-attention); }
/* Phones/tablets: centre the selected country in the strip and keep it on-screen.
   The desktop active width (372px) overran a ~350px phone, so the chosen card was
   cut off on the right ("+65 . SGT" and the last text line clipped). Here the active
   card is capped to the viewport (so it shrinks on narrow phones) and its detail
   grows DOWNWARD; the strip's side padding + the JS centring in interactions.js (the
   .country-strip handler) hold the chosen card middle-aligned. Desktop is unchanged. */
@media (max-width: 900px) {
  /* Side padding lets the first/last card sit centred in the strip; JS (interactions.js)
     scrolls a tapped card to the middle. Cards are kept comfortably narrower than the
     screen (and a touch more compact) so the centred one always shows in full and does
     not feel oversized (Leonard: the card felt too big / showed only half). */
  .country-strip {
    padding-left: max(6px, calc((100% - min(330px, calc(100vw - 72px))) / 2));
    padding-right: max(6px, calc((100% - min(330px, calc(100vw - 72px))) / 2));
  }
  /* Every card is the same fit-width so the selected one never resizes sideways
     (no growth that runs off-screen or fights the centring scroll); its detail still
     reveals downward. Dropping flex-basis from the transition keeps centring exact. */
  .country-tile,
  .country-tile.is-active { flex-basis: min(330px, calc(100vw - 72px)); transition: border-color var(--duration-base) var(--ease-out), box-shadow var(--duration-base) var(--ease-out); }
  .country-tile { padding: 16px 18px; min-height: 0; }
}

/* ============================================================
   Technical depth — coming-soon panel (deliberate placeholder)
   ============================================================ */
.insights-soon {
  position: relative; border: 1px dashed var(--border-strong); border-radius: var(--radius-xl);
  background: linear-gradient(135deg, color-mix(in oklab, var(--color-brand-blue) 5%, #fff), var(--surface-elevated));
  padding: var(--space-8) var(--space-7); display: flex; flex-direction: column; align-items: flex-start; gap: 16px;
  max-width: 760px;
}
.insights-soon__badge { font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: #fff; background: var(--color-brand-blue); padding: 6px 12px; border-radius: var(--radius-pill); }
.insights-soon__text { color: var(--text-secondary); font-size: 16px; line-height: 1.7; margin: 0; max-width: 60ch; }
.insights-soon__cta { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-display); font-weight: 600; font-size: 15px; color: var(--accent-primary); }
.insights-soon__cta .arrow { transition: transform var(--duration-base) var(--ease-spring); }
.insights-soon__cta:hover .arrow { transform: translateX(5px); }

/* ============================================================
   Blog: landing index (mightyloc/blog-index), category archive,
   single article. Cards share one .ml-blog-card system across the
   hand-built landing cards and the core-block archive cards.
   ============================================================ */
.ml-blog { display: block; }

/* Category jump chips at the top of the landing. */
.ml-blog-chips { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 var(--space-8); }
.ml-blog-chip {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.04em; font-weight: 600;
  color: var(--color-brand-blue-deep); text-decoration: none;
  padding: 8px 16px; border-radius: var(--radius-pill);
  border: 1px solid var(--border-subtle); background: var(--surface-elevated);
  transition: background 200ms var(--ease-out), border-color 200ms var(--ease-out), color 200ms var(--ease-out), transform 200ms var(--ease-spring);
}
.ml-blog-chip:hover { background: var(--color-brand-blue-deep); border-color: var(--color-brand-blue-deep); color: #fff; transform: translateY(-2px); }
.ml-blog-chip:focus-visible { outline: 2px solid var(--color-brand-blue); outline-offset: 3px; }

/* One section per category. Separated by a hairline + generous space. */
.ml-blog-cat { padding-top: var(--space-8); margin-top: var(--space-8); border-top: 1px solid var(--border-subtle); scroll-margin-top: var(--header-space); }
.ml-blog-cat:first-of-type { padding-top: 0; margin-top: 0; border-top: 0; }
.ml-blog-cat__head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-6); margin: 0 0 var(--space-6); flex-wrap: wrap; }
.ml-blog-cat__title {
  font-family: var(--font-display); font-weight: 700; font-size: var(--text-h1-size); line-height: 1.05;
  letter-spacing: -0.02em; color: var(--text-primary); margin: 0 0 10px;
  position: relative; padding-left: 16px;
}
.ml-blog-cat__title::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 4px; border-radius: 4px; background: var(--color-brand-red); }
.ml-blog-cat__desc { font-size: var(--text-body-size); line-height: 1.6; color: var(--text-secondary); max-width: 60ch; margin: 0; }
.ml-blog-cat__all {
  flex: 0 0 auto; white-space: nowrap; font-family: var(--font-mono); font-size: 13px; font-weight: 600;
  color: var(--accent-primary); text-decoration: none; display: inline-flex; align-items: center; gap: 8px;
}
.ml-blog-cat__all .arrow { transition: transform var(--duration-base, 200ms) var(--ease-spring); }
.ml-blog-cat__all:hover .arrow { transform: translateX(5px); }
.ml-blog-cat__all:focus-visible { outline: 2px solid var(--color-brand-blue); outline-offset: 3px; border-radius: 4px; }

/* Card grid (shared by landing + archive). auto-fit (not auto-fill) so 2-3 cards
   expand to fill the row instead of leaving empty tracks (Leonard 2026-06-05). */
.ml-blog-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: var(--space-5); list-style: none; padding: 0; margin: 0; }

/* Single-post category = horizontal "feature" card (editorial: art left, copy right)
   so a lone article reads as composed, not stranded in an empty grid (Leonard 2026-06-05). */
.ml-blog-feature {
  position: relative; background: var(--surface-elevated); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: 0 1px 2px rgba(25,22,64,0.04), 0 8px 24px -16px rgba(25,22,64,0.30);
  transition: transform 280ms var(--ease-spring), box-shadow 280ms var(--ease-out), border-color 280ms var(--ease-out);
}
.ml-blog-feature:hover { transform: translateY(-4px); border-color: color-mix(in oklab, var(--color-brand-blue) 40%, var(--border-subtle)); box-shadow: 0 2px 4px rgba(25,22,64,0.05), 0 26px 50px -22px rgba(42,61,152,0.45); }
.ml-blog-feature::after { content: ""; position: absolute; left: 0; top: 0; height: 3px; width: 100%; z-index: 3; background: linear-gradient(90deg, var(--color-brand-blue), var(--color-brand-red)); transform: scaleX(0); transform-origin: left; transition: transform 320ms var(--ease-out); }
.ml-blog-feature:hover::after, .ml-blog-feature:focus-within::after { transform: scaleX(1); }
.ml-blog-feature__link { display: grid; grid-template-columns: minmax(0, 0.95fr) 1fr; text-decoration: none; color: inherit; }
.ml-blog-feature__art { position: relative; min-height: 100%; }
.ml-blog-feature .ml-blog-card__art { aspect-ratio: auto; height: 100%; min-height: 300px; margin: 0; width: 100%; }
.ml-blog-feature__body { padding: var(--space-7); display: flex; flex-direction: column; justify-content: center; }
.ml-blog-feature__body .ml-blog-card__tag { margin-bottom: 16px; }
.ml-blog-feature__title { font-family: var(--font-display); font-weight: 700; font-size: clamp(22px, 2.4vw, 28px); line-height: 1.18; letter-spacing: -0.015em; color: var(--text-primary); margin: 0 0 14px; }
.ml-blog-feature:hover .ml-blog-feature__title { color: var(--color-brand-red); }
.ml-blog-feature__excerpt { font-size: 16px; line-height: 1.65; color: var(--text-secondary); margin: 0 0 22px; max-width: 54ch; }
.ml-blog-feature__body .ml-blog-card__cta { margin-top: 16px; }
@media (max-width: 760px) {
  .ml-blog-feature__link { grid-template-columns: 1fr; }
  .ml-blog-feature .ml-blog-card__art { min-height: 0; aspect-ratio: 16 / 9; height: auto; }
  .ml-blog-feature__body { padding: var(--space-5); }
}

.ml-blog-card {
  position: relative; background: var(--surface-elevated); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); overflow: hidden; padding: 0;
  box-shadow: 0 1px 2px rgba(25,22,64,0.04), 0 8px 24px -16px rgba(25,22,64,0.30);
  transition: transform 280ms var(--ease-spring), box-shadow 280ms var(--ease-out), border-color 280ms var(--ease-out);
}
.ml-blog-card:hover { transform: translateY(-4px); border-color: color-mix(in oklab, var(--color-brand-blue) 40%, var(--border-subtle)); box-shadow: 0 2px 4px rgba(25,22,64,0.05), 0 26px 50px -22px rgba(42,61,152,0.45); }
.ml-blog-card::after { content: ""; position: absolute; left: 0; top: 0; height: 3px; width: 100%; background: linear-gradient(90deg, var(--color-brand-blue), var(--color-brand-red)); transform: scaleX(0); transform-origin: left; transition: transform 320ms var(--ease-out); }
.ml-blog-card:hover::after, .ml-blog-card:focus-within::after { transform: scaleX(1); }

/* Landing card is a single anchor; archive card is a group with a title link. */
.ml-blog-card__link { display: flex; flex-direction: column; height: 100%; padding: var(--space-5); text-decoration: none; color: inherit; }
.ml-blog-card > :not(.ml-blog-card__link) { margin-left: var(--space-5); margin-right: var(--space-5); }
.ml-blog-card > .wp-block-post-terms:first-child { margin-top: var(--space-5); }
.ml-blog-card > .wp-block-post-date:last-child { margin-bottom: var(--space-5); }

/* Tag (mono chip). */
.ml-blog-card__tag, .ml-blog-card__tag a {
  font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--color-brand-blue); text-decoration: none;
}
.ml-blog-card__tag { display: inline-block; margin-bottom: 14px; }

/* Title. */
.ml-blog-card__title { font-family: var(--font-display); font-weight: 700; font-size: 20px; line-height: 1.25; letter-spacing: -0.01em; color: var(--text-primary); margin: 0 0 12px; }
.ml-blog-card__title a { color: inherit; text-decoration: none; }
.ml-blog-card:hover .ml-blog-card__title, .ml-blog-card:hover .ml-blog-card__title a { color: var(--color-brand-red); }

/* Excerpt. */
.ml-blog-card__excerpt, .ml-blog-card__excerpt p { font-size: 15px; line-height: 1.6; color: var(--text-secondary); margin: 0 0 18px; }
.ml-blog-card__link .ml-blog-card__excerpt { flex: 1 1 auto; }

/* Meta + CTA. */
.ml-blog-card__meta, .ml-blog-card__meta time { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.03em; color: var(--neutral-500); display: inline-flex; align-items: center; gap: 8px; }
.ml-blog-card__cta { margin-top: 14px; font-family: var(--font-mono); font-size: 13px; font-weight: 600; color: var(--accent-primary); display: inline-flex; align-items: center; gap: 8px; }
.ml-blog-card__cta .arrow { transition: transform var(--duration-base, 200ms) var(--ease-spring); }
.ml-blog-card:hover .ml-blog-card__cta .arrow { transform: translateX(5px); }

/* Empty (coming soon) state. */
.ml-blog-empty { border: 1px dashed var(--border-strong); border-radius: var(--radius-lg); padding: var(--space-6); text-align: center; color: var(--text-secondary); background: color-mix(in oklab, var(--color-brand-blue) 4%, var(--surface-elevated)); }
.ml-blog-empty__badge { display: inline-block; font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-brand-blue); background: color-mix(in oklab, var(--color-brand-blue) 12%, transparent); padding: 5px 12px; border-radius: var(--radius-pill); margin-bottom: 12px; }
.ml-blog-empty p { margin: 0; }

/* ---- Category archive ---- */
.ml-archive-hero__eyebrow a { color: rgba(255,255,255,0.7); text-decoration: none; font-family: var(--font-mono); letter-spacing: 0.08em; }
.ml-archive-hero__eyebrow a:hover { color: #fff; }
.ml-archive-hero__title { font-family: var(--font-display); font-weight: 700; font-size: var(--text-display-2-size); line-height: 1.0; letter-spacing: var(--text-display-2-tr); color: #fff; margin: 0 0 16px; }
.ml-archive-hero__desc { font-size: var(--text-body-lg-size); line-height: 1.6; color: rgba(255,255,255,0.82); max-width: 60ch; margin: 0; }
.ml-archive .ml-blog-grid { margin-top: 0; }
.ml-blog-pagination { margin-top: var(--space-8); font-family: var(--font-mono); font-size: 14px; }
.ml-blog-pagination a { color: var(--accent-primary); text-decoration: none; }
.ml-blog-pagination a:hover { text-decoration: underline; }
.ml-blog-pagination .wp-block-query-pagination-numbers .page-numbers { padding: 2px 8px; border-radius: 6px; }
.ml-blog-pagination .wp-block-query-pagination-numbers .current { background: var(--color-brand-blue-deep); color: #fff; }

/* ---- Single article ---- */
.ml-post-hero__eyebrow, .ml-post-hero__eyebrow a { font-family: var(--font-mono); font-size: 12px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #ff9ea0; text-decoration: none; margin: 0 0 16px; }
.ml-post-hero__eyebrow a:hover { color: #fff; }
.ml-post-hero__title { font-family: var(--font-display); font-weight: 700; font-size: var(--text-display-3-size); line-height: 1.05; letter-spacing: -0.02em; color: #fff; margin: 0 0 18px; max-width: 20ch; }
.ml-post-hero__meta, .ml-post-hero__meta time { font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.04em; color: rgba(255,255,255,0.7); }
.ml-post-body { padding-top: var(--space-8); padding-bottom: var(--space-9); }
.ml-post-featured { margin: 0 0 var(--space-7); }
.ml-post-featured img { width: 100%; height: auto; border-radius: var(--radius-lg); box-shadow: 0 30px 60px -30px rgba(25,22,64,0.5); }
.ml-prose { font-size: var(--text-body-lg-size); line-height: 1.75; color: var(--neutral-800); }
.ml-prose > * + * { margin-top: var(--space-5); }
.ml-prose h2 { font-family: var(--font-display); font-weight: 700; font-size: var(--text-h1-size); line-height: 1.1; letter-spacing: -0.02em; color: var(--text-primary); margin-top: var(--space-8); margin-bottom: 0; }
.ml-prose h3 { font-family: var(--font-display); font-weight: 700; font-size: var(--text-h3-size); color: var(--text-primary); margin-top: var(--space-6); }
.ml-prose ul, .ml-prose ol { padding-left: 1.4em; }
.ml-prose li { margin-top: 8px; }
.ml-prose li::marker { color: var(--color-brand-red); }
.ml-prose a { color: var(--color-brand-blue); text-decoration: underline; text-underline-offset: 2px; }
.ml-prose strong { color: var(--text-primary); }

/* Answer-first TL;DR callout - the first-30%-of-page GEO citation zone. */
.ml-prose .ml-tldr { background: var(--neutral-100); border-left: 3px solid var(--color-brand-red); border-radius: 0 var(--radius-md) var(--radius-md) 0; padding: var(--space-4) var(--space-5); margin-top: 0; }
.ml-prose .ml-tldr > * { margin-top: 0; }
.ml-prose .ml-tldr p { margin: 0; font-size: var(--text-body-lg-size); }
.ml-prose .ml-tldr__label { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent-primary); display: block; margin-bottom: 8px; font-weight: 600; }

/* Comparison tables - specimen style (mirrors the product spec-table aesthetic). */
.ml-prose .wp-block-table { margin-top: var(--space-6); overflow-x: auto; }
.ml-prose table { width: 100%; border-collapse: collapse; font-size: 0.94em; border: 1px solid var(--border-subtle); border-radius: var(--radius-md); overflow: hidden; }
.ml-prose thead th { background: var(--color-brand-blue-deep); color: #fff; font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.03em; text-transform: uppercase; font-weight: 600; text-align: left; padding: 14px 16px; line-height: 1.35; }
.ml-prose td, .ml-prose th { padding: 12px 16px; border-bottom: 1px solid var(--border-subtle); vertical-align: top; text-align: left; line-height: 1.55; }
.ml-prose tbody th { font-weight: 600; color: var(--text-primary); background: var(--surface-elevated); }
.ml-prose tbody tr:nth-child(even) td { background: var(--neutral-100); }
.ml-prose tbody tr:last-child td, .ml-prose tbody tr:last-child th { border-bottom: 0; }
.ml-prose .wp-block-table figcaption { font-family: var(--font-mono); font-size: 12px; color: var(--text-secondary); margin-top: 8px; text-align: left; }
/* Phones: shrink the article comparison tables so a 3-4 column table fits the screen
   with the words better spaced (Leonard: the cells were too cramped after the first
   pass). The default .ml-prose sizes (~16.9px body, 12px uppercase mono headers,
   12-16px padding) jam each cell to 1-2 words per line in a narrow column; smaller
   type, no header letter-spacing, and tight padding let more words sit per line.
   table-layout:fixed keeps the columns inside the screen (no left-right scroll). */
@media (max-width: 620px) {
  .ml-prose table { table-layout: fixed; width: 100%; font-size: 11px; }
  .ml-prose thead th { font-size: 9.5px; letter-spacing: 0; padding: 7px 6px; line-height: 1.3; }
  .ml-prose td, .ml-prose th { padding: 7px 6px; line-height: 1.4; overflow-wrap: anywhere; hyphens: none; }
}

.ml-post-foot { margin-top: var(--space-8); padding-top: var(--space-5); border-top: 1px solid var(--border-subtle); }
.ml-post-foot a { font-family: var(--font-mono); font-size: 14px; font-weight: 600; color: var(--accent-primary); text-decoration: none; }
.ml-post-foot a:hover { text-decoration: underline; }
.ml-post-foot__back { margin: 0; }
.ml-post-foot__cta { margin: 0; }

/* Product -> technical-tools links row (cure / service-temp / substrate selector). */
.ml-prod-tools { font-family: var(--font-mono); font-size: 13px; color: var(--text-secondary); margin: 0 0 var(--space-6); display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: baseline; }
.ml-prod-tools__label { text-transform: uppercase; letter-spacing: 0.08em; font-size: 11px; }
.ml-prod-tools a { color: var(--accent-primary); font-weight: 600; text-decoration: none; }
.ml-prod-tools a:hover { text-decoration: underline; }
.ml-prod-tools__sep { color: var(--border-strong); }

/* TDS tab: shown in place of the download button when a product has no TDS yet. */
.ml-tds-note { font-size: 14px; line-height: 1.6; color: var(--text-secondary); margin: var(--space-4) 0 0; }
.ml-tds-note svg { vertical-align: -3px; margin-right: 6px; color: var(--text-secondary); }
.ml-tds-note a { color: var(--accent-primary); font-weight: 600; text-decoration: none; }
.ml-tds-note a:hover { text-decoration: underline; }

@media (max-width: 600px) {
  .ml-blog-cat__head { flex-direction: column; align-items: flex-start; gap: var(--space-3); }
  .ml-blog-grid { grid-template-columns: 1fr; }
}

/* ============================================================
   Animated "blueprint" grids (Terminal Industries-style) -- Leonard 2026-06-04
   Two layered effects on the site's grid-line backgrounds:
   1. DRIFT: the whole grid slowly shifts (background-position) so the lattice
      reads as living, not static -- "the grid lock shifts, very slowly and
      intentionally". Each grid sets --ml-grid-dx/dy = its own tile so the loop
      is seamless (custom props inherit into the ::after glow layer too).
   2. GLOW SWEEP: on the dark grids that are their own element (a free ::after),
      a brighter copy of the same lattice is revealed by a soft radial mask that
      travels diagonally across -- "lines moving across the grid that glow".
   Both honor prefers-reduced-motion (stopped + the glow layer removed).
   ============================================================ */
@keyframes ml-grid-drift {
  to { background-position: var(--ml-grid-dx, 56px) var(--ml-grid-dy, 56px); }
}
@keyframes ml-grid-sweep {
  0%   { -webkit-mask-position: -45% 145%; mask-position: -45% 145%; opacity: 0; }
  14%  { opacity: 1; }
  86%  { opacity: 1; }
  100% { -webkit-mask-position: 145% -45%; mask-position: 145% -45%; opacity: 0; }
}

/* Drift on every grid-line background. Square grids drift diagonally; the hero's
   responsive 12-column grid drifts vertically only (its column width is fluid). */
.hero__grid,
.coverage-map__grid,
.industry-hero__grid,
.grid-bg::before,
.ml-about-brand--top::before,
.hero-showcase__visual::before {
  animation: ml-grid-drift 42s linear infinite;
}
.hero__grid { --ml-grid-dx: 0px; --ml-grid-dy: 80px; }
.coverage-map__grid { --ml-grid-dx: 34px; --ml-grid-dy: 34px; }
.hero-showcase__visual::before { --ml-grid-dx: 24px; --ml-grid-dy: 24px; }
/* .industry-hero__grid, .grid-bg, .ml-about-brand--top all use the 56px default. */

/* Traveling band of glowing lines on the dark, free-::after grids. The bright
   lattice inherits the host tile (background-size + the drift custom props), so
   it stays aligned with the faint base grid while only the lit window travels. */
.hero__grid::after,
.coverage-map__grid::after,
.industry-hero__grid::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(to right, rgba(198,216,255,0.62) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(198,216,255,0.62) 1px, transparent 1px);
  background-size: inherit;
  filter: drop-shadow(0 0 6px rgba(130,160,255,0.7)) drop-shadow(0 0 14px rgba(90,120,235,0.45));
  -webkit-mask-image: radial-gradient(circle at center, #000 0%, rgba(0,0,0,0.4) 36%, transparent 62%);
          mask-image: radial-gradient(circle at center, #000 0%, rgba(0,0,0,0.4) 36%, transparent 62%);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: 55% 70%; mask-size: 55% 70%;
  will-change: mask-position, background-position;
  animation: ml-grid-sweep 13s linear infinite, ml-grid-drift 42s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .hero__grid,
  .coverage-map__grid,
  .industry-hero__grid,
  .grid-bg::before,
  .ml-about-brand--top::before,
  .hero-showcase__visual::before,
  .hero__grid::after,
  .coverage-map__grid::after,
  .industry-hero__grid::after { animation: none; }
  .hero__grid::after,
  .coverage-map__grid::after,
  .industry-hero__grid::after { display: none; }
}

/* Phones: the travelling glow window read as a hard, over-bright block on the small
   hero / industry grids (Leonard). Soften it into a true gradient -- a single smooth
   radial falloff (no bright plateau), a wider/softer lit window, and dimmer lines +
   glow -- so the light eases from bright at the centre out into the dark. */
@media (max-width: 768px) {
  .hero__grid::after,
  .industry-hero__grid::after,
  .coverage-map__grid::after {
    background-image:
      linear-gradient(to right, rgba(198, 216, 255, 0.42) 1px, transparent 1px),
      linear-gradient(to bottom, rgba(198, 216, 255, 0.42) 1px, transparent 1px);
    filter: drop-shadow(0 0 5px rgba(130, 160, 255, 0.36)) drop-shadow(0 0 12px rgba(90, 120, 235, 0.2));
    -webkit-mask-image: radial-gradient(circle at center, #000 0%, transparent 74%);
            mask-image: radial-gradient(circle at center, #000 0%, transparent 74%);
    -webkit-mask-size: 82% 92%; mask-size: 82% 92%;
  }
}
