/* ==========================================================================
   Laserglow — light-theme site stylesheet
   Brand tokens are the canonical values from design/design-system.html.
   The theme is light by default; dark is a *section* treatment, not a mode.
   Contrast floor on light surfaces: text/icons 4.5:1, borders/large type 3:1.
   ========================================================================== */

:root {
  /* Brand */
  /* SITE RULE (user, 2026-08-12): UI Red #ff3a42 is THE red for every interface
     element — buttons, bars, eyebrows, accents, rules, icons — on light and dark
     alike. Logo Red #ed1c24 belongs to the logo artwork only and must not be used
     in CSS; the logo is an SVG file that carries its own colour. Both aliases
     below resolve to UI Red so older `var(--red-ui)` usages stay correct. */
  --red: #ff3a42;          /* UI Red — the only red for interface elements */
  --red-ui: #ff3a42;       /* alias, kept so existing rules keep working */
  --red-logo: #ed1c24;     /* logo artwork only — do not use in CSS */
  --red-deep: #e02a32;     /* pressed/hover state of UI Red */
  --red-tint: #fff1f1;
  --red-line: rgba(255, 58, 66, 0.24);

  /* Light surfaces */
  --page: #ffffff;
  --surface: #f4f5f7;      /* visibly distinct from white */
  --surface-2: #eceef1;
  --line: #d5d9e0;         /* 3:1 against white */
  --line-soft: #e4e7ec;

  /* Ink */
  /* MEASURED off live on 2026-08-24, five pages sampled: laserglow.com uses a
     SINGLE ink, rgb(45,39,39), for every heading and every paragraph. It has no
     separate lighter body grey. Ours used two, which showed up as a colour
     difference on essentially every line of text on every page.
     #2d2727 is 13.7:1 on white, so this loses nothing on readability. */
  --ink: #2d2727;          /* body + headings */
  --ink-2: #2d2727;        /* live does not differentiate; kept as a token so
                              the hundreds of existing uses need no edit */
  --ink-3: #5b616b;        /* minimum for small text on light */

  /* Dark sections */
  --dark: #0d0d0d;
  --dark-2: #161719;
  --dark-3: #1f2124;
  --dark-line: #33363b;
  --on-dark: #ffffff;
  --on-dark-2: #c3c7cd;

  --ok: #1f9d55;

  /* Type */
  --font-display: 'Wix Madefor Display', system-ui, sans-serif;
  --font-text: 'Wix Madefor Text', system-ui, sans-serif;
  --font-mono: 'Space Mono', ui-monospace, monospace;

  /* Layout */
  /* SITE RULE (user, 2026-08-13): the content container tracks the display.
     A fixed 1320px ribbon is what made a 3440px monitor look squeezed — the
     copy stayed narrow while full-bleed media ran to the screen edge, so the
     two disagreed and headlines wrapped to three lines. 94vw matches what the
     live site does (about 50px of gutter at 1600), and the cap keeps the
     measure readable instead of running away on an ultrawide. */
  --wrap: min(1800px, 94vw);
  --wrap-narrow: 780px;
  --gutter: clamp(1.25rem, 4vw, 3.5rem);
  /* --header-h is the WHOLE header: the bar PLUS the red rule under it.
     24 rules across site.css and article.css read it as "how far down the
     page the header pushes things" (full-screen sections, sticky columns,
     scroll-padding for anchor jumps, the mega-menu panel), so it has to be
     the real occupied height. It used to be 84px, the bar alone, while the
     header occupied 88px, so all 24 were quietly 4px out. */
  --header-h: 96px;        /* 84px bar + 12px rule */
  --header-rule: 12px;     /* the red line under the header (user, 2026-08-26,
                              three times the old 4px) */
  --r: 18px;
  --r-sm: 10px;
  --r-pill: 999px;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --t-fast: 160ms;
  --t: 300ms;
  --hero-interval: 7000ms;  /* hero auto-advance; keep in sync with site.js */

  --shadow: 0 18px 44px rgba(16, 17, 20, 0.10);
  --shadow-lg: 0 30px 80px rgba(16, 17, 20, 0.16);
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--page);
  color: var(--ink-2);
  font-family: var(--font-text);
  font-size: 1.1875rem;   /* base body up from 1.0625 — page read too small */
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, video { max-width: 100%; display: block; }
a { color: var(--red); text-underline-offset: 3px; }
a:hover { color: var(--red-deep); }
:focus-visible { outline: 3px solid var(--red); outline-offset: 3px; border-radius: 6px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------------------------------------------------------------- typography */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--ink);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.02;
  margin: 0 0 .55em;
  text-wrap: balance;
  text-transform: uppercase;
}
/* Type ramp — one logical step per level, ~1.25 ratio. Do not add ad-hoc sizes;
   reuse these. Eyebrow < h4 < body < h3 < lede < h2 < h1. */
h1 { font-size: clamp(2.6rem, 5.4vw, 4.7rem); }
h2 { font-size: clamp(1.85rem, 3.4vw, 2.85rem); }
h3 { font-size: clamp(1.15rem, 1.5vw, 1.4rem); letter-spacing: -0.015em; }
h4 { font-size: 1.02rem; letter-spacing: 0; text-transform: none; }

p { margin: 0 0 1.1em; max-width: 66ch; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

.eyebrow {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: .875rem;      /* fixed so every red kicker is identical, no vw drift */
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--red);
  margin: 0 0 1rem;
  display: block;
}
.lede { font-size: clamp(1.15rem, 1.5vw, 1.32rem); color: var(--ink-2); line-height: 1.55; }
.mark { color: var(--red); }

/* ------------------------------------------------------------------- layout */

.wrap { width: min(var(--wrap), 100% - var(--gutter) * 2); margin-inline: auto; }
.wrap--narrow { width: min(var(--wrap-narrow), 100% - var(--gutter) * 2); }

.section { padding-block: clamp(4rem, 8vw, 8rem); position: relative; }
.section--tight { padding-block: clamp(2.5rem, 5vw, 4.5rem); }
.section--surface { background: var(--surface); }

.section--dark {
  background: var(--dark);
  color: var(--on-dark-2);
}
.section--dark h1, .section--dark h2, .section--dark h3 { color: var(--on-dark); }
.section--dark .eyebrow { color: var(--red-ui); }
.section--dark .mark { color: var(--red-ui); }
.section--dark a { color: var(--red-ui); }

/* curved mesh on the LEFT half only, faded into the white, warping toward the cursor */
.section--grid { perspective: 1200px; overflow: hidden; }
.section--grid::before {
  content: "";
  position: absolute; top: 0; bottom: 0; left: 0; width: 56%;
  background: url('/assets/approved/section-bg.jpg') left center / cover no-repeat;
  opacity: .4;
  transform-origin: 50% 50%; transform-style: preserve-3d;
  transform: rotateX(calc(var(--tly, 0) * 5deg)) rotateY(calc(var(--tlx, 0) * 5deg)) scale(1.03);
  transition: transform .4s var(--ease);
  will-change: transform; pointer-events: none;
  -webkit-mask-image: linear-gradient(90deg, #000 55%, transparent 100%);
  mask-image: linear-gradient(90deg, #000 55%, transparent 100%);
}
.section--grid > * { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) { .section--grid::before { transition: none; transform: scale(1.01); } }
/* section 2 title: give the copy column more width so the headline fills ~2 lines
   on a wide monitor, and the fluid h2 size adapts it down on smaller screens */
/* minmax(0,fr) forces the ratio so the wide image can't squeeze the text column */
@media (min-width: 960px) { #laserglow-technologies .split { grid-template-columns: minmax(0, 0.6fr) minmax(0, 1.4fr); } }
#laserglow-technologies h2 { font-size: clamp(1.8rem, 2.9vw, 2.55rem); text-wrap: normal; }
/* the wide-screen line break disappears below 960px, so the title re-wraps
   naturally on tablets/phones instead of being locked to two lines */
.tlt-br { display: none; }
@media (min-width: 960px) { .tlt-br { display: inline; } }

/* ---- section 2: animated product circle -------------------------------- */
/* Four square 1024px PNGs that already share one centre, stacked and absolutely
   positioned. Only transform and opacity ever animate. The default state here is
   the FINAL composed circle, so no-JS still renders correctly; the pre-entrance
   state lives behind .js (set inline in <head>) so the scrub can't flash. */

/* The artwork fills only 70.2% of its 1024px square (359.5px radius), so a 30vw
   BOX gives the ~21vw CIRCLE the live site has. Capped by the 0.6fr column on
   wide screens; the 340px floor keeps it readable in the one-column phone layout. */
.pcircle {
  width: min(100%, max(30vw, 340px));
  aspect-ratio: 1;
  margin-inline: auto;
}
.pcircle__inner { position: relative; width: 100%; height: 100%; }
/* the ring gets a wrapper so its entrance scale and its idle spin are separate
   transforms that can't overwrite each other, so pin both levels */
.pcircle__inner > *, .pcircle__ringwrap > * { position: absolute; inset: 0; width: 100%; height: 100%; }
/* .split__media img adds a radius/shadow meant for photos — not wanted here */
.pcircle img { display: block; border-radius: 0; box-shadow: none; }

/* Each layer turns about its OWN measured centre (disc 514/1024, ring 516/1024)
   so nothing wobbles as it rotates. */
.pcircle__disc { transform-origin: 50.2% 50%; will-change: transform; }
.pcircle__ringwrap, .pcircle__ring { transform-origin: 50.4% 50.15%; }
/* ring.png is drawn at exactly the disc's radius, so at scale 1 it hides behind
   the navy border. 1.02 lifts it just clear of the edge as a hairline halo. */
.pcircle__ringwrap { transform: scale(1.02); will-change: transform; }

/* Rest state: only the thin ring moves — 360deg / 48s = 7.5deg/s, clockwise.
   Deliberately slow: imperceptible drift, not a spinner. Opposite direction to
   the disc's entrance spin, which is what stops the rest state reading as an
   entrance that never finished. */
@keyframes pcircle-drift { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
.js .pcircle__ring { animation: pcircle-drift 48s linear infinite; }

/* Pre-entrance state (JS then scrubs these inline). */
.js .pcircle__inner { transform: scale(.6); }
.js .pcircle__ringwrap { transform: scale(1.09); }
.js .pcircle__disc { transform: rotate(180deg); }
.js .pcircle__products, .js .pcircle__logo { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .js .pcircle__inner, .js .pcircle__disc { transform: none; }
  .js .pcircle__ringwrap { transform: scale(1.02); }
  .js .pcircle__products, .js .pcircle__logo { opacity: 1; }
  .js .pcircle__ring { animation: none; }
}

/* oversized watermark word behind a section */
.watermark {
  position: absolute;
  inset-inline: 0;
  top: clamp(1rem, 4vw, 3rem);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(3.5rem, 15vw, 16rem);   /* unified watermark scale */
  letter-spacing: -0.04em;
  text-transform: uppercase;
  text-align: center;
  line-height: .85;
  color: rgba(16, 17, 20, 0.05);
  pointer-events: none;
  user-select: none;
  white-space: nowrap;
}
.section--dark .watermark { color: rgba(255, 255, 255, 0.045); }

.head { max-width: 900px; }              /* wider column keeps titles to <=2 lines */
.head p { font-size: 1.075rem; }
.head--center { margin-inline: auto; text-align: center; }
.head--center { max-width: 940px; }
.head--center p { margin-inline: auto; }

/* ------------------------------------------------------------------ buttons */

.btn {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  font-family: var(--font-text);
  font-weight: 700;
  font-size: .95rem;
  line-height: 1;
  padding: 1rem 1.7rem;
  border-radius: var(--r-pill);
  border: 2px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.btn__arrow { font-size: 1.05em; line-height: 0; transition: transform var(--t-fast) var(--ease); }
.btn:hover .btn__arrow { transform: translate(2px, 2px); }
.btn--primary { background: var(--red); color: #fff; }
.btn--primary:hover { background: var(--red-deep); color: #fff; }
.btn--outline { border-color: var(--red); color: var(--red); background: transparent; }
.btn--outline:hover { background: var(--red); color: #fff; }
.btn--ghost { border-color: var(--line); color: var(--ink); }
.btn--ghost:hover { border-color: var(--ink); }
.btn--sm { padding: .72rem 1.25rem; font-size: .88rem; }
.section--dark .btn--ghost { border-color: var(--dark-line); color: var(--on-dark); }
.section--dark .btn--outline { border-color: var(--red-ui); color: var(--red-ui); }
.section--dark .btn--outline:hover { background: var(--red-ui); color: #fff; }
/* the generic `.section--dark a` colour was beating .btn--primary, leaving the
   button label red-on-red (invisible until hover) — keep primary labels white */
.section--dark .btn--primary, .section--dark .btn--primary:hover { color: #fff; }

.btn-row { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: 2rem; }

/* ------------------------------------------------------------------- header */

/* SITE RULE (user, 2026-08-12): the header stays one solid white, always.
   It used to be rgba(255,255,255,.94) with backdrop-filter saturate(150%), so
   page content showed through and the saturation boost tinted it pink whenever
   a red section scrolled underneath. No transparency, no backdrop-filter. */
.header {
  position: sticky; top: 0; z-index: 200;
  background: #ffffff;
  border-bottom: var(--header-rule) solid var(--red);
}
/* the BAR is the header minus its rule, so the logo and nav are unchanged */
.header__bar { height: calc(var(--header-h) - var(--header-rule));
  display: flex; align-items: center; gap: 2rem; }
.header__logo { flex: 0 0 auto; display: flex; align-items: center; }
.header__logo img { height: 44px; width: auto; }

.header__nav { display: none; margin-inline-start: auto; align-items: center; gap: .25rem; }
@media (min-width: 1200.02px) { .header__nav { display: flex; } }

.navitem__button {
  font-family: var(--font-text); font-weight: 600; font-size: 1rem;
  color: var(--ink); background: none; border: 0;
  padding: .7rem 1.1rem; border-radius: var(--r-sm);
  cursor: pointer; display: inline-flex; align-items: center; gap: .45rem;
  transition: color var(--t-fast) var(--ease);
}
.navitem__button::after {
  content: ""; width: 7px; height: 7px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--t-fast) var(--ease); opacity: .6;
}
.navitem:hover .navitem__button, .navitem[data-open="true"] .navitem__button { color: var(--red); }
.navitem[data-open="true"] .navitem__button::after { transform: translateY(1px) rotate(-135deg); }

.header__search {
  background: none; border: 0; cursor: pointer;
  width: 44px; height: 44px; display: grid; place-items: center;
  color: var(--ink); border-radius: var(--r-pill);
}
.header__search:hover { color: var(--red); background: var(--surface); }

/* visual mega panel: image cards, not a link list */
.panel {
  position: fixed;
  left: 0; right: 0; top: var(--header-h);
  background: #fff;
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
  padding: clamp(1.75rem, 3vw, 2.75rem) 0 clamp(2rem, 3vw, 3rem);
  opacity: 0; visibility: hidden; transform: translateY(-8px);
  transition: opacity var(--t) var(--ease), transform var(--t) var(--ease), visibility var(--t);
}
.navitem[data-open="true"] .panel { opacity: 1; visibility: visible; transform: none; }

.panel__grid { display: grid; gap: clamp(1.25rem, 2vw, 2rem); grid-template-columns: repeat(4, 1fr); }
/* two-column panel collapse removed 2026-08-26: under 1200px the phone
   drawer is the menu, so the mega panel never renders at that width */
.panel__group-label {
  font-family: var(--font-mono); font-size: .68rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3);
  margin: 0 0 .9rem; padding-bottom: .6rem; border-bottom: 1px solid var(--line);
}
.navcard {
  display: grid; grid-template-columns: 64px 1fr; gap: .9rem; align-items: center;
  padding: .7rem; margin-inline: -.7rem;
  border-radius: var(--r-sm); text-decoration: none;
  transition: background var(--t-fast) var(--ease);
}
.navcard:hover { background: var(--surface); }
.navcard__thumb {
  width: 64px; height: 56px; border-radius: 8px; overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--line-soft);
}
.navcard__thumb img { width: 100%; height: 100%; object-fit: cover; }
.navcard__title {
  display: block; font-family: var(--font-display); font-weight: 800;
  font-size: .96rem; color: var(--ink); letter-spacing: -.01em; line-height: 1.2;
}
.navcard:hover .navcard__title { color: var(--red); }
.navcard__desc { display: block; font-size: .82rem; color: var(--ink-3); line-height: 1.4; margin-top: .2rem; }

.panel__feature {
  background: var(--dark); border-radius: var(--r); overflow: hidden;
  display: flex; flex-direction: column; color: var(--on-dark-2);
  text-decoration: none; min-height: 100%;
}
.panel__feature img { width: 100%; aspect-ratio: 16/9; object-fit: cover; }
.panel__feature div { padding: 1.1rem 1.25rem 1.35rem; }
.panel__feature strong { color: #fff; font-family: var(--font-display); display: block; margin-bottom: .3rem; }
.panel__feature span { font-size: .85rem; }

/* mobile drawer */
.header__toggle {
  margin-inline-start: auto; display: inline-flex; flex-direction: column;
  justify-content: center; gap: 5px; width: 46px; height: 46px;
  background: none; border: 0; cursor: pointer; padding: 0 9px;
}
@media (min-width: 1200.02px) { .header__toggle { display: none; } }
.header__toggle span { display: block; height: 2.5px; background: var(--ink); border-radius: 2px; transition: transform var(--t) var(--ease), opacity var(--t-fast); }
.header__toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7.5px) rotate(45deg); }
.header__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.header__toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7.5px) rotate(-45deg); }

.drawer { display: none; background: #fff; border-top: 1px solid var(--line); max-height: calc(100dvh - var(--header-h)); overflow-y: auto; padding-bottom: 2rem; }
.drawer[data-open="true"] { display: block; }
@media (min-width: 1200.02px) { .drawer { display: none !important; } }
.drawer__group { border-bottom: 1px solid var(--line-soft); }
.drawer__toggle {
  width: 100%; background: none; border: 0; color: var(--ink);
  font-family: var(--font-display); font-weight: 800; font-size: 1.05rem;
  text-transform: uppercase; letter-spacing: -.01em;
  text-align: left; padding: 1.1rem 0; display: flex; justify-content: space-between; cursor: pointer;
}
.drawer__toggle::after { content: "+"; color: var(--red); font-size: 1.5rem; line-height: 1; }
.drawer__group[data-open="true"] .drawer__toggle::after { content: "\2013"; }
.drawer__list { display: none; padding: 0 0 1rem; margin: 0; list-style: none; }
.drawer__group[data-open="true"] .drawer__list { display: block; }
.drawer__list a { display: block; padding: .6rem 0 .6rem 1rem; color: var(--ink-2); text-decoration: none; border-left: 2px solid var(--line); }
.drawer__list a:hover { color: var(--red); border-left-color: var(--red); }

/* --------------------------------------------------------------------- hero */

.hero { position: relative; background: var(--dark); color: #fff; overflow: hidden; }
.heroslide {
  position: relative;
  /* fill the screen below the sticky header on entry (dvh handles mobile chrome) */
  min-height: calc(100vh - var(--header-h));
  min-height: calc(100dvh - var(--header-h));
  display: flex; align-items: center;
  padding-block: clamp(4rem, 10vw, 8rem) clamp(6rem, 12vw, 9rem);
}
.heroslide[hidden] { display: none; }
.hero__media { position: absolute; inset: 0; }
.hero__media img, .hero__media video { width: 100%; height: 100%; object-fit: cover; }
.hero__media::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(100deg, rgba(0,0,0,.78) 0%, rgba(0,0,0,.46) 42%, rgba(0,0,0,.06) 78%),
    linear-gradient(0deg, rgba(13,13,13,.72) 0%, transparent 42%);
}
.hero__inner { position: relative; z-index: 2; }

/* ---- light split hero (solution pages) -------------------------------- */
/* Copy on white at the left, photo bleeding off the right with a feathered
   edge so it dissolves into the page rather than sitting in a box. */
.hero--split {
  background: var(--page);
  color: var(--ink);
  display: flex;
  align-items: center;
  min-height: min(80vh, 780px);
}
.hero--split .hero__photo {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 62%;
  z-index: 1;
}
/* feather the left edge so the photo dissolves into the white page */
.hero--split .hero__photo img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: 60% 50%;
  -webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.55) 16%, #000 40%);
  mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.55) 16%, #000 40%);
}
.hero--split .hero__inner { padding-block: clamp(3.5rem, 8vw, 7rem); }
.hero--split .hero__copy { max-width: min(52%, 44rem); }
/* .hero h1/p set white for the dark homepage hero and match this on
   specificity, so the light variant has to out-weigh them explicitly */
.hero.hero--split .eyebrow { color: var(--red); }
.hero.hero--split h1 {
  color: var(--ink);
  font-size: clamp(2.4rem, 4.6vw, 4.3rem);
  line-height: .95;
  letter-spacing: -0.02em;
  margin-bottom: .45em;
}
.hero.hero--split p { color: var(--ink-2); font-size: clamp(1rem, .5vw + .85rem, 1.15rem); max-width: 33rem; }

@media (max-width: 900px) {
  .hero--split { display: block; min-height: 0; }
  .hero--split .hero__photo { position: relative; width: 100%; height: 46vh; }
  .hero--split .hero__photo img {
    -webkit-mask-image: linear-gradient(#000 76%, rgba(0,0,0,0) 100%);
    mask-image: linear-gradient(#000 76%, rgba(0,0,0,0) 100%);
  }
  .hero--split .hero__copy { max-width: none; }
  .hero--split .hero__inner { padding-block: clamp(2rem, 6vw, 3rem) clamp(2.5rem, 7vw, 4rem); }
}
.hero .eyebrow { color: var(--red-ui); font-size: clamp(.8rem, .55vw + .6rem, .95rem); margin-bottom: 1.15rem; }
.hero h1 { color: #fff; font-size: clamp(2.3rem, 4.4vw, 4.4rem); margin-bottom: .5em; }
.hero p { color: rgba(255,255,255,.92); font-size: clamp(1.05rem, 1.15vw, 1.3rem); max-width: none; }
/* Fluid width: the text block fills the content column, so on a wide monitor the
   kicker and sub-line each sit on one line, while on a tablet/phone the narrower
   column lets them wrap. Nothing here forces a fixed line count. */
.heroslide__body { max-width: min(80rem, 100%); animation: heroIn 520ms var(--ease) both; }
@keyframes heroIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

.hero__foot {
  position: absolute; z-index: 3; inset-inline: 0; bottom: clamp(1.5rem, 3vw, 2.5rem);
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
.scrollcue { display: inline-flex; align-items: center; gap: .7rem; color: rgba(255,255,255,.75); font-size: .85rem; }
.scrollcue__mouse {
  width: 22px; height: 34px; border: 2px solid rgba(255,255,255,.6); border-radius: 12px;
  position: relative; flex: 0 0 auto;
}
.scrollcue__mouse::after {
  content: ""; position: absolute; left: 50%; top: 6px; width: 3px; height: 6px;
  background: #fff; border-radius: 2px; transform: translateX(-50%);
  animation: wheel 1.8s var(--ease) infinite;
}
@keyframes wheel { 0%,100% { opacity: 0; transform: translate(-50%, 0); } 40% { opacity: 1; transform: translate(-50%, 8px); } }

.herodots { display: flex; align-items: center; gap: .75rem; }
.herodots__dot {
  position: relative; overflow: hidden;
  width: clamp(64px, 10vw, 168px); height: 5px; border: 0; padding: 0;
  background: rgba(255,255,255,.28); cursor: pointer; border-radius: 3px;
  transition: background var(--t-fast) var(--ease);
}
/* progress fill: animates 0 -> 100% across the fixed slide interval, so the
   auto-advance reads as deliberate and consistent, never random */
.herodots__dot::after {
  content: ""; position: absolute; inset: 0; width: 0;
  background: var(--red-ui); border-radius: inherit;
}
.herodots__dot[aria-current="true"]::after {
  animation: dotprogress var(--hero-interval, 7000ms) linear forwards;
}
@keyframes dotprogress { from { width: 0; } to { width: 100%; } }
@media (prefers-reduced-motion: reduce) {
  .herodots__dot[aria-current="true"]::after { animation: none; width: 100%; }
}

/* -------------------------------------------------------------------- split */

.split { display: grid; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; grid-template-columns: 1fr; }
@media (min-width: 960px) {
  .split { grid-template-columns: 1fr 1fr; }
  .split--reverse .split__media { order: 2; }
}
.split__media img, .split__media video { width: 100%; border-radius: var(--r); }
.split__media--bleed img { border-radius: var(--r); box-shadow: var(--shadow); }

/* --------------------------------------------------------------- accordion */

.acc { border-top: 1px solid var(--line); margin-top: 2.25rem; }
.acc__item { border-bottom: 1px solid var(--line); }
.acc__toggle {
  width: 100%; background: none; border: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1.15rem 0; text-align: left;
  font-family: var(--font-display); font-weight: 800; font-size: clamp(.98rem, 1.3vw, 1.12rem);
  text-transform: uppercase; letter-spacing: -.01em; color: var(--ink);
}
.acc__toggle:hover { color: var(--red); }
.acc__toggle::after {
  content: ""; flex: 0 0 auto; width: 9px; height: 9px;
  border-right: 2.5px solid var(--red); border-bottom: 2.5px solid var(--red);
  transform: rotate(45deg) translateY(-2px);
  transition: transform var(--t) var(--ease);
}
.acc__item[data-open="true"] .acc__toggle::after { transform: rotate(-135deg) translateY(-2px); }
.acc__panel { display: none; padding-bottom: 1.5rem; }
.acc__item[data-open="true"] .acc__panel { display: block; }

.minicards { display: grid; gap: .85rem; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.minicard {
  background: var(--surface); border: 1px solid var(--line-soft);
  border-radius: var(--r-sm); padding: 1.15rem 1rem; text-align: center;
}
.minicard__icon { width: 40px; height: 40px; margin: 0 auto .8rem; color: var(--red); }
.minicard__icon svg { width: 100%; height: 100%; }
.minicard h4 {
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  font-size: .98rem; letter-spacing: .01em; color: var(--ink); margin: 0 0 .4rem; line-height: 1.15;
}
.minicard p { font-size: .95rem; color: var(--ink-3); margin: 0; line-height: 1.45; }

/* ------------------------------------------------------------ logo marquee */

.marquee { overflow: hidden; mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent); }
.marquee__track { display: flex; gap: clamp(1.75rem, 3vw, 3rem); width: max-content; align-items: center; animation: slide 46s linear infinite; }
.marquee--rev .marquee__track { animation-direction: reverse; }
.marquee:hover .marquee__track { animation-play-state: paused; }
@keyframes slide { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* the logo PNGs are white marks on transparent, so they show as white on the
   dark strip (the filter normalises any stray colour to a clean white silhouette) */
.marquee img {
  height: clamp(64px, 7vw, 110px); width: auto; object-fit: contain;
  filter: brightness(0) invert(1); opacity: .95; flex: 0 0 auto;
}
@media (prefers-reduced-motion: reduce) {
  .marquee { overflow-x: auto; }
  .marquee__track { animation: none; }
}

/* ------------------------------------------------------------- testimonials */

.quotes { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); }
/* when testimonials share the logos screen, tighten them so all fit one view */
.quotes--compact { margin-top: clamp(2.25rem, 4vw, 3.25rem); }
.quotes--compact .quote { padding: clamp(1.15rem, 1.8vw, 1.6rem); gap: 1rem; }
.quotes--compact .quote p { font-size: .95rem; }
.quotes--compact .quote__mark { font-size: clamp(3.25rem, 4vw, 4.25rem); }
.quote {
  background: var(--dark-2); border: 1px solid var(--dark-line);
  border-radius: var(--r); padding: clamp(1.5rem, 2.4vw, 2.25rem);
  display: flex; flex-direction: column; gap: 1.5rem;
}
.quote__mark { font-family: var(--font-display); font-weight: 800; font-size: clamp(3.5rem, 4.5vw, 5rem); line-height: .55; color: var(--red-ui); }
.quote p { color: var(--on-dark-2); flex: 1; }
.quote__who { display: flex; align-items: center; gap: .8rem; }
.quote__logo {
  width: 52px; height: 52px; border-radius: 50%; background: #fff;
  display: grid; place-items: center; flex: 0 0 auto; overflow: hidden; padding: 0;
}
/* brand logo images are saved with their own background and fill the circle */
.quote__logo img { width: 100%; height: 100%; object-fit: cover; }
.quote__who strong { display: block; color: var(--red-ui); font-size: .95rem; }
.quote__who span { display: block; color: var(--on-dark-2); font-size: .85rem; }

/* ----------------------------------------------------------- stats / map */

.mapwrap { position: relative; border-radius: var(--r); overflow: hidden; background: var(--dark-2); border: 1px solid var(--dark-line); }
.mapwrap img { width: 100%; opacity: .9; }
.statcard {
  background: #fff; border-radius: var(--r); padding: clamp(1.5rem, 2.5vw, 2.4rem);
  box-shadow: var(--shadow-lg); max-width: 420px;
}
.statcard h2 { font-size: clamp(1.5rem, 2.3vw, 2.1rem); }
.stats { display: flex; gap: 2.5rem; margin: 1.5rem 0; }
.stat__n { font-family: var(--font-display); font-weight: 800; font-size: clamp(1.8rem, 3vw, 2.6rem); color: var(--ink); line-height: 1; letter-spacing: -.03em; }
.stat__l { font-size: .96rem; color: var(--ink-3); }
.legend { display: flex; flex-wrap: wrap; gap: 1.25rem; margin-top: 1.25rem; font-size: .92rem; color: var(--on-dark-2); }
.legend span { display: inline-flex; align-items: center; gap: .5rem; }
.legend i { width: 12px; height: 12px; background: var(--red-ui); border-radius: 3px; display: inline-block; }

/* ------------------------------------------------------------- rail (timeline) */

.rail { display: flex; gap: 1.25rem; overflow-x: auto; scroll-snap-type: x mandatory; padding-bottom: 1.5rem; scrollbar-width: thin; }
.rail::-webkit-scrollbar { height: 6px; }
.rail::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }
.rail__item { scroll-snap-align: start; flex: 0 0 min(88vw, 420px); border-radius: var(--r); overflow: hidden; }
.rail__item--media { flex-basis: min(88vw, 460px); }
.rail__item--media img, .rail__item--media video { width: 100%; height: 100%; object-fit: cover; min-height: 340px; }
.rail__card { background: #fff; border: 1px solid var(--line); padding: clamp(1.5rem, 2.4vw, 2.25rem); height: 100%; display: flex; flex-direction: column; }
.rail__card--accent { background: var(--red); border-color: var(--red); color: #fff; }
.rail__card--accent h3, .rail__card--accent .rail__step { color: #fff; }
.rail__card--accent p { color: rgba(255,255,255,.94); }
.rail__step {
  font-family: var(--font-mono); font-size: .72rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--red); margin-bottom: 1rem; display: block;
}
.rail__card h3 { font-size: clamp(1.3rem, 2vw, 1.8rem); text-transform: uppercase; }
.rail__card p { font-size: .95rem; }

/* --------------------------------------------------------------- post cards */

.cards { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr)); }
.card {
  background: #fff; border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden; display: flex; flex-direction: column;
  transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.card__media { aspect-ratio: 16/10; background: var(--surface-2); }
.card__media img { width: 100%; height: 100%; object-fit: cover; }
.card__body { padding: 1.35rem; display: flex; flex-direction: column; gap: .7rem; flex: 1; }
.card__meta { font-size: .8rem; color: var(--ink-3); font-family: var(--font-mono); letter-spacing: .04em; }
.pill {
  align-self: flex-start; background: var(--red); color: #fff;
  font-size: .72rem; font-weight: 700; padding: .3rem .75rem; border-radius: var(--r-pill);
  text-transform: uppercase; letter-spacing: .05em; text-decoration: none;
}
.card h3 { font-size: 1.08rem; text-transform: none; letter-spacing: -.02em; line-height: 1.25; margin: 0; }
.card h3 a { color: var(--ink); text-decoration: none; }
.card h3 a:hover { color: var(--red); }
.card p { font-size: .9rem; color: var(--ink-3); margin: 0; }
.card__link { margin-top: auto; padding-top: .5rem; font-weight: 700; font-size: .88rem; text-decoration: none; }

.pager { display: flex; justify-content: center; align-items: center; gap: .35rem; margin-top: 2.5rem; }
.pager button, .pager a {
  min-width: 38px; height: 38px; border-radius: var(--r-pill);
  border: 0; background: none; color: var(--ink-2); cursor: pointer;
  font-family: var(--font-text); font-size: .95rem; display: grid; place-items: center; text-decoration: none;
}
.pager [aria-current="true"] { background: var(--red); color: #fff; font-weight: 700; }
.pager button:hover:not([aria-current="true"]) { background: var(--surface); color: var(--red); }

/* -------------------------------------------------------------- trade shows */

.shows { display: grid; gap: clamp(1rem, 1.4vw, 1.5rem); grid-template-columns: repeat(5, 1fr); }
.show {
  background: #fff; border-radius: var(--r); padding: clamp(1.25rem, 1.7vw, 1.75rem);
  display: flex; flex-direction: column; gap: .6rem; color: var(--ink-2); box-shadow: var(--shadow);
}
.show__status {
  font-family: var(--font-mono); font-size: .74rem; letter-spacing: .12em; text-transform: uppercase;
  padding-bottom: .85rem; border-bottom: 1px solid var(--line); margin-bottom: .4rem; color: var(--ink-3);
}
.show__status--up { color: var(--ok); }
.show__name { font-family: var(--font-display); font-weight: 800; text-transform: uppercase; color: var(--red); font-size: clamp(1.1rem, 1.35vw, 1.4rem); letter-spacing: -.01em; line-height: 1.1; }
.show__booth { font-family: var(--font-display); font-weight: 800; color: var(--ink); text-transform: uppercase; font-size: clamp(1rem, 1.2vw, 1.15rem); }
.show__meta { font-size: .95rem; color: var(--ink-2); line-height: 1.4; }
.show a { font-size: .92rem; font-weight: 600; }
@media (max-width: 1080px) { .shows { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 680px) { .shows { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------------- signup */

.signup {
  background: var(--red); border-radius: var(--r);
  padding: clamp(2rem, 4vw, 3.25rem); color: #fff;
  display: grid; gap: 2rem; align-items: center; grid-template-columns: 1fr;
}
@media (min-width: 900px) { .signup { grid-template-columns: 1.1fr 1fr; } }
.signup h2 { color: #fff; }
.signup p { color: rgba(255,255,255,.94); }
.signup__form { display: flex; gap: .6rem; flex-wrap: wrap; }
.signup__form input {
  flex: 1 1 240px; border: 0; border-radius: var(--r-pill);
  padding: 1rem 1.35rem; font-family: var(--font-text); font-size: .98rem; color: var(--ink);
}
.signup__form input::placeholder { color: var(--ink-3); }
.signup__form .btn { background: var(--ink); color: #fff; }
.signup__form .btn:hover { background: #000; }
.signup small { display: block; margin-top: .85rem; color: rgba(255,255,255,.85); font-size: .8rem; }

/* ------------------------------------------------------------------- footer */

.footer { background: var(--dark); color: var(--on-dark-2); padding-block: clamp(3.5rem, 6vw, 5rem) 2rem; }
.footer__top { display: grid; gap: 2.5rem; grid-template-columns: 1fr; }
@media (min-width: 820px) { .footer__top { grid-template-columns: 1.4fr 1fr 1fr 1fr; } }
.footer__brand img { height: 46px; margin-bottom: 1.25rem; }
.footer address { font-style: normal; font-size: .92rem; line-height: 1.75; color: var(--on-dark-2); }
.footer h3 {
  font-family: var(--font-mono); font-size: .7rem; letter-spacing: .18em; text-transform: uppercase;
  color: #fff; font-weight: 400; margin-bottom: 1.1rem;
}
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.footer a { color: var(--on-dark-2); text-decoration: none; font-size: .93rem; }
.footer a:hover { color: var(--red-ui); }
.footer__bottom {
  margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--dark-line);
  display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between;
  font-size: .85rem; color: var(--on-dark-2);
}

/* --------------------------------------------------------------- utilities */

.skip-link { position: absolute; left: -9999px; background: var(--red); color: #fff; padding: .9rem 1.3rem; z-index: 300; font-weight: 700; border-radius: 0 0 var(--r-sm) 0; }
.skip-link:focus { left: 0; top: 0; }
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.breadcrumb { font-size: .85rem; color: var(--ink-3); }
.breadcrumb a { color: var(--ink-3); text-decoration: none; }
.breadcrumb a:hover { color: var(--red); }

.tags { display: flex; flex-wrap: wrap; gap: .5rem; }
.tag {
  font-family: var(--font-mono); font-size: .72rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-2); border: 1px solid var(--line); border-radius: var(--r-pill); padding: .45rem .9rem;
}
.section--dark .tag { color: var(--on-dark-2); border-color: var(--dark-line); }

.chips { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.chip {
  border: 1px solid var(--line); border-radius: var(--r-sm); background: #fff;
  padding: 1.25rem 1rem; text-align: center; font-family: var(--font-display);
  font-weight: 800; color: var(--ink); font-size: .95rem; text-transform: uppercase; line-height: 1.2;
}
.specs { list-style: none; margin: 0; padding: 0; display: grid; gap: .4rem; }
.specs li { display: flex; gap: .7rem; padding: .6rem 0; border-bottom: 1px solid var(--line-soft); font-size: .94rem; }
.specs li::before { content: ""; flex: 0 0 auto; width: 7px; height: 7px; margin-top: .55em; border-radius: 50%; background: var(--red); }

.gallery { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.gallery figure { margin: 0; border-radius: var(--r); overflow: hidden; border: 1px solid var(--line); background: #fff; }
.gallery img { width: 100%; aspect-ratio: 4/3; object-fit: cover; }
.gallery figcaption { padding: .7rem .9rem; font-size: .85rem; color: var(--ink-3); }

.strip { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); align-items: center; }
.strip__item { background: #fff; border: 1px solid var(--line-soft); border-radius: var(--r-sm); padding: .9rem; display: grid; place-items: center; min-height: 84px; }
.strip__item img { max-height: 56px; width: auto; object-fit: contain; }

.article { padding-block: clamp(2.5rem, 6vw, 5rem); }
.article__meta { color: var(--ink-3); font-family: var(--font-mono); font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; margin-bottom: 1rem; }
.article__body { font-size: 1.1rem; color: var(--ink-2); }
.article__body h1 { text-transform: none; letter-spacing: -.035em; }
.article__body h2 { margin-top: 2.2em; text-transform: none; font-size: clamp(1.5rem, 2.6vw, 2.05rem); }
.article__body img, .article__body video { border-radius: var(--r); margin: 2rem 0; width: 100%; }
.article__body blockquote {
  margin: 2rem 0; padding: 1.3rem 1.6rem; border-left: 4px solid var(--red);
  background: var(--surface); border-radius: 0 var(--r-sm) var(--r-sm) 0; color: var(--ink); font-size: 1.12rem;
}
.article__body ul, .article__body ol { padding-left: 1.3rem; }
.article__body li { margin-bottom: .5rem; }

/* map pins */
.mapwrap { position: relative; }
.mapwrap img { filter: grayscale(1) brightness(.55) contrast(1.1); }
.mapwrap__pins { position: absolute; inset: 0; }
.pin {
  position: absolute; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px;
  border-radius: 50%; background: #fff; box-shadow: 0 0 0 3px rgba(255,58,66,.55);
}
.pin::after {
  content: ""; position: absolute; inset: -6px; border-radius: 50%;
  border: 1px solid rgba(255,58,66,.5); animation: ping 3.2s var(--ease) infinite;
}
@keyframes ping { 0% { transform: scale(.6); opacity: .9; } 70%,100% { transform: scale(1.9); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .pin::after { animation: none; } }

/* --- fixes ---------------------------------------------------------------- */

/* browser default blockquote margin was squeezing testimonial copy */
.quote blockquote { margin: 0; }
.quote p { max-width: none; font-size: 1rem; }

/* a light card inside a dark section must re-assert light-surface ink */
.statcard { color: var(--ink-2); }
.statcard h2, .statcard .stat__n { color: var(--ink); }
.statcard .eyebrow { color: var(--red); }
.statcard p { color: var(--ink-2); }
.statcard .btn--primary { background: var(--red); color: #fff; }
.statcard .btn--primary:hover { background: var(--red-deep); color: #fff; }

/* the map should carry the section, not sit in a corner of it */
.mapwrap img { width: 100%; }
#global-presence .split { align-items: center; }
@media (min-width: 960px) { #global-presence .split { grid-template-columns: 1fr 1.15fr; } }

/* show cards live on the dark section: keep their own ink */
.show { color: var(--ink-2); }
.show a { color: var(--red); }
.show a:hover { color: var(--red-deep); }

/* trade-show + blog watermarks must never overlap the heading */
#whats-new .head, #trade-shows .head { position: relative; z-index: 1; }

/* blockquote, not the paragraph, is the flexible child now */
.quote blockquote { flex: 1; }

/* customer logos: large white chips (sizing lives in the base .marquee img rule) */
.marquee + .head, .head + .marquee { margin-block: clamp(2.5rem, 4vw, 3.5rem) !important; }

/* ------------------------------------------------------------------ timeline */

.timeline { margin-top: clamp(2.5rem, 4vw, 3.5rem); position: relative; }
.timeline__rail {
  list-style: none; margin: 0; padding: 0 0 1.5rem;
  display: flex; gap: 0; overflow-x: auto;
  scroll-snap-type: x proximity; scrollbar-width: thin;
}
.timeline__rail::-webkit-scrollbar { height: 6px; }
.timeline__rail::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }

.step {
  flex: 0 0 min(78vw, 310px);
  scroll-snap-align: start;
  padding-inline-end: 1.25rem;
  position: relative;
}
/* the connector: one continuous rule through every node */
.step::before {
  content: ""; position: absolute;
  left: 0; right: 0; top: 21px; height: 2px;
  background: var(--line);
}
.step:first-child::before { left: 21px; }
.step:last-child::before { right: calc(1.25rem + 50%); }

.step__node {
  position: relative; z-index: 1;
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--red); color: #fff;
  display: grid; place-items: center;
  font-family: var(--font-mono); font-size: .82rem; font-weight: 700;
  box-shadow: 0 0 0 6px var(--surface);
}
.step__card { padding-top: 1.5rem; }
.step__label {
  font-family: var(--font-mono); font-size: .7rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--red); display: block; margin-bottom: .5rem;
}
.step__card h3 { font-size: clamp(1.15rem, 1.6vw, 1.4rem); text-transform: uppercase; margin-bottom: .6rem; }
.step__card p { font-size: 1rem; color: var(--ink-2); line-height: 1.5; }

.timeline__hint {
  font-family: var(--font-mono); font-size: .72rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3); margin: 0;
}

/* consecutive dark sections read as one band: don't double the padding */
.section--dark + .section--dark { padding-top: 0; }
#trusted { padding-bottom: clamp(2rem, 3vw, 3.5rem); }

/* ===== global presence — lg-worldmap component (dark, ported from the =====
   Component Library): faded equirectangular map as the section background,
   pulsing distributor pins + ringed HQ markers, content centred on top. */
.section--presence { position: relative; overflow: hidden; }
.lg-worldmap { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; }
.lg-worldmap::before {
  content: ''; position: absolute; inset: 0;
  /* stretch to fill (100% 100%) so plate-carree pin %s align to the image */
  background: url('/assets/world-equirectangular.jpg') center / 100% 100% no-repeat;
  filter: grayscale(1) brightness(.55) contrast(1.08);
  opacity: .3;
  -webkit-mask-image: radial-gradient(ellipse 84% 90% at 50% 50%, #000 55%, transparent 92%);
  mask-image: radial-gradient(ellipse 84% 90% at 50% 50%, #000 55%, transparent 92%);
}
.lg-wpin { position: absolute; transform: translate(-50%, -50%); border-radius: 50%; }
.lg-wpin-dep {
  width: 7px; height: 7px; background: var(--red-ui);
  box-shadow: 0 0 8px 1px rgba(255, 58, 66, .75); opacity: .85;
  animation: lgDepPulse 3.2s ease-in-out infinite;
}
@keyframes lgDepPulse { 0%, 100% { opacity: .4; } 50% { opacity: .95; } }
.lg-wpin-hq {
  width: 13px; height: 13px; background: var(--red-ui);
  box-shadow: 0 0 16px 4px rgba(255, 58, 66, .9); z-index: 2;
}
.lg-wpin-hq::after {
  content: ''; position: absolute; left: 50%; top: 50%; width: 13px; height: 13px;
  border-radius: 50%; border: 2px solid var(--red-ui);
  transform: translate(-50%, -50%); animation: lgHqRing 2.6s ease-out infinite;
}
@keyframes lgHqRing {
  0% { transform: translate(-50%, -50%) scale(1); opacity: .8; }
  100% { transform: translate(-50%, -50%) scale(3.4); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .lg-wpin-dep, .lg-wpin-hq::after { animation: none; }
}

.presence__inner { position: relative; z-index: 2; text-align: center; max-width: 760px; margin-inline: auto; }
.presence__inner .stats { justify-content: center; margin-inline: auto; }
/* stats sit on the dark map now, not a white card — force light ink */
.presence__inner .stat__n { color: #fff; }
.presence__inner .stat__l { color: var(--on-dark-2); }
.lg-map-legend { display: flex; gap: 22px; margin-top: 26px; flex-wrap: wrap; justify-content: center; }
.lg-map-legend span {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-mono); font-size: .8rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--on-dark-2);
}
.lg-legend-dot { width: 11px; height: 11px; border-radius: 50%; flex-shrink: 0; }
.lg-legend-dot.hq { background: var(--red-ui); box-shadow: 0 0 10px 2px rgba(255, 58, 66, .8); }
.lg-legend-dot.dep { background: var(--red-ui); opacity: .7; }

/* ===== Why OHS leaders choose us ======================================== */
.pillars { position: relative; background: var(--surface); }
/* the intro is now its OWN full-screen vertical panel that scrolls up before
   the horizontal pillar scroll begins (it is no longer part of the track) */
.pillars__open {
  position: relative; overflow: hidden;
  min-height: calc(100vh - var(--header-h)); min-height: calc(100dvh - var(--header-h));
  display: flex; align-items: center; justify-content: center;
}
.pillars__introcopy { position: relative; z-index: 1; width: 100%; text-align: center; display: flex; flex-direction: column; align-items: center; }
/* pinned horizontal scroll container (JS sets its height for the scroll distance) */
.pillars__pin { position: relative; }
.pillars__sticky {
  position: sticky; top: var(--header-h);
  height: calc(100vh - var(--header-h)); height: calc(100dvh - var(--header-h));
  overflow: hidden; display: flex; align-items: center;
}
.pillars__track {
  display: flex; align-items: center; gap: clamp(1.5rem, 3vw, 3rem);
  will-change: transform; position: relative; z-index: 1;
  padding-inline: clamp(1.5rem, 5vw, 6rem);
}
.pillars__track::after { content: ''; flex: 0 0 clamp(1rem, 12vw, 12rem); }
.pillars__panel { flex: 0 0 auto; }
.pillars__wm {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 0; pointer-events: none; user-select: none; text-align: center;
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  font-size: clamp(3.5rem, 15vw, 16rem); line-height: .82; letter-spacing: -.04em;
  color: rgba(16, 17, 20, .05);
}

/* big, centred cards — each pillar dominates the view */
.pcard {
  width: min(90vw, 640px); height: min(80vh, 640px);
  display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center;
  background: #fff; border: 1px solid var(--line-soft); border-radius: var(--r);
  padding: clamp(2rem, 4vw, 4rem); box-shadow: var(--shadow);
}
.pcard h3 { font-size: clamp(1.9rem, 3vw, 2.9rem); text-transform: uppercase; margin-bottom: 1.2rem; line-height: 1.05; }
.pcard p { color: var(--ink-2); font-size: clamp(1.05rem, 1.35vw, 1.32rem); max-width: 34ch; }
.pcard .btn-row { justify-content: center; }
.pcard--red { background: var(--red); border-color: var(--red); color: #fff; }
.pcard--red h3, .pcard--red p, .pcard--red .mark { color: #fff; }

/* animated 3-dot marker: a wave scaling left-to-right, reads as rightward motion */
.pdots { display: inline-flex; gap: .55rem; margin-bottom: 1.5rem; }
.pdots i { width: 10px; height: 10px; border-radius: 50%; background: var(--ink); animation: pdotWave 1.5s ease-in-out infinite; }
.pdots i:nth-child(2) { animation-delay: .2s; }
.pdots i:nth-child(3) { animation-delay: .4s; }
@keyframes pdotWave { 0%, 100% { transform: scale(.6); opacity: .45; } 50% { transform: scale(1.35); opacity: 1; } }
.pcard--red .pdots i { background: #fff; }
@media (prefers-reduced-motion: reduce) { .pdots i { animation: none; } }

.pcard--media {
  width: min(92vw, 820px); height: min(80vh, 640px);
  border-radius: var(--r); overflow: hidden; box-shadow: var(--shadow);
}
.pcard--media img { width: 100%; height: 100%; object-fit: cover; }

.pillars__progress {
  position: absolute; left: clamp(1.25rem, 6vw, 4rem); bottom: clamp(1.5rem, 4vw, 2.5rem);
  z-index: 2; display: flex; align-items: center; gap: 1rem;
}
.pillars__hint { font-family: var(--font-mono); font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3); }
.pillars__dots { display: flex; gap: .5rem; }
.pillars__dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--line); transition: background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease); }
.pillars__dots i.is-active { background: var(--red); transform: scale(1.35); }

/* no-pin fallback (mobile / reduced motion): plain horizontal swipe */
.pillars__pin.no-pin { height: auto !important; }
.pillars__pin.no-pin .pillars__sticky { position: static; height: auto; overflow-x: auto; padding-block: clamp(3rem, 8vw, 6rem); scroll-snap-type: x proximity; }
.pillars__pin.no-pin .pillars__track { transform: none !important; height: auto; }
.pillars__pin.no-pin .pillars__panel { scroll-snap-align: center; }
.pillars__pin.no-pin .pcard, .pillars__pin.no-pin .pcard--media { height: min(70vh, 520px); }

/* ===== compact splitter bars: catalog (red) + newsletter (grey) ========= */
/* catalog: copy left, catalog mockup bleeding off the right edge */
.catbar { position: relative; overflow: hidden; background: var(--red); color: #fff; padding-block: clamp(2.5rem, 5vw, 4rem); }
.catbar__inner { position: relative; z-index: 1; }
.catbar__copy { max-width: 42ch; }
.catbar__copy p { font-family: var(--font-text); font-weight: 700; font-size: clamp(1.1rem, 1.5vw, 1.45rem); line-height: 1.35; color: #fff; margin: 0 0 1.5rem; }
.catbar__btn { background: #fff; color: var(--red); border-color: #fff; }
.catbar__btn:hover { background: rgba(255, 255, 255, .88); color: var(--red); }
.catbar__media { position: absolute; z-index: 0; top: 50%; right: -3%; transform: translateY(-50%); width: clamp(340px, 42vw, 660px); pointer-events: none; }
.catbar__media img { width: 100%; display: block; }

/* newsletter: mockup bleeding off the left, copy right */
.newsbar { position: relative; overflow: hidden; background: var(--surface); color: var(--ink-2); padding-block: clamp(2.5rem, 5vw, 4rem); }
.newsbar__inner { position: relative; z-index: 1; display: flex; justify-content: flex-end; }
.newsbar__copy { max-width: 44ch; }
.newsbar__copy .eyebrow { color: var(--red); }
.newsbar__copy h2 { color: var(--ink); font-size: clamp(2rem, 3.6vw, 3rem); }
.newsbar__copy p { color: var(--ink-2); font-size: 1.075rem; margin: 0 0 1.5rem; }
.newsbar__media { position: absolute; z-index: 0; top: 50%; left: -2%; transform: translateY(-50%); width: clamp(320px, 40vw, 580px); pointer-events: none; }
.newsbar__media img { width: 100%; display: block; }

@media (max-width: 820px) {
  .catbar__media, .newsbar__media { display: none; }
  .newsbar__inner { justify-content: center; }
}

/* ===== blog: uniform 4:5 sharp-edged tiles + pagination ================= */
.blog__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(1rem, 1.6vw, 1.5rem); }
.btile {
  display: flex; flex-direction: column; aspect-ratio: 4 / 5;
  background: #fff; border: 1px solid var(--line); border-radius: 0; overflow: hidden;
  text-decoration: none; color: var(--ink);
  transition: box-shadow var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.btile:hover { box-shadow: var(--shadow); transform: translateY(-3px); }
.btile__media { flex: 0 0 50%; overflow: hidden; }
.btile__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.btile__body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: .55rem; padding: clamp(.9rem, 1.2vw, 1.35rem); }
.btile__tag {
  align-self: flex-start; font-family: var(--font-display); font-weight: 700;
  font-size: .68rem; letter-spacing: .06em; text-transform: uppercase; color: #fff;
  background: var(--red); padding: .3rem .65rem; border-radius: 6px;
}
.btile__title {
  font-family: var(--font-display); font-weight: 800; color: var(--ink);
  font-size: clamp(.98rem, 1.05vw, 1.12rem); line-height: 1.18; letter-spacing: -.01em;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.btile__desc {
  font-size: .85rem; color: var(--ink-3); line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.blog__nav { display: flex; align-items: center; justify-content: center; gap: .75rem; margin-top: clamp(1.75rem, 3vw, 2.5rem); }
.blog__arrow {
  width: 40px; height: 40px; border: 1px solid var(--line); background: #fff; border-radius: 50%;
  cursor: pointer; display: grid; place-items: center; color: var(--ink); font-size: 1.3rem; line-height: 1;
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.blog__arrow:hover:not([disabled]) { border-color: var(--red); color: var(--red); }
.blog__arrow[disabled] { opacity: .35; cursor: default; }
.blog__pages { display: flex; gap: .25rem; }
.blog__page {
  min-width: 36px; height: 36px; border: 0; background: none; cursor: pointer;
  font-family: var(--font-text); font-weight: 600; font-size: .95rem; color: var(--ink-3); border-radius: 6px;
}
.blog__page:hover { color: var(--ink); }
.blog__page.is-active { color: var(--red); }
@media (max-width: 1080px) { .blog__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .blog__grid { grid-template-columns: 1fr; } .btile { aspect-ratio: auto; } }

/* ===== Laserglow Way — vertical timeline with cross-fading media ======== */
.tl { position: relative; background: #fff; }   /* white to match the image plates */
.tl__open {
  position: relative; overflow: hidden;
  min-height: calc(100vh - var(--header-h)); min-height: calc(100dvh - var(--header-h));
  display: flex; align-items: center; justify-content: center;
}
.tl__opencopy { position: relative; z-index: 1; width: 100%; text-align: center; display: flex; flex-direction: column; align-items: center; }
.tl__wm {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 0; pointer-events: none; user-select: none; text-align: center;
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  font-size: clamp(3.5rem, 15vw, 16rem); line-height: .82; letter-spacing: -.04em;
  color: rgba(16, 17, 20, .05);
}
.tl__grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; gap: clamp(1.5rem, 3vw, 3.5rem); }
/* left column: sticky, all 6 images stacked, cross-fading on opacity — big */
.tl__media {
  position: sticky; top: var(--header-h); align-self: start;
  height: calc(100vh - var(--header-h)); height: calc(100dvh - var(--header-h));
  display: grid; place-items: center;
}
.tl__media img {
  grid-area: 1 / 1; width: 100%; max-height: 88%; object-fit: contain;
  opacity: 0; transition: opacity .6s var(--ease);
}
.tl__media img.is-active { opacity: 1; }
/* right column: one full-height, centred block per step, with a dotted spine
   running above and below the text (so it "comes from below" the centred copy) */
.tl__steps { position: relative; }
.tl__step {
  position: relative;
  min-height: calc(100vh - var(--header-h)); min-height: calc(100dvh - var(--header-h));
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  text-align: center; padding-inline: clamp(1rem, 2vw, 2rem);
}
.tl__step::before, .tl__step::after {
  content: ''; position: absolute; left: 50%; transform: translateX(-50%); width: 4px;
  background-image: radial-gradient(circle, var(--line) 1.8px, transparent 2px);
  background-size: 4px 16px; background-repeat: repeat-y;
}
.tl__step::before { top: 0; height: calc(50% - 290px); }
.tl__step::after  { bottom: 0; height: calc(50% - 290px); }
.tl__step:first-child::before { display: none; }
.tl__step:last-child::after { display: none; }
.tl__icon { width: 54px; height: 54px; color: var(--ink); margin: 0 auto 1.6rem; }
.tl__icon svg { width: 100%; height: 100%; }
/* the 3 equalizer knobs bob up and down in a wave */
.tl__icon svg circle { transform-box: fill-box; transform-origin: center; animation: tlKnob 2.2s ease-in-out infinite; }
.tl__icon svg circle:nth-of-type(2) { animation-delay: .35s; }
.tl__icon svg circle:nth-of-type(3) { animation-delay: .7s; }
@keyframes tlKnob { 0%, 100% { transform: translateY(-3px); } 50% { transform: translateY(3px); } }
@media (prefers-reduced-motion: reduce) { .tl__icon svg circle { animation: none; } }
.tl__step h3 { font-size: clamp(2rem, 3.2vw, 3.1rem); text-transform: uppercase; line-height: 1.06; margin-bottom: 1.2rem; }
.tl__step p { color: var(--ink-2); font-size: clamp(1.1rem, 1.4vw, 1.4rem); max-width: 32ch; margin-inline: auto; }
@media (max-width: 860px) {
  .tl__grid { grid-template-columns: 1fr; gap: 0; }
  .tl__media { height: 42vh; top: var(--header-h); }
  .tl__media img { max-height: 92%; width: auto; max-width: 100%; }
  .tl__step { min-height: 62vh; }
  .tl__step::before, .tl__step::after { display: none; }
}

/* ===== one section per screen ==========================================
   On desktop, every section fills the viewport below the sticky header and
   snaps into place, so scrolling completes the current section and lands on
   the next — no two sections half-visible, no leftover blank bands. Scoped to
   larger screens so content-heavy sections aren't trapped on phones/tablets. */
@media (min-width: 1024px) and (min-height: 640px) {
  /* `scroll-snap-type: y proximity` used to be here. It pulled the page
     toward every section boundary mid-scroll, which reads as grabby and
     stepped on a wheel (user, 2026-08-27). `scroll-padding-top` stays:
     that only affects where an anchor link lands. */
  html { scroll-padding-top: var(--header-h); }
  .hero { scroll-snap-align: start; }
  .section {
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100dvh - var(--header-h));
    display: flex; flex-direction: column; justify-content: center;
    scroll-snap-align: start;
  }
  /* the horizontal-scroll timeline keeps its own inner scroll; don't stretch it */
  #the-laserglow-way { justify-content: center; }
}


/* ---- Laserglow icon kit (subset) ---------------------------------------- */
/* Copied verbatim from _Projects/2026/Laserglow Icons/laserglow-icons.css.
   Mask-based, so the icon takes its colour from `color`. Do not hand-edit:
   regenerate the kit with its build_icons.py and re-copy. */
.lgi {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
}
.lgi-ultra-visible { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M5%2014s3.6-5.5%209-5.5%209%205.5%209%205.5-3.6%205.5-9%205.5S5%2014%205%2014z'/%3E%3Ccircle%20cx='14'%20cy='14'%20r='2.6'%20fill='currentColor'%20stroke='none'/%3E%3Cpath%20d='M14%205.4V4%20M6.6%207.4%205.6%206.4%20M21.4%207.4l1-1'%20opacity='0.6'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M5%2014s3.6-5.5%209-5.5%209%205.5%209%205.5-3.6%205.5-9%205.5S5%2014%205%2014z'/%3E%3Ccircle%20cx='14'%20cy='14'%20r='2.6'%20fill='currentColor'%20stroke='none'/%3E%3Cpath%20d='M14%205.4V4%20M6.6%207.4%205.6%206.4%20M21.4%207.4l1-1'%20opacity='0.6'/%3E%3C/svg%3E"); }
.lgi-durable { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M14%204l7.5%202.8v5.7c0%204.8-3.3%207.6-7.5%208.7-4.2-1.1-7.5-3.9-7.5-8.7V6.8z'/%3E%3Cpolyline%20points='10.8,13.5%2013,15.7%2017.2,11'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M14%204l7.5%202.8v5.7c0%204.8-3.3%207.6-7.5%208.7-4.2-1.1-7.5-3.9-7.5-8.7V6.8z'/%3E%3Cpolyline%20points='10.8,13.5%2013,15.7%2017.2,11'/%3E%3C/svg%3E"); }
.lgi-cost-effective { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Ccircle%20cx='14'%20cy='14'%20r='8.2'/%3E%3Cline%20x1='14'%20y1='8.2'%20x2='14'%20y2='19.8'/%3E%3Cpath%20d='M16.6%2011c-0.9-1.4-4.6-1.3-4.6%200.9%200%202%204.6%201.3%204.6%203.4%200%202.2-3.7%202.3-4.6%200.8'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Ccircle%20cx='14'%20cy='14'%20r='8.2'/%3E%3Cline%20x1='14'%20y1='8.2'%20x2='14'%20y2='19.8'/%3E%3Cpath%20d='M16.6%2011c-0.9-1.4-4.6-1.3-4.6%200.9%200%202%204.6%201.3%204.6%203.4%200%202.2-3.7%202.3-4.6%200.8'/%3E%3C/svg%3E"); }
.lgi-smart-triggered { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M14%204l-5%2011h4l-1%209%206-12h-4z'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M14%204l-5%2011h4l-1%209%206-12h-4z'/%3E%3C/svg%3E"); }
.lgi-modular { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Crect%20x='5'%20y='5'%20width='7.5'%20height='7.5'%20rx='1.2'/%3E%3Crect%20x='15.5'%20y='5'%20width='7.5'%20height='7.5'%20rx='1.2'/%3E%3Crect%20x='5'%20y='15.5'%20width='7.5'%20height='7.5'%20rx='1.2'/%3E%3Crect%20x='15.5'%20y='15.5'%20width='7.5'%20height='7.5'%20rx='1.2'%20stroke-dasharray='2%202'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Crect%20x='5'%20y='5'%20width='7.5'%20height='7.5'%20rx='1.2'/%3E%3Crect%20x='15.5'%20y='5'%20width='7.5'%20height='7.5'%20rx='1.2'/%3E%3Crect%20x='5'%20y='15.5'%20width='7.5'%20height='7.5'%20rx='1.2'/%3E%3Crect%20x='15.5'%20y='15.5'%20width='7.5'%20height='7.5'%20rx='1.2'%20stroke-dasharray='2%202'/%3E%3C/svg%3E"); }
.lgi-custom-projections { -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M4%2011l7-3%207%203-7%203z'/%3E%3Cpath%20d='M19.5%207.5a8.5%208.5%200%200%201%200%209'%20opacity='0.55'/%3E%3Cpath%20d='M22.5%206a12%2012%200%200%201%200%2012'%20opacity='0.3'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2028%2028'%20fill='none'%20stroke='currentColor'%20stroke-width='1.75'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M4%2011l7-3%207%203-7%203z'/%3E%3Cpath%20d='M19.5%207.5a8.5%208.5%200%200%201%200%209'%20opacity='0.55'/%3E%3Cpath%20d='M22.5%206a12%2012%200%200%201%200%2012'%20opacity='0.3'/%3E%3C/svg%3E"); }


/* ---- solution-page sections (projected walkways pattern) ---------------- */

/* Red feature bar under the hero: six brand icons over two-line labels. */
.iconbar { background: var(--red); color: #fff; padding-block: clamp(1.6rem, 3vw, 2.4rem); }
.iconbar__grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: clamp(1rem, 2vw, 2rem); }
.iconbar__item { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: .8rem; text-align: center; }
/* Icons lifted verbatim from the live page and driven as masks, so they take
   their colour from `color` — white here, reusable on light elsewhere. A fixed
   HEIGHT (not width) is what keeps six different icon shapes on one baseline. */
.iconbar__icon { display: block; height: clamp(34px, 2.9vw, 46px); width: clamp(40px, 3.4vw, 56px);
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain; }
.iconbar__label {
  font-family: var(--font-display); font-weight: 800; color: #fff;
  font-size: clamp(.82rem, .5vw + .68rem, 1rem); line-height: 1.15; letter-spacing: -0.01em;
}
@media (max-width: 900px) { .iconbar__grid { grid-template-columns: repeat(3, 1fr); row-gap: 1.6rem; } }
@media (max-width: 520px) { .iconbar__grid { grid-template-columns: repeat(2, 1fr); } }

/* Copy left, media right, separated by a hairline rule down the middle. */
.duo { display: grid; grid-template-columns: 1fr 1.15fr; gap: clamp(2rem, 4.5vw, 4.5rem); align-items: center; }
.duo--ruled > * + * { border-left: 1px solid var(--line); padding-left: clamp(2rem, 4.5vw, 4.5rem); }
.duo__copy .eyebrow { color: var(--red); }
.duo__copy h2 { font-size: clamp(1.9rem, 3.4vw, 3rem); line-height: 1; letter-spacing: -0.02em; margin-bottom: .5em; }
.duo__rule { border: 0; border-top: 1px solid var(--line); margin: 0 0 1.5rem; }
.duo__copy p { color: var(--ink-2); }
@media (max-width: 900px) {
  .duo { grid-template-columns: 1fr; }
  .duo--ruled > * + * { border-left: 0; padding-left: 0; }
}

/* Sub-heading + dark pill chips for the industries list. */
.pillrow { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.1rem; }
.pill {
  background: var(--ink); color: #fff; border-radius: var(--r-pill);
  padding: .45rem 1rem; font-size: .8rem; font-weight: 500; white-space: nowrap;
  text-transform: none; letter-spacing: 0;
}
.duo__subhead { font-size: clamp(1.05rem, 1.5vw, 1.35rem); line-height: 1.2; margin: 2.2rem 0 0; letter-spacing: -0.01em; }

/* Photo mosaic: a tall lead image with smaller tiles packed around it. */
/* Justified photo gallery. Each figure's flex-grow is its true aspect ratio, so
   every row ends flush at a common height and every photo keeps its native
   shape — no cropping, no distortion. Rows break naturally by flex-wrap. */
.mosaic { display: flex; flex-direction: column; gap: clamp(.45rem, .8vw, .8rem); }
/* Two explicit rows of three, matching the live layout. Within a row each
   figure's flex-grow is its true aspect ratio, so the row ends flush at one
   height and every photo keeps its native shape — nothing is cropped. */
/* No height here on purpose: with flex-basis:0 and flex-grow set to each
   photo aspect ratio, every figure resolves to width = rowWidth*a/sum(a), so
   height = width/a = rowWidth/sum(a) — identical for the whole row. The row
   self-levels and the images are never cropped or stretched. */
.mosaic__row { display: flex; gap: clamp(.45rem, .8vw, .8rem); align-items: flex-start; }
.mosaic figure { margin: 0; min-width: 0; flex-basis: 0; }
.mosaic img { width: 100%; height: auto; border-radius: var(--r-sm); display: block; }

/* Video on the left, copy on the right. */
.videosplit { display: grid; grid-template-columns: 55fr 45fr; gap: clamp(2rem, 4vw, 4rem); align-items: center; }
.videosplit__media { border-radius: var(--r); overflow: hidden; background: var(--dark); aspect-ratio: 16/9; }
.videosplit__media video, .videosplit__media iframe { width: 100%; height: 100%; display: block; border: 0; object-fit: cover; }
@media (max-width: 900px) { .videosplit { grid-template-columns: 1fr; } }

/* Use-case carousel: horizontal scroll-snap strip with round red arrows. */
.usecases { display: grid; grid-template-columns: 1fr 2fr; gap: clamp(2rem, 4vw, 4rem); align-items: center; }
.uccarousel { position: relative; min-width: 0; }
.uccarousel__track {
  display: grid; grid-auto-flow: column; grid-auto-columns: calc((100% - 2 * .5rem) / 2.35);
  gap: .5rem;
  overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth;
  scrollbar-width: none; padding-bottom: .25rem;
}
.uccarousel__track::-webkit-scrollbar { display: none; }
.uctile { position: relative; scroll-snap-align: start; border-radius: var(--r); overflow: hidden; aspect-ratio: 3/4; }
.uctile img { transition: transform 420ms var(--ease); }
.uctile:hover img, .uctile:focus-within img { transform: scale(1.06); }
@media (prefers-reduced-motion: reduce) { .uctile img { transition: none; } .uctile:hover img { transform: none; } }
.uctile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.uctile__label {
  position: absolute; top: 0; left: 0; right: 0; padding: 1rem 1.1rem;
  color: #fff; font-family: var(--font-display); font-weight: 700; font-size: clamp(.85rem, .9vw, 1rem);
  background: linear-gradient(180deg, rgba(0,0,0,.55), rgba(0,0,0,0));
}
.ucnav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 34px; height: 34px; border-radius: 50%; border: 0; cursor: pointer;
  background: var(--red); color: #fff; display: grid; place-items: center;
  box-shadow: var(--shadow);
}
.ucnav[hidden] { display: none; }
.ucnav--prev { left: -17px; }
.ucnav--next { right: -17px; }
.ucnav svg { width: 16px; height: 16px; }
@media (max-width: 900px) {
  .usecases { grid-template-columns: 1fr; }
  .uccarousel__track { grid-auto-columns: 78%; }
}

/* Estimator band: full-bleed still, copy anchored left, darkened from the
   left so white type stays legible over the brighter right-hand side. */
.estband { position: relative; overflow: hidden; background: var(--dark); color: var(--on-dark);
  min-height: min(58vh, 560px); display: flex; align-items: center; }
.estband__media { position: absolute; inset: 0; z-index: 0; }
.estband__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }
.estband__media::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(10,10,12,.92) 0%, rgba(10,10,12,.78) 38%, rgba(10,10,12,.35) 70%, rgba(10,10,12,.18) 100%); }
.estband__inner { position: relative; z-index: 1; padding-block: clamp(3rem, 6vw, 5rem); }
.estband .eyebrow { color: var(--red); }
/* the explicit <br> sets the two lines; max-width must not force extra wraps */
.estband h2 { color: #fff; font-size: clamp(1.5rem, 2.6vw, 2.5rem); line-height: 1.05; max-width: none; }
.estband__inner .btn { margin-top: 1.75rem; }
.btn__play { display: inline-grid; place-items: center; width: 1em; height: 1em; margin-left: .1rem; }
.btn__play svg { width: .8em; height: .8em; }

/* All-products comparison columns. */
.prodgrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: clamp(2rem, 5vw, 5rem); }
.prodcol { text-align: center; }
/* The shot sits in its own fixed block with the image contained inside it.
   height on the wrapper + max-height on the image (not the other way round)
   is what stops tall product photos spilling over the copy beneath. */
.prodcol__shot { height: clamp(260px, 30vw, 420px); display: flex; align-items: center; justify-content: center; margin-bottom: 2rem; overflow: hidden; }
.prodcol__shot img { max-height: 100%; max-width: 100%; width: auto; height: auto; object-fit: contain; }
/* product names keep their real casing (SafetyCast 300, VirtuaLine FILL);
   the global heading rule uppercases h1-h3, so opt out here */
.prodcol__name { color: var(--red); font-size: clamp(1.05rem, 1.5vw, 1.3rem); margin-bottom: .6rem; text-transform: none; letter-spacing: -0.01em; }
.prodcol__lede { color: var(--ink-2); font-size: .98rem; max-width: 34ch; margin-inline: auto; }
.prodcol__specs, .prodcol__feats { border-top: 1px solid var(--line); margin-top: 1.5rem; padding-top: 1.25rem; list-style: none; }
.prodcol__specs li { font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-2); margin-bottom: .5rem; }
.prodcol__swatch { margin: 1.25rem 0; }
.prodcol__swatch img { width: 100%; border-radius: var(--r-sm); }
.prodcol__feats li { display: flex; align-items: center; justify-content: center; gap: .6rem; font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-2); margin-bottom: .55rem; }
.prodcol__feats svg { width: 15px; height: 15px; color: var(--ok); flex: none; }
.prodcol__link { display: inline-flex; align-items: center; gap: .5rem; margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--line); width: 100%; justify-content: center; color: var(--red); font-weight: 600; }
.head--rule { text-align: center; margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.head--rule h2 { display: inline-block; border-bottom: 2px solid var(--ink); padding-bottom: .5rem; font-size: clamp(1.1rem, 1.6vw, 1.4rem); letter-spacing: .02em; }
@media (max-width: 760px) { .prodgrid { grid-template-columns: 1fr; } }


/* ---- reference reconciliation (projected walkways) ---------------------- */

/* Product name marks: SafetyCast is customisable so it carries the custom
   projection icon; FILL projects a solid band so it carries a solid block. */
.prodcol__name { display: inline-flex; align-items: center; gap: .5rem; }
.prodcol__mark { display: inline-block; width: 1.1em; height: 1.1em; background-color: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain; flex: none; }
.prodcol__mark--solid { width: .95em; height: .62em; border-radius: 2px; -webkit-mask-image: none; mask-image: none; }

/* circled arrow on See Product, matching the live link treatment */
.linkarrow { width: 1.15em; height: 1.15em; flex: none; }

/* the mosaic sits in a positioned wrapper so its round control can hang off
   the right edge the way the live page's does */
.mosaicwrap { position: relative; min-width: 0; }
.mosaic__next { right: -17px; }

/* Copy column: the live page runs body copy the full width of the column and
   keeps the industry pills on a single row. */
.duo { grid-template-columns: 1fr 1.05fr; }
.duo__copy p { max-width: none; }
.pillrow { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
.pillrow::-webkit-scrollbar { display: none; }
.pill { padding: .42rem .85rem; font-size: clamp(.68rem, .55vw + .5rem, .8rem); }
@media (max-width: 900px) { .pillrow { flex-wrap: wrap; } }

/* Back to top — present on every page of the live site. */
.totop { position: fixed; right: clamp(1rem, 2vw, 2rem); bottom: clamp(1rem, 2vw, 2rem);
  width: 46px; height: 46px; border-radius: 50%; border: 0; cursor: pointer; z-index: 300;
  background: var(--red); color: #fff; display: grid; place-items: center;
  box-shadow: var(--shadow); opacity: 0; visibility: hidden;
  transition: opacity var(--t) var(--ease), visibility var(--t) var(--ease); }
.totop svg { width: 20px; height: 20px; }
.totop.is-on { opacity: 1; visibility: visible; }
.totop:hover { background: var(--red-deep); }
@media (prefers-reduced-motion: reduce) { .totop { transition: none; } }


/* ---- product page template --------------------------------------------- */

/* Product hero: spec-led copy on the left, product shot on the right. Light,
   like the solution hero, but the copy carries a badge, a spec list and two
   CTAs rather than a single lede. */
.prodhero { background: var(--page); color: var(--ink); padding-block: clamp(2.5rem, 5vw, 4.5rem); }
.prodhero__grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 5rem); align-items: center; }
.prodhero .eyebrow { color: var(--red); }
.prodhero h1 {
  color: var(--ink); font-size: clamp(2.1rem, 4.2vw, 3.9rem);
  line-height: .96; letter-spacing: -0.02em; margin-bottom: .5em;
}
.prodhero__badge {
  display: inline-block; background: var(--ink); color: #fff; border-radius: var(--r-sm);
  padding: .5rem 1rem; font-size: .88rem; font-weight: 600; margin-bottom: 1.5rem;
}
.prodhero__rule { border: 0; border-top: 1px solid var(--line); margin: 0 0 1.5rem; }
.prodhero__copy > p { color: var(--ink-2); }
.prodhero__specs-title { font-weight: 700; color: var(--ink); margin: 1.5rem 0 .6rem; font-size: 1rem; }
.prodhero__specs { list-style: none; margin: 0 0 1.75rem; }
/* weight 500, not the inherited 400. The key specs are DELIBERATELY a size down
   from the description, but at 15px Madefor Text's 400 strokes thin out and the
   list reads lighter than the copy above it rather than merely smaller. Half a
   step up puts the stroke back on par; the size difference still does the
   hierarchy. Same reasoning on .herospecs li. */
.prodhero__specs li { color: var(--ink-2); font-size: .96rem; font-weight: 500; margin-bottom: .35rem; padding-left: 1rem; position: relative; }
.prodhero__specs li::before { content: "•"; position: absolute; left: 0; color: var(--ink-2); }
.prodhero__segments { margin-top: 2.25rem; }
.prodhero__media { display: flex; align-items: center; justify-content: center; min-width: 0; }
.prodhero__media img { width: 100%; height: auto; max-height: 30rem; object-fit: contain; display: block; }
@media (max-width: 900px) { .prodhero__grid { grid-template-columns: 1fr; } }

/* Documentation: one card per downloadable asset. */
.doccards { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(.75rem, 1.5vw, 1.25rem); align-items: start; }
.doccard { text-align: center; }
/* fixed shot height so all three titles and buttons sit on one baseline */
.doccard__shot { height: clamp(90px, 9vw, 130px); display: flex; align-items: center; justify-content: center;
  overflow: hidden; margin-bottom: 1rem; }
.doccard__shot img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; display: block; }
.doccard h3 { font-size: .92rem; letter-spacing: .03em; margin-bottom: .9rem; }
.doccard .btn { white-space: nowrap; font-size: .78rem; padding-inline: .9rem; }
@media (max-width: 760px) { .doccards { grid-template-columns: 1fr; } }

/* Accessories: photo beside its description and spec chips. */
.accgrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: clamp(2rem, 4vw, 4rem); }
/* Live layout: shot on top, red name, centred copy beneath. */
.acccard { text-align: center; }
.acccard__shot { display: flex; align-items: center; justify-content: center;
  height: clamp(200px, 22vw, 320px); margin-bottom: 1.5rem; overflow: hidden; }
.acccard__shot img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; display: block; }
.acccard h3 { color: var(--red); font-size: clamp(1rem, 1.3vw, 1.2rem); text-transform: none;
  letter-spacing: -.01em; margin-bottom: .6rem; }
.acccard p { color: var(--ink-2); font-size: .94rem; margin: 0 auto; max-width: 38ch; }
@media (max-width: 700px) { .accgrid { grid-template-columns: 1fr; } }

/* BEGIN generated icon masks — tools/sync_icons.py. Do not hand-edit. */
.icon--adjustable-line-length { -webkit-mask-image: url('/assets/icons/adjustable-line-length.svg'); mask-image: url('/assets/icons/adjustable-line-length.svg'); }
.icon--adjustable-line-length-2 { -webkit-mask-image: url('/assets/icons/adjustable-line-length-2.svg'); mask-image: url('/assets/icons/adjustable-line-length-2.svg'); }
.icon--adjustable-mounting-bracket { -webkit-mask-image: url('/assets/icons/adjustable-mounting-bracket.svg'); mask-image: url('/assets/icons/adjustable-mounting-bracket.svg'); }
.icon--built-for-harsh-environments { -webkit-mask-image: url('/assets/icons/built-for-harsh-environments.svg'); mask-image: url('/assets/icons/built-for-harsh-environments.svg'); }
.icon--built-in-real-time-ai-model { -webkit-mask-image: url('/assets/icons/built-in-real-time-ai-model.svg'); mask-image: url('/assets/icons/built-in-real-time-ai-model.svg'); }
.icon--chevron-down-circle { -webkit-mask-image: url('/assets/icons/chevron-down-circle.svg'); mask-image: url('/assets/icons/chevron-down-circle.svg'); }
.icon--configurable-warning-detection-zones { -webkit-mask-image: url('/assets/icons/configurable-warning-detection-zones.svg'); mask-image: url('/assets/icons/configurable-warning-detection-zones.svg'); }
.icon--cost-effective { -webkit-mask-image: url('/assets/icons/cost-effective.svg'); mask-image: url('/assets/icons/cost-effective.svg'); }
.icon--custom-projection { -webkit-mask-image: url('/assets/icons/custom-projection.svg'); mask-image: url('/assets/icons/custom-projection.svg'); }
.icon--easily-interchangeable { -webkit-mask-image: url('/assets/icons/easily-interchangeable.svg'); mask-image: url('/assets/icons/easily-interchangeable.svg'); }
.icon--easy-to-install { -webkit-mask-image: url('/assets/icons/easy-to-install.svg'); mask-image: url('/assets/icons/easy-to-install.svg'); }
.icon--easy-to-install-2 { -webkit-mask-image: url('/assets/icons/easy-to-install-2.svg'); mask-image: url('/assets/icons/easy-to-install-2.svg'); }
.icon--easy-to-install-3 { -webkit-mask-image: url('/assets/icons/easy-to-install-3.svg'); mask-image: url('/assets/icons/easy-to-install-3.svg'); }
.icon--easy-to-install-4 { -webkit-mask-image: url('/assets/icons/easy-to-install-4.svg'); mask-image: url('/assets/icons/easy-to-install-4.svg'); }
.icon--easy-to-install-5 { -webkit-mask-image: url('/assets/icons/easy-to-install-5.svg'); mask-image: url('/assets/icons/easy-to-install-5.svg'); }
.icon--focus-adjustable { -webkit-mask-image: url('/assets/icons/focus-adjustable.svg'); mask-image: url('/assets/icons/focus-adjustable.svg'); }
.icon--forkcast-forklift-arc { -webkit-mask-image: url('/assets/icons/forkcast-forklift-arc.svg'); mask-image: url('/assets/icons/forkcast-forklift-arc.svg'); }
.icon--forkcast-forklift-arrow { -webkit-mask-image: url('/assets/icons/forkcast-forklift-arrow.svg'); mask-image: url('/assets/icons/forkcast-forklift-arrow.svg'); }
.icon--forkcast-forklift-line { -webkit-mask-image: url('/assets/icons/forkcast-forklift-line.svg'); mask-image: url('/assets/icons/forkcast-forklift-line.svg'); }
.icon--forkcast-forklift-moving-line-of-dots { -webkit-mask-image: url('/assets/icons/forkcast-forklift-moving-line-of-dots.svg'); mask-image: url('/assets/icons/forkcast-forklift-moving-line-of-dots.svg'); }
.icon--forkcast-forklift-spot { -webkit-mask-image: url('/assets/icons/forkcast-forklift-spot.svg'); mask-image: url('/assets/icons/forkcast-forklift-spot.svg'); }
.icon--forkcast-forklift-stop { -webkit-mask-image: url('/assets/icons/forkcast-forklift-stop.svg'); mask-image: url('/assets/icons/forkcast-forklift-stop.svg'); }
.icon--full-picture-view-around-forklifts { -webkit-mask-image: url('/assets/icons/full-picture-view-around-forklifts.svg'); mask-image: url('/assets/icons/full-picture-view-around-forklifts.svg'); }
.icon--high-visibility-illuminated-projections { -webkit-mask-image: url('/assets/icons/high-visibility-illuminated-projections.svg'); mask-image: url('/assets/icons/high-visibility-illuminated-projections.svg'); }
.icon--highly-durable { -webkit-mask-image: url('/assets/icons/highly-durable.svg'); mask-image: url('/assets/icons/highly-durable.svg'); }
.icon--highly-visible-projection { -webkit-mask-image: url('/assets/icons/highly-visible-projection.svg'); mask-image: url('/assets/icons/highly-visible-projection.svg'); }
.icon--highly-visible-projection-2 { -webkit-mask-image: url('/assets/icons/highly-visible-projection-2.svg'); mask-image: url('/assets/icons/highly-visible-projection-2.svg'); }
.icon--highly-visible-projection-3 { -webkit-mask-image: url('/assets/icons/highly-visible-projection-3.svg'); mask-image: url('/assets/icons/highly-visible-projection-3.svg'); }
.icon--highly-visible-projection-4 { -webkit-mask-image: url('/assets/icons/highly-visible-projection-4.svg'); mask-image: url('/assets/icons/highly-visible-projection-4.svg'); }
.icon--integrates-with-existing-systems { -webkit-mask-image: url('/assets/icons/integrates-with-existing-systems.svg'); mask-image: url('/assets/icons/integrates-with-existing-systems.svg'); }
.icon--long-operating-life { -webkit-mask-image: url('/assets/icons/long-operating-life.svg'); mask-image: url('/assets/icons/long-operating-life.svg'); }
.icon--long-operating-life-2 { -webkit-mask-image: url('/assets/icons/long-operating-life-2.svg'); mask-image: url('/assets/icons/long-operating-life-2.svg'); }
.icon--long-operating-life-3 { -webkit-mask-image: url('/assets/icons/long-operating-life-3.svg'); mask-image: url('/assets/icons/long-operating-life-3.svg'); }
.icon--menu-alignment-solutions { -webkit-mask-image: url('/assets/icons/menu-alignment-solutions.svg'); mask-image: url('/assets/icons/menu-alignment-solutions.svg'); }
.icon--menu-blog { -webkit-mask-image: url('/assets/icons/menu-blog.svg'); mask-image: url('/assets/icons/menu-blog.svg'); }
.icon--menu-careers { -webkit-mask-image: url('/assets/icons/menu-careers.svg'); mask-image: url('/assets/icons/menu-careers.svg'); }
.icon--menu-contact-us { -webkit-mask-image: url('/assets/icons/menu-contact-us.svg'); mask-image: url('/assets/icons/menu-contact-us.svg'); }
.icon--menu-crane-lights { -webkit-mask-image: url('/assets/icons/menu-crane-lights.svg'); mask-image: url('/assets/icons/menu-crane-lights.svg'); }
.icon--menu-distributor-list { -webkit-mask-image: url('/assets/icons/menu-distributor-list.svg'); mask-image: url('/assets/icons/menu-distributor-list.svg'); }
.icon--menu-estimator-tool { -webkit-mask-image: url('/assets/icons/menu-estimator-tool.svg'); mask-image: url('/assets/icons/menu-estimator-tool.svg'); }
.icon--menu-manuals { -webkit-mask-image: url('/assets/icons/menu-manuals.svg'); mask-image: url('/assets/icons/menu-manuals.svg'); }
.icon--menu-mhe-lights { -webkit-mask-image: url('/assets/icons/menu-mhe-lights.svg'); mask-image: url('/assets/icons/menu-mhe-lights.svg'); }
.icon--menu-privacy-policy { -webkit-mask-image: url('/assets/icons/menu-privacy-policy.svg'); mask-image: url('/assets/icons/menu-privacy-policy.svg'); }
.icon--menu-projected-lines { -webkit-mask-image: url('/assets/icons/menu-projected-lines.svg'); mask-image: url('/assets/icons/menu-projected-lines.svg'); }
.icon--menu-projected-signs { -webkit-mask-image: url('/assets/icons/menu-projected-signs.svg'); mask-image: url('/assets/icons/menu-projected-signs.svg'); }
.icon--menu-projected-walkways { -webkit-mask-image: url('/assets/icons/menu-projected-walkways.svg'); mask-image: url('/assets/icons/menu-projected-walkways.svg'); }
.icon--menu-spec-sheets { -webkit-mask-image: url('/assets/icons/menu-spec-sheets.svg'); mask-image: url('/assets/icons/menu-spec-sheets.svg'); }
.icon--menu-uwb-tag-system { -webkit-mask-image: url('/assets/icons/menu-uwb-tag-system.svg'); mask-image: url('/assets/icons/menu-uwb-tag-system.svg'); }
.icon--menu-visual-a-i-system { -webkit-mask-image: url('/assets/icons/menu-visual-a-i-system.svg'); mask-image: url('/assets/icons/menu-visual-a-i-system.svg'); }
.icon--menu-warranty-service-and { -webkit-mask-image: url('/assets/icons/menu-warranty-service-and.svg'); mask-image: url('/assets/icons/menu-warranty-service-and.svg'); }
.icon--minimal-loss-in-productivity { -webkit-mask-image: url('/assets/icons/minimal-loss-in-productivity.svg'); mask-image: url('/assets/icons/minimal-loss-in-productivity.svg'); }
.icon--minimal-loss-in-productivity-2 { -webkit-mask-image: url('/assets/icons/minimal-loss-in-productivity-2.svg'); mask-image: url('/assets/icons/minimal-loss-in-productivity-2.svg'); }
.icon--minimal-loss-in-productivity-3 { -webkit-mask-image: url('/assets/icons/minimal-loss-in-productivity-3.svg'); mask-image: url('/assets/icons/minimal-loss-in-productivity-3.svg'); }
.icon--minimal-loss-in-productivity-4 { -webkit-mask-image: url('/assets/icons/minimal-loss-in-productivity-4.svg'); mask-image: url('/assets/icons/minimal-loss-in-productivity-4.svg'); }
.icon--modular-design { -webkit-mask-image: url('/assets/icons/modular-design.svg'); mask-image: url('/assets/icons/modular-design.svg'); }
.icon--multiple-detection-zones { -webkit-mask-image: url('/assets/icons/multiple-detection-zones.svg'); mask-image: url('/assets/icons/multiple-detection-zones.svg'); }
.icon--no-surface-preparation { -webkit-mask-image: url('/assets/icons/no-surface-preparation.svg'); mask-image: url('/assets/icons/no-surface-preparation.svg'); }
.icon--no-surface-preparation-2 { -webkit-mask-image: url('/assets/icons/no-surface-preparation-2.svg'); mask-image: url('/assets/icons/no-surface-preparation-2.svg'); }
.icon--oem-independent { -webkit-mask-image: url('/assets/icons/oem-independent.svg'); mask-image: url('/assets/icons/oem-independent.svg'); }
.icon--oem-independent-2 { -webkit-mask-image: url('/assets/icons/oem-independent-2.svg'); mask-image: url('/assets/icons/oem-independent-2.svg'); }
.icon--oem-independent-3 { -webkit-mask-image: url('/assets/icons/oem-independent-3.svg'); mask-image: url('/assets/icons/oem-independent-3.svg'); }
.icon--overcome-complacency { -webkit-mask-image: url('/assets/icons/overcome-complacency.svg'); mask-image: url('/assets/icons/overcome-complacency.svg'); }
.icon--reduce-incidents-of-human-error-injury { -webkit-mask-image: url('/assets/icons/reduce-incidents-of-human-error-injury.svg'); mask-image: url('/assets/icons/reduce-incidents-of-human-error-injury.svg'); }
.icon--reduce-incidents-of-human-error-injury-2 { -webkit-mask-image: url('/assets/icons/reduce-incidents-of-human-error-injury-2.svg'); mask-image: url('/assets/icons/reduce-incidents-of-human-error-injury-2.svg'); }
.icon--reduce-incidents-of-human-error-injury-3 { -webkit-mask-image: url('/assets/icons/reduce-incidents-of-human-error-injury-3.svg'); mask-image: url('/assets/icons/reduce-incidents-of-human-error-injury-3.svg'); }
.icon--reduce-incidents-of-human-error-injury-4 { -webkit-mask-image: url('/assets/icons/reduce-incidents-of-human-error-injury-4.svg'); mask-image: url('/assets/icons/reduce-incidents-of-human-error-injury-4.svg'); }
.icon--reduce-insurance-claims { -webkit-mask-image: url('/assets/icons/reduce-insurance-claims.svg'); mask-image: url('/assets/icons/reduce-insurance-claims.svg'); }
.icon--reduce-lost-time { -webkit-mask-image: url('/assets/icons/reduce-lost-time.svg'); mask-image: url('/assets/icons/reduce-lost-time.svg'); }
.icon--reduces-product-damage { -webkit-mask-image: url('/assets/icons/reduces-product-damage.svg'); mask-image: url('/assets/icons/reduces-product-damage.svg'); }
.icon--safety-booster { -webkit-mask-image: url('/assets/icons/safety-booster.svg'); mask-image: url('/assets/icons/safety-booster.svg'); }
.icon--safety-booster-2 { -webkit-mask-image: url('/assets/icons/safety-booster-2.svg'); mask-image: url('/assets/icons/safety-booster-2.svg'); }
.icon--set-up-a-meeting { -webkit-mask-image: url('/assets/icons/set-up-a-meeting.svg'); mask-image: url('/assets/icons/set-up-a-meeting.svg'); }
.icon--skill-booster { -webkit-mask-image: url('/assets/icons/skill-booster.svg'); mask-image: url('/assets/icons/skill-booster.svg'); }
.icon--smart-triggered { -webkit-mask-image: url('/assets/icons/smart-triggered.svg'); mask-image: url('/assets/icons/smart-triggered.svg'); }
.icon--smart-triggered-projection { -webkit-mask-image: url('/assets/icons/smart-triggered-projection.svg'); mask-image: url('/assets/icons/smart-triggered-projection.svg'); }
.icon--smart-triggered-projection-2 { -webkit-mask-image: url('/assets/icons/smart-triggered-projection-2.svg'); mask-image: url('/assets/icons/smart-triggered-projection-2.svg'); }
.icon--smart-triggered-projection-3 { -webkit-mask-image: url('/assets/icons/smart-triggered-projection-3.svg'); mask-image: url('/assets/icons/smart-triggered-projection-3.svg'); }
.icon--smart-triggered-projection-4 { -webkit-mask-image: url('/assets/icons/smart-triggered-projection-4.svg'); mask-image: url('/assets/icons/smart-triggered-projection-4.svg'); }
.icon--tagless-detection { -webkit-mask-image: url('/assets/icons/tagless-detection.svg'); mask-image: url('/assets/icons/tagless-detection.svg'); }
.icon--training-tools { -webkit-mask-image: url('/assets/icons/training-tools.svg'); mask-image: url('/assets/icons/training-tools.svg'); }
.icon--ultra-visible { -webkit-mask-image: url('/assets/icons/ultra-visible.svg'); mask-image: url('/assets/icons/ultra-visible.svg'); }
.icon--visual-cue { -webkit-mask-image: url('/assets/icons/visual-cue.svg'); mask-image: url('/assets/icons/visual-cue.svg'); }
.icon--visual-cues { -webkit-mask-image: url('/assets/icons/visual-cues.svg'); mask-image: url('/assets/icons/visual-cues.svg'); }
.icon--vl-line-thick { -webkit-mask-image: url('/assets/icons/vl-line-thick.svg'); mask-image: url('/assets/icons/vl-line-thick.svg'); }
.icon--vl-line-thin { -webkit-mask-image: url('/assets/icons/vl-line-thin.svg'); mask-image: url('/assets/icons/vl-line-thin.svg'); }
.icon--vl-switch { -webkit-mask-image: url('/assets/icons/vl-switch.svg'); mask-image: url('/assets/icons/vl-switch.svg'); }
.icon--vl-tag-new { -webkit-mask-image: url('/assets/icons/vl-tag-new.svg'); mask-image: url('/assets/icons/vl-tag-new.svg'); }
/* END generated icon masks */
/* Product hero media is a carousel on the live site: one shot at a time, arrows
   either side. Same scroll-snap engine as the use-case strip. */
.prodcarousel { width: 100%; }
.prodcarousel .uccarousel__track { grid-auto-columns: 100%; }
.prodshot { margin: 0; scroll-snap-align: start; display: flex; align-items: center;
  justify-content: center; background: var(--page); border-radius: var(--r); overflow: hidden; }
.prodshot img { width: 100%; height: auto; max-height: 30rem; object-fit: contain; display: block; }
.prodcarousel .ucnav { background: #fff; color: var(--ink); box-shadow: var(--shadow); width: 40px; height: 40px; }
.prodcarousel .ucnav:hover { background: var(--surface); }
.prodcarousel .ucnav--prev { left: 10px; }
.prodcarousel .ucnav--next { right: 10px; }


/* Even split for the video + documentation pair, and a reversed video split
   (copy left, video right) for the installation guide. */
.duo--even { grid-template-columns: 1fr 1fr; align-items: start; }
.videosplit--rev { grid-template-columns: 45fr 55fr; }
.videosplit--rev .duo__copy { order: 0; }


/* ---- final pass: full-height sections, button motion, sizing ------------ */

/* 1. A main section owns the screen. One at a time, nothing peeking. Banner
   strips (.iconbar, .catbar) are deliberately excluded — they are dividers,
   not sections. :has() lets this key off what a section actually contains. */
.prodhero,
.section:has(> .wrap > .duo),
.section:has(> .wrap > .usecases),
.section:has(> .wrap > .videosplit),
.section:has(> .wrap > .prodgrid),
.section:has(> .wrap > .accgrid) {
  min-height: 100vh;
  display: flex;
  align-items: center;
}
.prodhero > .wrap,
.section:has(> .wrap > .duo) > .wrap,
.section:has(> .wrap > .usecases) > .wrap,
.section:has(> .wrap > .videosplit) > .wrap,
.section:has(> .wrap > .prodgrid) > .wrap,
.section:has(> .wrap > .accgrid) > .wrap { width: min(var(--wrap), 100% - var(--gutter) * 2); }
.hero--split { min-height: 100vh; }
/* 6. The estimator band sits at 70% — a break in the rhythm, not a full stop. */
.estband { min-height: 70vh; }

/* 7. Hairline rules between sections. The live page uses them to give the big
   empty stretches an edge, which is what stops a sparse section reading as a
   mistake rather than as breathing room. */
.section:has(> .wrap > .duo),
.section:has(> .wrap > .usecases),
.section:has(> .wrap > .videosplit),
.section:has(> .wrap > .prodgrid),
.section:has(> .wrap > .accgrid) { border-top: 1px solid var(--line); }
.prodhero { border-bottom: 1px solid var(--line); }

/* 3. Hero CTAs stack, primary on top. */
.prodhero .btn-row, .hero--split .btn-row { flex-direction: column; align-items: flex-start; gap: .7rem; }
.prodhero .btn-row .btn, .hero--split .btn-row .btn { min-width: 15rem; justify-content: center; }

/* 4. Hero carousel shots are 2:3 portrait, not square. */
.prodshot { aspect-ratio: 2 / 3; }
.prodshot img { width: 100%; height: 100%; max-height: none; object-fit: contain; }

/* 5. Buttons answer the cursor: the arrow turns, the button swings from a pivot
   above it like a hanging sign, and a solid red button inverts to white so the
   label stays legible. */
.btn { transform-origin: 50% -180%; transition: background-color var(--t) var(--ease),
  color var(--t) var(--ease), border-color var(--t) var(--ease), box-shadow var(--t) var(--ease); }
.btn .btn__arrow, .btn .btn__play, .btn .linkarrow { display: inline-flex;
  transition: transform 520ms var(--ease); }
.btn:hover .btn__arrow, .btn:hover .btn__play, .btn:hover .linkarrow,
.btn:focus-visible .btn__arrow, .btn:focus-visible .btn__play { transform: rotate(360deg); }
.btn:hover, .btn:focus-visible { animation: btn-swing 1.15s var(--ease); }
@keyframes btn-swing {
  0%   { transform: rotate(0deg); }
  22%  { transform: rotate(1.7deg); }
  45%  { transform: rotate(-1.1deg); }
  68%  { transform: rotate(.6deg); }
  86%  { transform: rotate(-.25deg); }
  100% { transform: rotate(0deg); }
}
.btn--primary:hover, .btn--primary:focus-visible {
  background: #fff; color: var(--red); border-color: var(--red);
}
.catbar__btn:hover, .catbar__btn:focus-visible { background: var(--ink); color: #fff; border-color: var(--ink); }
.btn--ghost:hover, .btn--ghost:focus-visible { background: var(--red); color: #fff; border-color: var(--red); }
.prodcol__link .linkarrow { transition: transform 520ms var(--ease); }
.prodcol__link:hover .linkarrow { transform: rotate(360deg); }
@media (prefers-reduced-motion: reduce) {
  .btn:hover, .btn:focus-visible { animation: none; }
  .btn .btn__arrow, .btn .btn__play, .btn .linkarrow { transition: none; }
  .btn:hover .btn__arrow, .btn:hover .btn__play, .btn:hover .linkarrow { transform: none; }
}

/* 8. Use-case tiles match the hero shots in size, packed tighter. */
.usecases { grid-template-columns: 1fr 2.35fr; }
.uccarousel__track { grid-auto-columns: calc((100% - 2 * .4rem) / 2.6); gap: .4rem; }
.uctile { aspect-ratio: 2 / 3; }

/* 9. Body copy widens with the viewport, so a short paragraph is about two
   lines on a large monitor and reflows to more lines on a small one, rather
   than being locked to a narrow measure at every size. */
.acccard p, .catbar__copy p, .prodcol__lede {
  max-width: clamp(28ch, 42vw, 72ch);
}
.catbar__copy { max-width: clamp(30ch, 46vw, 78ch); }

/* The hero must actually FIT the screen it fills — a section that is 100vh tall
   but 1100px of content still lets the next one peek. Vertical rhythm is tuned
   down here so the whole hero lands inside one viewport at common heights. */
.prodhero { padding-block: clamp(1.5rem, 3vh, 2.75rem); }
.prodhero h1 { font-size: clamp(1.9rem, 3.4vw, 3.2rem); margin-bottom: .35em; }
.prodhero__badge { margin-bottom: 1rem; padding: .42rem .85rem; font-size: .82rem; }
.prodhero__rule { margin-bottom: 1rem; }
.prodhero__copy > p { font-size: clamp(.92rem, .45vw + .8rem, 1.02rem); line-height: 1.5; }
.prodhero__specs-title { margin: 1rem 0 .45rem; font-size: .95rem; }
.prodhero__specs { margin-bottom: 1.15rem; }
.prodhero__specs li { font-size: .9rem; margin-bottom: .22rem; }
.prodhero .btn-row { margin-top: 1.1rem; }
.prodhero__segments { margin-top: 1.35rem; }
.prodhero__media { max-height: 78vh; }
.prodshot { max-height: 78vh; }

/* Same discipline for the light solution hero. */
.hero--split .hero__inner { padding-block: clamp(2rem, 5vh, 4rem); }

/* The video + documentation pair ran 65px over one screen. Cap the video and
   tighten the card stack so it lands inside the viewport like its neighbours. */
.duo--even .videosplit__media { max-height: 46vh; }
.duo--even .videosplit__media video { height: 100%; }
.duo--even .doccards { margin-top: 1.5rem !important; }
.duo--even .duo__copy h2 { font-size: clamp(1.6rem, 2.7vw, 2.4rem); }
.duo--even .duo__rule { margin-bottom: 1.1rem; }
.duo--even .doccard__shot { height: clamp(70px, 7vw, 100px); margin-bottom: .7rem; }
.duo--even .doccard h3 { font-size: .82rem; margin-bottom: .6rem; }
.duo--even .videosplit__media { max-height: 42vh; }


/* ---- footer: one definition, every page (tools/footer.py) --------------- */
.footer { background: #000; color: #fff; padding-block: clamp(3rem, 6vw, 5rem) clamp(2rem, 4vw, 3rem); }
.footer__top { display: flex; align-items: center; justify-content: center; gap: clamp(1rem, 2.5vw, 2.2rem);
  flex-wrap: wrap; margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.footer__logo img { height: 40px; width: auto; display: block; }
.footer__socials { display: flex; align-items: center; gap: 1.1rem; }
.footer__social { color: #fff; display: grid; place-items: center; transition: color var(--t) var(--ease); }
.footer__social svg { width: 22px; height: 22px; display: block; }
.footer__social:hover { color: var(--red); }
.footer__cols { display: grid; grid-template-columns: repeat(5, 1fr); gap: clamp(1.25rem, 2.5vw, 2.5rem);
  align-items: start; }
.footer__col > .footer__group + .footer__group { margin-top: 1.75rem; }
.footer__group h3 { color: var(--red); font-size: .82rem; letter-spacing: .02em; text-transform: none;
  margin-bottom: 1.1rem; }
.footer__group ul { list-style: none; margin: 0; padding: 0; }
.footer__group li { margin-bottom: .72rem; }
.footer__spacer { height: 1.1rem; margin: 0; }
.footer__group a { color: #fff; font-size: .82rem; text-decoration: underline;
  text-underline-offset: 3px; text-decoration-thickness: 1px; }
.footer__group a:hover { color: var(--red); }
.footer__addresses { display: flex; justify-content: center; gap: clamp(1.5rem, 4vw, 3.5rem);
  flex-wrap: wrap; margin-top: clamp(2.5rem, 5vw, 4rem); }
.footer__addresses span { display: inline-flex; align-items: center; gap: .6rem; font-size: .85rem; color: #fff; }
.footer__flag { width: 26px; height: 17px; border-radius: 2px; flex: none; }
.footer__legal { text-align: center; font-size: .78rem; color: #b9bcc2; margin: 1.5rem 0 0; font-style: italic; }
.footer__legal a { color: #b9bcc2; text-decoration: underline; }
.footer__legal a:hover { color: var(--red); }
@media (max-width: 1100px) { .footer__cols { grid-template-columns: repeat(3, 1fr); row-gap: 2.5rem; } }
@media (max-width: 700px) { .footer__cols { grid-template-columns: repeat(2, 1fr); } }


/* ---- final tweak pass 2 ------------------------------------------------- */

/* GRIDLINES. The live page draws a hairline down the middle of a split section
   and a hairline across the section boundary. Done as a pseudo-element on the
   section so the vertical rule spans the FULL section height, not just the
   height of the taller column — that is what makes it read as a page grid
   rather than a border on a box. */
.prodhero, .section:has(> .wrap > .duo), .section:has(> .wrap > .usecases),
.section:has(> .wrap > .videosplit) { position: relative; }
.prodhero::after, .section:has(> .wrap > .duo)::after,
.section:has(> .wrap > .usecases)::after,
.section:has(> .wrap > .videosplit)::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 50%;
  width: 1px; background: var(--ink); opacity: .85; pointer-events: none;
}
.section:has(> .wrap > .prodgrid), .section:has(> .wrap > .accgrid),
.prodhero, .section:has(> .wrap > .duo), .section:has(> .wrap > .usecases),
.section:has(> .wrap > .videosplit) { border-top: 1px solid var(--ink); }
/* the inner column rule is now redundant with the full-height one */
.duo--ruled > * + * { border-left: 0; }
@media (max-width: 900px) {
  .prodhero::after, .section:has(> .wrap > .duo)::after,
  .section:has(> .wrap > .usecases)::after,
  .section:has(> .wrap > .videosplit)::after { display: none; }
}

/* 1. Both hero CTAs the same size. */
.prodhero .btn-row .btn, .hero--split .btn-row .btn { width: min(18rem, 100%); }

/* 2. The segments label is black, not an accent eyebrow. */
.prodhero__segments .eyebrow { color: var(--ink); }

/* 3. Documentation thumbnails at double size. */
.doccards { grid-template-columns: repeat(3, 1fr); gap: clamp(1rem, 2vw, 1.75rem); }
.doccard__shot { height: clamp(150px, 15vw, 215px); }
.duo--even .doccard__shot { height: clamp(140px, 14vw, 200px); margin-bottom: 1rem; }
.duo--even .doccard h3 { font-size: .92rem; }
.duo--even .videosplit__media { max-height: 34vh; }

/* 4. Video corners stay square. */
.videosplit__media { border-radius: 0; }

/* 5. Application tiles: 40% larger, gap almost closed. */
.usecases { grid-template-columns: 0.85fr 2.9fr; }
.uccarousel__track { grid-auto-columns: calc((100% - 2 * 4px) / 2.4); gap: 4px; }

/* 6. Catalog banner is a fixed standard: the copy column is capped so the
   bleeding artwork can never run over the text, on any page that uses it. */
.catbar__inner { position: relative; }
.catbar__copy { max-width: min(52ch, 54%); position: relative; z-index: 1; }
.catbar__media { width: clamp(300px, 38vw, 620px); right: -2%; }
@media (max-width: 900px) {
  .catbar__copy { max-width: none; }
  .catbar__media { position: relative; width: 70%; right: auto; top: auto; transform: none; margin-top: 2rem; }
}
/* footer column heads use the display face, not the mono eyebrow face */
.footer__group h3 { font-family: var(--font-display); font-weight: 700; letter-spacing: -.01em; }
.footer__legal { max-width: 60ch; margin-inline: auto; }


/* ==========================================================================
   SPLIT SECTION SYSTEM — supersedes the earlier gridline attempt.

   THE RULE: a divider is never *drawn over* a layout. It occupies its own
   1px grid column with a real gap either side, so content physically cannot
   sit under it — the browser's own layout keeps the clearance. The previous
   version pinned a hairline at left:50% while the columns were asymmetric
   (0.85fr / 2.9fr), so 50% fell in the middle of the content, not between
   the columns. Position a rule by construction, never by guessing a percentage.

   Consequences of that rule, all enforced here:
   - columns are symmetric, so the divider is genuinely central
   - column-gap guarantees breathing room on both sides
   - align-items: start makes the two headings share a baseline
   - the wrap widens for these sections so nothing is squeezed to the centre
   ========================================================================== */

/* kill the old overlay divider entirely */
.prodhero::after, .section:has(> .wrap > .duo)::after,
.section:has(> .wrap > .usecases)::after,
.section:has(> .wrap > .videosplit)::after { content: none !important; display: none !important; }

/* these sections get room to breathe rather than being pinned to 1320px */
.prodhero > .wrap,
.section:has(> .wrap > .duo) > .wrap,
.section:has(> .wrap > .usecases) > .wrap,
.section:has(> .wrap > .videosplit) > .wrap,
.section:has(> .wrap > .prodgrid) > .wrap,
.section:has(> .wrap > .accgrid) > .wrap {
  width: min(1600px, 100% - var(--gutter) * 2);
}

/* the divider column */
.duo, .usecases, .videosplit, .prodhero__grid {
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
  column-gap: clamp(1.75rem, 3.5vw, 4rem);
  align-items: start;
}
.duo::before, .usecases::before, .videosplit::before, .prodhero__grid::before {
  content: ""; grid-column: 2; grid-row: 1;
  background: var(--ink); align-self: stretch; min-height: 100%;
}
.duo > *, .usecases > *, .videosplit > *, .prodhero__grid > * { grid-row: 1; min-width: 0; }
.duo > *:nth-child(1), .usecases > *:nth-child(1),
.videosplit > *:nth-child(1), .prodhero__grid > *:nth-child(1) { grid-column: 1; }
.duo > *:nth-child(2), .usecases > *:nth-child(2),
.videosplit > *:nth-child(2), .prodhero__grid > *:nth-child(2) { grid-column: 3; }

/* the old per-column border is now redundant and would double the line */
.duo--ruled > * + * { border-left: 0; padding-left: 0; }

/* vertical rhythm: content sits inside the section's padding, not pinned to
   the top of a 100vh box, while the divider still spans the full height */
.prodhero, .section:has(> .wrap > .duo), .section:has(> .wrap > .usecases),
.section:has(> .wrap > .videosplit) { align-items: center; padding-block: clamp(2.5rem, 6vh, 5rem); }

@media (max-width: 900px) {
  .duo, .usecases, .videosplit, .prodhero__grid { grid-template-columns: 1fr; row-gap: 2.5rem; }
  .duo::before, .usecases::before, .videosplit::before, .prodhero__grid::before { display: none; }
  .duo > *:nth-child(2), .usecases > *:nth-child(2),
  .videosplit > *:nth-child(2), .prodhero__grid > *:nth-child(2) { grid-column: 1; grid-row: 2; }
}

/* HERO CTAs: one line each, both the width of the widest. Never a fixed width —
   that is what forced the long label onto two lines and made them unequal. */
.prodhero .btn-row, .hero--split .btn-row {
  display: inline-flex; flex-direction: column; align-items: stretch; width: max-content; max-width: 100%;
}
.prodhero .btn-row .btn, .hero--split .btn-row .btn {
  width: auto; min-width: 0; white-space: nowrap; justify-content: center;
}

/* the media column fills its half rather than being capped narrow */
.prodhero__media, .prodshot { max-height: none; }
.prodshot { aspect-ratio: 2 / 3; max-height: 62vh; }
.usecases .duo__copy { min-width: 0; }

/* Full-height dividers were attempted here by making section -> wrap -> grid all
   stretch. It broke the hero: on the product page `.wrap` and `.prodhero__grid`
   are the SAME element, so `display: flex` on the wrap overrode `display: grid`
   and the whole layout collapsed. If this is retried, target the grid and the
   wrap separately and never set `display` on a shared element. */


/* ---- use-case carousel: sized by measurement, not by guesswork ----------
   Two faults were compounding. (a) The split-section rule forced symmetric
   columns, halving the carousel container to 50% — so every attempt to enlarge
   the tiles was fighting a container that had just been shrunk. The divider
   still gets its own column here, the two content columns are simply not equal.
   (b) `aspect-ratio` was deriving tile WIDTH from a constrained height, so a
   tile never filled its grid column and the leftover column space read as a
   huge gap. Width now comes from the column and height follows from it. */
.usecases { grid-template-columns: 0.72fr 1px 2.68fr; }
.uccarousel { min-width: 0; }
.uccarousel__track { grid-auto-columns: calc((100% - 2 * 6px) / 2.15); column-gap: 6px; }
.uctile { width: 100%; height: auto; aspect-ratio: 3 / 4; }
.uctile img { width: 100%; height: 100%; object-fit: cover; }
/* balance: copy needs a readable measure (~400px), carousel takes the rest */
.usecases { grid-template-columns: 1fr 1px 2.4fr; }
.uccarousel__track { grid-auto-columns: calc((100% - 2 * 6px) / 2.15); }


/* ==========================================================================
   TYPE CONSOLIDATION — single source of truth for section type.

   Component overrides had accumulated one fix at a time, so the ramp drifted:
   four different h2 sizes, and hero body copy at 1.02rem against 1.1875rem
   everywhere else — visibly smaller, which is what prompted this. Sizes are
   tokens now; components reference them and never invent their own.

   Rule: display type scales with clamp(); small UI text is fixed rem, because
   scaling label-sized text buys nothing and lets matching components drift.
   ========================================================================== */
:root {
  --fs-copy: clamp(1rem, .35vw + .88rem, 1.125rem);   /* 16 -> 18px: all section body copy */
  --fs-h2: clamp(1.85rem, 3.2vw, 2.85rem);            /* section heading */
  --fs-h2-tight: clamp(1.55rem, 2.5vw, 2.25rem);      /* heading in a half-width column */
  --fs-label: .875rem;                                /* eyebrows and small labels */
  --fs-micro: .8rem;                                  /* spec lists, footer, chips */
}

/* one body-copy size everywhere a section sets prose */
.prodhero__copy > p,
.duo__copy p,
.acccard p,
.prodcol__lede,
.hero.hero--split p { font-size: var(--fs-copy); line-height: 1.55; }

/* one heading size per context, not per component */
.duo__copy h2,
.section:has(> .wrap > .usecases) .duo__copy h2 { font-size: var(--fs-h2); }
.duo--even .duo__copy h2,
.videosplit .duo__copy h2,
.estband h2 { font-size: var(--fs-h2-tight); }

/* small UI text shares one value instead of five near-identical ones */
.prodhero__specs li,
.prodcol__specs li,
.prodcol__feats li,
.doccard h3,
.duo--even .doccard h3,
.footer__group h3,
.footer__group a,
.pill { font-size: var(--fs-micro); }
.prodhero__specs-title { font-size: var(--fs-copy); }
.prodhero__badge { font-size: var(--fs-micro); }
.acccard h3, .prodcol__name { font-size: clamp(1.02rem, 1.2vw, 1.18rem); }
/* Spec lists and product ledes are read, not scanned as labels, so they get
   their own step between body copy and micro rather than being lumped in with
   footer links and chips. --fs-micro stays for true labels only. */
:root { --fs-small: .95rem; }
.prodhero__specs li, .prodcol__specs li, .prodcol__feats li, .doccard h3 { font-size: var(--fs-small); }
.footer__group h3, .footer__group a, .pill { font-size: var(--fs-micro); }
/* .duo--even .doccard h3 (0,2,1) outranks .doccard h3 (0,1,1), so it has to be
   named explicitly or it keeps the old micro size. */
.duo--even .doccard h3 { font-size: var(--fs-small); }


/* ==========================================================================
   ALIGNMENT STANDARDS — apply to every page built from these components.

   RULE A: a text column beside MEDIA is vertically centred against it.
           `align-items: start` is only correct when BOTH columns are text and
           their headings need a shared baseline (the video + documentation
           pair). Anywhere a heading sits next to a tall image or video, start-
           alignment strands the copy at the top.
   RULE B: a divider only exists where two columns are genuinely peers. A copy
           column beside a full-bleed media carousel is not a pair, so no rule.
   RULE C: carousel slides are centred in their own slide box, so every image
           lands in the same place when you page through.
   ========================================================================== */

/* RULE A — centre copy next to media; keep start only for the text pair */
.videosplit, .usecases { align-items: center; }
.duo--even { align-items: start; }

/* RULE B — no divider on the use-case section */
.usecases::before { content: none; display: none; }
.usecases { grid-template-columns: 1fr 2.4fr; column-gap: clamp(2rem, 4vw, 4.5rem); }
.usecases > *:nth-child(1) { grid-column: 1; }
.usecases > *:nth-child(2) { grid-column: 2; }

/* RULE C — every slide centres its image, so paging does not shift it */
.prodcarousel .prodshot { display: flex; align-items: center; justify-content: center; }
.prodcarousel .prodshot img { width: auto; max-width: 100%; height: 100%; object-fit: contain;
  margin-inline: auto; }

/* (the documentation cards used to be nudged down by a hand-tuned margin here;
   they share a grid row with the video now — see "documentation row" below) */

/* accessories: title centred on the section, the pair pulled toward the middle */
.section:has(> .wrap > .accgrid) .head--rule { width: 100%; text-align: center; }
.accgrid { max-width: 60rem; margin-inline: auto; gap: clamp(1.5rem, 3vw, 3rem); }
/* A lone accessory (the three SafetyCast pages have exactly one) would sit in
   the left half of a two-column grid and read as a layout that lost its second
   card. With one child the grid collapses to a single centred column sized to
   the card, so it reads as deliberate. */
.accgrid:has(> .acccard:only-child) { grid-template-columns: minmax(0, 26rem); justify-content: center; }
/* `.head { max-width: 900px }` has no auto margin, so a centred heading inside
   it centres on the 900px block, not on the page. Any centred section head must
   also centre its own box — text-align alone is not enough. */
.head--rule { margin-inline: auto; max-width: none; width: 100%; }
/* Hero slide: the image BOX fills the slide and `object-fit: contain` centres the
   painted pixels inside it, so the visible photo always sits midway between the
   two arrows regardless of its aspect ratio. `width: auto` was the bug — it sized
   the box to the image and left it flush against the start of the slide, which
   put the left arrow on top of the photo and dumped the slack on the right. */
.prodcarousel .prodshot { display: block; }
.prodcarousel .prodshot img { width: 100%; height: 100%; object-fit: contain;
  object-position: center; margin: 0; }
/* (the documentation row lives at the END of this file — it has to outrank the
   later `.duo` column rules, which share its specificity) */
/* The slide box must fill the track, or the arrows (pinned to the track edges)
   end up asymmetric around a narrower slide. `aspect-ratio` + `max-height` was
   letting the box size itself from its height (374px inside a 672px track), so
   both are dropped here in favour of an explicit height and a full width. The
   photo is then centred by `object-fit: contain` inside a full-width box. */
.prodcarousel .prodshot {
  aspect-ratio: auto; max-height: none;
  width: 100%; min-width: 100%; height: clamp(320px, 56vh, 620px);
  display: flex; align-items: center; justify-content: center;
}
.prodcarousel .prodshot img { width: 100%; height: 100%; object-fit: contain; object-position: center; }

/* ---- stat band (solution pages) ------------------------------------------
   Number + label pairs on a dark strip, e.g. visual-ai's +120 Active Sites /
   +450 Installed Kits / 73% Less Near-misses. Sizes come from the type ramp
   above rather than new ad-hoc values; only the number itself is display-scale,
   because it is the whole point of the section. */
.statband { text-align: center; }
.statband .eyebrow { color: var(--red); margin-bottom: clamp(1.5rem, 3vw, 2.5rem); }
.statband__grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.5rem, 4vw, 4rem); align-items: start;
}
.statband__item { display: flex; flex-direction: column; align-items: center; gap: .35rem; }
.statband__num {
  font-family: var(--font-display); font-weight: 800; color: var(--on-dark);
  font-size: clamp(2.6rem, 6vw, 5rem); line-height: .95; letter-spacing: -0.03em;
}
.statband__label {
  color: var(--on-dark-2); font-size: var(--fs-copy); line-height: 1.3; max-width: 18ch;
}
@media (max-width: 700px) { .statband__grid { grid-template-columns: 1fr; row-gap: 2rem; } }

/* ---- solution template: review round 1 (user, 2026-08-13) ----------------
   Applied to the template, so every solution page picks them up on the next
   `python tools/solution_page.py --all`. */

/* 1. The hero title holds two lines on a wide monitor and re-wraps by itself
   on smaller ones — the same fluid principle as the homepage hero, not a fixed
   line count. The copy column was too narrow for the type size, which is what
   pushed a 31-character headline onto three lines; widening the column and
   easing the top of the clamp fixes it at every width, and `balance` stops the
   second line being left with one orphaned word. */
.hero--split .hero__copy { max-width: min(58%, 54rem); }
.hero.hero--split h1 {
  font-size: clamp(1.9rem, 3.6vw, 3.6rem);
  text-wrap: balance;
}
.hero.hero--split p { max-width: min(46rem, 100%); font-size: var(--fs-copy); }

/* 2. Hero CTAs side by side. The row is `max-content` wide and each button
   takes an equal share of it, so the pair ends up the width of the wider label
   with no fixed width guessed at — the mistake that made them different
   heights last time. The product hero keeps its stacked pair. */
.hero--split .btn-row {
  flex-direction: row; flex-wrap: wrap; align-items: center;
  gap: .8rem; width: max-content; max-width: 100%;
}
.hero--split .btn-row .btn { flex: 1 1 0; width: auto; min-width: 0; white-space: nowrap; }

/* 3. A full-screen section must never clip its own content. With
   `align-items: center`, content taller than the section overflows equally top
   and bottom and the part above is unreachable — that is what cut the bottom
   off the products section and made its last image look broken. `safe center`
   falls back to start the moment centring would overflow. */
.prodhero,
.section:has(> .wrap > .duo),
.section:has(> .wrap > .usecases),
.section:has(> .wrap > .videosplit),
.section:has(> .wrap > .prodgrid),
.section:has(> .wrap > .accgrid) { align-items: safe center; }

/* 4. The photo mosaic pages, so the round arrow reaches every photo instead of
   being decoration over a gallery that silently dropped its surplus. */
.mosaic {
  --mos-gap: clamp(.45rem, .8vw, .8rem);
  flex-direction: row; overflow-x: auto; scroll-snap-type: x mandatory;
  scroll-behavior: smooth; scrollbar-width: none; gap: var(--mos-gap);
}
.mosaic::-webkit-scrollbar { display: none; }
.mosaic__page {
  flex: 0 0 100%; scroll-snap-align: start;
  display: flex; flex-direction: column; gap: var(--mos-gap);
}
/* A part-filled row keeps tile width and sits left, the way a justified
   gallery treats its last row — it must not stretch one photo across the
   full width. */
.mosaic__row--part { justify-content: flex-start; }
.mosaic__row--part figure { flex: 0 0 calc((100% - 2 * var(--mos-gap)) / 3); }
@media (prefers-reduced-motion: reduce) { .mosaic { scroll-behavior: auto; } }

/* 5. Products: about 30% smaller, and every column aligned row for row.
   `subgrid` is what aligns them: each column adopts the grid's own rows, so
   shot / name / lede / specs / swatch / features / link sit on shared
   baselines however much copy a product carries. The generator emits all seven
   slots on every column, empty ones included, so the rows always correspond. */
.prodgrid {
  grid-template-rows: repeat(7, auto);
  grid-auto-rows: auto;
  gap: clamp(1.25rem, 3vw, 3.25rem);
  align-items: start;
}
.prodcol { display: grid; grid-row: span 7; grid-template-rows: subgrid; row-gap: 0; }
.prodcol__shot { height: clamp(170px, 20vw, 285px); margin-bottom: 1.1rem; }
.prodcol__name { font-size: clamp(.95rem, 1.1vw, 1.15rem); margin-bottom: .45rem; }
.prodcol__lede { font-size: var(--fs-small); max-width: 34ch; }
.prodcol__specs, .prodcol__feats { margin-top: 1rem; padding-top: .85rem; }
.prodcol__specs li, .prodcol__feats li { font-size: var(--fs-micro); margin-bottom: .35rem; }
.prodcol__link { margin-top: 1rem; padding-top: .85rem; font-size: var(--fs-small); }
/* empty slots collapse to nothing but still hold their row */
.prodcol__lede:empty, .prodcol__swatch:empty { margin: 0; }
.prodcol__specs:empty, .prodcol__feats:empty { margin: 0; padding: 0; border-top: 0; }

/* 6. The colour swatch is a photograph, and on SWITCH an animated GIF. Show it
   whole: capped by height, never cropped to a box. */
.prodcol__swatch { margin: .9rem 0 0; display: flex; align-items: center; justify-content: center; }
.prodcol__swatch img {
  width: 100%; height: auto; max-height: clamp(80px, 9vw, 130px);
  object-fit: contain; border-radius: var(--r-sm);
}

/* 7. Type on the page comes off the ramp, so nothing drifts a half-step out of
   step with its neighbours. */
.duo__copy p, .prodcol__lede, .esttease p { font-size: var(--fs-copy); }
.prodcol__lede { font-size: var(--fs-small); }
.duo__subhead { font-size: clamp(1rem, 1.3vw, 1.25rem); }

/* 5b. Product name: it carries `display:inline-flex` so the little red mark can
   sit beside it, and as a grid item that blockifies to a flex box which packs
   its content at flex-start — which is why the names sat hard left while every
   other row was centred. Centre the flex content, not just the text. */
.prodcol__name { justify-content: center; text-align: center; }

/* 5c. The swatch gets a box of its own fixed height so the two products show
   the same shape whatever their source images are. `contain` inside a fixed
   box means the photo is scaled to fit, never cropped — a colour swatch on one
   product and an animated GIF on another still line up. */
.prodcol__swatch { height: clamp(72px, 8vw, 112px); margin: .9rem 0 0; }
.prodcol__swatch img {
  width: auto; height: auto; max-width: 100%; max-height: 100%;
  object-fit: contain;
}

/* 5d. A little more off the product shot, so the whole grid sits inside one
   screen rather than running past the fold. */
.prodcol__shot { height: clamp(150px, 17vw, 240px); }

/* ==========================================================================
   SITE RULE — display text adapts to the display (user, 2026-08-13)
   Applies to every page, not just the one it was reported on.

   The complaint was a hero headline sitting on three squeezed lines on a very
   wide monitor. The cause was not the type size. It was that a split hero
   divided the VIEWPORT while its copy sat in a fixed 1320px centred wrap: the
   photo took 62% of a 3440px screen starting at 38%, the centred copy column
   started at 30%, and past roughly 2000px the two overlap, so the headline had
   a few hundred pixels of clear white to wrap inside.

   Two rules follow, and both are needed:

   1. A SPLIT LAYOUT DIVIDES THE LAYOUT, NEVER THE VIEWPORT. Copy and media get
      real grid columns, so the copy column cannot be crowded out at any width.
      The copy still lines up with the header logo, because its left padding is
      the wrap's own left edge.

   2. DISPLAY TYPE CARRIES A VIEWPORT TERM WITH A CEILING SIZED FOR A LARGE
      MONITOR. A headline then holds two lines on a wide screen and re-wraps by
      itself on a tablet or a phone. Never a fixed line count, and never a rem
      cap so low that the type looks stranded on a big display.
   ========================================================================== */

@media (min-width: 901px) {
  .hero--split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.06fr);
    align-items: center;
  }
  .hero--split .hero__inner {
    grid-area: 1 / 1;
    width: 100%; max-width: none; margin-inline: 0;
    /* left edge of the wrap, so the hero copy starts under the header logo;
       falls back to the gutter once the viewport is narrower than the wrap */
    padding-inline: max(var(--gutter), calc((100vw - var(--wrap)) / 2))
                    clamp(1.25rem, 2.5vw, 3rem);
  }
  .hero--split .hero__copy { max-width: min(100%, 46rem); }
  .hero--split .hero__photo {
    grid-area: 1 / 2;
    position: relative; inset: auto; width: 100%; height: 100%;
    align-self: stretch;
  }
  /* the photo no longer sits under the copy, so it only needs a short feather
     on its leading edge to dissolve into the white rather than a 40% fade */
  .hero--split .hero__photo img {
    -webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, #000 22%);
    mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, #000 22%);
    object-position: 55% 50%;
  }
}

/* Fluid display type. The vw term is what makes these grow with the screen;
   the ceiling is what stops them running away on an ultrawide. */
.hero.hero--split h1 { font-size: clamp(2rem, 3.1vw, 4.6rem); }
.hero.hero--split p {
  font-size: clamp(1rem, .55vw + .78rem, 1.45rem);
  max-width: min(100%, 52ch);
}

/* The hero copy column is the wider of the two, so the headline has room to
   hold two lines at the top of the range. Media still bleeds to the screen
   edge; it is the LAYOUT that is divided, not the viewport. */
@media (min-width: 901px) {
  .hero--split { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
}

/* --- SITE RULE, part 3: a headline sizes itself from its own length --------
   A clamp alone cannot promise a line count, because it knows the viewport but
   not how long the headline is: at one size "REPLACE PAINT AND TAPE FOR GOOD"
   fits two lines and "LASER GUIDES TURN GUESSWORK INTO PRECISION" needs three.

   So the page generators stamp the headline's character count on the element
   as --h1ch, and the type size is the column's own width divided by it. The
   result is the largest size that still lands the headline on two lines: a
   short title gets big type, a long one gets smaller type, and both re-wrap
   naturally on a tablet or phone. The clamp stays as the outer bound.

   `cqw` is 1% of the copy column, which is why the column is a container. The
   constant 320 comes from this typeface: at 0.6em average character width,
   two lines of n/2 characters need a size of about 3.2 x column / n. */
.hero--split .hero__copy,
.prodhero__copy { container-type: inline-size; }

.hero.hero--split h1 {
  font-size: min(clamp(1.9rem, 2.9vw, 4rem), calc(320cqw / var(--h1ch, 30)));
  line-height: .97;
}
/* The product hero read as a small section rather than a hero (user). Measured
   against live at a 1500px window: live sets this headline at roughly 72px and
   lets it take two lines, while we were rendering 42.7px on one — the 2.7vw
   term was binding long before the 3.4rem cap ever could. At 4.4vw it lands
   near 70px, which is what makes the section read as the top of a page.
   The `cqw` term still governs: it divides the copy column by the headline's
   character count, so a long name (CRANECAST(TM) DUAL PROJECTION SYSTEM) steps
   itself down instead of overflowing. */
.prodhero h1 {
  font-size: min(clamp(2.2rem, 4.4vw, 4.4rem), calc(320cqw / var(--h1ch, 24)));
}
/* and a taller slot for the product shot, so it carries the other half of the
   hero. `contain` still, and no crop: several of these cutouts are 600x800 with
   white baked into the file, so the box gets bigger, never the crop. */
.prodcarousel .prodshot { height: clamp(360px, 64vh, 760px); }
/* the copy column may use its full grid column; the headline governs itself */
@media (min-width: 901px) { .hero--split .hero__copy { max-width: 100%; } }

/* ==========================================================================
   SITE RULE — nothing after the hero aligns to the top (user, 2026-08-13)
   The copy column beside media is centred against it, and so is the media.
   `align-items: start` is right in exactly one case: two columns that are BOTH
   text and whose headings need a shared first baseline (the video +
   documentation pair on a product page). Everywhere else it strands the copy at
   the top of a row as tall as the photo next to it, which is what made the
   "A Smarter Way to Stay Aware" section read as broken.
   ========================================================================== */
.duo, .usecases, .videosplit, .prodhero__grid { align-items: center; }
.duo--even { align-items: start; }          /* the one exception: text + text */
.mosaicwrap, .mosaic, .mosaic__page { align-self: center; }
.mosaic__page { justify-content: center; }

/* These sections are content-led, not full-page. Forcing 100vh on a section
   whose content is shorter leaves a stranded band; a generous block padding and
   a floor well under a screen keeps the rhythm without the hole. */
.section:has(> .wrap > .duo),
.section:has(> .wrap > .usecases),
.section:has(> .wrap > .videosplit) {
  min-height: 0;
  padding-block: clamp(3rem, 7vh, 6rem);
}

/* The divider belongs to sections whose two columns are genuine peers. A copy
   column beside a video is not a pair, so the video sections lose theirs. */
.section:has(> .wrap > .videosplit)::after { content: none; }

/* Video sections: the video is the point, so give it the larger share and let
   it grow with the viewport.

   The middle track must stay: the split-section system places these children in
   columns 1 and 3 with the divider occupying column 2. Redefining this as a
   two-column grid left the second child asking for a column 3 that no longer
   existed, which is what collapsed the section. The track is kept and set to
   zero width instead, and the divider line itself is switched off. */
.videosplit { grid-template-columns: minmax(0, 1.5fr) 0px minmax(0, 1fr);
  column-gap: clamp(1rem, 2vw, 2.25rem); }
.videosplit--rev { grid-template-columns: minmax(0, 1fr) 0px minmax(0, 1.5fr); }
.videosplit::before { display: none; }
.videosplit__media { border-radius: 0; max-height: none; width: 100%; }
.duo--even .videosplit__media { max-height: none; }

/* --- products: colour squares and the tick/cross checklist ---------------- */
/* Eight rows now, not seven: colours and media are separate slots so they line
   up across columns like everything else. */
.prodgrid { grid-template-rows: repeat(8, auto); }
.prodcol { grid-row: span 8; }

.prodcol__colors {
  list-style: none; margin: .85rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .4rem; justify-content: center; align-items: center;
}
.swatchdot {
  width: 15px; height: 15px; background: var(--c, #ccc);
  /* a white swatch on a white page needs an outline to exist at all */
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .28);
}
.swatchdot--switch {
  background: none; box-shadow: none; color: var(--ink-2);
  width: 17px; height: 17px; display: inline-flex; align-items: center; justify-content: center;
}
.swatchdot--switch svg { width: 100%; height: 100%; }

/* The media strip under the colours: both products get the same rectangle, with
   square corners. */
.prodcol__swatch { height: auto; aspect-ratio: 16 / 6; margin: .9rem 0 0; overflow: hidden; }
.prodcol__swatch img {
  width: 100%; height: 100%; max-width: none; max-height: none;
  object-fit: cover; object-position: center; border-radius: 0;
}

/* The checklist is a comparison: same labels on every product, a green tick or
   a red cross saying whether that product has it. */
.prodcol__feats { text-align: left; max-width: 22rem; margin-inline: auto; }
.prodcol__feats li {
  display: grid; grid-template-columns: 1.1rem 1fr; align-items: center;
  gap: .55rem; justify-content: start; text-align: left;
}
.prodcol__feats svg { width: 1.1rem; height: 1.1rem; }
.prodcol__feats .is-yes svg, .prodcol__feats li.is-yes svg { color: #38C68B; }
.prodcol__feats li.is-no svg { color: var(--red); }
.prodcol__feats li.is-no span { color: var(--ink-3); }

/* stat band: centre the eyebrow on the section, not on its own box, and size
   the band to its content like the other post-hero sections */
.statband .eyebrow { display: block; width: 100%; text-align: center; margin-inline: auto; }
.statband { min-height: 0; padding-block: clamp(3.5rem, 9vh, 7rem); }

/* ==========================================================================
   Solutions review round 4 (user, 2026-08-13)
   ========================================================================== */

/* 1. The product media strip is NOT a swatch and must never be cropped.
   On projected-walkways those files are wide composites — 5.5:1 and 9.2:1 —
   holding three walkway samples side by side, so the 16/6 crop box was showing
   the middle one and slicing the outer two off. Contain, never cover. Identical
   shape across two products is not achievable without cropping when the source
   images differ in aspect, so equal HEIGHT is what is held and the whole image
   is always shown. */
.prodcol__swatch {
  aspect-ratio: auto; height: auto; overflow: visible;
  display: flex; align-items: center; justify-content: center;
}
.prodcol__swatch img {
  width: 100%; height: auto;
  max-height: clamp(110px, 12vw, 190px);
  object-fit: contain; object-position: center; border-radius: 0;
}

/* 2. The photo section carries the page. Give it most of a screen and a much
   wider media column, so the collage reads at size instead of sitting in a
   half-width box. The red bar above stays visible. */
.section:has(> .wrap > .duo) { min-height: 88vh; }
.duo { grid-template-columns: minmax(0, .82fr) 1px minmax(0, 1.5fr); }

/* 3. projected-signs hero: the white feather ran to the middle of the photo,
   which is far past where the headline ends. Pull it in so the photo starts
   resolving just after the copy. */
.hero--split .hero__photo img {
  -webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, #000 12%);
  mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, #000 12%);
}

/* 4. The proven-performance band is red with white type: the label large on the
   left, the figures in a row beside it — one bar, not a full dark screen. */
.statband {
  background: var(--red); color: #fff; min-height: 0;
  padding-block: clamp(1.75rem, 4vh, 3rem);
}
.statband > .wrap {
  display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  align-items: center; gap: clamp(1.5rem, 5vw, 5rem);
}
.statband .eyebrow {
  color: #fff; text-align: left; width: auto; margin: 0;
  font-family: var(--font-display); font-weight: 800; letter-spacing: -0.02em;
  font-size: clamp(1.5rem, 3vw, 3rem); line-height: .95; text-transform: uppercase;
  max-width: 9ch;
}
.statband__grid { gap: clamp(1rem, 4vw, 4rem); }
.statband__num { font-size: clamp(1.6rem, 2.6vw, 2.6rem); }
.statband__label { color: #fff; font-weight: 700; font-size: var(--fs-small); }
@media (max-width: 800px) {
  .statband > .wrap { grid-template-columns: 1fr; }
  .statband .eyebrow { max-width: none; text-align: center; }
}

/* review mode badges — only ever visible on a ?review URL */
.review-mode main > section { position: relative; outline: 1px dashed rgba(255,58,66,.35); }
.review-badge {
  position: absolute; top: 8px; left: 8px; z-index: 60;
  background: var(--red); color: #fff; font-family: var(--font-mono);
  font-size: .78rem; line-height: 1; padding: .35rem .5rem; border-radius: 4px;
  pointer-events: none;
}

/* --- video / documentation pair (SPDS, visual-ai-system) ------------------
   Two peers split by a rule. The documentation thumbnails sit side by side and
   together span the same width as the media opposite, so the two halves balance
   rather than one being a pair of stamps beside a full-size video. */
.section--docs { padding-block: clamp(3rem, 7vh, 6rem); }
.section--docs .videosplit { align-items: start; }
.docmedia { margin-top: 1.75rem; }
.docmedia img { width: 100%; height: auto; display: block; }
.section--docs .videosplit__media { margin-top: 1.75rem; aspect-ratio: 16/9; }
.doccards--pair { grid-template-columns: repeat(2, 1fr); gap: clamp(1rem, 2.5vw, 2rem); margin-top: 1.75rem; }
.doccards--pair .doccard__shot { height: auto; aspect-ratio: 4/3; }
.doccards--pair .doccard__shot img { width: 100%; height: 100%; object-fit: contain; }
@media (max-width: 760px) { .doccards--pair { grid-template-columns: repeat(2, 1fr); } }

/* --- Built to Fit Your Fleet (visual-ai-system) --------------------------
   Copy left, the four vehicle diagrams in ONE row right, caption bold beneath
   them, and the mesh artwork as a faint section background rather than a fifth
   image in the collage. Content-led height, and no divider: a copy column
   beside a diagram strip is not a pair of peers. */
.section--fleet { position: relative; overflow: hidden; min-height: 0;
  padding-block: clamp(2.5rem, 6vh, 4.5rem); background: var(--surface); }
.section--fleet::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background-image: var(--fleet-bg); background-size: cover; background-position: center;
  opacity: .13; pointer-events: none;
}
.section--fleet > .wrap { position: relative; z-index: 1; }
.fleet { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 2fr);
  gap: clamp(2rem, 4vw, 4rem); align-items: center; }
.fleet__row { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(.5rem, 1.2vw, 1.1rem); }
.fleetshot { margin: 0; background: #fff; padding: clamp(.5rem, 1vw, 1rem); }
.fleetshot img { width: 100%; height: auto; display: block; }
.fleet__caption { margin-top: 1.1rem; text-align: center; font-weight: 700;
  color: var(--ink); font-size: var(--fs-small); }
@media (max-width: 900px) {
  .fleet { grid-template-columns: 1fr; }
  .fleet__row { grid-template-columns: repeat(2, 1fr); }
}

/* --- Adapted to Your Workflow: one section, a row of three ---------------- */
.section--workflow { min-height: 0; padding-block: clamp(2.5rem, 6vh, 4.5rem); }
.wfrow { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.25rem, 3vw, 2.75rem); }
.wfcard { text-align: left; }
.wfcard__shot { aspect-ratio: 4/3; overflow: hidden; margin-bottom: 1.1rem; }
.wfcard__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.wfcard h3 { font-size: clamp(1rem, 1.3vw, 1.25rem); text-transform: none;
  letter-spacing: -.01em; margin-bottom: .5rem; }
.wfcard p { color: var(--ink-2); font-size: var(--fs-small); }
@media (max-width: 900px) { .wfrow { grid-template-columns: 1fr; } }

/* --- key specs + documentation: a near-full-page pair --------------------
   The product shot and the two document thumbnails carry this section, so they
   are sized like the video they sit opposite elsewhere, not like stamps. */
.section--docs { min-height: 88vh; padding-block: clamp(3rem, 7vh, 6rem); }
.section--docs .videosplit { grid-template-columns: minmax(0, 1fr) 1px minmax(0, 1fr); align-items: start; }
.section--docs .videosplit::before { display: block; }
.docmedia img { width: 100%; height: auto; max-height: 52vh; object-fit: contain; }
.doccards--pair .doccard__shot { height: auto; aspect-ratio: 4/3; }
.section--docs .doccard h3 { font-size: clamp(1rem, 1.4vw, 1.35rem); margin-block: .9rem; }

/* nine slots: the plain note (e.g. installation height) sits below the checklist */
.prodgrid { grid-template-rows: repeat(9, auto); }
.prodcol { grid-row: span 9; }
.prodcol__note { margin-top: .9rem; }
.prodcol__note p { font-size: var(--fs-micro); text-transform: uppercase;
  letter-spacing: .04em; color: var(--ink-2); margin: 0 0 .3rem; }
.prodcol__note:empty { margin: 0; }
/* section heads centre their eyebrow on the section, not on its own box */
.head--rule .eyebrow { display: block; width: 100%; text-align: center; }

/* an unused slot must collapse, or families that skip one (SafetyCast has no
   colours and no media strip) show a band of dead space mid-column */
.prodcol__colors:empty, .prodcol__swatch:empty, .prodcol__feats:empty,
.prodcol__specs:empty, .prodcol__lede:empty { margin: 0; padding: 0; height: 0;
  min-height: 0; border: 0; }
.fleet__media { min-width: 0; }

/* hero component gallery — only appears on a hero that has extra images (SPDS) */
.heroparts { display: flex; flex-wrap: wrap; gap: clamp(.75rem, 1.6vw, 1.5rem); margin-top: 2rem; }
.heropart { margin: 0; width: clamp(84px, 9vw, 130px); text-align: center; }
.heropart img { width: 100%; height: auto; display: block; }
.heropart figcaption { margin-top: .4rem; font-size: var(--fs-micro); color: var(--ink-3); line-height: 1.2; }

/* a use-case tile may carry a sentence under its name */
.uctile__desc { display: block; margin-top: .35rem; font-weight: 400; font-size: var(--fs-micro);
  line-height: 1.35; color: rgba(255,255,255,.88); text-transform: none; letter-spacing: 0; }

/* ==========================================================================
   Projected Lines review (user, 2026-08-13)
   ========================================================================== */

/* 1. The hero's white column ends where the copy ends, not halfway across the
   screen. The photo takes the larger share so it starts just past the text. */
@media (min-width: 901px) {
  .hero--split { grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); }
}

/* 2. Industry chips must all be readable. They were on one nowrap row with
   horizontal scroll, so "Manufacturing" was clipped and "Food Process." was off
   the edge entirely — on every solution page. */
.pillrow { flex-wrap: wrap; overflow-x: visible; }

/* 3. Product columns: one grid, equal columns, everything centred, and the two
   products parallel row for row. */
.prodgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); justify-items: stretch; }
.prodcol { text-align: center; }
.prodcol__lede, .prodcol__specs, .prodcol__feats, .prodcol__note { margin-inline: auto; }
.prodcol__specs li, .prodcol__feats li, .prodcol__note p {
  font-weight: 700; color: var(--ink);
}
/* the checklist rows stay left-aligned to each other but the block is centred,
   so the ticks form one column instead of drifting */
.prodcol__feats { width: max-content; max-width: 100%; text-align: left; }
.prodcol__feats li { justify-content: start; }

/* 4. The media image is cropped to one common rectangle so both products match.
   A composite strip is exempt — cropping those loses two thirds of the file. */
.prodcol__swatch { aspect-ratio: 16 / 7; width: 100%; overflow: hidden; }
.prodcol__swatchimg { width: 100%; height: 100%; object-fit: cover; object-position: center;
  max-height: none; border-radius: 0; }
.prodcol__swatch:has(.prodcol__swatch--strip) { aspect-ratio: auto; overflow: visible; }
.prodcol__swatchimg.prodcol__swatch--strip { height: auto; object-fit: contain; }

/* 5. The infographic artwork was bleeding so far off the banner that it read as
   cut off. Smaller, and pulled back inside the edge. */
.catbar__media { width: clamp(260px, 30vw, 460px); right: 1%; }

/* The media box, stated with enough specificity to beat the earlier
   `.prodcol__swatch img` rule (a descendant selector outranks a single class,
   which is why the first attempt at this did nothing). Both products get the
   same rectangle; a composite strip opts out and is shown whole. */
.prodcol .prodcol__swatch {
  display: block; width: 100%; height: auto; aspect-ratio: 16 / 7;
  overflow: hidden; margin: .9rem 0 0;
}
.prodcol .prodcol__swatch img {
  width: 100%; height: 100%; max-width: none; max-height: none;
  object-fit: cover; object-position: center; border-radius: 0;
}
.prodcol .prodcol__swatch:has(> .prodcol__swatch--strip) {
  aspect-ratio: auto; overflow: visible;
}
.prodcol .prodcol__swatch img.prodcol__swatch--strip {
  height: auto; object-fit: contain;
}
.prodcol .prodcol__swatch:empty { height: 0; aspect-ratio: auto; margin: 0; }

/* ---- Projected Signs review (user, 2026-08-13) --------------------------- */

/* 1. The products grid's `gap` was applying to ROWS as well as columns. With
   nine rows that is eight row-gaps of up to 52px each, held open even when the
   rows are empty — which is the large gap between the description and the
   checklist on the pages that carry no specs, colours or media. Columns keep
   their gap; rows own their own spacing. */
.prodgrid { column-gap: clamp(1.25rem, 3vw, 3.25rem); row-gap: 0; }

/* 2. Buttons run a little longer everywhere except the hero and the header,
   which keep their existing measure. */
main .btn, .footer .btn { padding-inline: 2.45rem; }
.hero .btn, .prodhero .btn, .header .btn { padding-inline: 1.7rem; }

/* 3. The banner artwork sat almost off the right edge, which read as a break in
   the layout rather than a bleed. Pulled back in. */
.catbar__media { right: 6%; }

/* ---- Signs / Walkways review (user, 2026-08-13) -------------------------- */

/* The two products sit closer together and closer to the centre, rather than
   being pushed to the outer edges of a very wide wrap. */
.prodgrid {
  max-width: min(1180px, 100%);
  margin-inline: auto;
  column-gap: clamp(1rem, 2vw, 2.25rem);
}

/* Composite strips share ONE box of the same height in both columns and sit on
   its top edge, so the walkway samples and the colour samples start on the same
   line. Their source files are 5.5:1 and 9.2:1, so at equal width the painted
   heights differ; aligning the top edge is what can be held without cropping
   away two of the three samples in each file. */
.prodcol .prodcol__swatch:has(> .prodcol__swatch--strip) {
  height: clamp(70px, 7.5vw, 118px);
  aspect-ratio: auto; overflow: visible;
  display: flex; align-items: flex-start; justify-content: center;
}
.prodcol .prodcol__swatch img.prodcol__swatch--strip {
  width: 100%; height: auto; object-fit: contain; align-self: flex-start;
}

/* ---- Visual A.I. review (user, 2026-08-13) ------------------------------- */

/* 1. The fleet caption centres on the row of diagrams, not on the section. It
   was inheriting a narrower measure and so centred inside its own box. */
.fleet__caption { width: 100%; max-width: none; margin-inline: auto; text-align: center; }

/* 2. Adapted to Your Workflow: both titles left-aligned and on the same edge,
   the card copy centred under each tile. */
.section--workflow .head--rule { text-align: left; margin-inline: 0; max-width: none; }
.section--workflow .head--rule .eyebrow { text-align: left; width: auto; margin: 0 0 .5rem; }
.section--workflow .head--rule h2 { display: block; }
.wfcard, .wfcard h3, .wfcard p { text-align: center; }
.wfcard p { margin-inline: auto; }

/* 3. Key specs: the product shot carries this half, so it runs large, and the
   six bullets read as a list. */
.docmedia { margin-top: 1.5rem; }
.docmedia img { width: 100%; height: auto; max-height: 62vh; object-fit: contain; }
.speclist { list-style: none; margin: .25rem 0 0; padding: 0; }
.speclist li { position: relative; padding-left: 1.1rem; margin-bottom: .4rem;
  color: var(--ink-2); font-size: var(--fs-copy); }
.speclist li::before { content: "•"; position: absolute; left: 0; color: var(--ink-2); }

/* 4. Documentation: bigger thumbnails, and the pair pushed to the foot of its
   column so the download buttons finish level with the base of the product
   shot opposite. */
.section--docs .videosplit { align-items: stretch; }
.section--docs .videosplit > .duo__copy { display: flex; flex-direction: column; }
.section--docs .doccards--pair { margin-top: auto; }
.doccards--pair .doccard__shot { height: auto; aspect-ratio: 4/3; }
.doccards--pair .doccard__shot img { width: 100%; height: 100%; object-fit: contain; }
.doccards--pair .doccard .btn { margin-top: .5rem; }

/* Both halves of the docs pair put their media on a SHARED row, so the product
   shot and the document thumbnails start on the same line however much copy sits
   above them. `margin-top: auto` was tried first and pushed the thumbnails to
   the very bottom of the column, which is not the same thing as aligned. */
.section--docs .videosplit { grid-template-rows: auto 1fr; align-items: start; }
.section--docs .videosplit > .duo__copy {
  display: grid; grid-row: 1 / span 2; grid-template-rows: subgrid; row-gap: 0;
}
.section--docs .doccards--pair { margin-top: 1.5rem; }
.section--docs .docmedia { margin-top: 1.5rem; }
.docs__head { min-width: 0; }

/* Docs pair: the two columns FINISH together. Aligning only the tops left the
   product shot running far below the download buttons, because it was sized to
   a fixed 62vh while the documentation column is as tall as its own content.
   The media row now takes the remaining height of the taller column and the
   product shot fills it, so both halves end on the same line — which is what
   the reference shows. */
.section--docs .videosplit { grid-template-rows: auto 1fr; align-items: stretch; }
.section--docs .videosplit > .duo__copy { grid-template-rows: auto 1fr; }
.section--docs .docmedia {
  height: 100%; min-height: 0; margin-top: 1.5rem;
  display: flex; align-items: center; justify-content: center;
}
.section--docs .docmedia img {
  width: 100%; height: 100%; max-height: none; object-fit: contain; object-position: center;
}
/* the spec bullets sit as a tight block, as in the reference */
.speclist li { margin-bottom: .15rem; }

/* The docs pair is content-led. Forcing the section to 88vh stretched both
   columns and blew the product shot up to a thousand pixels, which is what left
   the download buttons stranded near the top of a very tall column. Sized to its
   content, with the product shot capped so the two halves come out close in
   height and finish together, the way the reference does. */
.section--docs { min-height: 0; padding-block: clamp(2.5rem, 6vh, 5rem); }
.section--docs .videosplit { grid-template-rows: auto auto; align-items: start; }
.section--docs .videosplit > .duo__copy { grid-template-rows: subgrid; }
.section--docs .docmedia { height: auto; align-items: flex-start; }
.section--docs .docmedia img { height: auto; max-height: min(46vh, 470px); }

/* Bigger document thumbnails, which also brings the download buttons down level
   with the base of the product shot opposite (measured 5645 -> ~5735 against an
   image bottom of 5740). */
.section--docs .doccards--pair .doccard__shot { aspect-ratio: 1 / 1; }

/* The divider is a grid child, so it inherited `grid-row: 1` from the
   split-section rule and stopped at the foot of the head row — which is exactly
   where it was vanishing halfway down. Span every row instead. */
.section--docs .videosplit::before { display: block; grid-row: 1 / -1; }

/* Documentation sits a little further from the rule. */
.section--docs .videosplit > .duo__copy:last-child { padding-left: clamp(1rem, 3vw, 3.5rem); }

/* Product shot eased down a step. */
.section--docs .docmedia img { max-height: min(38vh, 390px); }

/* ---- UWB Tag System review (user, 2026-08-14) ---------------------------- */

/* 1. Hero key specs — the list that belongs between the description and the
   buttons. */
.herospecs__title { font-weight: 700; color: var(--ink); margin: 1.4rem 0 .5rem; }
.herospecs { list-style: none; margin: 0 0 .5rem; padding: 0; }
.herospecs li { position: relative; padding-left: 1.1rem; margin-bottom: .3rem;
  color: var(--ink-2); font-size: var(--fs-small); font-weight: 500; }
.herospecs li::before { content: "•"; position: absolute; left: 0; color: var(--ink-2); }

/* 2. The icon bar centres whatever number of icons it has. Six columns were
   hard-coded, so a bar of five filled the first five and looked left-aligned. */
.iconbar__grid { grid-template-columns: repeat(var(--cols, 6), minmax(0, 1fr));
  max-width: calc(var(--cols, 6) * 12rem); margin-inline: auto; justify-content: center; }
@media (max-width: 900px) {
  .iconbar__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); max-width: none; }
}
@media (max-width: 520px) {
  .iconbar__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 3. A hero whose copy carries a spec list needs more of the width, and its
   photo must not push the section past one screen. */
.hero--split .hero__photo img { object-fit: cover; height: 100%; }

/* The hero photo must not decide the hero's height. With `height: 100%` in a
   row of no definite height it resolved to auto, so a tall portrait shot made
   the SPDS hero 1902px against a 1169px screen. Taking the image out of flow
   inside its cell means the row is sized by the copy, with the 100vh floor
   underneath it, and the photo simply fills whatever that comes to. */
@media (min-width: 901px) {
  .hero--split .hero__photo { position: relative; overflow: hidden; min-height: 0; }
  .hero--split .hero__photo img {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; object-position: 55% 50%;
  }
  /* a hero carrying a spec list needs more of the width for its copy */
  .hero--split:has(.herospecs) { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); }
}

/* ---- MHE Lights review (user, 2026-08-14) -------------------------------- */

/* Simple product family: rows of three, rounded photos, and nothing but a name,
   a sentence and the link. */
.prodgrid--simple { grid-template-columns: repeat(var(--pcols, 3), minmax(0, 1fr));
  grid-template-rows: none; max-width: none; column-gap: clamp(1.5rem, 3vw, 3.5rem);
  row-gap: clamp(2.5rem, 5vw, 4.5rem); }
.prodcol--simple { display: block; grid-row: auto; }
.prodcol--simple .prodcol__shot { height: auto; aspect-ratio: 3 / 2; margin-bottom: 1.1rem; }
.prodcol--simple .prodcol__shot img { width: 100%; height: 100%; max-height: none;
  object-fit: cover; border-radius: var(--r); }
.prodcol--simple .prodcol__name { font-size: clamp(1rem, 1.2vw, 1.2rem); margin-bottom: .6rem; }
.prodcol--simple .prodcol__lede { font-size: var(--fs-small); max-width: 38ch; margin-inline: auto; }
.prodcol--simple .prodcol__link { border-top: 0; padding-top: 0; margin-top: 1rem; }
@media (max-width: 900px) { .prodgrid--simple { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .prodgrid--simple { grid-template-columns: 1fr; } }

/* The photo section keeps to one screen: a wider copy column and a collage
   capped so the whole section fits without scrolling past it. */
.section:has(> .wrap > .duo) { min-height: 0; padding-block: clamp(2.5rem, 5vh, 4.5rem); }
.duo { grid-template-columns: minmax(0, .95fr) 1px minmax(0, 1.35fr); }
/* NOTE: do not cap .mosaic img with a height + object-fit: cover. That was
   tried to keep the section inside one screen and it cropped every photo in the
   justified gallery, which the standing rule forbids. The section is content-led
   now, so it does not need the cap. */
.mosaic__row { min-height: 0; }

/* Simple product cards: the link sits at the foot of each card so the row of
   See Product links lines up even when one description runs a line longer. */
.prodgrid--simple { align-items: stretch; }
.prodcol--simple { display: flex; flex-direction: column; }
.prodcol--simple .prodcol__link { margin-top: auto; }

/* A long single word ("PEDESTRIANS") can still force a third line at the size
   the formula allows, so the constant is eased from 320 to 300. */
.hero.hero--split h1 {
  font-size: min(clamp(1.9rem, 2.9vw, 4rem), calc(300cqw / var(--h1ch, 30)));
}

/* The clamp ceiling, not the length formula, was binding on "STOP MAKING
   PEDESTRIANS GUESS": at 64px the words pack as 11 / 11 / 5 and balance cannot
   pull it back to two lines. 3.6rem gives the balancer the room it needs, and
   every other headline was already inside this. */
.hero.hero--split h1 {
  font-size: min(clamp(1.9rem, 2.9vw, 3.6rem), calc(300cqw / var(--h1ch, 30)));
}

/* ---- Crane Lights review (user, 2026-08-14) ----------------------------- */

/* 1. A longer, smoother feather on every solution hero — the 12% fade was an
   abrupt edge rather than a dissolve. Extra stops keep the ramp even instead of
   bunching the change at one point. */
@media (min-width: 901px) {
  .hero--split .hero__photo img {
    -webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.35) 12%,
      rgba(0,0,0,.75) 24%, #000 38%);
    mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.35) 12%,
      rgba(0,0,0,.75) 24%, #000 38%);
  }
}

/* 2. Bigger icons on the red bar, and the bar uses more of the page again. The
   `max-width: cols * 12rem` cap added to centre a five-icon bar also pulled a
   six-icon bar in and made the icons read small. */
.iconbar__grid { max-width: calc(var(--cols, 6) * 15.5rem); gap: clamp(1.5rem, 3vw, 3.25rem); }
.iconbar__icon { height: clamp(44px, 3.6vw, 60px); width: clamp(52px, 4.2vw, 72px); }
.iconbar__label { font-size: clamp(.88rem, .55vw + .72rem, 1.06rem); }

/* 3. The product media box follows the page's own images (see solution_page.py),
   so a near-square projection shot is shown almost whole instead of being cut to
   a wide strip. Both columns still share one box. */
.prodcol .prodcol__swatch { aspect-ratio: var(--media-ratio, 16 / 7); }

/* 4. The checklist reads centred. */
.prodcol__feats { width: 100%; text-align: center; }
.prodcol__feats li { justify-content: center; grid-template-columns: auto auto; }

/* ---- Laser Guides review (user, 2026-08-14) ----------------------------- */

/* Feather: 12% was an edge, 38% was too much and resolved too far right. 24%. */
@media (min-width: 901px) {
  .hero--split .hero__photo img {
    -webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.45) 9%,
      rgba(0,0,0,.85) 17%, #000 24%);
    mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.45) 9%,
      rgba(0,0,0,.85) 17%, #000 24%);
  }
}

/* The checklist BLOCK is centred in the column, but the rows inside it share a
   left edge so the ticks form one column. Centring each row individually left
   the ticks stepping in and out with the label length. */
.prodcol__feats { width: max-content; max-width: 100%; margin-inline: auto; text-align: left; }
.prodcol__feats li { justify-content: start; grid-template-columns: 1.1rem 1fr; }

/* The simple card's photo box follows the page's own shots, so a near-square
   product cutout is not cropped to a landscape strip. */
.prodcol--simple .prodcol__shot { aspect-ratio: var(--shot-ratio, 3 / 2); }
.prodcol--simple .prodcol__shot img { object-fit: contain; }

/* ---- follow-ups (user, 2026-08-14) -------------------------------------- */

/* 1. Product photos are one consistent HEIGHT on every page rather than a box
   shaped from that page's own images. Deriving the box from the artwork kept it
   uncropped but made a near-square cutout enormous at half the page width; a
   fixed height with `contain` is both consistent and still uncropped. */
.prodcol--simple .prodcol__shot {
  aspect-ratio: auto; height: clamp(170px, 17vw, 260px);
  display: flex; align-items: center; justify-content: center;
}
.prodcol--simple .prodcol__shot img {
  width: auto; height: 100%; max-width: 100%; object-fit: contain;
}

/* 2. A smoother feather. Four stops banded; this ramps in small steps on an
   eased curve and is fully resolved by 30%, so it blends without reaching far
   across the photo. */
@media (min-width: 901px) {
  .hero--split .hero__photo img {
    -webkit-mask-image: linear-gradient(90deg,
      rgba(0,0,0,0) 0%, rgba(0,0,0,.04) 4%, rgba(0,0,0,.14) 8%, rgba(0,0,0,.30) 12%,
      rgba(0,0,0,.48) 16%, rgba(0,0,0,.66) 20%, rgba(0,0,0,.82) 24%,
      rgba(0,0,0,.94) 27%, #000 30%);
    mask-image: linear-gradient(90deg,
      rgba(0,0,0,0) 0%, rgba(0,0,0,.04) 4%, rgba(0,0,0,.14) 8%, rgba(0,0,0,.30) 12%,
      rgba(0,0,0,.48) 16%, rgba(0,0,0,.66) 20%, rgba(0,0,0,.82) 24%,
      rgba(0,0,0,.94) 27%, #000 30%);
  }
}

/* 3. Checklist: the ticks form ONE left-hand column and the label is centred in
   the space beside them. Left-aligning the labels was wrong; centring each whole
   row was also wrong because it moved the ticks. The tick column is fixed, the
   text cell is centred. */
.prodcol__feats { width: max-content; max-width: 100%; margin-inline: auto; }
.prodcol__feats li { display: grid; grid-template-columns: 1.1rem 1fr;
  justify-content: start; align-items: center; gap: .55rem; }
.prodcol__feats li > span { text-align: center; }
.prodcol--simple .prodcol__shot { height: clamp(150px, 17vw, 240px); }

/* Two-column product grids sit closer together. */
.prodgrid:not(.prodgrid--simple) {
  max-width: min(1000px, 100%);
  column-gap: clamp(.75rem, 1.4vw, 1.75rem);
}

/* Hero feather reverted to the version that stood before today's experiments:
   a single short ramp resolved by 12%. The longer multi-stop ramps (38%, 24%,
   and a nine-stop eased 30%) all read worse to the user, so this is the one to
   keep. */
@media (min-width: 901px) {
  .hero--split .hero__photo img {
    -webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, #000 12%);
    mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0%, #000 12%);
  }
}


/* ---- documentation row ---------------------------------------------------
   The video and the documentation are one row, not two columns that happen to
   sit beside each other. Both halves carry the same five parts — eyebrow,
   heading, rule, lede, media — so those parts now share grid lines: whichever
   lede wraps deeper sets the row height, and the video and the document cards
   always start together.

   This replaces two stacked hand-tuned constants (`margin-top: clamp(2rem, 5vh,
   4rem)` and `clamp(4rem, 12vh, 9rem)`, both `!important`, the second silently
   winning) that guessed at the offset. A guess only lines up at the viewport it
   was measured on: at 1582px it put the cards 80px below the video and ran them
   48px past its foot, which is what the user saw. Rows cannot drift. */
/* `align-content: start` is not optional here, it is the whole thing.
   The section is `min-height: 100vh`, so the grid box is taller than its
   content, and a grid's DEFAULT `align-content: stretch` hands that leftover
   height to the auto rows. Sharing rows between the two columns therefore
   bought the alignment and paid for it in air: 87px between the eyebrow and the
   heading, 89px more before the rule, 264px before the video — the "ridiculous"
   spacing the user reported. Starting the rows makes each one hug its content;
   the section's own flex centring still places the block vertically. */
/* The two halves are not equal work. The video is the thing being watched; the
   documentation half is three thumbnails and three buttons, which read fine
   narrower. Giving the film the larger column takes it from 555px wide to about
   790 (312px tall -> 440), which is what "much bigger" asked for, without
   breaking the shared rows. */
.duo--even { grid-template-columns: 1.45fr 1px 1fr; }
/* `row-gap: 0` is the actual fix for the spacing the user called ridiculous.
   `.duo` sets `gap: clamp(2rem, 4.5vw, 4.5rem)` — a SHORTHAND, so it is a 72px
   ROW gap as well as a column gap. That never showed while each column was one
   block in a single row. Making the columns subgrids spanning five rows put
   that 72px between every part: eyebrow, heading, rule, lede and media each
   pushed 72px from the last. The rows carry their own margins already. */
.duo--even { grid-template-rows: auto auto auto auto auto; align-content: start; row-gap: 0; }
.duo--even::before { grid-row: 1 / -1; }
.duo--even > .duo__copy {
  grid-row: 1 / -1; display: grid; grid-template-rows: subgrid; align-content: start;
}
.duo--even > .duo__copy > * { align-self: start; }
.duo--even > .duo__copy > .eyebrow { grid-row: 1; }
.duo--even > .duo__copy > h2 { grid-row: 2; }
.duo--even > .duo__copy > .duo__rule { grid-row: 3; align-self: stretch; }
.duo--even > .duo__copy > p:not(.eyebrow) { grid-row: 4; }
/* one shared offset, so neither half can start before the other. `!important`
   only because the generator still stamps an inline margin on these two. */
.duo--even > .duo__copy > .videosplit__media,
.duo--even > .duo__copy > .doccards { grid-row: 5; margin-top: 1.75rem !important; }
/* ---- documentation with fewer than three downloads -----------------------
   Fourteen pages have one or two documents, not three. In a `repeat(3, 1fr)`
   grid they took a third of the column each and left the rest empty, reading as
   a row that had lost its siblings. With one or two, the group centres on the
   copy above it and the thumbnails grow to use the room (user, 2026-08-14).

   Applies to EVERY page with the section, `.doccards--pair` included. It was
   first written with `:not(.doccards--pair)` to spare SPDS and visual-ai-system,
   whose two-up treatment had already been reviewed — but the instruction was
   "any of these pages", and those two are exactly the ones that then stayed
   left-aligned. Scoping a rule narrower than the instruction is what left it
   looking unfixed. */
.doccards:has(> .doccard:only-child),
.doccards:has(> .doccard:nth-child(2):last-child) {
  justify-content: center;
}
.doccards:has(> .doccard:only-child) {
  grid-template-columns: minmax(0, 24rem);
}
.doccards:has(> .doccard:nth-child(2):last-child) {
  grid-template-columns: repeat(2, minmax(0, 19rem));
}
/* bigger artwork, since there is room for it. Outranks both the
   `.duo--even .doccard__shot` height and the `--pair` aspect-ratio set earlier. */
.doccards:has(> .doccard:only-child) .doccard__shot,
.doccards:has(> .doccard:nth-child(2):last-child) .doccard__shot {
  height: clamp(190px, 19vw, 300px); aspect-ratio: auto;
}
/* `minmax(0, 1fr)`, not `1fr`. A bare `1fr` floors at min-content, and the
   min-content of these cards is the width of "Download Spec Sheet" — so once
   the video took the larger column, MINI's three cards ran 34px past their own
   column instead of shrinking. Measured, not guessed. */
.doccards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.doccards .btn { min-width: 0; }
.doccard h3 { overflow-wrap: anywhere; }


/* ---- use-case tile label: a scrim that gets out of the way -----------------
   The label sits at the top of the photo and the tiles are bright industrial
   shots, so white text on the old `rgba(0,0,0,.55)` -> transparent gradient was
   hard to read (user, 2026-08-14): by the time the ramp reaches the text it is
   down to roughly .35 alpha.

   The scrim is now its own layer on the tile rather than the label's own
   background, which is what lets it fade WITHOUT taking the text with it. It
   sits above the photo and below the label, covers the top 42% so the ramp is
   gentle rather than a band, and clears on hover/focus so the image is
   unobstructed the moment anyone looks at it. Subtle, and gone when in the way.

   Applies to all 20 pages carrying the section — it is one shared class. */
.uctile::before {
  content: "";
  position: absolute; inset: 0 0 auto 0; height: 42%;
  background: linear-gradient(180deg, rgba(0, 0, 0, .58) 0%, rgba(0, 0, 0, .34) 45%, rgba(0, 0, 0, 0) 100%);
  opacity: 1;
  transition: opacity 320ms var(--ease);
  pointer-events: none;
  z-index: 1;
}
.uctile:hover::before,
.uctile:focus-within::before { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .uctile::before { transition: none; }
}
/* the label keeps the text and gives up the background to the scrim above */
.uctile__label { background: none; z-index: 2; }


/* ---- feature tiles (SmartEye DETECT FEATURES) ----------------------------
   The same `.uctile` the application-idea carousel uses, but each tile also
   carries a sentence under its name, as live has it. The label block therefore
   needs to read as two levels rather than one long line, and the scrim above
   covers a little more of the tile because there is more text to sit on. */
.uctile__label:has(.uctile__desc) { padding-bottom: 1.25rem; }
.uctile__desc {
  display: block; margin-top: .45rem;
  font-family: var(--font-text); font-weight: 400;
  font-size: clamp(.74rem, .72vw, .85rem); line-height: 1.35;
  color: rgba(255, 255, 255, .92);
}
.uctile:has(.uctile__desc)::before { height: 62%; }


/* ---- video boxes take the film's own aspect ------------------------------
   `.videosplit__media` was a hard 16/9 with `object-fit: cover` on the video.
   That is fine for the four 1920x1080 films and destroys the fifth: MHE Lights'
   clip is 608x1080 PORTRAIT, so a landscape box cropped away most of the frame
   — the "looks cropped" the user reported, and the no-forced-images rule broken
   by a default rather than a decision.

   The generators now stamp `--media-ratio` from the file's real pixel size, so
   the box matches the film and `contain` has nothing left to crop. YouTube
   iframes keep 16/9: an iframe has no intrinsic ratio and YouTube letterboxes
   inside it. */
.videosplit__media--file { aspect-ratio: var(--media-ratio, 16 / 9); }
.videosplit__media--file video { object-fit: contain; }
/* a portrait film would otherwise run the section absurdly tall */
.videosplit__media--file { max-height: min(70vh, 620px); margin-inline: auto; }

/* ---- ALL PRODUCTS tiles: bigger artwork in the same box -------------------
   The six forklift tiles showed the product small inside its card and the
   projection on the floor was hard to make out (user). The card keeps its size
   and its place in the grid; the photo simply gets more of it — a taller box
   and less padding under it. `cover` is right here and stays: these are
   photographs shot for the card, not cutouts with baked-in margins. */
.prodcol--simple .prodcol__shot { aspect-ratio: 4 / 3; margin-bottom: .85rem; }
/* A portrait film in a full-width column letterboxes into wide black bars. Size
   the BOX from the film instead: fixed height, width derived from the ratio,
   centred in the column. Landscape films are unaffected — they fill the column
   as before. */
.videosplit__media--portrait {
  height: min(70vh, 620px); width: auto; max-width: 100%;
  aspect-ratio: var(--media-ratio, 9 / 16); margin-inline: auto;
}
/* `.prodcol__shot` is given an explicit `height` earlier in the file, and an
   explicit height beats `aspect-ratio` — so the 4/3 box above did nothing on its
   own and the photo stayed small. Height released here, and the image told
   plainly to fill the box. */
.prodcol--simple .prodcol__shot { height: auto; aspect-ratio: 4 / 3; }
.prodcol--simple .prodcol__shot img {
  width: 100%; height: 100%; max-width: none; max-height: none;
  object-fit: cover; border-radius: var(--r);
}


/* ---- visual-ai: separating the fleet section from the one above ----------
   `.section--fleet` and the tagless-system section above it were BOTH
   `var(--surface)`, so they ran together as one long band with no edge — the
   user's "no wearables section is blending into this one". The section above
   takes the page white that the rest of the page uses, and the fleet section
   keeps the surface tint plus a hairline top edge and a little more height, so
   it reads as its own section.

   Targeted with `:has(+ .section--fleet)` rather than by changing
   `.section--surface` globally: that class is used on other pages where the
   tint is correct. */
.section--surface:has(+ .section--fleet) { background: var(--page); }
.section--fleet {
  border-top: 1px solid var(--line);
  padding-block: clamp(3.5rem, 8vh, 6rem);
}

/* ---- type scale: the workflow heading was smaller than its own eyebrow ----
   Measured at 1582px: this h2 rendered 22.4px against a 17.2px eyebrow, while
   every other section heading on the site sits at 36-45.6px. Nothing had given
   it `--fs-h2`, so it fell back to the browser's h2 default and the section
   read as if the red kicker were the title.

   `--fs-h2` is the site's one section-heading size; using it here is what keeps
   the hierarchy consistent rather than picking a new number. */
.section--workflow .head--rule h2 { font-size: var(--fs-h2); line-height: 1.02; }
/* the three cards under it carry red names, like every other card title */
.wfcard h3 { color: var(--red); }


/* ---- Smart Industry 4.0: the "Smart Industry Era" section ----------------
   Three paragraphs reading as one column beneath the heading, the trigger
   pills, then four cards rather than the three the workflow row assumes. */
.siprose { max-width: 68ch; }
.siprose p { color: var(--ink-2); margin-bottom: 1rem; }
.siprose p:last-child { margin-bottom: 0; }
.section--workflow .prodhero__segments { margin-block: clamp(1.75rem, 3.5vh, 2.75rem); }
.wfrow--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1100px) { .wfrow--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px)  { .wfrow--4 { grid-template-columns: 1fr; } }


/* ---- Book a Meeting form -------------------------------------------------
   Fields stack in one column; the two conditional ones (I am, State/Province)
   are `hidden` until the answer above calls for them. */
.section--meeting .duo { align-items: start; }
.mtform { display: grid; gap: 1.1rem; max-width: 30rem; }
.mtfield { display: grid; gap: .4rem; }
.mtfield[hidden] { display: none; }
.mtfield__label { font-family: var(--font-display); font-weight: 700;
  font-size: .82rem; letter-spacing: .04em; text-transform: uppercase; color: var(--ink); }
.mtfield__select, .mtfield__input {
  width: 100%; padding: .8rem .9rem; font: inherit; font-size: .95rem;
  color: var(--ink); background: #fff;
  border: 1px solid var(--line); border-radius: var(--r-sm);
}
.mtfield__select:focus-visible, .mtfield__input:focus-visible {
  outline: 2px solid var(--red); outline-offset: 1px; border-color: var(--red);
}
.mtform__send { justify-self: start; margin-top: .35rem; }
.mtform__note { font-size: .9rem; color: var(--ink-2); margin: 0; }
.mtform__note--warn { color: var(--red); }


/* ---- Smart Industry 4.0: the two-column era section ----------------------
   Copy left; the right column carries its own kicker + title above the tile
   carousel, so the pair reads as the carousel's heading rather than as more of
   the left column's copy. */
.sicarousel { min-width: 0; }
.sicarousel .eyebrow { color: var(--red); }
.sicarousel__title {
  font-family: var(--font-display); font-weight: 800; color: var(--ink);
  font-size: var(--fs-h2-tight); line-height: 1.06; letter-spacing: -.02em;
  text-transform: none; margin: .3rem 0 1.4rem;
}

/* four smaller tiles at a time (Smart Industry 4.0's applications section) */
.section--ucquad .uccarousel__track {
  grid-auto-columns: calc((100% - 3 * .6rem) / 4);
  gap: .6rem;
}
.section--ucquad .uctile { aspect-ratio: 3 / 4; }
@media (max-width: 900px) {
  .section--ucquad .uccarousel__track { grid-auto-columns: calc((100% - .6rem) / 2); }
}

/* ---- Book a Meeting: copy + form left, photo right ----------------------- */
.section--meeting .duo { align-items: center; }
.mtmedia { display: flex; align-items: center; justify-content: center; min-width: 0; }
.mtmedia .docmedia { margin-top: 0; width: 100%; }
.mtmedia img { width: 100%; height: auto; max-height: 62vh; object-fit: contain; }


/* ==========================================================================
   MEGA MENU — three panels, matching live
   Solutions puts ONE looping video per family on the left with that family's
   items beside it. The older menu gave every item its own thumbnail, which is
   not what live does.
   ========================================================================== */
.header__tools { display: flex; align-items: center; gap: .9rem; margin-left: auto; }
.header__search { width: 34px; height: 34px; display: grid; place-items: center;
  background: none; border: 0; color: var(--ink); cursor: pointer; border-radius: 50%; }
.header__search:hover { background: var(--surface); }
.header__search svg { width: 19px; height: 19px; }

.panel__grid { display: grid; gap: clamp(1.5rem, 2.6vw, 3rem); padding: 2rem 0 2.4rem;
  align-items: start; }
.panel__grid--solutions { grid-template-columns: minmax(0, 2.05fr) minmax(0, .82fr) minmax(0, 1fr); }
.panel__grid--resources { grid-template-columns: minmax(0, 1.35fr) repeat(2, minmax(0, .8fr)) minmax(0, 1fr); }
.panel__grid--contact   { grid-template-columns: minmax(0, 1.6fr) minmax(0, .9fr) minmax(0, 1fr); }

.panel__col-label, .panel__group-label {
  font-family: var(--font-mono, var(--font-display)); font-size: .68rem; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; color: var(--ink-3, #8a8f98);
  margin: 0 0 .55rem; padding-bottom: .5rem; border-bottom: 1px solid var(--line);
}

/* --- a family: video left, its items right --- */
.panel__fams { display: grid; gap: 1.15rem; min-width: 0; }
.navfam { display: grid; grid-template-columns: 152px minmax(0, 1fr); gap: 1.15rem;
  align-items: start; }
.navfam__video { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block;
  border-radius: var(--r-sm); background: var(--surface); }
.navfam__body { min-width: 0; }
.navfam__links { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .5rem 1.4rem; }
.navfam:nth-child(2) .navfam__links,
.navfam:nth-child(3) .navfam__links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.navfam:nth-child(4) .navfam__links { grid-template-columns: minmax(0, 1fr); }

/* --- one link: red icon, red title, sentence --- */
.navrow { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: .6rem;
  align-items: start; padding: .45rem 0; text-decoration: none; }
/* (sizing and masking for .navrow__icon are set in the corrections block at the
   end of this file. The `mask:` SHORTHAND that used to live here reset
   mask-image to none and killed every icon — see the note down there.) */
.navrow__title { display: block; color: var(--red); font-family: var(--font-display);
  font-weight: 700; font-size: .95rem; line-height: 1.2; }
.navrow__desc { display: block; color: var(--ink-2); font-size: .8rem; line-height: 1.35;
  margin-top: .18rem; }
.navrow:hover .navrow__title { text-decoration: underline; }

.panel__stack { display: grid; gap: 1.6rem; min-width: 0; }
.navgroup__rows { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .4rem 1.4rem; }
.panel__grid--resources .navgroup__rows { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* --- promo card --- */
.navpromo { position: relative; display: block; overflow: hidden; text-decoration: none;
  background: var(--red); color: #fff; border-radius: var(--r-sm);
  padding: .85rem .95rem 1.1rem; margin-bottom: .8rem; min-height: 128px; }
.navpromo--big { min-height: 232px; }
.navpromo__kicker { display: inline-block; background: #fff; color: var(--red);
  font-family: var(--font-display); font-weight: 800; font-size: .6rem; letter-spacing: .1em;
  text-transform: uppercase; padding: .22rem .5rem; border-radius: 4px; margin-bottom: .5rem; }
.navpromo__title { display: block; position: relative; z-index: 1; max-width: 60%;
  font-family: var(--font-display); font-weight: 800; font-size: clamp(.95rem, 1.1vw, 1.25rem);
  line-height: 1.1; }
.navpromo__art { position: absolute; right: -6%; bottom: 0; width: 52%; height: 82%;
  object-fit: contain; object-position: bottom right; }
.navpromo__arrow { position: absolute; top: .8rem; right: .85rem; width: 18px; height: 18px; }

/* --- case-study card --- */
.navposts { display: grid; gap: .55rem; }
.navpost { display: grid; grid-template-columns: minmax(0, 1fr) 88px; gap: .7rem;
  align-items: center; text-decoration: none; border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: .6rem; }
.navpost:hover { border-color: var(--red); }
.navpost__title { display: block; color: var(--ink); font-family: var(--font-display);
  font-weight: 700; font-size: .82rem; line-height: 1.25; }
.navpost__tag { display: inline-block; background: var(--red); color: #fff; font-size: .6rem;
  font-weight: 700; letter-spacing: .04em; padding: .18rem .45rem; border-radius: 999px;
  margin-top: .4rem; }
.navpost__meta { display: block; color: var(--ink-2); font-size: .68rem; margin-top: .3rem; }
.navpost__thumb { width: 88px; height: 62px; object-fit: cover; border-radius: 4px; display: block; }

/* --- search --- */
.searchbar { border-top: 1px solid var(--line); background: #fff; }
.searchbar[hidden] { display: none; }
.searchbar__form { display: flex; align-items: center; gap: .7rem; padding: .9rem 0; }
.searchbar__form svg { width: 20px; height: 20px; color: var(--ink-2); flex: 0 0 auto; }
.searchbar__form input { flex: 1; border: 0; font: inherit; font-size: 1.05rem;
  color: var(--ink); background: none; }
.searchbar__form input:focus-visible { outline: none; }
.searchbar__close { background: none; border: 0; cursor: pointer; color: var(--ink-2);
  width: 30px; height: 30px; display: grid; place-items: center; }
.searchbar__results { padding-bottom: 1rem; }
.searchresult { display: block; padding: .5rem 0; text-decoration: none; color: var(--ink);
  border-top: 1px solid var(--line-soft, var(--line)); font-size: .92rem; }
.searchresult:hover { color: var(--red); }
.searchresult span { display: block; color: var(--ink-2); font-size: .78rem; }
.searchbar__empty { color: var(--ink-2); font-size: .9rem; padding: .6rem 0; }

/* The one-column panel collapse that used to sit here fired under 1200px
   and made the Solutions panel 2383px tall inside a 642px window, with no
   way to scroll it. Removed 2026-08-26 with the two-column rule above:
   under 1200px the phone drawer is the menu. */


/* ==========================================================================
   MEGA MENU — corrections (user, 2026-08-18)
   ========================================================================== */

/* 1. The icons were solid red squares. `.icon--*` supplies ONLY `mask-image`;
   everything else (repeat, position, size, the paint colour) comes from the
   consuming class. My rule used a `mask` SHORTHAND with an undefined
   `var(--icon)`, which is invalid, so no mask applied at all and the element
   painted as its background colour. Long-hand, exactly like `.iconbar__icon`. */
.navrow__icon {
  width: 20px; height: 20px; margin-top: .1rem;
  background-color: var(--red);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
}

/* 2. One grid for every family, so item N of each family lands in column N.
   The families had 3 / 2 / 2 / 1 columns, so each row stretched its items to
   fill the width and nothing lined up below the first column. A single
   three-column track fixes the alignment AND leaves the empty cells where a
   family has fewer items, which is the "as if it had a vertical row below it"
   spacing. */
.navfam__links,
.navfam:nth-child(2) .navfam__links,
.navfam:nth-child(3) .navfam__links,
.navfam:nth-child(4) .navfam__links {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .35rem clamp(1rem, 1.6vw, 2rem);
  align-items: start;
}
.navgroup__rows,
.panel__grid--resources .navgroup__rows {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .35rem clamp(1rem, 1.6vw, 2rem);
  align-items: start;
}
/* every family block gets the same rhythm, so the left column reads as a grid
   rather than four blocks of different heights */
.panel__fams { gap: clamp(1rem, 1.5vw, 1.5rem); }
.navfam { align-items: start; }
.navrow { padding: .4rem 0; align-items: start; }

/* 3. The three panel columns are the same height. `align-items: start` on the
   panel grid let each column collapse to its own content, so Need help? and
   Want to know more? ended shorter than the families beside them. */
.panel__grid { align-items: stretch; }
.panel__col, .panel__stack, .panel__fams { display: flex; flex-direction: column; }
.panel__col .navposts { flex: 1; }
.panel__col .navpromo:last-child { margin-bottom: 0; }
/* the post list grows to fill its column. The promo cards do NOT -- they hold
   the artwork's own ratio; see the promo-card block further down. */
.navposts .navpost { flex: 1; }


/* ==========================================================================
   MEGA MENU — round 2 corrections (user, 2026-08-18)
   ========================================================================== */

/* 1. The icons are IMAGES, not masks. Each is two-colour artwork: a red rounded
   tile with a white glyph on it. A CSS mask uses the alpha channel, so masking
   an opaque tile paints a solid red square — which is what was on screen. The
   earlier check only proved a mask was APPLIED, never that it looked right. */
img.navrow__icon {
  width: 22px; height: 22px; flex: 0 0 auto; display: block;
  background: none; -webkit-mask-image: none; mask-image: none;
  object-fit: contain;
}

/* 2. The nav sits at the RIGHT of the bar, just left of the search glass and
   the Set Up a Meeting button, rather than beside the logo. */
.header__bar { display: flex; align-items: center; gap: clamp(1rem, 2vw, 2.2rem); }
.header__logo { margin-right: auto; }
.header__nav { display: flex; align-items: center; gap: clamp(1rem, 1.8vw, 2rem); }
.header__tools { margin-left: 0; }

/* 3. The open panel stops short of both edges and its bottom corners are
   rounded, so it reads as a card dropped from the bar rather than a full-bleed
   band. */
.navitem .panel {
  left: clamp(12px, 1.6vw, 28px); right: clamp(12px, 1.6vw, 28px); width: auto;
  border-radius: 0 0 var(--r) var(--r);
  border: 1px solid var(--line); border-top: 0;
  box-shadow: 0 18px 40px rgba(16, 17, 20, .12);
  overflow: hidden;
}

/* the family video keeps its box even before the file loads */
.navfam__video { background: var(--surface); }


/* ---- promo cards are one image ------------------------------------------
   The red block, its wording and the photo are all baked into a single
   artwork by the designer, so the card is just that image. No padding, no
   background, nothing drawn in CSS on top of it. */
.navpromo { background: none; padding: 0; min-height: 0; overflow: hidden;
  border-radius: var(--r-sm); display: block; }
.navpromo__img { width: 100%; height: 100%; object-fit: cover; display: block; }
.navpromo--big { min-height: 0; }
/* Each card keeps the artwork's OWN aspect ratio (set inline by header.py from
   the file itself). The five artworks are not one shape, so a shared slot
   height would crop the wording out of the odd one. Never stretch these. */
.panel__grid .panel__col .navpromo,
.panel__grid .panel__col .navpromo--big { flex: 0 0 auto; width: 100%; min-height: 0; }
/* Need help? holds two cards of different shapes. Space them apart rather than
   stretching either one, so the column still reaches the family height. */
.panel__grid--solutions .panel__col:nth-of-type(2) { gap: clamp(.75rem, 1.4vw, 1.5rem); }

/* ==== BEGIN blog.css (folded in 2026-08-24) ==============================
   blog.css was kept separate only while a second session was building the
   blog lane and could not safely share this file (see BLOG-LANE.md). That
   lane is merged, so its rules live here now, verbatim and in order.
   Everything between these two markers came from blog.css; edit it here.
   ======================================================================== */
/* Blog listings and article pages.
   ---------------------------------------------------------------------------
   TEMPORARY SEPARATE FILE. This belongs in site.css and will be folded into it
   at the merge described in BLOG-LANE.md. It is split out only because a second
   session is editing site.css right now and there is no git here to merge with.
   Do not add anything to this file that is not blog-specific.

   Tokens come from site.css :root. Nothing here redefines one. */


/* Category tab bar ---------------------------------------------------------
   Live goes header -> red band -> tabs, with no hero. */

.blogtabs {
  border-bottom: 1px solid var(--line);
}

.blogtabs__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(1.5rem, 2.6vw, 2.75rem);
  margin: 0;
  padding: clamp(1.75rem, 3vw, 2.75rem) 0;
  list-style: none;
}

.blogtabs__link {
  display: inline-block;
  font-family: var(--font-text);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
  text-decoration: none;
  transition: color var(--t-fast) var(--ease);
}

.blogtabs__link:hover {
  color: var(--red-deep);
}

/* Active tab is UI Red, matching live and the rest of the site. Flagged in
   BLOG-LANE.md: #ff3a42 on white measures 3.55:1, which clears the 3:1 floor
   for large text and UI components but not the 4.5:1 body-text floor. Left
   matching the site rather than diverging on one page. */
.blogtabs__link[aria-current="page"] {
  color: var(--red);
}


/* Post grid ---------------------------------------------------------------- */

.postgrid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.5rem, 2.4vw, 2.25rem);
  align-items: start;          /* live's cards are uneven, they do not stretch */
  padding: clamp(2.5rem, 4vw, 4rem) 0 clamp(3.5rem, 6vw, 6rem);
}

@media (max-width: 1100px) {
  .postgrid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 700px) {
  .postgrid { grid-template-columns: 1fr; }
}


/* Post card ---------------------------------------------------------------- */

.postcard {
  display: flex;
  flex-direction: column;
  background: var(--page);
  border: 1px solid var(--line);
  overflow: hidden;
}

.postcard__media {
  display: block;
  margin: 0;
}

/* No aspect-ratio and no object-fit: the site rule forbids forcing an image
   into a shape it does not have, and live's own cards are visibly uneven
   because its covers range from landscape to near-square. */
.postcard__media img {
  display: block;
  width: 100%;
  height: auto;
}

.postcard__body {
  padding: clamp(1.25rem, 1.8vw, 1.75rem);
}

.postcard__byline {
  font-family: var(--font-text);
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ink-3);
}

.postcard__author {
  display: block;
  color: var(--ink-2);
}

.postcard__sep {
  padding: 0 0.35em;
}

.postcard__cat {
  display: inline-block;
  margin: 0.85rem 0 0;
  padding: 0.34rem 0.85rem;
  border-radius: var(--r-pill);
  background: var(--red);
  color: #fff;
  font-family: var(--font-text);
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
}

.postcard__cat:hover {
  background: var(--red-deep);
}

/* site.css uppercases h1-h4 globally, which is correct everywhere else on the
   site but wrong here -- live's blog card headlines are title case. */
.postcard__title {
  margin: 0.85rem 0 0;
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 1.35vw, 1.45rem);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
  text-transform: none;
}

.postcard__title a {
  color: var(--ink);
  text-decoration: none;
}

.postcard__title a:hover {
  color: var(--red-deep);
}

.postcard__excerpt {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  margin: 0.7rem 0 0;
  font-family: var(--font-text);
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--ink-2);
}


/* Pagination --------------------------------------------------------------- */

.blogpager {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  padding: 0 0 clamp(3rem, 6vw, 5rem);
}

.blogpager__link {
  min-width: 2.5rem;
  padding: 0.55rem 0.75rem;
  border: 1px solid var(--line);
  font-family: var(--font-text);
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ink);
  text-align: center;
  text-decoration: none;
}

.blogpager__link:hover {
  border-color: var(--red);
  color: var(--red-deep);
}

.blogpager__link[aria-current="page"] {
  background: var(--red);
  border-color: var(--red);
  color: #fff;
}

.blogpager__gap {
  padding: 0 0.25rem;
  color: var(--ink-3);
}


/* ===========================================================================
   ARTICLE PAGES
   A redesign on the brand book, not a replica of live. Text is never rewritten;
   only the structure it sits in changes.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   THE GRID -- read this before adding anything below.

   ONE content column. Two tracks, sharing both edges:

       text / wide   the content column: copy, media, callouts, bands, cards
       full          edge-to-edge background bands only

   `text` and `wide` deliberately resolve to the SAME grid lines. Body copy,
   images, video, the CTA band and the related-post cards therefore have
   identical left and right edges all the way down the page.

   History, so it is not re-litigated: v1 used 780 / 1100 / 1410 and v2 used
   704 / 1056. Both were wrong. Media wider than the text it belongs to reads as
   a mismatch, and a narrow measure inside a wide viewport reads as squeezed.
   One column, and a generous one.

   Never hand-compute a width with margin/transform tricks. Use the column.
   --------------------------------------------------------------------------- */

.artgrid {
  /* ONE content column. Copy, images, video, callouts, the CTA band and the
     related posts are all exactly this wide and share both edges. An earlier
     version gave text 704px and media 1056px, which is the mismatch the user
     flagged: media should not be wider than the text it belongs to. */
  --measure: 60rem;

  /* Type scale. Every size on the page comes from this list -- there is no
     ad-hoc font-size below. Body copy is ONE size wherever it appears: in a
     paragraph, a list, a callout or an FAQ answer. */
  --fs-label: 0.75rem;    /* Space Mono, 700, uppercase, tracked */
  --fs-body: 1.1875rem;   /* Madefor Text, 400 */
  --fs-lead: 1.375rem;    /* Madefor Text, 400 */
  --fs-quote: 1.5rem;     /* Madefor Display, 600 */
  --fs-h2: 2rem;          /* Madefor Display, 800 */

  display: grid;
  grid-template-columns:
    [full-start] minmax(var(--gutter), 1fr)
    [text-start wide-start] min(var(--measure), 100% - var(--gutter) * 2) [text-end wide-end]
    minmax(var(--gutter), 1fr) [full-end];
}

.artgrid > * {
  grid-column: text;
}

.artgrid > .u-wide {
  grid-column: wide;
}

.artgrid > .u-full {
  grid-column: full;
}


/* Reading progress ---------------------------------------------------------
   A long article feels shorter when its length is legible. 2px, brand red. */

.artprogress {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 60;
  width: 100%;
  height: 2px;
  background: transparent;
  pointer-events: none;
}

.artprogress__bar {
  height: 100%;
  width: 0;
  background: var(--red);
  transition: width 90ms linear;
}


/* Hero ---------------------------------------------------------------------
   Dark ground so the cover can run full bleed behind the type and still clear
   contrast comfortably. Hero copy sits on `text`, same edge as the body. */

.arthero {
  position: relative;
  background: var(--dark);
  color: var(--on-dark);
  overflow: hidden;
}

.arthero__media {
  position: absolute;
  inset: 0;
}

.arthero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;      /* a background plate, not a content image */
  opacity: 0.4;
}

/* Without a scrim the type sits on whatever the photo happens to be doing,
   which is the exact failure the light-background contrast rule warns about. */
.arthero--media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg,
              rgba(13, 13, 13, 0.96) 0%,
              rgba(13, 13, 13, 0.88) 46%,
              rgba(13, 13, 13, 0.6) 100%);
}

.arthero__inner {
  position: relative;
  z-index: 1;
  padding: clamp(2.25rem, 4vw, 3.5rem) 0 clamp(2rem, 3.5vw, 2.75rem);
}

.arthero__crumb {
  margin: 0 0 1.5rem;
  font-family: var(--font-text);
  font-size: 0.875rem;
}

.arthero__crumb a {
  color: var(--on-dark-2);
  text-decoration: none;
}

.arthero__crumb a:hover {
  color: #fff;
}

/* Space Mono, uppercase, letter-spaced: the brand book's eyebrow treatment. */
.arthero__eyebrow {
  display: inline-block;
  margin-bottom: 1.1rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--red);
  text-decoration: none;
}

.arthero__eyebrow:hover {
  color: #fff;
}

.arthero__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 3.3vw, 2.9rem);
  font-weight: 800;
  line-height: 1.06;
  letter-spacing: -0.03em;
  color: #fff;
}

.arthero__meta {
  margin: 1.5rem 0 0;
  font-family: var(--font-text);
  font-size: 0.9375rem;
  color: var(--on-dark-2);
}


/* Body ---------------------------------------------------------------------
   Bigger type, looser leading and more space between blocks. The complaint was
   that solid text felt overwhelming; the fix is rhythm, not fewer words. */

.artbody {
  padding: clamp(2.75rem, 5vw, 4.5rem) 0 clamp(2.5rem, 4vw, 4rem);
  row-gap: 0;
}

/* :not([class]) matters. The callout, CTA, TOC and FAQ blocks all sit INSIDE
   .artbody, so an unqualified `.artbody p` outranks their own colour rules and
   repaints them --ink. That put near-black text on the near-black CTA band and
   killed the red Key Takeaway label. Everything structured carries a class;
   plain body copy does not. Same reasoning for excluding .btn from the link
   colour, which was painting red button labels red-on-red. */
.artbody p:not([class]) {
  margin: 0 0 1.5em;
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: 1.75;
  color: var(--ink);
}

.artbody a:not(.btn) {
  color: var(--red-deep);
  text-underline-offset: 0.2em;
}

.artbody a:not(.btn):hover {
  color: var(--ink);
}

/* The opening paragraph, set larger. Gives the reader a way in and breaks the
   wall of even-weight text at the top. */
.artlead {
  margin: 0 0 1.75em;
  font-family: var(--font-text);
  font-size: var(--fs-lead);
  line-height: 1.65;
  color: var(--ink);
}

/* Sentence case, deliberately. site.css uppercases headings globally and that
   is right for a hero, but seven uppercase subheads in an article reads as
   shouting. Generous space above is what separates one section from the next. */
.arth {
  margin: 3.5rem 0 1.1rem;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 2.4vw, var(--fs-h2));
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-transform: none;
  color: var(--ink);
  scroll-margin-top: 6rem;    /* so TOC jumps do not hide under the header */
}

/* The brand book's line language: a short red rule, not a box. */
.arth::before {
  content: "";
  display: block;
  width: 2.5rem;
  height: 3px;
  margin-bottom: 1.1rem;
  background: var(--red);
  border-radius: 2px;
}

.artbody ul,
.artbody ol {
  margin: 0 0 1.75em;
  padding-left: 1.35rem;
}

.artbody li {
  margin-bottom: 0.7em;
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: 1.75;
  color: var(--ink);
}

.artbody ul li::marker {
  color: var(--red);
}


/* Contents -----------------------------------------------------------------
   Built from the article's own subheads. Shows the shape of a long read up
   front and lets someone jump, which is the cheapest fix for "overwhelming". */

.arttoc {
  margin: 0 0 2.75rem;
  padding: clamp(1.15rem, 2vw, 1.6rem) clamp(1.25rem, 2.2vw, 1.75rem);
  background: var(--surface);
  border: 1px solid var(--line);
}

.arttoc__label {
  margin: 0 0 0.85rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.arttoc__list {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: toc;
}

.arttoc__list li {
  margin: 0;
  padding: 0.4rem 0;
  border-top: 1px solid var(--line-soft);
  counter-increment: toc;
}

.arttoc__list li:first-child {
  border-top: 0;
}

.arttoc__list a {
  display: flex;
  gap: 0.75rem;
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: 1.5;
  color: var(--ink) !important;
  text-decoration: none;
}

.arttoc__list a::before {
  content: counter(toc, decimal-leading-zero);
  flex: none;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--red-deep);
}

.arttoc__list a:hover {
  color: var(--red-deep) !important;
}


/* Key Takeaway callout ------------------------------------------------------ */

.artnote {
  margin: 2.5rem 0;
  padding: clamp(1.35rem, 2.4vw, 2rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 3px solid var(--red);
}

.artnote__label {
  margin: 0 0 0.7rem !important;
  font-family: var(--font-mono);
  font-size: var(--fs-label) !important;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--red-deep);
}

.artnote__body {
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: 1.75;
  color: var(--ink);
}

.artnote__body p:last-child {
  margin-bottom: 0;
}


/* Pull quote ---------------------------------------------------------------- */

.artquote {
  margin: 2.5rem 0;
  padding: 0.35rem 0 0.35rem 1.6rem;
  border-left: 3px solid var(--red);
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 1.9vw, var(--fs-quote));
  font-weight: 600;
  line-height: 1.45;
  letter-spacing: -0.01em;
  color: var(--ink);
}


/* Figures ------------------------------------------------------------------
   On `wide`. No aspect-ratio and no object-fit: the site rule forbids forcing
   an image into a shape it does not have. */

.artfig {
  margin: 3rem 0;
}

.artfig img,
.artfig video {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-sm);
}

.artfig--grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
  align-items: start;
}

@media (max-width: 640px) {
  .artfig--grid { grid-template-columns: 1fr; }
}


/* Inline CTA band ----------------------------------------------------------- */

.artcta {
  margin: 3.25rem 0;
  padding: clamp(1.75rem, 4vw, 3rem);
  background: var(--dark);
  color: #fff;
  text-align: center;
}

.artcta__eyebrow {
  margin: 0 0 0.9rem !important;
  font-family: var(--font-mono);
  font-size: var(--fs-label) !important;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--red);
}

.artcta__head {
  margin: 0 0 1.6rem !important;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3vw, 2.3rem) !important;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: #fff !important;
}

.artcta__head span {
  display: block;
}

/* With the banner's own artwork absorbed, the band becomes two columns: copy
   left, cutout right. Capped in height -- at full width the original renders
   as a two-metre-tall floating portrait. */
.artcta--media {
  display: grid;
  grid-template-columns: 1fr minmax(0, 0.72fr);
  gap: clamp(1.5rem, 3vw, 3rem);
  align-items: end;
  text-align: left;
  padding-bottom: 0;
}

.artcta--media .artcta__text {
  padding-bottom: clamp(1.75rem, 4vw, 3rem);
}

.artcta__media img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 18rem;
  object-fit: contain;
  object-position: bottom;
}

@media (max-width: 720px) {
  .artcta--media {
    grid-template-columns: 1fr;
    text-align: center;
    padding-bottom: clamp(1.75rem, 4vw, 3rem);
  }

  .artcta--media .artcta__media {
    display: none;    /* the cutout adds nothing at phone width */
  }
}


/* Tags ---------------------------------------------------------------------- */

.arttags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 3rem 0 0;
  padding-top: 1.75rem;
  border-top: 1px solid var(--line);
}

.arttags__label {
  margin: 0 0.35rem 0 0 !important;
  font-family: var(--font-mono);
  font-size: var(--fs-label) !important;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.arttags a {
  padding: 0.35rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-family: var(--font-text);
  font-size: 0.875rem;
  color: var(--ink-2) !important;
  text-decoration: none;
}

.arttags a:hover {
  border-color: var(--red);
  color: var(--red-deep) !important;
}


/* FAQ ----------------------------------------------------------------------
   A real disclosure list: every question visible, first one open. <details>
   rather than scripted panels, so it works without JS, is keyboard operable
   and is announced correctly by screen readers. */

.artfaq {
  margin: 3.5rem 0 0;
}

.artfaq__head {
  margin: 0 0 1.25rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.artfaq__item {
  border-top: 1px solid var(--line);
}

.artfaq__item:last-of-type {
  border-bottom: 1px solid var(--line);
}

.artfaq__q {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: 1.1rem 0;
  font-family: var(--font-display);
  font-size: var(--fs-body);
  font-weight: 700;
  line-height: 1.45;
  color: var(--ink);
  cursor: pointer;
  list-style: none;
}

.artfaq__q::-webkit-details-marker {
  display: none;
}

/* A plus that becomes a minus. Drawn with two rules rather than a glyph so it
   stays on the brand's line language and cannot pick up a font fallback. */
.artfaq__q::after {
  content: "";
  flex: none;
  width: 0.9rem;
  height: 0.9rem;
  margin-left: auto;
  margin-top: 0.3rem;
  background:
    linear-gradient(var(--red), var(--red)) center / 100% 2px no-repeat,
    linear-gradient(var(--red), var(--red)) center / 2px 100% no-repeat;
  transition: transform var(--t-fast) var(--ease);
}

/* Open state is a minus. Rotating 90deg would stand the remaining horizontal
   rule on end and render a vertical bar; 180deg animates and still reads flat. */
.artfaq__item[open] > .artfaq__q::after {
  transform: rotate(180deg);
  background:
    linear-gradient(var(--red), var(--red)) center / 100% 2px no-repeat;
}

.artfaq__q:hover {
  color: var(--red-deep);
}

.artfaq__a {
  padding: 0 0 1.4rem;
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: 1.75;
  color: var(--ink-2);
}

.artfaq__a p {
  margin: 0;
}


/* End CTA and related -------------------------------------------------------- */

.artend {
  background: var(--red);
  color: #fff;
}

.artend__inner {
  grid-column: wide;
  padding: clamp(2.5rem, 5vw, 4rem) 0;
  text-align: center;
}

.artend__eyebrow {
  margin: 0 0 0.75rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.9);
}

.artend__head {
  margin: 0 0 1.75rem;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 3.2vw, 2.5rem);
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.artend__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.85rem;
}

/* On the red band the standard ghost button's --line border and --ink text
   would both fail against red, so it is inverted here. */
.artend .btn--primary {
  background: #fff;
  border-color: #fff;
  color: var(--ink);
}

.artend .btn--ghost {
  border-color: rgba(255, 255, 255, 0.75);
  color: #fff;
}

.artend .btn--ghost:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: #fff;
}

.artrel {
  padding: clamp(3rem, 5vw, 4.5rem) 0 clamp(3.5rem, 6vw, 5.5rem);
}

.artrel__inner {
  grid-column: wide;
}

.artrel__label {
  margin: 0 0 1.5rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.artrel .postgrid {
  padding: 0;
}

/* site.css sets a reading measure on every paragraph: `p { max-width: 66ch }`.
   Right for body copy, wrong for a label or a display line -- at a 0.72rem
   Space Mono eyebrow, 66ch is only ~465px, so a centred eyebrow centres inside
   a 465px box and lands well left of the band it sits on. Anything here that is
   a <p> but is not body copy opts out. */
/* site.css caps EVERY paragraph at `p { max-width: 66ch }`. On this page that
   silently held body copy to 803px inside a 960px column, so the text was
   narrower than the images and video sitting right beside it -- the exact
   mismatch this layout exists to remove. The column is the measure here, so
   nothing on the article opts into the global cap. */
.artbody p,
.artlead,
.artnote__body p,
.artfaq__a p,
.artend__eyebrow,
.artend__head,
.artcta__eyebrow,
.artcta__head,
.artnote__label,
.artrel__label,
.arttoc__label,
.arttags__label,
.arthero__crumb,
.arthero__meta {
  max-width: none;
}
/* ==== END blog.css ====================================================== */

/* ---- newsletter section, corrected 2026-08-25 ---------------------------
   The picture and the copy are ONE centred group, with the copy ranged left
   against the picture. Three earlier rules are overridden here rather than
   edited in place, because they sit in the middle of the file next to
   `.catbar__media`, which must stay hidden:

     .newsbar__media { position: absolute; left: -2% }   bleeding off the edge
     .catbar__media, .newsbar__media { display: none }   hid it completely
     .newsbar__inner { justify-content: center }         copy alone, centred

   Only `.newsbar__media` is re-shown; `.catbar__media` keeps its own rule. */
.newsbar__inner {
  display: flex; align-items: center; justify-content: center;
  gap: clamp(1.5rem, 4vw, 4rem); flex-wrap: wrap;
}
.newsbar__media {
  display: block; position: static; transform: none; left: auto; top: auto;
  flex: 0 1 clamp(260px, 34vw, 470px); min-width: 0;
}
.newsbar__media img { width: 100%; height: auto; display: block; }
.newsbar__copy { flex: 0 1 44ch; min-width: 0; text-align: left; }
@media (max-width: 760px) {
  .newsbar__inner { flex-direction: column; }
  .newsbar__media { flex-basis: auto; max-width: 340px; }
}

/* ---- estimator pop-up, added 2026-08-25 ---------------------------------
   The estimator wizard shown over the page instead of navigating to
   /estimator-tool. Built by site.js on first use; the markup does not exist
   in any page's HTML. The panel is capped so the wizard keeps a usable
   height on a laptop, and the scrim is dark enough that the page behind it
   reads as inactive. */
.estmodal[hidden] { display: none; }
.estmodal {
  position: fixed; inset: 0; z-index: 120;
  display: grid; place-items: center; padding: clamp(.75rem, 3vw, 2.5rem);
}
.estmodal__scrim { position: absolute; inset: 0; background: rgba(13, 13, 13, .62); }
.estmodal__panel {
  position: relative; z-index: 1;
  width: min(1100px, 100%); height: min(880px, 92vh);
  display: flex; flex-direction: column;
  background: #fff; border-radius: var(--r); overflow: hidden;
  box-shadow: var(--shadow-lg);
}
.estmodal__head {
  display: flex; align-items: center; gap: 1rem;
  padding: .85rem 1rem .85rem 1.35rem; border-bottom: 1px solid var(--line);
}
.estmodal__title {
  margin: 0; font-family: var(--font-display); font-weight: 800;
  font-size: clamp(1rem, .5vw + .88rem, 1.2rem); color: var(--ink);
}
.estmodal__spacer { flex: 1 1 auto; }
.estmodal__full { font-size: .9rem; white-space: nowrap; }
.estmodal__x {
  flex: 0 0 auto; width: 38px; height: 38px; border-radius: 50%;
  border: 1px solid var(--line); background: #fff; color: var(--ink);
  cursor: pointer; display: grid; place-items: center; font-size: 1.15rem;
  line-height: 1;
}
.estmodal__x:hover { border-color: var(--ink); }
.estmodal__body { flex: 1 1 auto; min-height: 0; background: var(--surface); }
.estmodal__body iframe { width: 100%; height: 100%; border: 0; display: block; }
/* while the dialog is open the page behind it must not scroll */
body.has-estmodal { overflow: hidden; }
@media (max-width: 640px) {
  .estmodal { padding: 0; }
  .estmodal__panel { height: 100%; width: 100%; border-radius: 0; }
  .estmodal__full { display: none; }
}

/* ==== mobile header, added 2026-08-25 ===================================
   Until now the mega menu was the ONLY navigation, forced to display:flex at
   every width, so on a phone it ran off the right edge and took the whole page
   with it: 879px of content on a 390px screen. Under 1080px the desktop nav is
   hidden and the hamburger opens .navdrawer instead. */
@media (max-width: 1200px) {
  .header__nav { display: none !important; }
  .header__toggle { display: inline-flex !important; }
  /* the bar is logo | hamburger | search | CTA, and must never be wider than
     the screen: the CTA gives up its label before anything overflows */
  .header__bar { gap: .75rem; }
  .header__logo img { width: auto; height: 34px; }
  .header__cta { padding-inline: 1rem; font-size: .82rem; }
}
@media (max-width: 560px) {
  /* below this the Set Up a Meeting button cannot fit beside everything else */
  .header__cta { display: none; }
}
.header__toggle {
  order: 3; margin-inline-start: 0; flex: 0 0 auto;
  width: 44px; height: 44px; padding: 0 11px; border: 0; background: none;
  cursor: pointer; display: none; flex-direction: column; justify-content: center;
  gap: 5px;
}
.header__toggle span {
  display: block; height: 2.5px; background: var(--ink); border-radius: 2px;
}
.header__tools { order: 4; }

.navdrawer[hidden] { display: none; }
.navdrawer { position: fixed; inset: 0; z-index: 130; }
.navdrawer__scrim { position: absolute; inset: 0; background: rgba(13, 13, 13, .5); }
.navdrawer__panel {
  position: absolute; inset: 0 0 0 auto; width: min(420px, 92vw);
  background: #fff; display: flex; flex-direction: column;
  box-shadow: var(--shadow-lg);
}
.navdrawer__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: .9rem 1rem; border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.navdrawer__logo img { display: block; width: auto; height: 32px; }
.navdrawer__x {
  width: 42px; height: 42px; border-radius: 50%; border: 1px solid var(--line);
  background: #fff; color: var(--ink); cursor: pointer; display: grid;
  place-items: center; flex: 0 0 auto;
}
.navdrawer__x svg { width: 20px; height: 20px; }
.navdrawer__scroll { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.navdrawer__sec { border-bottom: 1px solid var(--line-soft); }
.navdrawer__head {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: 1.05rem 1.1rem; background: none; border: 0; cursor: pointer;
  font-family: var(--font-display); font-weight: 800; font-size: 1.05rem;
  color: var(--ink); text-align: left;
}
.navdrawer__chev { width: 20px; height: 20px; flex: 0 0 auto; transition: transform var(--t-fast); }
.navdrawer__head[aria-expanded="true"] .navdrawer__chev { transform: rotate(180deg); }
.navdrawer__body { padding: 0 1.1rem .9rem; }
.navdrawer__body[hidden] { display: none; }
.navdrawer__group {
  margin: .55rem 0 .35rem; font-family: var(--font-mono, var(--font-display));
  font-size: .72rem; letter-spacing: .08em; text-transform: uppercase;
  font-weight: 700; color: var(--red);
}
.navdrawer__list { list-style: none; margin: 0 0 .5rem; padding: 0; }
/* 44px tall so it is a real tap target, not a line of text */
.navdrawer__link {
  display: block; padding: .62rem .2rem; min-height: 44px; line-height: 1.35;
  color: var(--ink); text-decoration: none; font-size: 1rem;
}
.navdrawer__link:hover, .navdrawer__link:focus-visible { color: var(--red); }
.navdrawer__foot { padding: 1.1rem; border-top: 1px solid var(--line); }
.navdrawer__cta { width: 100%; justify-content: center; }
body.has-navdrawer { overflow: hidden; }
@media (min-width: 1200.02px) { .navdrawer { display: none !important; } }

/* The decorative watermark words ("Trade shows", "Trade Shows") are set very
   large and absolutely positioned. Their BOX fits, but the TEXT inside runs
   wider than the box, and text overflow still extends the document: it was the
   last thing making the homepage and /contact-us 557px wide on a 504px screen,
   after the header was fixed. Clipped below the desktop breakpoint only, since
   at desktop widths the word fits and is meant to bleed to the section edge. */
@media (max-width: 1200px) {
  .watermark, .uwatermark { overflow: hidden; max-width: 100%; }
}

/* The product comparison columns must stack on a phone. site.css sets them
   three times and the LAST one wins: line 1313 repeat(2,1fr), line 1333
   @media(max-width:760px) 1fr, then line 2477 repeat(2,minmax(0,1fr)) with NO
   media query, which is later and so beat the mobile rule. The result was two
   169px columns on a 390px screen holding content that needs 302px, which is
   what pushed /projected-signs, /projected-walkways and /crane-lights wider
   than the screen. Restated here, after all three. */
@media (max-width: 760px) {
  .prodgrid, .prodgrid--simple { grid-template-columns: 1fr; }
}

/* ---- phone tap targets, added 2026-08-25 -------------------------------
   Measured at 390px: blog tabs 19px tall, post-card category chips 28px, and
   27 footer links per page at 16px. Grown to ~44px, the figure both Apple and
   Google give as the minimum. Under 1080px only. Vertical padding on an INLINE
   element grows the paint box but not the line box, so anything inline becomes
   inline-flex here or the hit area does not actually change. */
@media (max-width: 1079.98px) {
  .blogtabs__link {
    display: inline-flex; align-items: center; min-height: 44px;
    padding-block: .35rem;
  }
  .postcard__cat {
    display: inline-flex; align-items: center; min-height: 34px;
  }
  /* the footer link columns are <a> inside <li>; naming the columns by class
     missed them, so match on the list instead */
  .footer li > a, .footer__legal a, .footer__col a, .footer__links a {
    display: inline-flex; align-items: center; min-height: 44px;
  }
}


/* ==== phone: stack anything still side by side, added 2026-08-25 =========
   LAST IN THE FILE ON PURPOSE. Every one of these components already had a
   mobile rule somewhere above; each was overridden by a later unconditional
   rule for the same class, because at equal specificity the last rule wins
   whether or not the earlier one sat in a media query. `.prodgrid` was set
   three times and lost the same way. Anything added after this block must
   check here first. */
@media (max-width: 760px) {
  .usecases,
  .videosplit,
  .duo,
  .duo--ruled,
  .duo--even {
    grid-template-columns: minmax(0, 1fr) !important;
    row-gap: clamp(1.5rem, 5vw, 2.5rem);
  }
  /* the centre rule in .duo--ruled and the empty middle track in .videosplit
     are vertical dividers between two columns; with one column there is
     nothing for them to divide */
  .duo--ruled::before, .usecases::before { content: none !important; display: none !important; }
  /* BOTH axes. Resetting only the column left two .duo__copy blocks in
     .videosplit still placed at "grid-row: 1 / span 2", so they landed in the
     SAME cell and rendered on top of each other: 879px of overlap on
     /visual-ai-system and /smart-proximity-detection-system-spds, and the same
     on the product pages. Explicit placement has to be undone on the row as
     well as the column, which is exactly what the /lp-safety-projections hero
     taught and I did not carry across. */
  .duo > *, .usecases > *, .videosplit > * {
    grid-column: 1 !important; grid-row: auto !important;
  }

  .doccards { grid-template-columns: minmax(0, 1fr) !important; }
  .minicards { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 560px) {
  .prodgrid--simple, .lpprod__grid { grid-template-columns: minmax(0, 1fr); }
}

/* The carousel arrows hang 17px outside their track, which is right on a
   desktop where there is gutter to hang in. On a phone the track starts at the
   screen edge, so the Previous arrow sat half off the left of the screen.
   Brought inside, over the first tile. */
@media (max-width: 760px) {
  .ucnav--prev { left: 6px; }
  .ucnav--next { right: 6px; }
}

/* Carousel tiles on a phone. At 390px the shared track packed 2.35 tiles, so
   each was 157px wide, and a tile that carries a NAME PLUS A SENTENCE needed
   236px of text in a 210px box: on /smart-industry-4-point-0 the copy spilled
   out of the tile and was clipped mid-sentence over the photograph.
   One tile plus a peek of the next, which also signals that it scrolls. */
@media (max-width: 760px) {
  .uccarousel__track { grid-auto-columns: 86%; }
  /* belt and braces: text can never leave its tile again, whatever the copy */
  .uctile__label { overflow: hidden; }
}


/* ==== the HOMEPAGE on a phone, to the user's spec 2026-08-25 =============
   Ordered to match the page. Scoped to `.home` where a rule would otherwise
   reach other pages that share the component. */
@media (max-width: 760px) {

  /* 1 + 2. Hero and everything else centred. */
  .hero__inner, .heroslide__body { text-align: center; }
  .heroslide__body .btn-row { justify-content: center; }
  .heroslide__body .eyebrow { justify-content: center; text-align: center; }
  .hero__foot { justify-content: center; }

  .home .section > .wrap > .head,
  .home .split > div,
  .home .catbar__copy,
  .home .newsbar__copy,
  .home .pillars__introcopy,
  .home .tl__opencopy { text-align: center; }
  .home .split > div .btn-row,
  .home .catbar__copy .btn-row,
  .home .newsbar__copy .btn-row { justify-content: center; }
  .home .lede, .home .split > div > p { margin-inline: auto; }

  /* 3. The product circle gets the width it needs, on its own, above the
        copy. It was rendering at about 250px inside a stacked grid cell. */
  #laserglow-technologies .split { grid-template-columns: minmax(0, 1fr); row-gap: 0; }

  /* The mesh is `left: 0; width: 56%` with `cover`. Stacked on a phone that is
     a tall narrow strip behind the copy as well as the circle, and `cover` on
     a tall narrow box is what magnified it so hard. Off here; it moves onto
     the spinner block below. */
  .section--grid::before { display: none; }

  /* The spinner gets a screen of its own, with the mesh behind IT, at the
     image's full width (100% auto, not cover) so nothing is magnified. */
  #laserglow-technologies .split__media {
    order: -1; width: 100%; position: relative;
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100dvh - var(--header-h));
    display: grid; place-items: center;
  }
  /* section-bg.jpg is SQUARE (2162x2160). At the full width the user asked for
     it can only ever be 390 tall on a 390 screen, so stretching it down a
     700px block would either magnify it again or tile with a seam. It sits as
     a square, centred, with the circle centred on it: full width, no
     distortion, no repeat. Edges feathered so the square does not show. */
  #laserglow-technologies .split__media::before {
    content: ''; position: absolute; z-index: 0; pointer-events: none;
    left: 0; width: 100%; top: 50%; transform: translateY(-50%);
    aspect-ratio: 1 / 1;
    background: url('/assets/approved/section-bg.jpg') center center / 100% 100% no-repeat;
    opacity: .38;
    -webkit-mask-image: radial-gradient(ellipse 78% 78% at 50% 50%, #000 55%, transparent 92%);
    mask-image: radial-gradient(ellipse 78% 78% at 50% 50%, #000 55%, transparent 92%);
  }
  #laserglow-technologies .pcircle {
    position: relative; z-index: 1; width: min(92vw, 460px); margin-inline: auto;
  }
  /* and a full screen between the spinner and the copy */
  #laserglow-technologies .split > div:not(.split__media) {
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100dvh - var(--header-h));
    display: flex; flex-direction: column; justify-content: center;
    padding-block: clamp(2rem, 8vw, 4rem);
  }

  /* 4. The accordion keeps its rows readable while the block is centred. */
  #laserglow-technologies .acc__toggle { text-align: left; }
  #laserglow-technologies .minicard { text-align: center; }

  /* 6. The map is a full-bleed BACKGROUND on desktop with the copy floating on
        top of it. On a phone that put white text over a busy map and squeezed
        both. The map becomes a block of its own, then the copy sits under it
        on a white card, which is what the user's reference shows. */
  .section--presence { overflow: visible; padding-block: clamp(2rem, 6vw, 3rem); }
  .section--presence .lg-worldmap {
    position: relative; inset: auto; width: auto; height: auto;
    aspect-ratio: 16 / 10; margin: 0 var(--gutter);
    border-radius: var(--r); overflow: hidden;
  }
  .section--presence .lg-worldmap::before { opacity: .55; }
  .section--presence .presence__inner {
    background: #fff; color: var(--ink); border-radius: var(--r);
    margin: clamp(1.25rem, 4vw, 2rem) var(--gutter) 0;
    padding: clamp(1.75rem, 6vw, 2.5rem) clamp(1.1rem, 5vw, 1.75rem);
    max-width: none;
  }
  /* the numbers were forced white to read on the map; on white they must not be */
  .section--presence .presence__inner h2,
  .section--presence .presence__inner .stat__n { color: var(--ink); }
  .section--presence .presence__inner p,
  .section--presence .presence__inner .stat__l { color: var(--ink-2); }
  .section--presence .lg-map-legend { color: var(--ink-2); }
  .section--presence .lg-map-legend span { color: var(--ink-2); }

  /* 7. The pillars DO scroll sideways already, but every panel was the full
        width of the screen, so there was nothing to show it could be swiped.
        A peek of the next card and a snap point is what makes it read as a
        strip rather than a stack. */
  .pillars__pin.no-pin .pillars__sticky {
    scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
    padding-inline: var(--gutter); scroll-padding-inline: var(--gutter);
  }
  .pillars__pin.no-pin .pillars__track {
    display: flex; gap: .75rem; width: auto;
  }
  /* 86vw, NOT 86%. A percentage flex-basis inside a max-content track is
     circular: the track sizes to its children and the children size to the
     track, and the panel came out 769px wide on a 489px screen. A viewport
     unit has nothing to resolve against and gives a real peek. */
  .pillars__pin.no-pin .pillars__panel {
    flex: 0 0 86vw; max-width: 86vw; scroll-snap-align: start; width: auto;
  }

  /* 8. Two lines where the words allow it. A display heading set to fill the
        measure breaks to four or five lines on a 390px screen. */
  .home .tl__opencopy h2,
  .home .pillars__introcopy h2,
  .home .head--center h2 { text-wrap: balance; }

  /* 9. The timeline: no pictures, and the dotted line runs down the centre
        joining one step to the next. Both rules exist already and are the
        wrong way round on a phone: the images show and the dots are hidden. */
  .tl__grid { grid-template-columns: minmax(0, 1fr); }
  .tl__media { display: none !important; }
  .tl__steps { position: relative; }
  /* ONE FULL SCROLL for the title, and one for each step. Both are
     `min-height: calc(100vh - header)` by design and my previous pass cut them
     to `min-height: 0` to save space, which is what removed the rhythm. */
  .tl__open {
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100dvh - var(--header-h));
  }
  .tl__step {
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100dvh - var(--header-h));
    padding-block: 0; text-align: center;
  }
  .tl__step::before, .tl__step::after { display: block !important; }
  /* The desktop offset is `calc(50% - 290px)`, sized for a content block about
     580px tall. On a phone the content is a bigger share of the step, so that
     expression went NEGATIVE and the line collapsed: those were the "broken"
     few dots. max() clamps it at zero and 210px suits the phone's block. */
  .tl__step::before { top: 0; height: max(0px, calc(50% - 210px)); }
  .tl__step::after { bottom: 0; height: max(0px, calc(50% - 210px)); }
  .tl__step:first-child::before, .tl__step:last-child::after { display: none !important; }
  /* --line is #d5d9e0, a hairline grey for borders on white: that is the "so
     transparent" complaint. A readable ink, a bigger dot, even spacing. */
  .tl__step::before, .tl__step::after {
    width: 6px;
    background-image: radial-gradient(circle, var(--ink-3) 2.4px, transparent 2.6px);
    background-size: 6px 18px;
  }
  /* Each connector starts its dot pattern at the edge it is anchored to, so
     the run above a step and the run below the one before it meet in step
     rather than half a dot out. The default origin is the top-left corner,
     which put the lower connector out of phase at the join. */
  .tl__step::before { background-position: center bottom; }
  .tl__step::after { background-position: center top; }
  .tl__icon { margin-inline: auto; }

  /* 10. The catalogue band: near full screen, copy, button, then the
         catalogue picture LARGE. The picture is in the markup and was hidden
         by `.catbar__media, .newsbar__media { display: none }` further up. */
  .catbar { padding-block: clamp(2.5rem, 9vw, 4rem); }
  .catbar__inner { display: flex; flex-direction: column; align-items: center; gap: clamp(1.5rem, 6vw, 2.5rem); }
  .catbar__copy { text-align: center; }
  .catbar .catbar__media {
    display: block; position: relative; inset: auto; transform: none;
    width: min(100%, 520px); margin: 0 auto;
  }
  .catbar .catbar__media img { width: 100%; height: auto; display: block; }

  /* 11. One post per page means TWELVE numbered buttons instead of three, and
         in one row they ran 76px past the screen. They wrap now, centred. */
  .blog__nav { flex-wrap: wrap; row-gap: .5rem; }
  .blog__pages { flex-wrap: wrap; justify-content: center; max-width: 100%; }

  /* 12. Newsletter: centred, and the picture smaller. Its source file is only
         572x381, so drawing it any larger is what made it look soft. */
  .newsbar__inner { flex-direction: column; }
  .newsbar__media { flex-basis: auto; max-width: min(78vw, 300px); margin-inline: auto; }
  .newsbar__copy { text-align: center; }
}


/* The line under the hero headline is set in italic on all three slides
   (user, 2026-08-25). It is the only <p> in the slide body that is not the
   eyebrow above the headline, which is why the selector excludes .eyebrow. */
.heroslide__body > p:not(.eyebrow) { font-style: italic; }

/* Pillar photographs on a phone: they are LANDSCAPE (1.27, 1.28 and 1.78) and
   the card was a portrait box 433 wide by 520 tall, so object-fit:cover cut
   most of the width away. A 4/3 box is within a whisker of two of the three
   images, so almost nothing is lost from those, and the third loses far less. */
@media (max-width: 760px) {
  /* Every panel in the strip is the SAME BOX: same width, same height, tops
     aligned, exactly as it was before I touched it. Giving the photo card its
     own 4/3 height left it 252 tall beside 520-tall text cards, floating in
     the middle of the strip.
     The photo FILLS that box and is cropped to do so (user, 2026-08-25:
     "crop them or zoom in on them to fit the same size block as the
     corresponding text tile"). An earlier pass used "contain" to avoid the
     crop, which kept the whole picture but left empty bands above and below
     it; the user chose the crop. Uniformity wins here. */
  .pillars__pin.no-pin .pcard,
  .pillars__pin.no-pin .pcard--media {
    width: 92vw; max-width: 92vw; height: min(70vh, 520px); aspect-ratio: auto;
  }
  .pillars__pin.no-pin .pillars__panel { flex: 0 0 92vw; max-width: 92vw; }
  .pillars__pin.no-pin .pcard--media { overflow: hidden; }
  .pillars__pin.no-pin .pcard--media img {
    width: 100%; height: 100%; object-fit: cover; object-position: center;
  }
  /* the copy is capped near 34ch, which held it to the OLD card width; let it
     use the wider card */
  .pillars__pin.no-pin .pcard p { max-width: none; }
  .pillars__pin.no-pin .pillars__sticky,
  .pillars__pin.no-pin .pillars__track { align-items: center; }
}


/* ==== solution hero on a phone, and the industry tags ====================
   User spec 2026-08-25. `.hero--split` is the light solution hero (9 pages);
   `.pillrow` is the industry tag row (30 pages: 9 solutions + 21 products).
   The homepage hero is a plain `.hero` and is deliberately not touched. */
@media (max-width: 760px) {

  /* The photograph carries no information the copy does not, and on a phone it
     pushed the headline down a screen. The hero is the words. */
  .hero--split .hero__photo { display: none; }

  .hero--split {
    background: #fff; min-height: 0;
    padding-block: clamp(2.5rem, 10vw, 4.5rem);
  }
  .hero--split .hero__inner { display: block; }
  .hero--split .hero__copy { max-width: none; text-align: center; margin-inline: auto; }
  .hero--split .hero__copy .eyebrow { justify-content: center; }
  .hero--split .hero__copy > p { margin-inline: auto; }

  /* Both buttons full width and stacked, Set Up a Meeting first. They are
     already in that order in the markup, so the column direction is enough:
     nothing here reorders them, which keeps the tab order honest. */
  .hero--split .btn-row {
    display: flex; flex-direction: column; align-items: center;
    gap: .75rem; margin-inline: auto; width: min(100%, 22rem);
  }
  .hero--split .btn-row .btn { width: 100%; justify-content: center; }

  /* An even 3x2 grid, every tag the same size. Wrapping laid them out by word
     length, so the row read as six different-sized chips. `minmax(0, 1fr)`
     stops a long word from widening its column. */
  .pillrow {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .5rem; justify-items: stretch; align-items: stretch;
  }
  .pillrow .pill {
    width: 100%; min-width: 0; margin: 0;
    display: inline-flex; align-items: center; justify-content: center;
    text-align: center; padding: .5rem .35rem; line-height: 1.15;
    /* the labels differ in length ("Aerospace" against "Food Process."), so
       the type is allowed to shrink a little rather than the chip growing */
    font-size: clamp(.62rem, 2.6vw, .78rem);
  }
}


/* ==== stacked subgrids piling their children into one row ================
   `.duo__copy` is a SUBGRID: it borrows its rows from the parent instead of
   defining them. Desktop hands it two rows and its heading and media land one
   per row. The phone stacking block above makes the parent one column and
   resets every child to `grid-row: auto`, so each `.duo__copy` spans ONE parent
   row, and a subgrid with one row puts BOTH children in it. Measured at 390px:
   the children overlapped on 44 pages. A flex column stacks its own children
   and never asks the parent for rows. */
@media (max-width: 760px) {
  .duo__copy { display: flex !important; flex-direction: column; }

  /* ---- the two halves of `.section--docs` become two separate blocks -----
     (/visual-ai-system and /smart-proximity-detection-system-spds).
     User spec 2026-08-25:
       FEATURING              heading, then the image, then the specs
       PRODUCT DOCUMENTATION  the text, then the manual picture and its button,
                              then the spec sheet picture and its button
     `.docs__head` holds the heading, the rule, the paragraph AND the spec
     list, while the picture is its sibling, so `order` on the siblings alone
     cannot put the picture between them. `display: contents` dissolves the
     head's box and promotes its children into the same flex column, which is
     what makes the sequence expressible. It carries no styling of its own
     beyond `min-width: 0`, so nothing is lost with the box. */
  .section--docs .videosplit { row-gap: clamp(2.5rem, 10vw, 4.5rem); }
  .section--docs .docs__head { display: contents; }
  .section--docs .docs__head > .eyebrow  { order: 1; }
  .section--docs .docs__head > h2        { order: 2; }
  .section--docs .docs__head > .duo__rule { order: 3; }
  .section--docs .docs__head > p         { order: 4; }
  .section--docs .docmedia               { order: 5; margin-top: 1.25rem; }
  .section--docs .docs__head > .speclist { order: 6; margin-top: 1.25rem; }
  .section--docs .doccards               { order: 6; }

  /* one document per row: picture, name, button, then the next one */
  .section--docs .doccards--pair { grid-template-columns: minmax(0, 1fr); }
  .section--docs .doccards--pair .doccard__shot { aspect-ratio: 4 / 3; }
}


/* ==========================================================================
   2026-08-26 review
   ========================================================================== */

/* ---- 1. an open mega-menu panel can never be taller than the screen -------
   Even at 1440x900, in the four-column layout, the Solutions panel measured
   931px and ran 205px past the bottom edge with `overflow: hidden`, so its
   last row of links was unreachable. It now stops at the bottom of the window
   and scrolls inside itself. `overflow-x` stays hidden so the rounded bottom
   corners still clip. */
.navitem .panel {
  max-height: calc(100vh - var(--header-h) - 12px);
  max-height: calc(100dvh - var(--header-h) - 12px);
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* and take some height out of it before a scrollbar is needed, in the band
   where the window is only just wide enough for the desktop menu */
/* The padding and gap trim that used to sit here is gone with the same change:
   `zoom` already takes the height out proportionally, and trimming spacing on
   top of a proportional shrink is what makes a scaled design look cramped. */
/* ---- the panel SHRINKS at narrower widths, it does not re-flow ----------
   User, 2026-08-26: "the menu needs to shrink to fit smaller resolutions
   without breaking or ruining or changing the structure or design."

   The rule that used to sit here did the opposite: it dropped the family link
   columns from three to two between 1200px and 1400px, which is a change of
   structure. Replaced.

   `zoom` multiplies every computed length inside the element, so the panel lays
   out as though the window were wider and is then painted smaller. Column
   counts, type, icons, thumbnails and gaps all keep their proportions: the same
   design at a smaller size.

   It goes on `.panel__grid`, the CONTENT. `.panel` itself is absolutely
   positioned with left/right/top, and zooming that would reinterpret those
   offsets in the zoomed space and misplace the panel against the header.

   Stepped, not continuous: a zoom that changes on every pixel of a drag makes
   the type reflow the whole time the window is being resized. */
@media (min-width: 1200.02px) and (max-width: 1319.98px) {
  .panel__grid { zoom: .78; }
}
@media (min-width: 1320px) and (max-width: 1439.98px) {
  .panel__grid { zoom: .86; }
}
@media (min-width: 1440px) and (max-width: 1599.98px) {
  .panel__grid { zoom: .93; }
}

/* ---- 2. the phone footer is four things, not a sitemap ------------------
   User, 2026-08-26: on a phone the footer needs the logo, Set Up a Meeting,
   the social links and the copyright line. Nothing else. The five link
   columns are 40 destinations the drawer already carries, and stacked two-up
   they were the longest block on every page. */
@media (max-width: 760px) {
  .footer__cols, .footer__addresses { display: none; }
  .footer__top {
    display: flex; flex-direction: column; align-items: center;
    gap: 1.25rem; text-align: center;
  }
  .footer__socials { justify-content: center; }
  .footer__legal { text-align: center; margin-top: 1.5rem; }
}

/* ---- 3. the big section word sits BEHIND the section --------------------
   User, 2026-08-26: "What's new" and "Trade shows" should match the two that
   already read correctly, `.tl__wm` ("Customer / Focused") and `.pillars__wm`
   ("Laserglow / Way"): the same size, broken over two lines, and centred
   BEHIND the content rather than parked above the heading.

   Those two are centred on their section at z-index 0 with the copy at 1.
   This makes `.watermark` do the same. `white-space: nowrap` goes, because
   the word is now deliberately two lines; `overflow: hidden` on the section
   keeps it from widening the page. */
#whats-new, #trade-shows { overflow: hidden; }
.watermark {
  top: 50%;
  transform: translateY(-50%);
  z-index: 0;
  line-height: .82;
  white-space: normal;
}
#whats-new > .wrap, #trade-shows > .wrap { position: relative; z-index: 1; }

/* ---- 4. hiding a distributor card must actually hide it -----------------
   The state/province pickers on /distributors set `el.hidden = true`, which
   the browser styles through `[hidden] { display: none }` in its OWN default
   sheet. `.ucard { display: flex }` is an author class rule, so it wins, and
   every card stayed on screen: the dropdowns looked broken because nothing
   moved. Restated here with the weight to win. */
[hidden] { display: none !important; }


/* ==========================================================================
   THE BIG SECTION WORDS, measured off the user's screen recording
   (Screen Recording 2026-08-26 133850.mp4). See the 2026-08-26 entry in
   LOGBOOK.md for the frame-by-frame numbers.
   ========================================================================== */

/* Size. All four of these words are one size, which is what "consistent
   sizing with all of these big background title things" asks for. The old cap
   of 16rem (256px) held them well under what the recording shows on a 1902px
   screen, about 332px, so the cap goes up and the vw step with it. */
.watermark, .tl__wm, .pillars__wm {
  /* 15vw, not more: "Laserglow" in `.pillars__wm` is the longest line of the
     four and its width is a fixed 6.28x the font size, so anything above about
     15.8vw slices the L and the W off against the section edge. Measured at
     16.5vw it overflowed at every width tried: +33px at 390, +41px at 768,
     +49px at 1024, +81px at 1880. The CAP is what was really holding these
     back, though: the old 16rem pinned all four to 256px on any large screen. */
  font-size: clamp(3.5rem, 15vw, 21rem);
}

/* Spread. `.watermark` alone gets the wide line spacing: its two sections are
   tall enough that line one clears the cards above and line two clears them
   below. Measured from the recording: 418px of baseline spacing on 332px of
   type, so 1.26. `.tl__wm` and `.pillars__wm` keep their tight .82, because
   their sections are one screen tall and a 1.25 spread would push both lines
   out of view. */
.watermark { line-height: 1.25; }

/* Parallax. The word tracks the page at 0.63 of its speed, so relative to its
   own section it drifts DOWN by 0.37 of however far the page has scrolled.
   site.js writes that offset into --wm-shift; the -50% is the vertical
   centring the offset is measured from. With no JavaScript the word simply
   sits centred, which is the old behaviour and perfectly readable. */
.watermark {
  transform: translateY(calc(-50% + var(--wm-shift, 0px)));
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .watermark { transform: translateY(-50%); }
}

/* On the dark section the word was fainter than the recording: mine painted 27
   on a 16 ground, a difference of 11, where live shows 44 on 28, a difference
   of 16. 16/239 is an alpha of .067. */
.section--dark .watermark { color: rgba(255, 255, 255, .067); }


/* ==== hero blur-in, first visit only, first slide only ===================
   User, 2026-08-26. `hero-intro` is put on <html> by a script in the <head> of
   site/index.html, before the first paint, so the hero never renders sharp for
   a frame and then jump to blurred. That script also writes the
   `lg-hero-seen` flag to localStorage, which is what makes it first visit
   only, and honours `?hero=1` so it can be looked at again.

   The blur is on the MEDIA. Blurring the headline as well makes the type read
   as out of focus rather than as a deliberate entrance. The copy keeps its own
   `heroIn` fade, delayed a little so the two read as one movement.

   `[data-index="0"]` keeps it to the first slide. site.js removes the class
   from <html> when the animation ends, so no later slide can inherit it. */
@keyframes heroBlurIn {
  from { filter: blur(22px) saturate(1.15); transform: scale(1.055); opacity: .55; }
  60%  { opacity: 1; }
  to   { filter: blur(0) saturate(1); transform: scale(1); opacity: 1; }
}
.hero-intro .heroslide[data-index="0"] .hero__media {
  animation: heroBlurIn 1500ms cubic-bezier(.22, .61, .36, 1) both;
  will-change: filter, transform;
}
.hero-intro .heroslide[data-index="0"] .heroslide__body {
  animation-delay: 420ms;
}
@media (prefers-reduced-motion: reduce) {
  .hero-intro .heroslide[data-index="0"] .hero__media { animation: none; }
  .hero-intro .heroslide[data-index="0"] .heroslide__body { animation-delay: 0s; }
}


/* ==== the red button inverts on hover, everywhere ========================
   User, 2026-08-26: the button turns white, the label turns red, and a red
   border appears. It was going white-on-white because two three-class rules
   outranked the invert and put `color: #fff` back:

       .section--dark .btn--primary, .section--dark .btn--primary:hover
       .statcard .btn--primary:hover

   Confirmed with CSS.getMatchedStylesForNode against the one primary button on
   a dark section ("View our Distributor List"): the invert matched, then the
   dark rule matched after it. Adding `.btn` here makes this (0,3,0) and it sits
   later in the file, so it wins; the two known scopes are restated at (0,4,0)
   so they win on specificity too. No !important needed.

   THE LABEL IS `--red-deep`, NOT `--red`. Against white, --red #ff3a42 is
   3.54:1 and --red-deep #e02a32 is 4.62:1. The label is 700 weight at about
   15px, which is not "large text", so it needs 4.5:1 and only --red-deep
   reaches it. The BORDER keeps --red: a border needs 3:1 and 3.54 clears that.

   The border stays 2px, because `.btn` already carries
   `border: 2px solid transparent` at rest, so only the colour changes and the
   button does not resize under the cursor. */
.btn.btn--primary:hover,
.btn.btn--primary:focus-visible,
.section--dark .btn.btn--primary:hover,
.section--dark .btn.btn--primary:focus-visible,
.statcard .btn.btn--primary:hover,
.statcard .btn.btn--primary:focus-visible {
  background: #fff;
  color: var(--red-deep);
  border-color: var(--red);
}


/* ==== hero slide 1 has no poster ========================================
   User, 2026-08-26: slide 1 carried a poster of a red forklift while the clip
   it plays, hero-intersection.mp4, is a different scene AND opens on black. On
   load you got the photo, then a hard cut to black. The poster is gone; this
   gives the media box the same black the clip fades up from, so there is
   nothing to flash before the clip paints. Slides 2 and 3 keep their posters,
   which are frames from their own clips. */
.hero__media { background: #000; }
