/* ════════════════════════════════════════════════════════════════════
   AirIQ.com — Design System v1
   ONE source of truth. Mirrors core/knowledge/brand-style-guide.md §2–4.
   Blue-family brand: AirIQ Blue #086AAE + white theme (brand mandate).
   ~450 styles max. No new hues. WCAG AA enforced.
   ════════════════════════════════════════════════════════════════════ */

/* ── Tokens ─────────────────────────────────────────────────────── */
:root {
  /* Brand blues (ratified) */
  --blue:        #086AAE;   /* AirIQ Blue — CTA, links, accents   */
  --blue-deep:   #064E80;   /* hover / press / gradient mid       */
  --blue-mid:    #003D6B;   /* Midnight — full-bleed sections     */
  --navy:        #0A1929;   /* Navy Ink — headline ink, overlays  */
  --azure:       #1E9CE3;   /* accent pop, live dots, focus       */
  --blue-pale:   #9ED2F2;   /* headline accent on dark            */
  --blue-soft:   #8FC7EE;   /* eyebrow / label on dark            */

  /* Ink scale (blue-biased neutrals) */
  --ink-900: #1A2332; --ink-700: #3C4858; --ink-500: #5C6875;
  --ink-300: #B8C0CB; --ink-100: #E5EAF0; --ink-50:  #F7F9FC;
  --paper:   #FFFFFF;

  /* Gradients */
  --grad-hero:   linear-gradient(135deg, #003D6B 0%, #086AAE 100%);
  --grad-band:   linear-gradient(120deg, #064E80 0%, #086AAE 60%, #1E9CE3 140%);
  --grad-footer: linear-gradient(180deg, #003D6B 0%, #0A1929 100%);

  /* Type */
  --font: "Helvetica Neue", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --fs-display: clamp(2.5rem, 5.4vw, 3.9rem);
  --fs-h2: clamp(1.9rem, 3.6vw, 2.8rem);
  --fs-h3: clamp(1.25rem, 2vw, 1.5rem);
  --fs-body-lg: 1.125rem;
  --fs-body: 1rem;
  --fs-sm: .875rem;
  --fs-label: .8125rem;

  /* Shape & depth */
  --r-ctl: 8px; --r-card: 14px; --r-sheet: 20px; --r-pill: 999px;
  --shadow-1: 0 1px 2px rgba(10,25,41,.05), 0 4px 16px rgba(10,25,41,.06);
  --shadow-2: 0 2px 6px rgba(10,25,41,.07), 0 16px 40px -12px rgba(8,106,174,.18);
  --shadow-3: 0 24px 70px -20px rgba(0,61,107,.35);
  --ring: 0 0 0 4px rgba(30,156,227,.25);

  /* Spacing scale — 4px base. Every margin, padding, and gap resolves to a
     step on this scale. Before it existed the site used 4,5,6,7,8,9,10,12,13,
     14,16,18,20,22,24,26,28,30,32,36,40,44px, which is not a system. */
  --s-1:  4px;  --s-2:  8px;  --s-3: 12px;  --s-4: 16px;
  --s-5: 20px;  --s-6: 24px;  --s-7: 32px;  --s-8: 40px;
  --s-9: 48px;  --s-10:64px;  --s-11:80px;  --s-12:96px;

  /* Rhythm.
     sec-pad applies top AND bottom, so the gap between two stacked sections is
     double it. At the old clamp(72px,9vw,128px) that put 256px of empty space
     between every band on a 1440px screen: the page read as sparse and took far
     more scrolling than its content warranted. */
  --container: 1152px;
  --sec-pad: clamp(56px, 5.6vw, 88px);
  /* The page's side padding. Fluid, because 24px is a third of the text
     column on a 360px handset and a hairline on a 27-inch display. It is
     a custom property rather than a media query because negative margins
     are taken from it (a full-bleed table inside a .container, say), and
     those have to track it exactly. */
  --gutter: clamp(18px, 4.2vw, 32px);
  --stack: var(--s-4);      /* default gap between stacked text blocks */
  --stack-lg: var(--s-7);   /* gap before a CTA row or a sub-block */

  /* Motion */
  --ease: cubic-bezier(.32,.72,0,1);
  --ease-soft: cubic-bezier(.4,0,.2,1);
  --t-fast: .18s; --t-med: .35s; --t-slow: .7s;
}

/* ── Reset & base ───────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
/* Anchors land behind the sticky header unless the page reserves room for it.
   68px of global nav. Without this, following "See maintenance" puts the
   eyebrow and half the heading underneath the bar, so the section you asked
   for is the one thing you cannot read. */
html{scroll-padding-top:88px}
@media (max-width: 940px){html{scroll-padding-top:104px}}
body{
  font-family:var(--font); font-size:var(--fs-body); line-height:1.6;
  color:var(--ink-900); background:var(--paper);
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}
img,svg,video{max-width:100%;display:block}
a{color:var(--blue);text-decoration:none}
a:hover{color:var(--blue-deep)}
ul[class],ol[class]{list-style:none}
button{font-family:inherit;cursor:pointer}
:focus-visible{outline:none;box-shadow:var(--ring);border-radius:4px}
::selection{background:rgba(30,156,227,.25)}

.container{max-width:var(--container);margin-inline:auto;padding-inline:var(--gutter)}
.container-narrow{max-width:860px;margin-inline:auto;padding-inline:var(--gutter)}

/* Typography roles */
h1,h2,h3,h4{color:var(--navy);letter-spacing:-.02em;line-height:1.08;text-wrap:balance;font-weight:600}
.display{font-size:var(--fs-display);font-weight:650}
h2,.h2{font-size:var(--fs-h2)}
h3,.h3{font-size:var(--fs-h3);letter-spacing:-.01em}
/* Ledes are standfirsts, never body copy: capped at 56ch they run two or
   three lines, and an unbalanced wrap strands a word or two on the last row,
   which reads as odd spacing under a balanced heading. Balance like the
   headings; browsers without support wrap as before. */
.lede{font-size:var(--fs-body-lg);color:var(--ink-700);line-height:1.65;max-width:56ch;text-wrap:balance}
.eyebrow{
  display:inline-block;font-size:var(--fs-label);font-weight:600;color:var(--blue);
  text-transform:uppercase;letter-spacing:.09em;margin-bottom:14px;
}
.muted{color:var(--ink-500)}
.small{font-size:var(--fs-sm)}
.mono{font-family:var(--mono);font-size:.85em}
.accent{color:var(--blue)}

/* ── Buttons ────────────────────────────────────────────────────── */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  font-size:.9375rem;font-weight:500;line-height:1;border-radius:var(--r-ctl);
  padding:13px 22px;border:1px solid transparent;text-align:center;
  transition:background var(--t-fast) var(--ease-soft), border-color var(--t-fast),
             color var(--t-fast), transform var(--t-fast), box-shadow var(--t-fast);
}
.btn:active{transform:translateY(1px)}
.btn-primary{background:var(--blue);color:#fff}
.btn-primary:hover{background:var(--blue-deep);color:#fff;box-shadow:0 6px 20px -6px rgba(8,106,174,.5)}
.btn-secondary{background:var(--paper);color:var(--ink-900);border-color:var(--ink-100)}
.btn-secondary:hover{border-color:var(--ink-300);color:var(--navy)}
.btn-ghost-light{background:rgba(255,255,255,.12);color:#fff;border-color:rgba(255,255,255,.35)}
.btn-ghost-light:hover{background:rgba(255,255,255,.2);color:#fff}
.btn-lg{padding:16px 28px;font-size:1rem}
.btn-arrow::after{content:"→";font-weight:400;transition:transform var(--t-fast) var(--ease)}
a.btn-arrow:not(.btn)::after{margin-left:.4em;display:inline-block}
.btn-arrow:hover::after{transform:translateX(3px)}

/* ── Nav ────────────────────────────────────────────────────────── */
.nav{
  position:sticky;top:0;z-index:60;background:rgba(255,255,255,.85);
  backdrop-filter:saturate(180%) blur(14px);-webkit-backdrop-filter:saturate(180%) blur(14px);
  border-bottom:1px solid transparent;
  transition:border-color var(--t-med),box-shadow var(--t-med);
}
.nav.is-scrolled{border-bottom-color:var(--ink-100);box-shadow:0 4px 24px -12px rgba(10,25,41,.12)}
.nav-inner{
  max-width:var(--container);margin-inline:auto;padding-inline:var(--gutter);
  height:68px;display:flex;align-items:center;gap:24px;
}
.nav-logo{display:flex;align-items:center;flex-shrink:0}
.nav-logo img{height:34px;width:auto}
.nav-links{display:flex;align-items:center;gap:2px;font-size:.9375rem;margin-right:auto}
/* Each item fills the bar's full height so its bottom edge meets the top of
   the panel it opens. A shorter li left a strip of bare header between the
   two, and crossing it dropped :hover and closed the menu. */
.nav-links > li{position:relative;display:flex;align-items:center;align-self:stretch}
/* A panel is wider than the item that opens it, so the path to its top
   corners crosses the neighbouring item and the menu closed. Lifting the
   open item above its siblings puts the panel under the pointer instead. */
.nav-links > li:hover,.nav-links > li:focus-within,.nav-links > li.is-open{z-index:5}
.nav-links a.nav-link.is-here{color:var(--blue)}
.nav-link{
  display:inline-block;padding:8px 11px;border-radius:var(--r-ctl);white-space:nowrap;
  color:var(--ink-700);transition:color var(--t-fast),background var(--t-fast);
}
.nav-links a.nav-link:hover,.nav-links li:hover > a.nav-link{color:var(--blue);background:var(--ink-50)}
.nav-links a.nav-link[aria-current="page"]{color:var(--blue);font-weight:500}
.nav-actions{display:flex;align-items:center;gap:10px;flex-shrink:0}
.nav-contact,.nav-signin{font-size:.9375rem;color:var(--ink-700);padding:8px 10px}
.nav-contact:hover{color:var(--blue)}
.nav-signin:hover{color:var(--blue)}

/* Dropdown */
/* The panel box starts flush against the trigger and holds the visible
   card 10px inside it. A gap between the two was a dead zone: crossing it
   dropped :hover and closed the menu before the pointer arrived. */
.nav-drop{
  position:absolute;top:100%;left:50%;transform:translate(-50%,6px);z-index:5;
  padding-top:10px;background:none;border:0;box-shadow:none;
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity var(--t-fast) var(--ease-soft),transform var(--t-fast) var(--ease-soft),visibility var(--t-fast);
}
.nav-drop-card{
  min-width:260px;background:var(--paper);border:1px solid var(--ink-100);
  border-radius:var(--r-card);box-shadow:var(--shadow-2);padding:10px;
  /* The card's top edge sits 78px down (68px bar + 10px bridge), so the
     rest of the window is available to it, less a little room below. */
  max-height:calc(100vh - 94px);overflow:hidden auto;overscroll-behavior:contain;
}
/* The panel opens 6px below the trigger, and it is far wider than the
   trigger is. Moving the pointer down, or diagonally toward a far corner,
   left the li's box and closed the menu before the pointer arrived. This
   bridges the gap across the panel's full width, and holds the panel open
   for a moment on the way out so a small overshoot does not dismiss it. */

.nav-drop{transition-delay:.14s}
.nav-links li:hover .nav-drop,
.nav-links li:focus-within .nav-drop,
.nav-links li.is-open .nav-drop{
  opacity:1;visibility:visible;pointer-events:auto;transform:translate(-50%,0);
  transition-delay:0s;
}
/* Fourteen entries in one column ran 822px tall, past the fold on a
   laptop. Two columns, and a cap so it can never exceed the viewport. */
/* The Products menu carries the whole line, including the three cameras
   and the three Phillips Connect trailer devices, so it runs three
   columns rather than two. */
/* The wide panel is far wider than the trigger that opens it, and that
   trigger is the leftmost item in the row. Centering the panel on it put
   the whole first column off the left edge of the viewport. Dropping the
   li out of the positioning chain hands the panel to .nav — which spans
   the viewport, because backdrop-filter makes it a containing block — so
   left:50% now centers the panel in the window rather than on the word
   "Products". The width clamp then guarantees it always fits. */
.nav-links > li.has-mega,
.nav-links > li:has(> .nav-drop.is-wide){position:static}
.nav-drop.is-wide{width:min(920px, calc(100vw - var(--gutter) * 2))}
.nav-drop.is-wide .nav-drop-card{
  min-width:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));column-gap:6px;
}
.nav-drop .nav-col{min-width:0}
/* The "Choose" links used to sit at the foot of the third column, which
   made that column the tallest and left the other two trailing 80-120px of
   dead space. As a full-width footer they belong to the whole menu, and the
   three columns are free to end where their content does. */
.nav-drop.is-wide .nav-drop-foot{
  grid-column:1 / -1;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:2px;margin-top:8px;padding-top:6px;border-top:1px solid var(--ink-100);
}
.nav-drop.is-wide .nav-drop-foot a{align-self:center}
@media (max-width:900px){
  .nav-drop.is-wide .nav-drop-foot{grid-template-columns:1fr}
}
/* A model sits under its family, indented so the hierarchy is readable. */
.nav-drop .nav-col a.is-model{padding-left:22px;position:relative}
.nav-drop .nav-col a.is-model::before{
  content:"";position:absolute;left:10px;top:50%;width:5px;height:1px;
  background:var(--ink-300);
}
.nav-drop a{
  display:block;padding:9px 12px;border-radius:var(--r-ctl);color:var(--ink-900);
  font-size:.9rem;transition:background var(--t-fast),color var(--t-fast);
}
.nav-drop a:hover{background:var(--ink-50);color:var(--blue)}
.nav-drop .drop-label{
  font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
  color:var(--ink-500);padding:10px 12px 4px;
}
.nav-drop small{display:block;color:var(--ink-500);font-size:.78rem;margin-top:1px}

/* Mobile nav */
.nav-burger{display:none;background:none;border:0;width:44px;height:44px;border-radius:var(--r-ctl);color:var(--navy);align-items:center;justify-content:center}
.nav-burger svg{width:24px;height:24px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round}
.nav-mobile{display:none}
/* Six top-level items need more room than five. Nowrap on the links means
   the bar can never wrap a label; instead, secondary actions step out one
   at a time as the viewport narrows, keeping the row on one clean line.
   Order of sacrifice: Contact (still in footer, Company menu, and mobile
   nav), then Sign in. */
@media (max-width: 1240px){
  .nav-contact{display:none}
  .nav-inner{gap:18px}
}
@media (max-width: 1100px){
  .nav-signin{display:none}
  .nav-inner{gap:14px}
  .nav-link{font-size:.92rem;padding-inline:8px}
}

@media (max-width: 940px){
  .nav-links,.nav-signin,.nav-contact{display:none}
  .nav-burger{display:inline-flex}
  .nav-inner{gap:14px;justify-content:space-between}
  /* A strip of nav below the bar, so a sticky visual clears it instead of
     starting hard against it. It is padding on the bar rather than a gap
     above the visual because the gap would show the page sliding past
     underneath -- which is the thing it is here to stop. For the same
     reason the bar is opaque at this width: 15% of a translucent white
     over a dark section is exactly the "what is behind" this is hiding.
     Anything measured off the bar (the menu panel, the sticky pane, the
     anchor offset) is 68 + this. */
  .nav{padding-bottom:16px;background:#fff;backdrop-filter:none;-webkit-backdrop-filter:none}
  .nav-mobile{
    display:block;position:fixed;inset:84px 0 0 0;background:var(--paper);z-index:59;
    padding:20px var(--gutter) 40px;overflow-y:auto;
    transform:translateY(-8px);opacity:0;visibility:hidden;
    transition:opacity var(--t-med) var(--ease-soft),transform var(--t-med) var(--ease-soft),visibility var(--t-med);
  }
  .nav-mobile.is-open{opacity:1;visibility:visible;transform:none}
  .nav-mobile a:not(.btn){display:block;padding:14px 4px;font-size:1.05rem;color:var(--ink-900);border-bottom:1px solid var(--ink-50)}
  .nav-mobile .drop-label{font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-500);padding:22px 4px 6px}
  .nav-mobile .btn{margin-top:20px;width:100%}
  /* Forty-eight links in one flat list is a menu you scroll rather than
     read. airiq.js groups them under their existing drop-labels; closed,
     the whole menu is six rows and the section you are in is the one
     already open. */
  .nav-mobile .nav-acc{border-bottom:1px solid var(--ink-100)}
  .nav-mobile .nav-acc > summary{
    display:flex;align-items:center;justify-content:space-between;gap:12px;
    min-height:56px;padding:4px 4px;cursor:pointer;list-style:none;
    font-size:1.05rem;font-weight:600;color:var(--navy);
  }
  .nav-mobile .nav-acc > summary::-webkit-details-marker{display:none}
  .nav-mobile .nav-acc > summary::after{
    content:"";width:9px;height:9px;flex:none;margin-right:6px;
    border-right:2px solid var(--ink-300);border-bottom:2px solid var(--ink-300);
    transform:translateY(-2px) rotate(45deg);transition:transform var(--t-fast) var(--ease-soft);
  }
  .nav-mobile .nav-acc[open] > summary::after{transform:translateY(2px) rotate(-135deg)}
  .nav-mobile .nav-acc > a:not(.btn){
    padding:12px 4px 12px 16px;font-size:1rem;color:var(--ink-700);border-bottom:none;
  }
  .nav-mobile .nav-acc > a:not(.btn):last-of-type{padding-bottom:18px}
  body.nav-locked{overflow:hidden}
}
/* Small handsets: the logo + burger + full-size CTA overrun a 320px viewport. */
@media (max-width: 400px){
  .nav-actions .btn{padding-inline:14px;font-size:.9rem}
  .nav-logo img{height:30px}
}

/* ── Hero ───────────────────────────────────────────────────────── */
.hero{padding:clamp(56px,7vw,96px) 0 clamp(48px,6vw,80px);overflow:hidden}
.hero-grid{
  max-width:var(--container);margin-inline:auto;padding-inline:var(--gutter);
  display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(32px,5vw,64px);align-items:center;
}
.hero-badge{
  display:inline-flex;align-items:center;gap:8px;font-size:var(--fs-label);
  color:var(--blue-deep);background:var(--ink-50);border:1px solid var(--ink-100);
  border-radius:var(--r-pill);padding:6px 14px;font-weight:500;
}
.hero-badge .dot{width:7px;height:7px;border-radius:50%;background:var(--azure);animation:pulse 2.4s var(--ease-soft) infinite}
.hero h1{margin-top:22px}
.hero .lede{margin-top:22px}
.hero-ctas{margin-top:32px;display:flex;flex-wrap:wrap;gap:12px}
/* A flex row packs from the start edge and ignores the text-align it
   inherited, so in a centered band the heading and lede center while the
   buttons stay hard left. Anywhere the surrounding copy is centered, the
   buttons under it have to be too. */
.cta-final .hero-ctas,
.section-head.center + .hero-ctas,
.center .hero-ctas{justify-content:center}
.hero-micro{
  margin-top:26px;display:flex;flex-wrap:wrap;gap:8px 22px;
  font-size:var(--fs-sm);color:var(--ink-500);align-items:center;
}
.hero-micro span::before{content:"✓";color:var(--blue);font-weight:700;margin-right:7px}

/* "Is this for someone my size?" — answered above the fold, because the
   segment self-disqualifies: a small operator assumes a telematics site is
   built for a UPS-sized fleet and leaves before reading the offer. Sits under
   the lede, ahead of the CTA, so the reassurance arrives before the ask. */
.fit-note{
  margin-top:18px;font-size:var(--fs-sm);color:var(--ink-700);
  /* wide enough to hold the sentence on one line where there is room —
     at 46ch "vehicles." dropped to a line of its own */
  line-height:1.55;max-width:62ch;
}
.fit-note b{font-weight:650;color:var(--navy)}
/* On the dark home and landing heroes the same line has to hold against the
   photograph behind it. */
.hb-copy .fit-note,.lp-hero .fit-note{color:rgba(255,255,255,.82);text-shadow:0 2px 16px rgba(3,20,36,.4)}
.hb-copy .fit-note b,.lp-hero .fit-note b{color:#fff}

/* Hero visual — live map card */
.hero-visual{position:relative}
.map-card{
  position:relative;aspect-ratio:4/4.6;border-radius:var(--r-sheet);
  background:var(--grad-hero);overflow:hidden;box-shadow:var(--shadow-3);
}
.map-card svg.map{position:absolute;inset:0;width:100%;height:100%}
.map-route{
  fill:none;stroke:rgba(255,255,255,.85);stroke-width:2.5;stroke-linecap:round;
  stroke-dasharray:640;stroke-dashoffset:640;animation:draw 6s var(--ease-soft) .6s infinite;
}
.map-route-glow{
  fill:none;stroke:rgba(30,156,227,.5);stroke-width:7;stroke-linecap:round;filter:blur(3px);
  stroke-dasharray:640;stroke-dashoffset:640;animation:draw 6s var(--ease-soft) .6s infinite;
}
.map-dot{fill:#fff;filter:drop-shadow(0 0 6px rgba(30,156,227,.9));
  offset-path:path("M40 470 C 120 380, 90 300, 180 250 S 320 160, 360 60");
  animation:travel 6s var(--ease-soft) .6s infinite;
}
.map-grid line{stroke:rgba(255,255,255,.07);stroke-width:1}
.map-pin{animation:pulse 2.4s var(--ease-soft) infinite;transform-origin:center}
.float-card{
  position:absolute;left:20px;right:20px;background:rgba(255,255,255,.96);
  border:1px solid rgba(255,255,255,.6);border-radius:12px;box-shadow:var(--shadow-2);
  padding:14px 16px;backdrop-filter:blur(8px);
}
.float-card.top{top:20px;display:flex;align-items:center;justify-content:space-between;gap:10px}
.float-card.bottom{bottom:20px}
.float-label{font-size:.75rem;color:var(--ink-500)}
.float-title{font-weight:600;color:var(--navy);margin-top:2px;font-size:.95rem}
.live-chip{
  display:inline-flex;align-items:center;gap:6px;font-size:.75rem;font-weight:600;
  color:var(--blue-deep);background:rgba(30,156,227,.12);padding:5px 11px;border-radius:var(--r-pill);
}
.live-chip .dot{position:relative;width:6px;height:6px;border-radius:50%;background:var(--azure)}
.live-chip .dot::after{content:"";position:absolute;inset:-5px;border-radius:50%;background:var(--azure);opacity:.55;animation:dotRadar 2s var(--ease-soft) infinite}
.cam-strip{display:flex;gap:10px;margin-top:10px}
.cam-view{
  flex:1;aspect-ratio:16/9;border-radius:8px;position:relative;overflow:hidden;
  background:linear-gradient(160deg,#0A1929 20%,#064E80 140%);
}
.cam-view span{
  position:absolute;left:8px;bottom:6px;font-family:var(--mono);font-size:.62rem;
  color:rgba(255,255,255,.85);letter-spacing:.05em;
}
.cam-view .rec{
  position:absolute;right:8px;top:6px;width:7px;height:7px;border-radius:50%;
  background:#fff;opacity:.9;animation:blink 1.4s steps(2,start) infinite;
}
.cam-view svg{position:absolute;inset:0;width:100%;height:100%}

/* ── Sections ───────────────────────────────────────────────────── */
/* ── Spacing utilities ──────────────────────────────────────────────
   Selectors are doubled (.mt-4.mt-4) so a utility outranks the component
   rules defined later in this file. Without that, .section{padding:...}
   silently beats .pt-0 and the utility does nothing. */
.mt-0.mt-0{margin-top:0}          .mt-1.mt-1{margin-top:var(--s-1)}
.mt-2.mt-2{margin-top:var(--s-2)} .mt-3.mt-3{margin-top:var(--s-3)}
.mt-4.mt-4{margin-top:var(--s-4)} .mt-5.mt-5{margin-top:var(--s-5)}
.mt-6.mt-6{margin-top:var(--s-6)} .mt-7.mt-7{margin-top:var(--s-7)}
.mt-8.mt-8{margin-top:var(--s-8)} .mt-9.mt-9{margin-top:var(--s-9)}
.mt-10.mt-10{margin-top:var(--s-10)}
.mb-0.mb-0{margin-bottom:0}          .mb-2.mb-2{margin-bottom:var(--s-2)}
.mb-3.mb-3{margin-bottom:var(--s-3)} .mb-4.mb-4{margin-bottom:var(--s-4)}
.mb-6.mb-6{margin-bottom:var(--s-6)} .mb-8.mb-8{margin-bottom:var(--s-8)}
.pt-0.pt-0{padding-top:0}            .pb-0.pb-0{padding-bottom:0}
.pt-7.pt-7{padding-top:var(--s-7)}   .pt-8.pt-8{padding-top:var(--s-8)}
.pb-7.pb-7{padding-bottom:var(--s-7)}.pb-8.pb-8{padding-bottom:var(--s-8)}

.eyebrow.on-dark{color:var(--blue-soft)}

/* Visually hidden, still announced. The site had no such utility, so
   one-off cases inlined `position:absolute;left:-9999px`; this is the
   clip-path version, which does not push a scroll region sideways. */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

.section{padding:var(--sec-pad) 0}
.section.tint{background:var(--ink-50)}
/* Two blocks of the same tone in a row read as one long slab. A hairline
   says "new idea" without inventing a third background. .pt-0 is excluded:
   that class marks a section that is deliberately a continuation of the
   one above it. */
/* .pt-0 already opts a section out of the seam: no top padding means the two
   blocks are meant to read as continuous. .pb-0 says the same thing from the
   other side and was never covered, so the line landed directly on whatever
   sat flush against that edge — on /ca/ it ran straight out of the proof
   strip's rounded corners, and on trailer-asset-tracking out of the photo
   band's. Both edges now suppress it. */
.section:not(.tint):not(.cta-final):not(.pb-0) + .section:not(.tint):not(.pt-0):not(.cta-final),
.section:not(.tint):not(.cta-final):not(.pb-0) + .feature:not(.dark):not(.tinted),
.feature:not(.dark):not(.tinted):not(.pb-0) + .section:not(.tint):not(.pt-0):not(.cta-final),
.feature:not(.dark):not(.tinted):not(.pb-0) + .feature:not(.dark):not(.tinted){
  border-top:1px solid var(--ink-100);
}
.section.tint:not(.pb-0) + .section.tint,
.section.tint:not(.pb-0) + .feature.tinted,
.feature.tinted:not(.pb-0) + .section.tint{
  border-top:1px solid rgba(10,25,41,.07);
}
.section-head{max-width:640px;margin-bottom:clamp(28px,3.4vw,44px)}
.section-head.center{margin-inline:auto;text-align:center}
.section-head .lede{margin-top:18px}
/* `.lede` is capped at 56ch. Inside a centered head that cap makes a box
   narrower than its parent, and a capped box with no auto margins hugs the
   left edge: centered text in an off-center block, under a centered heading.
   The same applies to any centered container, not just section heads. */
.section-head.center .lede,
.center > .lede{margin-inline:auto}

/* Pillars */
.pillars{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(24px,3.5vw,44px)}
.pillar-icon{
  width:52px;height:52px;border-radius:12px;background:var(--ink-50);
  display:grid;place-items:center;color:var(--blue);margin-bottom:20px;
  transition:background var(--t-med),transform var(--t-med) var(--ease);
}
.pillar:hover .pillar-icon{background:rgba(8,106,174,.1);transform:translateY(-3px)}
.pillar-icon svg{width:28px;height:28px;stroke:currentColor;stroke-width:1.6;fill:none;stroke-linecap:round;stroke-linejoin:round}
.pillar h3{margin-bottom:8px}
.pillar p{color:var(--ink-700)}
.pillar .learn{display:inline-block;margin-top:12px;font-size:var(--fs-sm);font-weight:500}

/* ── Integration logo wall ──────────────────────────────────────────
   One sheet ruled into ten cells, not ten floating cards: partner marks are
   all different weights and widths, so a shared hairline grid is what keeps
   them from reading as noise. Cell counts are chosen so the last row is never
   ragged (10 = 5x2 = 2x5).
   Plates hold either a typeset wordmark (.logo-plate-mark) or licensed logo
   art (.logo-plate-art). The two are sized to sit at the same optical weight,
   so a wall part-way through the swap still looks deliberate. */
.logo-section{background:var(--paper)}
.logo-wall{
  display:grid;grid-template-columns:repeat(5,minmax(0,1fr));
  gap:1px;background:var(--ink-100);
  border:1px solid var(--ink-100);border-radius:var(--r-card);overflow:hidden;
  list-style:none;margin:0;padding:0;
}
.logo-plate{
  background:var(--paper);display:grid;place-items:center;
  min-height:104px;padding:20px 18px;
  transition:background var(--t-med);
}
.logo-plate:hover{background:var(--ink-50)}
.logo-plate-mark{
  font-weight:600;font-size:.98rem;line-height:1.25;letter-spacing:-.005em;
  color:var(--ink-500);text-align:center;text-wrap:balance;
  transition:color var(--t-med);
}
.logo-plate:hover .logo-plate-mark{color:var(--ink-900)}
.logo-plate-art{
  max-width:100%;max-height:38px;width:auto;height:auto;display:block;
  filter:grayscale(1);opacity:.62;
  transition:filter var(--t-med),opacity var(--t-med);
}
.logo-plate:hover .logo-plate-art{filter:grayscale(0);opacity:1}
.logo-wall-foot{margin-top:var(--s-6);text-align:center;font-size:var(--fs-sm)}
@media (max-width:940px){
  .logo-wall{grid-template-columns:repeat(2,minmax(0,1fr))}
  .logo-plate{min-height:92px}
}

/* Stats band */
.band{
  position:relative;color:#fff;background:var(--grad-band);
  padding:calc(var(--sec-pad) + 24px) 0;overflow:hidden;
}
.band .eyebrow{color:rgba(255,255,255,.75)}
.band h2{color:#fff}
.band .wave{position:absolute;left:0;width:100%;color:var(--paper);pointer-events:none}
.band .wave.top{top:-1px}
.band .wave.bottom{bottom:-1px;transform:scaleY(-1)}
.band .wave.tint-edge{color:var(--ink-50)}
.stats-grid{margin-top:52px;display:grid;grid-template-columns:repeat(4,1fr);gap:40px 24px}
.stat-num{font-size:clamp(2.6rem,4.5vw,3.6rem);font-weight:650;line-height:1;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.stat-num sup{font-size:.45em;font-weight:600;vertical-align:super}
.stat-label{margin-top:10px;color:rgba(255,255,255,.85)}
.band .source{margin-top:44px;font-size:.8rem;color:rgba(255,255,255,.6)}

/* Product cards */
.cards-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:24px}
.card{
  position:relative;background:var(--paper);border:1px solid var(--ink-100);
  border-radius:var(--r-card);overflow:hidden;
  transition:border-color var(--t-med),transform var(--t-med) var(--ease),box-shadow var(--t-med);
}
.card:hover{border-color:rgba(8,106,174,.4);transform:translateY(-4px);box-shadow:var(--shadow-2)}
/* .card-media only ever styled an svg. Once real device photography went
   in, nothing bounded the img and it ran over the card title. */
.card-media{aspect-ratio:4/3;background:var(--ink-50);display:flex;align-items:center;
  justify-content:center;overflow:hidden;padding:var(--s-5)}
.card-media svg{width:60%;height:auto;transition:transform var(--t-slow) var(--ease)}
.card-media img{max-width:82%;max-height:100%;min-height:0;width:auto;height:auto;
  object-fit:contain;transition:transform var(--t-slow) var(--ease)}

/* A photograph is not a product shot: it should fill the frame the way
   .icard-media does, rather than floating inset on the plate with the
   82% rule meant for a device on a white background. */
.card-media.is-photo{padding:0}
.card-media.is-photo img{max-width:none;width:100%;height:100%;object-fit:cover}.card:hover .card-media svg,.card:hover .card-media img{transform:scale(1.05)}
.card-body{padding:22px}
.card-body h3{font-size:1.1rem;margin-bottom:6px}
.card-body p{font-size:var(--fs-sm);color:var(--ink-700);line-height:1.55}
.card-tag{
  position:absolute;top:14px;left:14px;font-size:.68rem;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--blue-deep);background:rgba(255,255,255,.9);
  border:1px solid var(--ink-100);padding:4px 9px;border-radius:var(--r-pill);
}
.card > a.card-cover{position:absolute;inset:0;z-index:1}

/* Feature split rows */
.split{display:grid;grid-template-columns:1fr 1fr;gap:clamp(36px,6vw,80px);align-items:center}
.split + .split{margin-top:var(--sec-pad)}
/* Media on the left, copy on the right. Ordered off the first child, not the
   media wrapper — some rows use a plain grid of chips instead of .split-media. */
.split.flip > :first-child{order:2}
.split-media{
  border-radius:var(--r-sheet);overflow:hidden;background:var(--ink-50);
  border:1px solid var(--ink-100);box-shadow:var(--shadow-1);
}
.split h2,.split h3{margin-bottom:14px}
.split p{color:var(--ink-700)}
.check-list{margin-top:20px;display:grid;gap:12px}
.check-list li{display:flex;gap:12px;align-items:flex-start;color:var(--ink-700)}
.check-list li::before{
  content:"✓";flex-shrink:0;width:22px;height:22px;border-radius:50%;
  background:rgba(8,106,174,.1);color:var(--blue);font-size:.75rem;font-weight:700;
  display:grid;place-items:center;margin-top:2px;
}

/* Story / case block */
/* A link in here inherits the light-ground link blue, which lands at 3.1:1 on
   navy. --blue-pale is the token already used for the eyebrow on this panel
   and clears AA comfortably (10.9:1). */
.story a:not(.btn){color:var(--blue-pale)}
.story a:not(.btn):hover{color:#fff}
.story{
  background:var(--navy);color:#fff;border-radius:var(--r-sheet);
  padding:clamp(36px,5vw,64px);display:grid;grid-template-columns:1.2fr .8fr;
  gap:clamp(28px,4vw,56px);align-items:center;overflow:hidden;position:relative;
}
/* The grid above assumes a text half and a stat half. A story with only a
   text block (remote-asset-control) must span both tracks, or the text gets
   squeezed into the 1.2fr column beside a blank .8fr one. */
.story > :only-child{grid-column:1/-1}
.story h2,.story h3{color:#fff}
.story p{color:rgba(255,255,255,.85)}
.story .stat-inline{font-size:clamp(3rem,6vw,4.5rem);font-weight:650;letter-spacing:-.03em;line-height:1;color:var(--azure)}
.story .stat-cap{margin-top:8px;color:rgba(255,255,255,.7);font-size:var(--fs-sm)}

/* Testimonials */
.quote-hero{
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-sheet);
  box-shadow:0 24px 70px -32px rgba(8,106,174,.35);padding:clamp(32px,5vw,56px);
  max-width:820px;margin-inline:auto;
}
.quote-hero blockquote{
  font-size:clamp(1.35rem,2.4vw,1.8rem);font-weight:500;color:var(--navy);
  line-height:1.35;letter-spacing:-.01em;text-wrap:balance;
}
.quote-attrib{margin-top:28px;display:flex;align-items:center;gap:14px}
.avatar{
  width:46px;height:46px;border-radius:50%;background:var(--ink-100);color:var(--blue-deep);
  display:grid;place-items:center;font-weight:600;font-size:1.05rem;flex-shrink:0;
}
.quote-name{font-weight:600;color:var(--navy)}
.quote-role{font-size:var(--fs-sm);color:var(--ink-500)}
.quotes-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:28px}
.quote-mini{
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-card);padding:24px;
  transition:transform var(--t-med) var(--ease),box-shadow var(--t-med),border-color var(--t-med);
}
.quote-mini:hover{transform:translateY(-3px);box-shadow:var(--shadow-1);border-color:var(--ink-300)}
.quote-mini p{color:var(--ink-700);line-height:1.6}
.quote-mini .quote-attrib{margin-top:16px}
.quotes-source{margin-top:22px;font-size:.82rem;color:var(--ink-500);text-align:center}

/* Industries tiles */
.tiles{
  display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--ink-100);
  border:1px solid var(--ink-100);border-radius:var(--r-card);overflow:hidden;
}
.tile{position:relative;background:var(--paper);padding:26px;transition:background var(--t-med)}
.tile:hover{background:var(--ink-50)}
.tile h3,.tile .h3{font-size:1.05rem;margin-bottom:4px}
.tile p{font-size:var(--fs-sm);color:var(--ink-500)}
.tile .tile-arrow{
  position:absolute;top:24px;right:22px;color:var(--ink-300);
  transition:color var(--t-fast),transform var(--t-fast) var(--ease);
}
.tile:hover .tile-arrow{color:var(--blue);transform:translateX(3px)}
.tile > a.card-cover{position:absolute;inset:0}

/* Terms band */
.terms{
  border:1px solid var(--ink-100);border-radius:var(--r-sheet);background:var(--paper);
  padding:clamp(30px,4vw,48px);display:flex;flex-wrap:wrap;gap:28px 48px;
  align-items:center;justify-content:space-between;box-shadow:var(--shadow-1);
}
.terms-list{display:flex;flex-wrap:wrap;gap:14px 36px}
/* All four fit on one line only once the card is wide enough. Below that the
   flex row dropped the fourth item onto a line by itself, which read as a
   stray afterthought rather than the last of a set; a 2x2 grid keeps the
   list balanced, and one column once even a pair will not fit. */
@media (max-width:1151px){.terms-list{display:grid;grid-template-columns:repeat(2,max-content)}}
@media (max-width:560px){.terms-list{grid-template-columns:minmax(0,1fr)}}
.terms-item{display:flex;align-items:center;gap:10px;font-weight:500;color:var(--navy)}
.terms-item svg{width:22px;height:22px;stroke:var(--blue);stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}

/* ── Disclosure marker ──────────────────────────────────────────
   One indicator for everything that opens: FAQ answers, tech-spec groups,
   investor years, the mobile facet rail. These had grown four different
   markers — a 26px circled "+" that rotated into an ×, an 11px bare
   chevron, an 8px bare chevron, and another 8px one — so the same gesture
   looked like four different controls depending on the page.

   Built as two pseudo-elements pinned to the same point rather than a glyph
   inside a box: ::before is the ring, ::after is the chevron, both centred
   on the summary's right inset with translate(-50%,-50%). Concentric by
   construction, so there is no font metric to fight and no per-size nudge to
   re-tune at each breakpoint — the old circled "+" sat slightly high in its
   ring, which is what made the × look off-centre once it rotated.

   The chevron is a mask rather than a background image so one shape can be
   recoloured by state, and it is drawn symmetric about the centre of its
   viewBox so a 180° rotation lands exactly where it started. */
/* ══════════════════════════════════════════════════════════════════
   [hidden] MUST HIDE.

   The UA rule for [hidden] is display:none at the very bottom of the
   cascade, so any author `display` on the same element beats it. That is
   a footgun in a stylesheet where nearly every component sets display:
   JS sets el.hidden = true, the element stays on screen, and the bug
   looks like broken JS rather than CSS.

   It had already been patched one component at a time (.live-dot,
   .field-err, .step-panel, .facet-clear, .signup-msg, .help-search
   button) and still bit twice more: the blog category filters hid no
   cards, and every form on the site left its fields and a dead
   "Sending…" button on screen after a successful submission, which read
   as nothing having happened.

   One rule, once. !important because the whole point is to outrank the
   component `display` that broke it.
   ══════════════════════════════════════════════════════════════════ */
[hidden]{display:none!important}

:root{
  --disc-size:26px;
  --tick:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.3 4.9 8.7 9.5 3.6' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --disc-chev:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.4 4.25 6 7.75 9.6 4.25' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* Each host sets its own right inset: a bare `padding-right` here would be
   overridden by the component's own padding shorthand further down. */
.disc{position:relative}
.disc::before,
.disc::after{
  content:"";position:absolute;top:50%;
  translate:0 -50%;                     /* separate from transform, so rotation stays clean */
  pointer-events:none;
}
.disc::before{
  width:var(--disc-size);height:var(--disc-size);border-radius:50%;
  right:var(--disc-inset,0px);
  border:1px solid var(--ink-100);background:transparent;
  transition:background var(--t-fast),border-color var(--t-fast);
}
.disc::after{
  width:12px;height:12px;background-color:var(--ink-500);
  /* half the ring's diameter minus half the chevron's, so the two share a centre */
  right:calc(var(--disc-inset,0px) + (var(--disc-size) - 12px) / 2);
  -webkit-mask:var(--disc-chev) center/12px 12px no-repeat;
          mask:var(--disc-chev) center/12px 12px no-repeat;
  transition:transform var(--t-med) var(--ease),background-color var(--t-fast);
}
.is-open.disc::after,
[open] > .disc::after,
[aria-expanded="true"].disc::after{transform:rotate(180deg)}
.is-open.disc::before,
[open] > .disc::before,
[aria-expanded="true"].disc::before{background:rgba(8,106,174,.08);border-color:var(--blue)}
.is-open.disc::after,
[open] > .disc::after,
[aria-expanded="true"].disc::after{background-color:var(--blue)}
.disc:hover::before{border-color:var(--ink-300)}
.disc:hover::after{background-color:var(--navy)}

/* FAQ accordion */
.faq{max-width:860px;margin-inline:0}
/* margin-inline:0 is right only under a left-aligned heading. Where the
   section head is centred, a left-flush list reads as a misalignment: the
   heading sits on the page centre and the questions start 170px to its
   left. Centre the box so both share one axis. */
.section-head.center + .faq{margin-inline:auto}
.faq-item{border-bottom:1px solid var(--ink-100)}
/* Question and answer text sit at 0 left padding so they line up exactly
   with the section heading above the list; only the divider hairlines span
   the box. */
.faq-item summary{
  list-style:none;display:flex;align-items:center;justify-content:space-between;gap:20px;
  --disc-inset:4px;
  padding:22px calc(var(--disc-size) + 18px) 22px 0;
  font-weight:600;font-size:1.05rem;color:var(--navy);cursor:pointer;
  transition:color var(--t-fast);
}
.faq-item summary::-webkit-details-marker{display:none}
.faq-item summary:hover{color:var(--blue)}
.faq-a{padding:0 44px 24px 0;color:var(--ink-700);line-height:1.7}
.faq-a a{font-weight:500}
.faq-a p + p{margin-top:12px}

/* CTA band */
.cta-final{
  position:relative;background:var(--grad-hero);color:#fff;
  padding:calc(var(--sec-pad) + 12px) 0;text-align:center;overflow:hidden;
}
.cta-final h2{color:#fff;max-width:640px;margin-inline:auto}
.cta-final .lede{color:rgba(255,255,255,.85);margin:18px auto 0;text-align:center}
.cta-form{
  margin:36px auto 0;max-width:520px;display:grid;grid-template-columns:1fr auto;gap:12px;
}
.cta-form input{
  padding:15px 20px;border-radius:var(--r-ctl);border:1px solid rgba(255,255,255,.3);
  background:rgba(255,255,255,.12);color:#fff;font-size:.95rem;font-family:inherit;min-width:0;
  transition:border-color var(--t-fast),background var(--t-fast),box-shadow var(--t-fast);
}
.cta-form input::placeholder{color:rgba(255,255,255,.6)}
.cta-form input:focus{outline:none;background:rgba(255,255,255,.18);border-color:var(--azure);box-shadow:var(--ring)}
.cta-form .btn-primary{background:#fff;color:var(--blue-deep)}
.cta-form .btn-primary:hover{background:var(--ink-50);color:var(--blue-deep)}
.cta-micro{margin-top:22px;display:flex;flex-wrap:wrap;justify-content:center;gap:8px 24px;font-size:var(--fs-sm);color:rgba(255,255,255,.75)}
.cta-final .cta-phone{margin-top:26px;font-size:var(--fs-sm);color:rgba(255,255,255,.75)}
.cta-final .cta-phone a{color:#fff;font-weight:600}

/* ── Footer ─────────────────────────────────────────────────────── */
.footer{background:var(--grad-footer);color:rgba(255,255,255,.8);padding:72px 0 0}
.footer-grid{
  max-width:var(--container);margin-inline:auto;padding-inline:var(--gutter);
  display:grid;grid-template-columns:2fr 1fr 1fr 1fr 1fr;gap:40px;
}
/* width:auto or the width attribute on the tag wins and stretches the mark.
   The nav logo has always had it; the footer's omission is what squashed it. */
.footer-logo img{height:36px;width:auto;filter:brightness(0) invert(1)}
.footer-about{margin-top:18px;font-size:var(--fs-sm);line-height:1.7;max-width:280px;color:rgba(255,255,255,.65)}
.footer-social{display:flex;flex-wrap:wrap;gap:var(--s-1);margin-top:var(--s-5);margin-left:-11px}
.footer-social a{
  width:44px;height:44px;display:grid;place-items:center;border-radius:var(--r-ctl);
  color:rgba(255,255,255,.7);transition:color var(--t-fast),background var(--t-fast);
}
.footer-social a:hover{color:#fff;background:rgba(255,255,255,.08)}
.footer-social a:focus-visible{outline:none;color:#fff;box-shadow:var(--ring)}
.footer-social svg{display:block}
.footer h2{color:#fff;font-size:.8rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:16px}
.footer-links{display:grid;gap:10px}
.footer-links a{font-size:var(--fs-sm);color:rgba(255,255,255,.7)}
.footer-links a:hover{color:#fff}
.footer-bottom{
  max-width:var(--container);margin:56px auto 0;padding:28px var(--gutter);
  border-top:1px solid rgba(255,255,255,.12);display:flex;flex-wrap:wrap;gap:14px 28px;
  align-items:center;justify-content:space-between;font-size:.8rem;color:rgba(255,255,255,.55);
}
.footer-bottom a{color:rgba(255,255,255,.55)}
.footer-bottom a:hover{color:#fff}
.footer-legal{display:flex;gap:22px}
/* The <details> airiq.js wraps each footer column in. Above the phone
   breakpoint it is open, inert, and looks exactly like the h2 it holds. */
.footer .acc > summary{display:block;list-style:none;cursor:default}
.footer .acc > summary::-webkit-details-marker{display:none}
.footer .acc > summary::after{content:none}
.footer .acc > summary > h2{margin-bottom:16px}

/* ── Page-hero (interior pages) ─────────────────────────────────── */
.page-hero{padding:clamp(28px,3.2vw,44px) 0 clamp(32px,4vw,56px)}
/* The tighter bottom padding suits a hero that ends in text. When it ends in a
   visual panel the card's rounded corner landed ~30px above the next band's
   background change and read as colliding with the seam, so a hero carrying a
   panel takes the full section rhythm underneath instead. */
.page-hero:has(.viewfinder, .appshot, .split-media, .photo-band, .hero-visual,
               .call-card, .device-hero, .form-card){
  padding-bottom:var(--sec-pad);
}
.page-hero .lede{margin-top:18px}
.page-hero .hero-ctas{margin-top:28px}
/* The breadcrumb sits top-left on every page, without exception. It's fixed
   furniture, not part of the composition, so it does not follow a centred hero.
   .breadcrumbs is a flex container, so an ancestor text-align has no effect on
   it either way: the trap is someone adding justify-content:center by hand to
   match a centred stage. That's what .stage-crumbs exists to make unnecessary. */
.breadcrumbs{font-size:var(--fs-sm);color:var(--ink-500);margin-bottom:var(--s-5);display:flex;flex-wrap:wrap;gap:6px;align-items:center;justify-content:flex-start;text-align:left}
.breadcrumbs a{color:var(--ink-500)}
.breadcrumbs a:hover{color:var(--blue)}
.breadcrumbs .sep{color:var(--ink-300)}

/* Spec table */
.spec-table{width:100%;border-collapse:collapse;font-size:var(--fs-sm)}
.spec-table th{
  text-align:left;padding:12px 16px;font-size:.72rem;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--ink-500);background:var(--ink-50);border-bottom:1px solid var(--ink-100);
}
.spec-table td{padding:13px 16px;border-bottom:1px solid var(--ink-100);color:var(--ink-700);vertical-align:top}
.spec-table td:first-child{color:var(--navy);font-weight:500;white-space:nowrap}
.table-wrap{overflow-x:auto;border:1px solid var(--ink-100);border-radius:var(--r-card)}
.spec-table tr:last-child td{border-bottom:none}

/* Responsive column utilities.
   Use these instead of an inline `style="grid-template-columns:…"` — an inline
   style outranks every media query below, which silently freezes a grid at its
   desktop column count all the way down to 320px. minmax(0,1fr) lets columns
   shrink past their min-content width instead of overflowing the page.
   The class is doubled so the utility outranks the component rules
   (.steps, .index-list, …) that are defined later in this file. */
.cols-2.cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}
.cols-3.cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}
.cols-4.cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}
@media (max-width: 940px){
  .cols-4.cols-4,.cols-3.cols-3{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width: 620px){
  .cols-2.cols-2,.cols-3.cols-3,.cols-4.cols-4{grid-template-columns:minmax(0,1fr)}
}

/* Steps */
.steps{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;counter-reset:step}
.step{background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-card);padding:28px;counter-increment:step;position:relative}
.step::before{
  content:counter(step,decimal-leading-zero);font-family:var(--mono);font-size:.8rem;font-weight:600;
  color:var(--blue);display:block;margin-bottom:14px;letter-spacing:.05em;
}
/* categories rather than a sequence: same card, no ordinal */
.steps.is-plain .step::before{display:none}
.steps.is-plain .step{display:flex;flex-direction:column}
.sheet-foot{margin-top:auto;padding-top:var(--s-5)}
.step h3{font-size:1.05rem;margin-bottom:6px}
.step p{font-size:var(--fs-sm);color:var(--ink-700)}

/* Forms */
.form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px}
.form-grid .full{grid-column:1/-1}
/* Holds a form at a readable width while staying flush with the page's left
   rail, so its left edge lines up with the h1 above instead of centring
   independently (which offset the demo form by 146px against its own hero). */
.form-shell{max-width:860px}
.field label{display:block;font-size:var(--fs-sm);font-weight:600;color:var(--navy);margin-bottom:7px}
.field input,.field select,.field textarea{
  width:100%;padding:13px 16px;border-radius:var(--r-ctl);border:1px solid var(--ink-100);
  background:var(--paper);font-family:inherit;font-size:.95rem;color:var(--ink-900);
  transition:border-color var(--t-fast),box-shadow var(--t-fast);
}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--azure);box-shadow:var(--ring)}
.field .hint{font-size:.78rem;color:var(--ink-500);margin-top:6px}
.chip-group{display:flex;flex-wrap:wrap;gap:10px}
.chip-group label{
  display:inline-flex;align-items:center;gap:8px;padding:10px 16px;border:1px solid var(--ink-100);
  border-radius:var(--r-pill);font-size:var(--fs-sm);font-weight:500;color:var(--ink-700);cursor:pointer;
  transition:border-color var(--t-fast),background var(--t-fast),color var(--t-fast);margin-bottom:0;
}
/* The control is visually hidden but must stay inside its label: it inherits
   width:100% from `.field input` above, and without a positioned ancestor an
   absolutely-positioned 100%-wide box escapes to the initial containing block
   and drags the document width with it. */
.chip-group label{position:relative}
.chip-group input{
  position:absolute;opacity:0;pointer-events:none;
  width:1px;height:1px;margin:0;padding:0;border:0;
}
.chip-group label:has(input:checked){background:rgba(8,106,174,.08);border-color:var(--blue);color:var(--blue-deep)}
.chip-group label:hover{border-color:var(--ink-300)}

/* Glossary / guides lists */
.index-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px}
.index-item{
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-card);padding:24px;
  position:relative;transition:border-color var(--t-med),transform var(--t-med) var(--ease),box-shadow var(--t-med);
}
.index-item:hover{border-color:rgba(8,106,174,.4);transform:translateY(-3px);box-shadow:var(--shadow-1)}
.index-item h3{font-size:1.05rem;margin-bottom:6px}
.index-item p{font-size:var(--fs-sm);color:var(--ink-700)}
.index-item .meta{font-size:.75rem;color:var(--ink-500);margin-top:12px;font-family:var(--mono)}
.index-item > a.card-cover{position:absolute;inset:0}

/* Prose (guides, legal, glossary body) */
.prose{max-width:68ch}
.prose h2{font-size:1.6rem;margin:2.2em 0 .6em}
.prose h3{font-size:1.2rem;margin:1.8em 0 .5em}
.prose p{margin-bottom:1.1em;color:var(--ink-700);line-height:1.75}
.prose ul,.prose ol{margin:0 0 1.1em 1.3em;color:var(--ink-700)}
.prose li{margin-bottom:.45em;line-height:1.7}
.prose strong{color:var(--navy)}
.prose blockquote{border-left:3px solid var(--blue);padding:4px 0 4px 20px;margin:1.4em 0;color:var(--ink-500);font-style:italic}

/* ── Legal pages ─────────────────────────────────────────────────────
   The published policies are reproduced verbatim, which means inheriting
   their 2016 habits: a numbered contents list, an all-caps disclaimer block,
   and a contact address. Layout is ours; the words are not. */
.updated-meta{font-size:var(--fs-sm);color:var(--ink-500);margin-top:2.2em;padding-top:1.2em;border-top:1px solid var(--ink-100)}
.legal-meta{font-size:var(--fs-sm);color:var(--ink-500);padding-bottom:1.4em;border-bottom:1px solid var(--ink-100);margin-bottom:2em}
/* ul[class],ol[class] are reset to list-style:none globally, and a contents
   list is the one place the markers carry meaning. */
.legal-toc{list-style:decimal;margin-left:1.15em}
.legal-toc li{margin-bottom:.3em}
.legal-toc a{color:var(--ink-700)}
.legal-toc a:hover{color:var(--blue)}
/* Set at reading size rather than shouted: it is a wall of capitals and the
   default line-height makes it unreadable. */
.legal-caps{
  font-size:.82rem;line-height:1.85;letter-spacing:.005em;color:var(--ink-500);
  background:var(--ink-50);border-radius:var(--r-card);padding:20px 22px;
}
.legal-address{font-style:normal;line-height:1.9;color:var(--ink-700)}
@media (max-width:620px){.legal-caps{padding:16px}}

/* ── Motion utilities ───────────────────────────────────────────── */
.reveal{opacity:0;transform:translateY(18px);transition:opacity var(--t-slow) var(--ease-soft),transform var(--t-slow) var(--ease-soft)}
.reveal.is-in{opacity:1;transform:none}
.reveal-d1{transition-delay:.08s}.reveal-d2{transition-delay:.16s}.reveal-d3{transition-delay:.24s}.reveal-d4{transition-delay:.32s}

@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.45;transform:scale(.8)}}
@keyframes blink{50%{opacity:.15}}
@keyframes draw{0%{stroke-dashoffset:640}55%,100%{stroke-dashoffset:0}}
@keyframes travel{0%{offset-distance:0%;opacity:0}6%{opacity:1}55%,88%{offset-distance:100%;opacity:1}100%{offset-distance:100%;opacity:0}}
/* Live/status dot — steady core, radar ring reads as "broadcasting" rather
   than the whole dot fading in and out. Used everywhere a "LIVE"-style
   indicator appears outside the hero badge/tag (which keep the simpler
   pulse so the two don't compete for attention in the same viewport). */
@keyframes dotRadar{0%{transform:scale(.3);opacity:.55}80%,100%{transform:scale(2.8);opacity:0}100%{opacity:0}}

/* ── Responsive ─────────────────────────────────────────────────── */
@media (max-width: 1024px){
  .cards-grid{grid-template-columns:repeat(2,1fr)}
  .stats-grid{grid-template-columns:repeat(2,1fr)}
  .footer-grid{grid-template-columns:1fr 1fr 1fr}
}
@media (max-width: 860px){
  .hero-grid{grid-template-columns:1fr}
  .hero-visual{max-width:440px}
  .pillars{grid-template-columns:1fr;gap:36px}
  .split{grid-template-columns:1fr}
  .split.flip > :first-child{order:0}   /* stacked: copy leads, always */
  .story{grid-template-columns:1fr}
  .quotes-grid{grid-template-columns:1fr}
  .tiles{grid-template-columns:repeat(2,1fr)}
  .index-list{grid-template-columns:minmax(0,1fr)}
  .form-grid{grid-template-columns:minmax(0,1fr)}
  .steps{grid-template-columns:minmax(0,1fr)}
}
@media (max-width: 560px){
  .cards-grid{grid-template-columns:1fr}
  .tiles{grid-template-columns:1fr}
  .stats-grid{grid-template-columns:1fr 1fr;gap:32px 16px}
  .cta-form,.cta-form.has-size{grid-template-columns:1fr}
  .terms{flex-direction:column;align-items:flex-start}
}

/* ── Footer on a phone ──────────────────────────────────────────────
   Five blocks of very different length in two columns is what made this
   ragged: Company runs nine links against Solutions' five, so one column
   ran on while the other left a hole, and "Get in touch" sat alone on a
   full-width row underneath.

   One column, and each link group collapses. Twenty-four links is a
   screen and a half of footer nobody scrolls; closed, the whole thing is
   the blurb, the signup, four headings and the legal line. airiq.js wraps
   the groups in <details> (see FOOTER + MOBILE NAV ACCORDIONS) and opens
   them again above this width, so the markup is one shape everywhere and
   every link stays in the DOM. */
@media (max-width: 560px){
  .footer{padding-top:44px}
  .footer-grid{grid-template-columns:minmax(0,1fr);gap:0}
  .footer-about{max-width:none;margin-top:14px}
  .footer-signup{margin-top:22px}
  .footer .acc{border-top:1px solid rgba(255,255,255,.1)}
  .footer .acc:last-of-type{border-bottom:1px solid rgba(255,255,255,.1)}
  .footer .acc > summary{
    display:flex;align-items:center;justify-content:space-between;gap:12px;
    min-height:52px;padding:6px 2px;cursor:pointer;list-style:none;
    color:#fff;font-size:.8rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
  }
  .footer .acc > summary::-webkit-details-marker{display:none}
  .footer .acc > summary::after{
    content:"";width:9px;height:9px;flex:none;margin-right:4px;
    border-right:2px solid rgba(255,255,255,.5);border-bottom:2px solid rgba(255,255,255,.5);
    transform:translateY(-2px) rotate(45deg);transition:transform var(--t-fast) var(--ease-soft);
  }
  .footer .acc[open] > summary::after{transform:translateY(2px) rotate(-135deg)}
  .footer .acc > summary > h2{margin-bottom:0}
  .footer .acc .footer-links{gap:0;padding-bottom:10px}
  .footer .acc .footer-links a{display:flex;align-items:center;min-height:42px}
  .footer-bottom{margin-top:26px;padding-block:22px;gap:12px;
    flex-direction:column;align-items:flex-start;text-align:left}
  .footer-legal{gap:20px}
}

/* ── Reduced motion ─────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}
  .reveal{opacity:1;transform:none}
  .map-route,.map-route-glow{stroke-dashoffset:0;animation:none}
  .map-dot{animation:none;offset-distance:100%}
}

/* ════════════════════════════════════════════════════════════════════
   v2 components — richer motion, safety-first storytelling
   ════════════════════════════════════════════════════════════════════ */

/* ── Scroll progress bar ────────────────────────────────────────── */
.scroll-progress{
  position:fixed;top:0;left:0;height:3px;width:0;z-index:70;
  background:linear-gradient(90deg,var(--blue),var(--azure));
  box-shadow:0 0 10px rgba(30,156,227,.55);
}

/* ── Hero atmosphere (aurora, blue-family only) ─────────────────── */
.hero{position:relative}
.hero::before{
  content:"";position:absolute;inset:-20% -10% auto;height:120%;z-index:-1;pointer-events:none;
  background:
    radial-gradient(38% 45% at 78% 12%, rgba(30,156,227,.14), transparent 70%),
    radial-gradient(30% 40% at 12% 80%, rgba(8,106,174,.10), transparent 70%),
    radial-gradient(24% 32% at 40% 10%, rgba(0,61,107,.08), transparent 70%);
  animation:aurora 16s var(--ease-soft) infinite alternate;
}
@keyframes aurora{
  0%{transform:translate3d(0,0,0) scale(1)}
  50%{transform:translate3d(2%, -2%,0) scale(1.06)}
  100%{transform:translate3d(-2%, 1%,0) scale(1.02)}
}

/* ── Button shine ───────────────────────────────────────────────── */
.btn-primary{position:relative;overflow:hidden}
.btn-primary::before{
  content:"";position:absolute;top:0;bottom:0;left:-80%;width:50%;
  background:linear-gradient(105deg,transparent,rgba(255,255,255,.35),transparent);
  transform:skewX(-20deg);transition:left .6s var(--ease);
}
.btn-primary:hover::before{left:130%}

/* ── 3D tilt cards ──────────────────────────────────────────────── */
.tilt{transform-style:preserve-3d;will-change:transform}
.tilt.card:hover{transform:perspective(900px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg)) translateY(-4px)}
.tilt .card-media{transform:translateZ(24px)}
.tilt .card-body{transform:translateZ(12px)}
.card{position:relative}
.card::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;opacity:0;
  background:radial-gradient(420px circle at var(--mx,50%) var(--my,50%), rgba(30,156,227,.10), transparent 45%);
  transition:opacity var(--t-med);
}
.card:hover::after{opacity:1}

/* ── Exoneration timeline (dark cinematic) ──────────────────────── */
.incident{
  /* overflow:clip, not hidden — `hidden` makes this a scroll container,
     which silently kills position:sticky on the scrollytelling pane
     inside. `clip` contains the decorative bleed without that side effect. */
  position:relative;background:var(--navy);color:#fff;overflow:clip;
  padding:calc(var(--sec-pad) + 20px) 0;
}
.incident::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:
    radial-gradient(50% 60% at 85% 10%, rgba(8,106,174,.25), transparent 70%),
    radial-gradient(40% 50% at 8% 90%, rgba(30,156,227,.12), transparent 70%);
}
.incident .eyebrow{color:var(--azure)}
.incident h2{color:#fff}
.incident .lede{color:rgba(255,255,255,.75)}
/* Flush with the page's left rail: a capped box with auto margins centres
   under a left-aligned heading, which is the misalignment .faq already fixed
   with margin-inline:0. The rail and dots sit at the heading's edge; the
   40px padding is the rail gutter, not an indent. */
.tl{position:relative;max-width:720px;margin:clamp(40px,6vw,64px) 0 0;padding-left:40px}
.tl::before{
  content:"";position:absolute;left:11px;top:6px;bottom:6px;width:2px;
  background:rgba(255,255,255,.12);
}
.tl-fill{
  position:absolute;left:11px;top:6px;width:2px;height:0;
  background:linear-gradient(180deg,var(--azure),var(--blue));
  box-shadow:0 0 12px rgba(30,156,227,.6);transition:height 1.1s var(--ease-soft);
}
.tl.is-in .tl-fill{height:calc(100% - 12px)}
.tl-step{position:relative;padding:0 0 44px 12px;opacity:0;transform:translateX(14px);
  transition:opacity .6s var(--ease-soft),transform .6s var(--ease-soft)}
.tl-step:last-child{padding-bottom:0}
.tl.is-in .tl-step{opacity:1;transform:none}
.tl.is-in .tl-step:nth-child(2){transition-delay:.15s}
.tl.is-in .tl-step:nth-child(3){transition-delay:.35s}
.tl.is-in .tl-step:nth-child(4){transition-delay:.55s}
.tl.is-in .tl-step:nth-child(5){transition-delay:.8s}
.tl-step::before{
  content:"";position:absolute;left:-35px;top:4px;width:14px;height:14px;border-radius:50%;
  background:var(--navy);border:2px solid var(--azure);box-shadow:0 0 10px rgba(30,156,227,.5);
}
.tl-time{font-family:var(--mono);font-size:.78rem;letter-spacing:.08em;color:var(--azure);margin-bottom:6px}
.tl-step h3{color:#fff;font-size:1.15rem;margin-bottom:6px}
.tl-step p{color:rgba(255,255,255,.72);max-width:52ch}
.stamp{
  display:inline-block;margin-top:14px;padding:8px 18px;border:2.5px solid var(--azure);
  border-radius:8px;color:var(--azure);font-weight:800;letter-spacing:.18em;font-size:.9rem;
  text-transform:uppercase;transform:rotate(-4deg) scale(1.6);opacity:0;
  transition:transform .45s cubic-bezier(.2,1.6,.4,1),opacity .3s;
}
.tl.is-in .stamp{transform:rotate(-4deg) scale(1);opacity:1;transition-delay:1.15s}

/* ── Live dashboard mock ────────────────────────────────────────── */
.dash{
  display:grid;grid-template-columns:1.1fr .9fr;gap:0;border-radius:var(--r-sheet);
  overflow:hidden;border:1px solid var(--ink-100);box-shadow:var(--shadow-3);background:var(--paper);
}
.dash-map{position:relative;background:var(--grad-hero);min-height:380px}
.dash-map svg{position:absolute;inset:0;width:100%;height:100%}
.dash-chip{
  position:absolute;left:18px;top:18px;display:inline-flex;align-items:center;gap:8px;
  background:rgba(10,25,41,.55);border:1px solid rgba(255,255,255,.18);backdrop-filter:blur(6px);
  color:#fff;font-size:.75rem;font-weight:600;padding:7px 12px;border-radius:var(--r-pill);
}
.dash-chip .dot{position:relative;width:7px;height:7px;border-radius:50%;background:var(--azure)}
.dash-chip .dot::after{content:"";position:absolute;inset:-5px;border-radius:50%;background:var(--azure);opacity:.55;animation:dotRadar 2s var(--ease-soft) infinite}
.dash-side{display:flex;flex-direction:column;gap:0;min-width:0}
.dash-head{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:20px 22px;border-bottom:1px solid var(--ink-100);
}
.dash-title{font-weight:600;color:var(--navy)}
.dash-sub{font-size:.78rem;color:var(--ink-500)}
.ring-wrap{display:flex;align-items:center;gap:12px}
.ring{width:64px;height:64px;transform:rotate(-90deg)}
.ring circle{fill:none;stroke-width:6;stroke-linecap:round}
.ring .ring-bg{stroke:var(--ink-100)}
.ring .ring-val{stroke:url(#ringGrad);stroke-dasharray:163.36;stroke-dashoffset:163.36;transition:stroke-dashoffset 1.4s var(--ease-soft)}
.ring-num{font-size:1.35rem;font-weight:700;color:var(--navy);font-variant-numeric:tabular-nums;line-height:1}
.ring-label{font-size:.7rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-500)}
.feed{padding:10px 12px 14px;display:flex;flex-direction:column;gap:8px;overflow:hidden;flex:1;min-height:250px}
.feed-row{
  display:flex;align-items:center;gap:12px;background:var(--ink-50);
  border:1px solid var(--ink-100);border-radius:10px;padding:11px 14px;
}
.feed-row.slide-in{animation:feedIn .5s var(--ease) both}
@keyframes feedIn{from{opacity:0;transform:translateY(-10px) scale(.98)}to{opacity:1;transform:none}}
.feed-ico{
  flex-shrink:0;width:34px;height:34px;border-radius:9px;display:grid;place-items:center;
  background:rgba(8,106,174,.1);color:var(--blue);
}
.feed-ico svg{width:18px;height:18px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
.feed-row.is-event .feed-ico{background:rgba(10,25,41,.08);color:var(--navy)}
.feed-txt{min-width:0}
.feed-title{font-size:.86rem;font-weight:600;color:var(--navy);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.feed-meta{font-size:.72rem;color:var(--ink-500);font-family:var(--mono)}
.feed-tag{
  margin-left:auto;flex-shrink:0;font-size:.65rem;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--blue-deep);background:rgba(30,156,227,.12);
  padding:4px 9px;border-radius:var(--r-pill);
}

/* ── Outcomes band (stats redesign) ─────────────────────────────── */
.outcome-grid{margin-top:52px;display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.outcome{
  background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.16);
  border-radius:var(--r-card);padding:28px 26px;backdrop-filter:blur(4px);
  transition:transform var(--t-med) var(--ease),background var(--t-med);
}
.outcome:hover{transform:translateY(-4px);background:rgba(255,255,255,.12)}
.outcome-ico{
  width:44px;height:44px;border-radius:11px;background:rgba(255,255,255,.14);
  display:grid;place-items:center;margin-bottom:18px;
}
.outcome-ico svg{width:24px;height:24px;stroke:#fff;stroke-width:1.8;fill:none;stroke-linecap:round;stroke-linejoin:round}
.outcome .stat-num{font-size:clamp(2.4rem,4vw,3.2rem)}
.outcome .stat-label{margin-top:6px;font-weight:500;color:#fff}
.outcome .stat-sub{margin-top:4px;font-size:.82rem;color:rgba(255,255,255,.65)}
.band .footnote{margin-top:36px;font-size:.72rem;color:rgba(255,255,255,.45)}
.band-anchor{
  margin-top:44px;display:flex;flex-wrap:wrap;align-items:baseline;gap:10px 18px;
}
.band-anchor .stat-num{font-size:clamp(3.2rem,6vw,4.6rem)}
.band-anchor .anchor-cap{font-size:1.05rem;color:rgba(255,255,255,.85);max-width:34ch}

@media (max-width: 900px){
  .dash{grid-template-columns:1fr}
  .dash-map{min-height:260px}
  .outcome-grid{grid-template-columns:1fr}
}

/* ── Reduced motion for v2 ──────────────────────────────────────── */
@media (prefers-reduced-motion: reduce){
  .hero::before{animation:none}
  .tl-step,.stamp{opacity:1;transform:rotate(-4deg) scale(1)}
  .tl-fill{height:calc(100% - 12px);transition:none}
  .ring .ring-val{transition:none}
  .btn-primary::before{display:none}
  .feed-row.slide-in{animation:none}
  .scroll-progress{display:none}
}

/* ── Slick-family components (from the solution-slick system) ───── */
.proof-strip{
  display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--ink-100);
  border:1px solid var(--ink-100);border-radius:var(--r-card);overflow:hidden;
}
.proof-cell{background:var(--paper);padding:22px 20px;position:relative}
.proof-cell .n{
  font-family:var(--mono);font-size:.68rem;color:var(--ink-300);letter-spacing:.08em;
}
.proof-cell .v{font-size:1.45rem;font-weight:700;color:var(--blue-deep);letter-spacing:-.01em;margin-top:6px;line-height:1.1}
.proof-cell .l{font-size:.78rem;color:var(--ink-500);margin-top:5px;line-height:1.4}

.realities{
  border-left:3px solid var(--blue);background:var(--ink-50);
  border-radius:0 var(--r-card) var(--r-card) 0;padding:26px 28px;
}
.realities-label{
  font-size:.72rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--blue-deep);margin-bottom:16px;
}
.realities-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
.realities-grid strong{display:block;color:var(--navy);margin-bottom:5px;font-size:.95rem}
.realities-grid p{font-size:.85rem;color:var(--ink-700);line-height:1.55}

.six-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
.six-card{
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-card);
  padding:24px;transition:transform var(--t-med) var(--ease),box-shadow var(--t-med),border-color var(--t-med);
}
.six-card:hover{transform:translateY(-3px);box-shadow:var(--shadow-1);border-color:rgba(8,106,174,.35)}
.six-card.accent{background:var(--blue);border-color:var(--blue)}
.six-card.accent h3,.six-card.accent p{color:#fff}
.six-card.accent p{color:rgba(255,255,255,.85)}
.six-card .n{font-family:var(--mono);font-size:.7rem;color:var(--ink-300);letter-spacing:.08em}
.six-card.accent .n{color:rgba(255,255,255,.6)}
.six-card h3{font-size:1rem;margin:10px 0 6px}
.six-card p{font-size:.85rem;color:var(--ink-700);line-height:1.55}

/* Device model chooser */
.models{display:grid;grid-template-columns:1fr 1fr;gap:24px}
.model{
  position:relative;border:1px solid var(--ink-100);border-radius:var(--r-sheet);
  overflow:hidden;background:var(--paper);display:flex;flex-direction:column;
  transition:transform var(--t-med) var(--ease),box-shadow var(--t-med),border-color var(--t-med);
}
.model:hover{transform:translateY(-4px);box-shadow:var(--shadow-2);border-color:rgba(8,106,174,.4)}
.model-media{background:var(--ink-50);padding:28px;display:grid;place-items:center;min-height:230px}
.model-media img{max-height:200px;width:auto;object-fit:contain;transition:transform var(--t-slow) var(--ease)}
.model:hover .model-media img{transform:scale(1.04)}
.model-body{padding:26px;display:flex;flex-direction:column;gap:10px;flex:1}
.model-kicker{font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;color:var(--blue);text-transform:uppercase}
.model-body h3{font-size:1.35rem}
.model-tagline{color:var(--ink-700)}
.model-fit{font-size:.8rem;color:var(--ink-500);margin-top:auto;padding-top:12px;border-top:1px solid var(--ink-50)}
.model-specs{display:flex;flex-wrap:wrap;gap:8px;margin-top:4px}
.model-specs span{
  font-size:.72rem;font-weight:600;color:var(--blue-deep);background:rgba(30,156,227,.1);
  padding:5px 11px;border-radius:var(--r-pill);
}

.device-hero{position:relative;border-radius:var(--r-sheet);overflow:hidden;box-shadow:var(--shadow-3)}
.device-hero img{width:100%;height:100%;object-fit:cover}
.device-hero .device-inset{
  position:absolute;right:20px;bottom:20px;width:38%;max-width:230px;background:rgba(255,255,255,.94);
  border-radius:12px;padding:12px;box-shadow:var(--shadow-2);backdrop-filter:blur(6px);
}
.device-hero .device-inset img{object-fit:contain;max-height:120px}
.device-hero .device-inset .cap{font-family:var(--mono);font-size:.65rem;color:var(--ink-500);letter-spacing:.08em;margin-top:6px;text-align:center}

@media (max-width: 860px){
  .proof-strip{grid-template-columns:repeat(2,1fr)}
  .realities-grid{grid-template-columns:1fr}
  .six-grid{grid-template-columns:1fr 1fr}
  .models{grid-template-columns:1fr}
}
@media (max-width: 560px){ .six-grid{grid-template-columns:1fr} }

/* ════════════════════════════════════════════════════════════════════
   v3 — scrollytelling: road-scene hero, viewfinder replay, truck anatomy
   ════════════════════════════════════════════════════════════════════ */

/* ── Hero road scene ────────────────────────────────────────────── */
.road-scene{
  position:relative;border-radius:var(--r-sheet);overflow:hidden;
  aspect-ratio:4/4.2;background:linear-gradient(180deg,#003D6B 0%,#065A93 46%,#086AAE 62%,#0A1929 62.2%,#12233A 100%);
  box-shadow:var(--shadow-3);
}
.road-scene svg{position:absolute;inset:0;width:100%;height:100%}
.rs-layer{will-change:transform}
.rs-stars circle{fill:rgba(255,255,255,.5)}
.rs-hills{fill:#04477A}
.rs-hills-far{fill:#035087;opacity:.7}
/* lane dashes stream toward viewer */
.rs-lane{stroke:#F7F9FC;stroke-width:5;stroke-dasharray:34 46;animation:lane 1.1s linear infinite;opacity:.85}
@keyframes lane{to{stroke-dashoffset:-80}}
/* truck bob + wheel spin */
.rs-truck{animation:bob 2.6s ease-in-out infinite;transform-origin:center}
@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}
.rs-wheel{animation:spin 1.05s linear infinite;transform-box:fill-box;transform-origin:center}
@keyframes spin{to{transform:rotate(360deg)}}
/* telemetry pings rising from the cab */
.rs-ping{fill:none;stroke:#1E9CE3;stroke-width:2;opacity:0;animation:ping 3s var(--ease-soft) infinite}
.rs-ping.p2{animation-delay:1s}.rs-ping.p3{animation-delay:2s}
@keyframes ping{0%{opacity:0;transform:translateY(0) scale(.6)}20%{opacity:.9}70%{opacity:0;transform:translateY(-46px) scale(1.25)}100%{opacity:0}}
/* floating telemetry chips */
.rs-chip{
  position:absolute;display:inline-flex;align-items:center;gap:7px;
  background:rgba(255,255,255,.94);border:1px solid rgba(255,255,255,.5);border-radius:10px;
  padding:9px 13px;font-size:.75rem;font-weight:600;color:var(--navy);
  box-shadow:var(--shadow-2);backdrop-filter:blur(6px);animation:chipFloat 5s ease-in-out infinite;
}
.rs-chip .mono{color:var(--ink-500);font-weight:500}
.rs-chip.c1{top:16px;left:16px}
.rs-chip.c2{top:16px;right:16px;animation-delay:1.2s}
.rs-chip.c3{bottom:18px;left:16px;animation-delay:2.1s}
.rs-chip.c4{bottom:18px;right:16px;animation-delay:.6s}
.rs-chip .dot{position:relative;width:7px;height:7px;border-radius:50%;background:var(--azure)}
.rs-chip .dot::after{content:"";position:absolute;inset:-5px;border-radius:50%;background:var(--azure);opacity:.55;animation:dotRadar 2s var(--ease-soft) infinite}
.rs-chip .rec-dot{width:7px;height:7px;border-radius:50%;background:#fff;outline:2px solid var(--azure);animation:blink 1.4s steps(2,start) infinite}
@keyframes chipFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}

/* ── Scrollytelling scaffold ────────────────────────────────────── */
.scrolly{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,4vw,64px);align-items:start}
/* Sticky pane fills the viewport below the nav and centres its contents,
   so the visual tracks the middle of the screen rather than hanging off
   the top while the steps scroll past. */
.scrolly-visual{
  position:sticky;top:68px;height:calc(100vh - 68px);
  display:flex;flex-direction:column;justify-content:center;
}
/* The pane unpins the moment the last step's bottom clears the viewport,
   and the last step is the shortest, so the final scene used to land while
   the pane was already sliding out. The tail after the last step keeps
   the pane pinned through the stamp and long enough to read it. */
.scrolly-steps{display:flex;flex-direction:column;padding-bottom:50vh}
.scrolly-step{min-height:62vh;display:flex;flex-direction:column;justify-content:center;padding:8vh 0}
.scrolly-step:first-child{padding-top:2vh}
.scrolly-step:last-child{min-height:50vh}
.step-kicker{font-family:var(--mono);font-size:.78rem;letter-spacing:.1em;color:var(--azure);margin-bottom:10px}
.scrolly-step h3{font-size:clamp(1.3rem,2.2vw,1.7rem);margin-bottom:10px}
.scrolly-step p{max-width:44ch}
.scrolly-step{opacity:.28;transition:opacity var(--t-med) var(--ease-soft)}
.scrolly-step.is-active{opacity:1}
@media (max-width: 860px){
  .scrolly{grid-template-columns:1fr}
  .scrolly-visual{top:84px;height:auto;display:block;z-index:2}
  .scrolly-step{min-height:52vh}
  .scrolly-steps{padding-bottom:22vh}
}
/* On a phone the visual is no longer sticky, so half-viewport steps just
   leave a screen of dead space between the replay and the caption under
   it. Keep enough height to drive the scene changes, and no more. */
@media (max-width: 620px){
  .scrolly-step{min-height:34vh;padding:5vh 0}
  .scrolly-step:first-child{padding-top:3vh}
  .scrolly-step:last-child{min-height:26vh}
  .scrolly-steps{padding-bottom:10vh}
}

/* ── Contextual feature graphics ────────────────────────────────────
   One reticle used to stand in for seventeen different ideas, on both
   light and dark sections, where its fixed pale-blue strokes washed out
   against the light ones. Each graphic now draws its own subject and
   takes its palette from these four tokens, so the same markup reads
   correctly in a .feature and a .feature.dark. */
.dgraphic{
  --dg-line:var(--ink-300);
  --dg-dim:var(--ink-100);
  --dg-ink:var(--navy);
  --dg-accent:var(--blue);
}
.feature.dark .dgraphic{
  --dg-line:rgba(158,210,242,.55);
  --dg-dim:rgba(255,255,255,.16);
  --dg-ink:#fff;
  --dg-accent:#1E9CE3;
}

/* ── Feature scenes ─────────────────────────────────────────────────
   The drawn 560x400 scenes from tools/build-scenes.py. Their labels are
   sized for the desktop box; a phone shows the same box at 60% and the
   type fell to 6px. So below 620px the box turns square and the SVG,
   which is drawn with everything that matters inside its central
   400x400, is cropped with preserveAspectRatio=slice: the scene zooms
   in on a phone instead of shrinking. */
.feature-media.is-scene,.split-media.is-scene,.feature.dark .feature-media.is-scene{
  aspect-ratio:7/5;background:var(--navy);border:1px solid var(--ink-100);
  box-shadow:var(--shadow-2);border-radius:var(--r-sheet);overflow:hidden;
}
.feature.dark .feature-media.is-scene{
  border-color:rgba(255,255,255,.14);box-shadow:0 30px 80px -24px rgba(0,0,0,.6);
}
.is-scene svg{display:block;width:100%;height:100%}
.viewfinder.is-scene-hero{aspect-ratio:16/11}
.viewfinder.is-scene-hero svg{position:absolute;inset:0;width:100%;height:100%}
@media (max-width:620px){
  .feature-media.is-scene,.split-media.is-scene,.viewfinder.is-scene-hero{aspect-ratio:1/1}
}

/* ── IQ-CAM viewfinder (When it matters) ────────────────────────── */
.incident .scrolly-step h3{color:#fff}
.incident .scrolly-step p{color:rgba(255,255,255,.72)}
/* 16:9 to match the footage exactly. On any other ratio object-fit:cover
   crops the sides, and the car that cuts in ends up at the right edge of
   frame. Pages that use the frame for something else set their own. */
.viewfinder{
  position:relative;aspect-ratio:16/9;border-radius:var(--r-sheet);overflow:hidden;
  background:#0A1929;border:1px solid rgba(255,255,255,.14);
  box-shadow:0 30px 80px -24px rgba(0,0,0,.6);
  container-type:inline-size;
}
.vf-scene{position:absolute;inset:0;opacity:0;transition:opacity .55s var(--ease-soft)}
.vf-scene>svg{position:absolute;inset:0;width:100%;height:100%}
.viewfinder[data-scene="2"] .vf-s2,
.viewfinder[data-scene="3"] .vf-s3,
.viewfinder[data-scene="4"] .vf-s4{opacity:1}
/* The live channel: real IQ-CAM footage under scenes 1 and 2. A light
   grade lifts the overcast flatness. The scrim keeps the overlay text
   legible top and bottom and covers the blurred plate strip the footage
   ships with. */
.vf-live{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;
  filter:saturate(.92) contrast(1.08);
  opacity:0;transition:opacity .55s var(--ease-soft);
}
.viewfinder[data-scene="1"] .vf-live,
.viewfinder[data-scene="2"] .vf-live{opacity:1}
/* a dip to black on every seek-back, so the jump reads as a cut */
.viewfinder.is-cut .vf-live{opacity:0;transition-duration:.14s}
.vf-scrim{position:absolute;inset:0;z-index:1;pointer-events:none;opacity:0;transition:opacity .55s var(--ease-soft);
  background:linear-gradient(180deg, rgba(3,15,28,.42) 0%, transparent 22%, transparent 66%, rgba(3,15,28,.86) 100%)}
.viewfinder[data-scene="1"] .vf-scrim,
.viewfinder[data-scene="2"] .vf-scrim{opacity:1}
/* cinematic grade — grain + vignette sit above every scene uniformly, the
   same treatment used on the static "how it works" illustration elsewhere
   on the site, so the scroll story doesn't look like a flat icon set. */
.vf-vignette{position:absolute;inset:0;z-index:3;pointer-events:none;
  background:radial-gradient(ellipse 72% 64% at 50% 40%, transparent 55%, rgba(0,0,0,.5) 100%);
}
.vf-grain{position:absolute;inset:0;z-index:3;pointer-events:none;opacity:.05;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.4'/%3E%3C/svg%3E");
}
/* Camera overlay — live scenes only; it makes no sense over the platform
   UI (scene 3) or the claim outcome (scene 4). Laid out like a real
   dashcam burn-in: record light and channel up top, clock and speed
   along the bottom strip. */
.vf-hud{position:absolute;inset:0;pointer-events:none;z-index:5;
  font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;color:rgba(255,255,255,.85);
  transition:opacity .45s var(--ease-soft)}
.viewfinder[data-scene="3"] .vf-hud,
.viewfinder[data-scene="4"] .vf-hud{opacity:0}
.vf-rec{position:absolute;top:14px;left:16px;display:flex;align-items:center;gap:7px;font-weight:700}
/* Mostly-on with one quick blink per cycle, like an actual REC light,
   rather than a metronomic 50/50 flicker. */
.vf-rec i{width:9px;height:9px;border-radius:50%;background:#fff;outline:2px solid var(--azure);animation:camBlink 3.2s cubic-bezier(.6,0,.4,1) infinite}
@keyframes camBlink{0%,84%{opacity:1}90%{opacity:.12}96%{opacity:1}100%{opacity:1}}
.vf-cam{position:absolute;top:14px;right:16px}
.vf-stamp-time{position:absolute;bottom:14px;left:16px}
.vf-gps{position:absolute;bottom:14px;right:16px}
/* The AI box (scene 2). airiq.js sets its geometry from the footage
   clock, keyframed to the car: it locks on as the car starts across the
   line, tracks it in, and holds when the frame freezes. The label plate
   flips from what the model saw to what the platform called it. */
.vf-box{position:absolute;left:52%;top:63%;width:5%;height:7%;z-index:4;opacity:0;
  border:2px solid var(--azure);border-radius:3px;
  box-shadow:0 0 0 1px rgba(3,15,28,.45), inset 0 0 0 1px rgba(3,15,28,.3);
  transition:border-color .2s var(--ease-soft)}
.viewfinder.is-locked .vf-box{opacity:1;animation:boxLock .26s cubic-bezier(.2,.8,.2,1) both}
@keyframes boxLock{0%{transform:scale(1.7);opacity:0}100%{transform:scale(1);opacity:1}}
.viewfinder.is-frozen .vf-box{border-color:#fff}
.vf-box-tag,.vf-box-chip{position:absolute;left:-2px;bottom:100%;margin-bottom:4px;white-space:nowrap;
  font-family:var(--mono);font-weight:700;letter-spacing:.06em;line-height:1;border-radius:3px;text-transform:uppercase}
.vf-box-tag{font-size:clamp(8px,1.6cqw,10px);padding:3px 5px;background:var(--azure);color:#0A1929;
  transition:opacity .15s var(--ease-soft)}
.viewfinder.is-frozen .vf-box-tag{opacity:0}
.vf-box-chip{font-size:clamp(10px,2cqw,13px);padding:5px 8px;background:#fff;color:var(--navy);
  box-shadow:0 6px 16px -4px rgba(0,0,0,.5);opacity:0;transform:translateY(4px)}
.viewfinder.is-frozen .vf-box-chip{animation:chipLand .32s cubic-bezier(.2,.8,.2,1) .18s both}
@keyframes chipLand{0%{opacity:0;transform:translateY(4px)}100%{opacity:1;transform:none}}
/* The alert edge and the toast wait for the freeze, the moment the
   platform calls it an event, rather than firing on a wall-clock guess. */
.viewfinder.is-frozen::after{
  content:"";position:absolute;inset:0;z-index:4;pointer-events:none;
  border:3px solid rgba(30,156,227,.85);border-radius:inherit;opacity:0;
  animation:alertFlash 1.8s cubic-bezier(.16,1,.3,1) 1 forwards;
}
/* One alert, one decay — a quick double-flash that settles to a quiet
   edge, not three identical square-wave blinks (which read as an error
   state rather than "we caught this"). */
@keyframes alertFlash{
  0%{opacity:0} 8%{opacity:1} 22%{opacity:.25} 30%{opacity:.85}
  45%{opacity:.15} 100%{opacity:.15}
}
.vf-banner{
  position:absolute;left:50%;top:12%;transform:translateX(-50%);z-index:6;
  background:rgba(255,255,255,.95);color:var(--navy);border-radius:10px;
  padding:10px 16px;font-size:.8rem;font-weight:600;box-shadow:var(--shadow-2);
  display:flex;align-items:center;gap:9px;white-space:nowrap;
  opacity:0;transition:opacity .4s var(--ease-soft),transform .4s var(--ease-soft);
}
.vf-banner svg{width:16px;height:16px;stroke:var(--blue);stroke-width:2.2;fill:none;stroke-linecap:round;stroke-linejoin:round}
.vf-banner.b2{background:rgba(10,25,41,.85);color:#fff;border:1.5px solid var(--azure)}
.vf-banner.b2 svg{stroke:var(--azure)}
.viewfinder.is-frozen .vf-banner.b2{opacity:1;transform:translateX(-50%) translateY(4px);transition-delay:.55s}
.vf-exon{
  position:absolute;inset:0;z-index:6;display:grid;place-items:center;pointer-events:none;
}
/* ink-stamp over the paper claim card, not a glass chip */
.vf-exon .stamp{
  margin:0;font-size:1.3rem;padding:14px 30px;border-width:3px;
  background:none;backdrop-filter:none;-webkit-backdrop-filter:none;
  color:var(--blue);border-color:var(--blue);
  text-shadow:none;box-shadow:none;
}
.viewfinder[data-scene="4"] .vf-exon .stamp{transform:rotate(-4deg) scale(1);opacity:1;transition-delay:.1s}
/* Scene 3: the event card in AirIQ Fleet. Real HTML so the two tiles can
   hold footage. Everything is em-sized off a container-query font-size,
   so the card scales with the frame from a phone to a wide column. */
.vf-s3{font-family:var(--mono);font-size:clamp(9px,1.75cqw,12px);color:#fff}
.vf-card{position:absolute;left:8%;right:8%;top:8%;bottom:8%;display:flex;flex-direction:column;gap:.9em;
  background:#0E2236;border:1px solid rgba(255,255,255,.12);border-radius:1.1em;padding:1.3em 1.6em;
  box-shadow:0 10px 30px -8px rgba(0,0,0,.5)}
.vf-card-head{display:flex;justify-content:space-between;align-items:baseline;gap:1em}
.vf-ev{color:#9ED2F2;font-weight:700;letter-spacing:.14em;text-transform:uppercase}
.vf-ev-time{color:rgba(255,255,255,.55);font-size:.92em;white-space:nowrap}
.vf-tiles{display:grid;grid-template-columns:1fr 1fr;gap:1.1em;flex:1;min-height:0}
.vf-tile{margin:0;position:relative;border-radius:.7em;overflow:hidden;background:#0A1929;border:1px solid rgba(255,255,255,.14)}
.vf-tile video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.vf-tile figcaption{position:absolute;left:.7em;bottom:.6em;font-size:.8em;letter-spacing:.14em;text-transform:uppercase;
  color:#fff;background:rgba(10,25,41,.72);padding:.3em .6em;border-radius:.4em}
.vf-tl-labels{display:flex;justify-content:space-between;font-size:.8em;color:rgba(255,255,255,.45);margin-bottom:-.3em}
.vf-timeline{position:relative;height:.5em;border-radius:.25em;background:rgba(255,255,255,.12);flex:none}
.vf-tl-fill{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:inherit;background:var(--azure)}
/* the event marker sits where the freeze frame is in the clip */
.vf-tl-mark{position:absolute;left:65%;top:50%;width:.8em;height:.8em;margin:-.4em 0 0 -.4em;background:#fff;border-radius:.15em;
  rotate:45deg;animation:evPulse 2.2s var(--ease-soft) infinite}
.vf-tl-head{position:absolute;left:0;top:50%;width:.95em;height:.95em;margin:-.475em 0 0 -.475em;border-radius:50%;background:#fff;
  box-shadow:0 0 0 2px rgba(30,156,227,.55)}
.vf-actions{display:flex;align-items:center;gap:1.4em;flex:none}
.vf-share{background:var(--azure);color:#0A1929;font-weight:700;padding:.75em 1.5em;border-radius:.7em;white-space:nowrap}
.vf-sync{color:rgba(255,255,255,.55);font-size:.95em}
@container (max-width: 420px){ .vf-sync{display:none} }
/* event marker on a scene-3 SVG timeline (solution pages) breathes gently */
.vf-evmark{animation:evPulse 2.2s var(--ease-soft) infinite;transform-box:fill-box;transform-origin:center}
@keyframes evPulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(1.15)}}

/* ── Connected truck anatomy ────────────────────────────────────── */
/* A rendered rig rather than the flat vector: the devices are small and
   the labels are the point, so the truck has to read as a real object
   for the badges to mean anything against it. */
.truck-stage{
  position:relative;border-radius:var(--r-sheet);overflow:hidden;aspect-ratio:2/1;
  background:radial-gradient(ellipse 80% 60% at 50% 12%, #F7F9FC 0%, #EDF2F8 50%, #E1E8F1 100%);
  border:1px solid var(--ink-100);box-shadow:var(--shadow-2);
  container-type:inline-size;
}
.truck-ground{
  position:absolute;left:6%;right:4%;top:66%;height:10%;z-index:0;
  background:radial-gradient(ellipse 50% 100% at 50% 50%, rgba(10,25,41,.24) 0%, rgba(10,25,41,.1) 55%, transparent 80%);
  filter:blur(2px);
}
.truck-rig{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;z-index:1}
/* Leader from badge to label, measured in airiq.js so it meets the
   label's nearest edge at any width. */
.truck-leaders{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:2}
.truck-leaders line{stroke:var(--azure);stroke-opacity:.55;stroke-width:1.4;opacity:0;transition:opacity .4s var(--ease-soft)}
.truck-leaders line.is-on,
.truck-stage[data-active="all"] .truck-leaders line{opacity:1}
/* Badge: icon on a white disc, seated on the component. Inactive ones
   stay visible but recede, so the rig always reads as fully kitted. */
.device{position:absolute;z-index:3;width:0;height:0}
.device-badge{
  position:absolute;left:0;top:0;width:34px;height:34px;margin:-17px 0 0 -17px;border-radius:50%;
  background:#fff;border:1.5px solid var(--azure);display:grid;place-items:center;
  box-shadow:0 0 0 1px rgba(10,25,41,.14),0 0 0 5px rgba(255,255,255,.65),0 8px 18px -4px rgba(8,106,174,.55);
  opacity:.45;transform:scale(.82);
  transition:opacity .4s var(--ease-soft),transform .4s var(--ease-soft);
}
.device-badge svg{width:17px;height:17px;stroke:var(--blue-deep);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.device-badge::before,.device-badge::after{content:"";position:absolute;inset:-2px;border-radius:50%;border:2px solid var(--azure);opacity:0}
.device.is-on .device-badge,
.truck-stage[data-active="all"] .device-badge{opacity:1;transform:none}
/* Two rings half a cycle apart read as a device broadcasting, not one
   shape bouncing. */
.device.is-on .device-badge::before,.device.is-on .device-badge::after,
.truck-stage[data-active="all"] .device-badge::before,.truck-stage[data-active="all"] .device-badge::after{
  animation:hsPing 2.2s cubic-bezier(.16,.72,.3,1) infinite}
.device.is-on .device-badge::after,
.truck-stage[data-active="all"] .device-badge::after{animation-delay:1.1s}
@keyframes hsPing{0%{transform:scale(.6);opacity:.85}100%{transform:scale(2.1);opacity:0}}
.device-label{
  position:absolute;transform:translate(-50%,-50%);z-index:3;
  background:var(--navy);color:#fff;border-radius:9px;padding:7px 12px;
  font-size:.74rem;font-weight:600;white-space:nowrap;box-shadow:var(--shadow-2);
  opacity:0;transition:opacity .4s var(--ease-soft);pointer-events:none;
}
.device-label small{display:block;font-weight:400;color:rgba(255,255,255,.7);font-size:.66rem;letter-spacing:.01em}
.device-label.is-on,
.truck-stage[data-active="all"] .device-label{opacity:1}
/* A status strip along the bottom edge rather than a floating pill: the
   top of the frame belongs to the labels, and a pill anywhere in the
   middle band lands on one of them. */
.truck-glass{
  position:absolute;left:0;right:0;bottom:0;transform:translateY(100%);z-index:4;
  display:flex;align-items:center;justify-content:center;gap:10px;background:rgba(255,255,255,.94);
  border-top:1px solid var(--ink-100);padding:6px 14px;
  font-size:.72rem;font-weight:600;color:var(--navy);
  opacity:0;transition:opacity .45s var(--ease-soft),transform .45s var(--ease-soft);
}
.truck-glass .dot{position:relative;width:8px;height:8px;border-radius:50%;background:var(--azure)}
.truck-glass .dot::after{content:"";position:absolute;inset:-5px;border-radius:50%;background:var(--azure);opacity:.55;animation:dotRadar 2s var(--ease-soft) infinite}
.truck-stage[data-active="all"] .truck-glass{opacity:1;transform:none}
/* Badges and labels scale with the stage, not the viewport: a narrow
   column on a wide screen is the same problem as a phone. */
@container (max-width: 520px){
  .device-badge{width:26px;height:26px;margin:-13px 0 0 -13px}
  .device-badge svg{width:13px;height:13px}
  .device-label{font-size:.62rem;padding:5px 8px;border-radius:7px}
  .device-label small{font-size:.56rem}
  .truck-glass{font-size:.66rem;padding:5px 10px}
}
/* the step copy already says "single pane of glass"; on a phone the
   strip would sit on the bottom labels */
@media (max-width: 620px){
  .truck-glass{display:none}
}

@media (prefers-reduced-motion: reduce){
  .rs-lane,.rs-wheel,.rs-truck,.rs-ping,.rs-chip{animation:none}
  .scrolly-step{opacity:1}
  .vf-scene,.vf-live,.vf-scrim{transition:none}
  .viewfinder.is-frozen::after{animation:none;opacity:.4}
  .vf-rec i,.vf-tl-mark,.vf-evmark{animation:none}
  .vf-exon .stamp{opacity:0}
  .viewfinder[data-scene="4"] .vf-exon .stamp{opacity:1}
  .device-badge,.device-label,.truck-leaders line{transition:none}
  .device-badge{opacity:1;transform:none}
  .device-badge::before,.device-badge::after{animation:none !important}
}

/* ════════════════════════════════════════════════════════════════════
   v4 — immersive isometric hero (billor-inspired, AirIQ blues)
   ════════════════════════════════════════════════════════════════════ */
.hb{
  /* Height of the sticky nav (68px bar + 1px hairline). The hero's whole
     vertical system is measured against the space *below* it, so the copy
     block can never be pushed past the fold. */
  --hb-hdr:69px;
  /* Three-way min(): 980px caps the scene on a tall monitor, 92svh leaves a
     sliver of the next section showing as a scroll affordance, and
     100svh - nav is the hard ceiling — on any viewport short enough that the
     affordance would cost more than it's worth, the hero stops exactly at the
     fold instead. svh (not vh) because a mobile vh is the URL-bar-hidden
     height: sizing to it hides the CTAs behind the bar on first paint. */
  position:relative;min-height:min(980px, 92svh, calc(100svh - var(--hb-hdr)));overflow:hidden;
  background:
    radial-gradient(38% 34% at 96% 8%, rgba(158,210,242,.22), transparent 70%),
    radial-gradient(120% 100% at 80% -20%, rgba(30,156,227,.35), transparent 60%),
    radial-gradient(90% 80% at 10% 110%, rgba(0,61,107,.55), transparent 65%),
    linear-gradient(158deg,#0B5F9C 0%, #086AAE 48%, #064E80 100%);
  display:flex;align-items:center;
}
.hb::before{ /* film grain */
  content:"";position:absolute;inset:0;z-index:6;pointer-events:none;
  background:url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");background-size:220px 220px;opacity:.05;mix-blend-mode:overlay;
}
.hb-scene{position:absolute;inset:0;pointer-events:none}
.hb-scene::after{
  content:"";position:absolute;inset:0;
  background:
    /* Copy-column scrim. The road's lane markings are near-white (.8 alpha) and
       were cutting straight through the CTA row and the solution chips. This
       ramps darkness across the left column where the copy sits, then clears
       before the truck so the scene still reads. */
    linear-gradient(96deg,
      rgba(4,26,46,.78) 0%,
      rgba(4,26,46,.68) 26%,
      rgba(4,26,46,.34) 45%,
      rgba(4,26,46,.06) 58%,
      transparent 66%),
    radial-gradient(56% 62% at 22% 74%, rgba(4,26,46,.5), transparent 70%),
    linear-gradient(180deg, rgba(4,26,46,.28), transparent 14%);
}
/* Below the two-column split the copy sits over the middle of the scene, so the
   directional scrim is replaced with an even one. */
@media (max-width: 900px){
  .hb-scene::after{
    background:
      linear-gradient(180deg, rgba(4,26,46,.62), rgba(4,26,46,.46) 55%, rgba(4,26,46,.66)),
      radial-gradient(56% 62% at 22% 74%, rgba(4,26,46,.4), transparent 70%);
  }
}
/* On a phone the copy runs the full width over the middle of the scene, so
   the vehicle was reading through the lede. Deepen it further. */
@media (max-width: 620px){
  .hb-scene::after{
    background:
      linear-gradient(180deg, rgba(4,26,46,.86), rgba(4,26,46,.74) 46%, rgba(4,26,46,.62) 72%, rgba(4,26,46,.8));
  }
  .hb-copy .lede{text-shadow:0 2px 20px rgba(3,20,36,.72)}
}
/* the road: a wide strip rotated to the truck's iso axis.
   Height keys the whole scene's scale. It must track viewport HEIGHT as
   well as width: the truck tops out ~3.6 road-heights above the road's
   center, so on a short laptop viewport a width-only size pushed the cab
   and its pin labels past the hero's top edge, where the sticky nav
   appeared to swallow them. min(22vw,24vh) keeps the rig inside the frame
   on any aspect ratio. */
/* The +140px seats the scene clear of the copy. The H1's rotating word
   changes width, and on its longest state ("asset management.") the line ran
   ~55px into the cab and touched the IQ-Plug label at 1280. Clearance has to
   be measured against that widest state, not the one that happens to be
   showing. A fixed px offset rather than a bigger % because the copy is
   container-anchored: a % would re-open the drift as the screen widens. */
.hb-road{
  position:absolute;left:calc(53.5% + 140px);top:51%;width:180vmax;height:clamp(180px,min(22vw,24vh),340px);
  transform:translate(-50%,-50%) rotate(-26.57deg);
  background-image:
    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E"),
    /* soft sheen where the light falls */
    radial-gradient(42% 130% at 58% 50%, rgba(158,210,242,.08), transparent 70%),
    /* wheel-polished tracks in each lane */
    linear-gradient(180deg,
      transparent 17%, rgba(0,0,0,.20) 24%, rgba(0,0,0,.20) 33%, transparent 40%,
      transparent 60%, rgba(0,0,0,.20) 67%, rgba(0,0,0,.20) 76%, transparent 83%),
    /* asphalt base */
    linear-gradient(180deg,#1B2D44 0%, #16263A 48%, #101E2E 100%);
  background-size:190px 190px, 100% 100%, 100% 100%, 100% 100%;
  background-blend-mode:overlay, normal, normal, normal;
  box-shadow:0 0 90px rgba(4,32,56,.6), inset 0 2px 0 rgba(255,255,255,.05), inset 0 -2px 0 rgba(0,0,0,.3);
}
.hb-road::before{ /* shoulders + edge lines */
  content:"";position:absolute;inset:0;
  background:
    /* far-side barrier: one clean rail, no posts */
    linear-gradient(180deg, rgba(233,240,247,.5), rgba(233,240,247,.5)) 0 0/100% 2.5px no-repeat,
    /* light shoulder bands inside the rails */
    linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.05)) 0 4%/100% 7% no-repeat,
    linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,.04)) 0 96%/100% 6% no-repeat,
    /* solid edge lines */
    linear-gradient(180deg, rgba(247,249,252,.8), rgba(247,249,252,.8)) 0 13%/100% 3px no-repeat,
    linear-gradient(180deg, rgba(247,249,252,.8), rgba(247,249,252,.8)) 0 87%/100% 3px no-repeat;
}
.hb-road::after{ /* streaming centre dashes */
  content:"";position:absolute;left:0;right:0;top:50%;height:5px;transform:translateY(-50%);
  background:repeating-linear-gradient(90deg,
    transparent 0 6px, rgba(247,249,252,.92) 6px 58px, transparent 58px 128px);
  border-radius:3px;opacity:.85;animation:roadflow 1.3s linear infinite;
  filter:drop-shadow(0 1px 1px rgba(0,0,0,.4));
}
@keyframes roadflow{to{background-position-x:128px}}
/* Lane-locked vehicle anchor. left/top are % of the road strip; the
   wrapper's bottom-center is the anchor point, seated on the far-lane
   wheel track, and rotate(+26.57deg) exactly cancels the road's tilt so
   the iso sprite reads upright. Everything scales with the road. */
.hb-veh{
  position:absolute;left:57.9%;top:-136%;height:164%;width:0;
  transform:rotate(26.57deg);transform-origin:bottom center;
}
.hb-truck{
  position:absolute;left:0;bottom:-38.6%;height:100%;aspect-ratio:298/258;width:auto;max-width:none;
  transform:translateX(-50%);
  filter:drop-shadow(-26px 34px 26px rgba(3,18,32,.5)) drop-shadow(-6px 10px 7px rgba(3,18,32,.4));
  animation:bob-veh 3s ease-in-out infinite;
  transition:opacity .45s var(--ease-soft);
}
@keyframes bob-veh{0%,100%{transform:translate(-50%,0)}50%{transform:translate(-50%,-3px)}}
/* van keeps its own spot along the road */
.hb-veh:has(.is-van){left:54%}

/* Handoff: the outgoing vehicle accelerates away down-road (bottom-left),
   the next one closes from behind (top-right). The wrapper's counter-
   rotation cancels the road tilt, so the sprite's own axes are screen
   axes — travel along the road is 2:-1 in x:y, matching atan(0.5)=26.57deg. */
.hb-truck.is-leaving{
  animation:veh-depart 1.35s cubic-bezier(.5,0,.85,.3) forwards;
}
.hb-truck.is-arriving{
  animation:veh-approach 1.6s cubic-bezier(.16,.72,.3,1) both;
}
@keyframes veh-depart{
  0%  {transform:translate(-50%,0);opacity:1}
  70% {opacity:1}
  100%{transform:translate(-50%,0) translate(-150vw,75vw);opacity:0}
}
@keyframes veh-approach{
  0%  {transform:translate(-50%,0) translate(150vw,-75vw);opacity:0}
  18% {opacity:1}
  100%{transform:translate(-50%,0);opacity:1}
}
.hb-truck.is-van{height:46%;aspect-ratio:1600/1290;bottom:-19.5%}

/* ── Product pins on the hero vehicle ───────────────────────────────
   .hb-pins mirrors the sprite box exactly (same anchor, height and
   aspect), so each pin's left/top is a % of the vehicle itself. */
.hb-pins{
  position:absolute;left:0;bottom:-38.6%;height:100%;aspect-ratio:298/258;
  transform:translateX(-50%);pointer-events:none;
}
.hb-veh:has(.is-van) .hb-pins{height:46%;aspect-ratio:1600/1290;bottom:-19.5%}
.hb-pin{position:absolute}
/* the dot sitting on the component */
.hb-pin i{
  position:absolute;left:0;top:0;width:7px;height:7px;margin:-3.5px 0 0 -3.5px;
  border-radius:50%;background:#fff;box-shadow:0 0 0 3px rgba(30,156,227,.35);
}
.hb-pin i::after{
  content:"";position:absolute;inset:-3.5px;border-radius:50%;
  border:1px solid rgba(255,255,255,.75);
  animation:hbPing 2.6s var(--ease-soft) infinite;
}
@keyframes hbPing{
  0%  {transform:scale(1);opacity:.7}
  70% {transform:scale(2.5);opacity:0}
  100%{transform:scale(2.5);opacity:0}
}
/* leader line up to the label */
.hb-pin b::before{
  content:"";position:absolute;left:14px;top:100%;width:1px;height:22px;
  background:linear-gradient(180deg,rgba(255,255,255,.55),rgba(255,255,255,0));
}
.hb-pin b{
  position:absolute;left:0;bottom:26px;display:block;white-space:nowrap;
  background:rgba(6,26,44,.62);border:1px solid rgba(255,255,255,.14);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  border-radius:9px;padding:6px 11px;
  font-size:.72rem;font-weight:600;color:#fff;letter-spacing:.01em;
  box-shadow:0 8px 22px rgba(3,18,32,.35);
  transform:translate(-14px,6px);opacity:0;
  transition:opacity .5s var(--ease-soft),transform .5s var(--ease-soft);
}
.hb-pin small{
  display:block;font-weight:400;font-size:.66rem;
  color:rgba(233,240,247,.66);letter-spacing:.02em;margin-top:1px;
}
/* settle in once the vehicle has arrived, one after another */
.hb-pins.is-on b{opacity:1;transform:translate(-14px,0)}
.hb-pin:nth-child(2) b{transition-delay:.09s}
.hb-pin:nth-child(3) b{transition-delay:.18s}
.hb-veh.is-transiting .hb-pins b{opacity:0;transition-duration:.2s}
.hb-veh.is-transiting .hb-pin i{opacity:0;transition:opacity .2s}

/* Telemetry tag — quiet, sits just off the vehicle's tail */
.hb-tag{
  position:absolute;left:0;bottom:-38%;
  display:inline-flex;align-items:center;gap:7px;white-space:nowrap;
  font-size:.72rem;letter-spacing:.02em;color:rgba(233,240,247,.72);
  background:rgba(6,26,44,.34);border:1px solid rgba(255,255,255,.10);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  padding:5px 11px;border-radius:999px;
  /* second translate pulls it in beside the cab's near-side wheel */
  transform:translateX(-50%) translateX(-132%);
  transition:opacity .35s var(--ease-soft);
}
.hb-tag .dot{
  width:5px;height:5px;border-radius:50%;background:var(--azure);
  box-shadow:0 0 0 3px rgba(30,156,227,.16);flex:none;
}
.hb-veh.is-transiting .hb-tag{opacity:0}
.hb-veh:has(.is-van) .hb-tag{bottom:-24%;transform:translateX(-50%) translateX(-78%)}
/* rotating solution word in the H1 */
.hb-rotate{display:inline-block;transition:opacity .32s var(--ease-soft),transform .32s var(--ease-soft)}
.hb-rotate.is-out{opacity:0;transform:translateY(14px)}

/* solution quick-links under the lede */
.hb-sol{display:flex;flex-wrap:wrap;gap:clamp(5px,1svh,8px);margin-top:clamp(7px,2.2svh,22px);max-width:520px}
.hb-sol a{
  font-size:.8rem;font-weight:500;color:rgba(233,240,247,.85);text-decoration:none;
  background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.18);
  border-radius:999px;padding:7px 14px;
  transition:background var(--t-fast) var(--ease-soft),border-color var(--t-fast) var(--ease-soft);
}
.hb-sol a:hover{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.38);color:#fff}
/* glass chips */
/* copy block */
.hb-copy{
  position:relative;z-index:5;width:100%;
  /* On the page container, not the viewport: a 6vw offset crossed the
     nav's left edge at ~1254px, so the headline sat right of the logo on
     a laptop and up to 290px left of it on a wide monitor. Sharing the
     container means the H1 starts under the logo at every width. */
  max-width:var(--container);margin-inline:auto;
  /* Every vertical value in the hero is a clamp with an svh middle term, so
     the block breathes on a 27" display and compresses on a 13" laptop or a
     landscape phone rather than overflowing. The block is centred by the
     flex parent, so on tall screens the exact padding barely reads — it
     earns its keep at the short end of the range. */
  padding:clamp(18px,7.4svh,96px) var(--gutter) clamp(14px,5.4svh,88px);
}
/* The utility class this carried (.mb-3) is doubled-specificity and fixed;
   the hero needs the trust line's gap to scale like everything else. */
.hb-copy .eyebrow{margin-bottom:clamp(4px,1.6svh,14px)}
.hb-copy h1{
  /* Three fixed lines of display type are the single biggest claim on the
     hero's height, so the preferred size is the smaller of a width-driven
     and a height-driven term. Without the svh term a 1366x768 laptop got
     the same 69px headline as a 1920x1080 desktop and pushed the CTAs off. */
  color:#fff;font-size:clamp(2.1rem,min(5.6vw,8.5svh),4.3rem);font-weight:650;
  line-height:1.02;letter-spacing:-.025em;text-shadow:0 4px 30px rgba(3,20,36,.4);
}
.hb-copy h1 .accent{color:var(--blue-pale)}
.hb-copy .lede{color:rgba(255,255,255,.85);margin-top:clamp(7px,2svh,20px);max-width:46ch;text-shadow:0 2px 16px rgba(3,20,36,.4)}
.hb-copy .fit-note{margin-top:clamp(5px,1.7svh,18px)}
.hb-copy .hero-ctas{margin-top:clamp(11px,3svh,30px)}
.hb-copy .btn-primary{background:#fff;color:var(--blue-deep)}
.hb-copy .btn-primary:hover{background:var(--ink-50);color:var(--blue-deep)}
.hb-copy .btn-secondary{background:rgba(255,255,255,.12);color:#fff;border-color:rgba(255,255,255,.4)}
.hb-copy .btn-secondary:hover{background:rgba(255,255,255,.2);border-color:rgba(255,255,255,.6);color:#fff}
/* Short desktop viewports: seat the scene a little lower so the space
   above the truck breathes instead of grazing the nav. */
@media (min-width: 901px) and (max-height: 820px){
  .hb-road{top:56%}
}
/* Between the stack breakpoint and the container width there is not enough
   frame left to hold the rig at full size once the scene is offset clear of
   the copy — the trailer's tail ran past the right edge at 901px. Shrink the
   scene here rather than pull it back under the headline. */
@media (min-width: 901px) and (max-width: 1151px){
  .hb-road{height:clamp(150px,min(17vw,20vh),240px)}
}
@media (max-width: 900px){
  .hb{min-height:min(88svh, calc(100svh - var(--hb-hdr)))}
  .hb-road{left:62%;top:58%;height:190px}
  .hb-veh{left:56%}
  .hb-copy .lede{max-width:100%}
  .hb-copy h1{font-size:clamp(2.1rem,min(11vw,8.5svh),3rem)}
  /* The scene sits behind the copy from here down, so the block is pushed
     lower to clear the rig — but as a proportion of the viewport, not the
     flat 104px that ate a third of a landscape phone's height. */
  .hb-copy{padding-top:clamp(24px,10svh,104px)}
  /* the vehicle sits behind the copy here — callouts would collide with
     the headline, so the labels go and only the component dots stay */
  .hb-pin b{display:none}
  .hb-tag{display:none}
}

/* ── Short viewports ────────────────────────────────────────────────
   Fluid type and margins carry the hero down to roughly 700px of viewport
   height. Below that the fixed-height parts — pill rows, button padding,
   the trust line — are what remain, and they have to be dealt with as
   layout, not as scale. Each tier trims the least load-bearing element
   left, so the headline, the lede, the fleet-size line and the CTAs stay
   above the fold on every screen. Height-only queries: these fire on a
   1366x768 laptop and a landscape phone alike, which is the point. */

/* Wrapped solution pills cost 50px on a laptop and 100px on a phone, where
   they run to three rows. One row that scrolls sideways costs 44px on any
   screen and loses nothing — every link is still reachable. Width OR height:
   a narrow portrait phone needs this as much as a short laptop does. */
@media (max-width: 620px), (max-height: 700px){
  .hb-sol{
    flex-wrap:nowrap;overflow-x:auto;max-width:100%;
    scrollbar-width:none;-ms-overflow-style:none;
    /* the row is a scroll container now, so let it run to the gutter edge
       and pad the tail rather than clipping the last pill against it */
    padding-right:var(--gutter);
    /* Fade the tail so a half-visible pill reads as "there is more, scroll"
       rather than as a clipped element. Drops away once you reach the end. */
    mask-image:linear-gradient(90deg,#000 calc(100% - 34px),transparent);
    -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 34px),transparent);
  }
  .hb-sol:not(:hover){scroll-snap-type:x proximity}
  .hb-sol::-webkit-scrollbar{display:none}
  .hb-sol a{flex:0 0 auto;scroll-snap-align:start}
  .hb-copy .btn-lg{padding:12px 22px}
}

/* Compact copy. Fires on a narrow phone at any height, and on anything
   phone-width that is also short — a 414x580 in-app browser sat in the gap
   between those two conditions and was the worst overflow in the sweep. The
   trust line steps down a size and loses a little tracking so it holds its
   line further down, and the lede steps down too — which takes it from six
   lines to five at 320px. Nothing is dropped at this tier. */
@media (max-width: 400px),
       (max-width: 1000px) and (max-height: 700px),
       (max-height: 620px){
  .hb-copy .eyebrow{font-size:12px;letter-spacing:.06em}
  .hb-copy .lede{font-size:var(--fs-sm)}
  .hb-copy{padding-top:clamp(18px,6.5svh,60px)}
}

/* The stretched CTA row is held to phone widths on its own query: at 900px
   the same flex:1 would give two 430px-wide buttons. */
@media (max-width: 400px), (max-width: 620px) and (max-height: 700px){
  /* Side by side rather than stacked. At 320px the pair was 1px too wide to
     share a row, so it wrapped and cost 96px for two buttons that fit in 46 —
     splitting the row evenly makes the fit deterministic instead of leaving
     it to a font-metric coin flip. Both keep a 44px tap target. Held to
     phone widths on purpose: flex:1 across a 1152px container would give a
     short desktop window two absurdly wide buttons. */
  .hb-copy .hero-ctas{flex-wrap:nowrap;gap:8px}
  .hb-copy .hero-ctas .btn{
    flex:1 1 0;min-width:0;justify-content:center;text-align:center;
    padding-inline:10px;font-size:.875rem;white-space:nowrap;
  }
}

/* Landscape phones and short split-screen windows. The trust line is the one
   element here that neither answers a question nor offers an action. */
@media (max-height: 560px){
  .hb-copy .eyebrow{display:none}
  .hb-copy{padding-top:clamp(16px,4svh,40px);padding-bottom:clamp(14px,4svh,40px)}
  .hb-copy .lede{font-size:var(--fs-sm)}
  .hb-copy .hero-ctas{gap:8px}
  .hb-copy .btn-lg{padding:11px 18px;font-size:.9375rem}
}

/* Small phones held upright, and any phone-width window short enough that
   the pill row would push a CTA under the fold. The pills are the only thing
   in the hero duplicated verbatim in the mobile nav two taps away, so they
   are what goes — before the fleet-size line, and long before a CTA. */
@media (max-width: 620px) and (max-height: 620px){
  .hb-sol{display:none}
}

/* Very short windows, at any width. The display type has to stop behaving
   like display type here: three lines of it is the whole budget otherwise.
   The threshold sits at 520 rather than hard against the 440 below it so
   there is real headroom either side — a hard edge one pixel above a tier
   was what put 901x441 and 621x561 over in the first place. */
@media (max-height: 520px){
  .hb-copy h1{font-size:clamp(1.75rem,min(9vw,8.4svh),2.2rem)}
  .hb-copy .lede{font-size:13px;line-height:1.45}
  .hb-copy .fit-note{font-size:13px;line-height:1.45}
  .hb-copy{padding-top:clamp(10px,2.5svh,28px);padding-bottom:clamp(10px,2.5svh,28px)}
}

/* Landscape phones. Only the pills go — the scene stays, because it is
   absolutely positioned and so costs the copy nothing in height. Hiding it
   bought no space and left a landscape phone looking like a blank blue
   field with the copy stranded in one corner. */
@media (max-height: 440px){
  .hb-sol{display:none}
}

/* The floor. Under ~380px of height there is no longer room for both the
   promise and the proof, so the fleet-size line goes — it is reassurance,
   where the lede is the product and the CTAs are the point. This is a
   browser window dragged to a sliver rather than any real device — the
   shortest viewport in circulation is a landscape phone at ~320px — and the
   hero still holds its headline, lede and both CTAs down at 280. */
@media (max-height: 380px){
  .hb-copy .fit-note{display:none}
  .hb-copy h1{font-size:clamp(1.5rem,min(8vw,8svh),1.9rem)}
  .hb-copy .lede{font-size:12px;line-height:1.4}
  .hb-copy{padding-top:clamp(8px,2svh,20px);padding-bottom:clamp(8px,2svh,20px)}
}

@media (prefers-reduced-motion: reduce){
  .hb-road::after{animation:none}
  .hb-truck{animation:none}
}

/* ── Photo bands & image utilities (real photography layer) ─────── */
.photo-band{
  position:relative;min-height:clamp(360px,46vw,520px);display:flex;align-items:flex-end;
  overflow:hidden;border-radius:var(--r-sheet);box-shadow:var(--shadow-2);
}
.photo-band img.bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform 1.2s var(--ease)}
.photo-band:hover img.bg{transform:scale(1.035)}
.photo-band::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(206deg, rgba(4,26,46,0) 32%, rgba(4,26,46,.85) 88%);
}
.photo-band .pb-content{position:relative;z-index:2;color:#fff;padding:clamp(24px,4.5vw,52px);max-width:640px}
.photo-band .eyebrow{color:var(--blue-pale)}
.photo-band blockquote{
  font-size:clamp(1.25rem,2.3vw,1.75rem);font-weight:500;line-height:1.35;
  letter-spacing:-.01em;text-wrap:balance;
}
.photo-band .pb-attrib{margin-top:14px;font-size:var(--fs-sm);color:rgba(255,255,255,.75)}
.photo-band h2{color:#fff}
.photo-band .lede{color:rgba(255,255,255,.85)}
.split-media img{width:100%;height:100%;object-fit:cover;display:block;aspect-ratio:4/3}
.img-round{border-radius:var(--r-sheet);overflow:hidden;box-shadow:var(--shadow-2)}

/* ── Help Center search ──────────────────────────────────────────
   Seventeen answers is past the point where scanning beats typing. */
.help-search{
  position:relative;display:flex;align-items:center;gap:12px;max-width:540px;
  background:var(--paper);border:1px solid var(--ink-100);border-radius:999px;
  padding:0 8px 0 20px;box-shadow:var(--shadow-1);
  transition:border-color var(--t-fast),box-shadow var(--t-fast);
}
.help-search:focus-within{border-color:var(--blue);box-shadow:0 0 0 4px rgba(8,106,174,.12)}
.help-search .hs-ico{
  width:19px;height:19px;flex:none;stroke:var(--ink-500);fill:none;
  stroke-width:2;stroke-linecap:round;
}
.help-search input{
  flex:1;min-width:0;border:0;background:none;padding:16px 0;
  font:inherit;font-size:16px;color:var(--ink-900);
}
.help-search input:focus{outline:none}
.help-search input::-webkit-search-cancel-button,
.help-search input::-webkit-search-decoration{-webkit-appearance:none;display:none}
.help-search button{
  flex:none;width:44px;height:44px;border:0;border-radius:50%;cursor:pointer;
  background:none;color:var(--ink-500);display:grid;place-items:center;
  transition:background var(--t-fast),color var(--t-fast);
}
.help-search button[hidden]{display:none}
.help-search button:hover{background:var(--ink-50);color:var(--ink-900)}
.help-search button svg{width:15px;height:15px;stroke:currentColor;stroke-width:2.2;stroke-linecap:round}
.help-count{margin-top:var(--s-4);font-size:var(--fs-sm);color:var(--ink-500)}
.help-count:empty{display:none}
.help-count b{color:var(--ink-900)}
/* while filtering, the category jump-links are noise and the extra
   section padding pushes matches under the fold */
[data-help].is-searching [data-help-tiles]{display:none}
[data-help].is-searching .section[data-help-group]{padding-block:var(--s-7)}
[data-help].is-searching [data-help-group] .section-head{display:none}
[data-help].is-searching .section[data-help-group] + .section[data-help-group]{padding-top:0}
.help-empty{
  border:1px dashed var(--ink-100);border-radius:var(--r-card);
  padding:var(--s-9) var(--s-6);text-align:center;color:var(--ink-700);
}
.help-empty h2{font-size:1.15rem;margin-bottom:8px}
/* a linked answer opens itself, and says so for a moment */
.faq-item:target{scroll-margin-top:96px}
.faq-item:target > summary{color:var(--blue)}

/* ── Help Center: walkthrough index ─────────────────────────────────
   Twenty-three walkthroughs is too many for a tile grid and too few for
   a search-only page. They group by category, and each row leads with the
   screen you will be looking at, because people recognize the screen
   faster than they read the title. */
.guide-cats{display:grid;gap:var(--s-9);margin-top:var(--s-8)}
.guide-cat-h{
  font-size:1.15rem;letter-spacing:-.01em;
  padding-bottom:var(--s-4);border-bottom:1px solid var(--ink-100);
}
.guide-cat-h span{
  display:block;margin-top:4px;font-size:var(--fs-sm);font-weight:400;color:var(--ink-500);
}
.guide-list{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-3);margin-top:var(--s-5)}
.guide-item{
  display:flex;align-items:center;gap:var(--s-4);
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-card);
  padding:12px 14px;text-decoration:none;color:inherit;
  transition:border-color var(--t-med),transform var(--t-med) var(--ease),box-shadow var(--t-med);
}
.guide-item:hover{
  border-color:rgba(8,106,174,.4);transform:translateY(-2px);box-shadow:var(--shadow-1);
}
.guide-shot{
  flex:none;width:84px;height:56px;border-radius:8px;overflow:hidden;
  border:1px solid var(--ink-100);background:var(--ink-50);
}
.guide-shot img{width:100%;height:100%;object-fit:cover;object-position:left top;display:block}
.guide-body{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1}
.guide-body strong{font-size:.97rem;font-weight:600;line-height:1.3}
.guide-body span{
  font-size:var(--fs-sm);color:var(--ink-500);line-height:1.45;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.guide-go{flex:none;color:var(--blue);font-size:1.1rem;transition:transform var(--t-fast) var(--ease-soft)}
.guide-item:hover .guide-go{transform:translateX(3px)}
@media (max-width:820px){
  .guide-list{grid-template-columns:1fr}
}

/* ── Callout ────────────────────────────────────────────────────────
   For the caveat a reader needs before they act. Light ground, so unlike
   .switch-footnote it can sit in any section. */
.callout{
  border-left:3px solid var(--blue);background:rgba(8,106,174,.05);
  padding:var(--s-5) var(--s-6);border-radius:0 var(--r-card) var(--r-card) 0;
  max-width:78ch;color:var(--ink-900);
}
.callout p{font-size:var(--fs-sm);line-height:1.65}
.callout p + p{margin-top:var(--s-3)}

/* ── Install guides: who fits this thing ────────────────────────────
   Two device classes with very different answers — a plug you fit at the
   curb, and a camera that needs a technician. The badge says which before
   anyone reads a step. */
.install-meta{
  display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap;
  margin-top:var(--s-5);font-size:var(--fs-sm);color:var(--ink-500);
}
.install-meta .dot{width:4px;height:4px;border-radius:50%;background:var(--ink-300);flex:none}
.pill{
  display:inline-flex;align-items:center;padding:5px 12px;border-radius:999px;
  font-size:.78rem;font-weight:650;letter-spacing:.02em;
}
.pill-self{background:rgba(8,106,174,.1);color:var(--blue-deep)}
.pill-fitter{background:rgba(214,138,16,.13);color:#8a5a06}

/* ── Help Center: one walkthrough ───────────────────────────────────
   Steps and screenshots alternate, so the screenshot has to read as
   evidence for the step above it rather than as decoration: full measure,
   framed like the app window it was cropped from. */
.help-article .shot{
  margin:var(--s-5) 0 var(--s-6);
  border:1px solid var(--ink-100);border-radius:var(--r-card);
  overflow:hidden;background:var(--ink-50);box-shadow:var(--shadow-1);
  /* a narrow capture keeps its own size rather than being blown up to the
     measure, which is what turned a 300px dialog into a blurry banner */
  width:fit-content;max-width:100%;
}
.help-article .shot img{display:block;max-width:100%;height:auto}
/* the button or icon the sentence just told you to click, at life size */
.help-article .chip{
  margin:var(--s-4) 0;display:inline-block;
  border:1px solid var(--ink-100);border-radius:6px;background:var(--paper);
  padding:5px 7px;
}
.help-article .chip img{display:block;max-width:100%;height:auto}
/* the source bolds a lead-in above each run of steps */
.help-article .step-lead{
  font-weight:600;color:var(--navy);margin-top:var(--s-6);
}
.help-article .step-lead + .shot{margin-top:var(--s-4)}
/* "Important:" warnings in the source were red text; a rule and a tint
   carry that weight without shouting in a body face */
.help-article .help-note{
  border-left:3px solid var(--blue);background:rgba(8,106,174,.05);
  padding:var(--s-4) var(--s-5);border-radius:0 var(--r-card) var(--r-card) 0;
  margin:var(--s-5) 0;color:var(--ink-900);
}
.help-article ol.howto{
  display:block;margin:var(--s-4) 0 var(--s-5);padding-left:0;
  list-style:none;counter-reset:step;
}
.help-article ol.howto > li{
  counter-increment:step;position:relative;padding-left:var(--s-8);margin-bottom:var(--s-4);
  line-height:1.6;
}
.help-article ol.howto > li::before{
  content:counter(step);position:absolute;left:0;top:-1px;
  width:26px;height:26px;border-radius:50%;
  background:var(--blue);color:#fff;
  font-size:.8rem;font-weight:650;font-variant-numeric:tabular-nums;
  display:flex;align-items:center;justify-content:center;
}

/* ── Product UI shots, framed as an app window ───────────────────
   The supplied platform captures are inside a laptop mockup whose bezel
   clips the app. Cropping to the viewport loses the sense that this is
   software, so the frame is rebuilt here instead. */
.appshot{
  border:1px solid var(--ink-100);border-radius:var(--r-sheet);overflow:hidden;
  background:var(--paper);box-shadow:var(--shadow-2);max-width:100%;
}
.appshot-bar{
  display:flex;align-items:center;gap:12px;padding:11px 14px;
  background:var(--ink-50);border-bottom:1px solid var(--ink-100);
}
.appshot-dots{display:flex;gap:6px;flex:none}
.appshot-dots i{width:9px;height:9px;border-radius:50%;background:var(--ink-300);display:block}
.appshot-url{
  flex:1;text-align:center;font-size:.8rem;letter-spacing:.01em;color:var(--ink-500);
  background:var(--paper);border:1px solid var(--ink-100);border-radius:999px;
  padding:4px 14px;max-width:280px;margin-inline:auto;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.appshot img,.appshot > img{
  display:block;width:100%;height:auto;aspect-ratio:auto;object-fit:contain;
  border:0;border-radius:0;box-shadow:none;
}
.appshot-cap{
  font-size:var(--fs-sm);color:var(--ink-500);margin-top:var(--s-4);text-align:center;
}
.appshot.on-dark{border-color:rgba(255,255,255,.14);box-shadow:none}
.appshot.on-dark .appshot-bar{background:rgba(255,255,255,.06);border-bottom-color:rgba(255,255,255,.12)}
.appshot.on-dark .appshot-dots i{background:rgba(255,255,255,.28)}
.appshot.on-dark .appshot-url{
  background:rgba(255,255,255,.05);border-color:rgba(255,255,255,.14);color:rgba(255,255,255,.65);
}
@media (max-width:560px){
  .appshot-bar{padding:9px 11px;gap:9px}
  .appshot-dots i{width:7px;height:7px}
  .appshot-url{font-size:.78rem;padding:3px 10px}
}

/* real photography inside split sections */
.split-media > img{width:100%;height:auto;border-radius:var(--r-sheet);box-shadow:var(--shadow-2);border:1px solid var(--ink-100)}

/* ── App captures in a media box ─────────────────────────────────────
   .split-media and .feature-media default to the 4:3 photo crop, which is
   right for photography and wrong for a screenshot: object-fit:cover cuts
   columns off the side of the asset grid, and the reader cannot tell the
   difference between a crop and a product that has fewer fields than it does.
   .is-screen keeps the capture whole and lets it size to its own aspect. */
.split-media.is-screen,.feature-media.is-screen{background:var(--paper);border:1px solid var(--ink-100)}
.split-media.is-screen img,.feature-media.is-screen img{
  width:100%;height:auto;aspect-ratio:auto;object-fit:contain;
}

/* ── Accessibility: skip to content ─────────────────────────────── */
.skip-link{
  position:absolute;left:-9999px;top:12px;z-index:100;
  background:var(--navy);color:#fff;padding:10px 18px;border-radius:var(--r-ctl);
  font-weight:600;font-size:.9rem;
}
.skip-link:focus{left:12px;color:#fff}

/* ══════════════════════════════════════════════════════════════════
   CONTACT PAGE
   Research-led structure: lead with the fastest path (phone), route by
   intent rather than by an unlabelled form, keep the form single-column,
   and put reassurance beside it instead of after it.
   ══════════════════════════════════════════════════════════════════ */

.contact-top{display:grid;grid-template-columns:1.32fr .68fr;gap:clamp(28px,4vw,56px);align-items:center}
@media (max-width:940px){.contact-top{grid-template-columns:minmax(0,1fr)}}

/* The fastest route out of the page: a real number, a real person. */
.call-card{
  background:var(--grad-hero);color:#fff;border-radius:var(--r-sheet);
  padding:clamp(28px,3.4vw,40px);box-shadow:var(--shadow-3);position:relative;overflow:hidden;
}
.call-card::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(70% 60% at 88% 6%, rgba(158,210,242,.22), transparent 70%);
}
.call-card .eyebrow{color:var(--blue-soft)}
/* A phone number is one thing to read, so it never breaks — and because the
   card clips its overflow, "never breaks" has to mean "always fits" rather
   than just "never wraps". At 2.4rem it needed 271px of a 271px column: no
   headroom at all, so Chrome fitted it and anything with slightly wider
   metrics — Safari, a zoom step, the fallback face before the webfont lands
   — broke it after the hyphen. 2.2rem leaves at least 8% spare at every
   width, and only the range above ~1200px was ever running at the cap. */
.call-card .call-num{
  display:inline-block;margin-top:var(--s-3);font-size:clamp(1.75rem,3vw,2.2rem);
  white-space:nowrap;
  font-weight:700;letter-spacing:-.02em;color:#fff;text-decoration:none;line-height:1.1;
}
.call-card .call-num:hover{color:var(--blue-pale)}
.call-card .call-sub{margin-top:var(--s-3);color:rgba(255,255,255,.82);font-size:var(--fs-sm);line-height:1.6}
.call-hours{
  margin-top:var(--s-6);padding-top:var(--s-5);border-top:1px solid rgba(255,255,255,.22);
  display:grid;gap:var(--s-2);font-size:var(--fs-sm);color:rgba(255,255,255,.9);position:relative;
}
.call-hours div{display:flex;justify-content:space-between;gap:var(--s-4)}
.call-hours span:last-child{color:rgba(255,255,255,.7)}
.live-dot{
  display:inline-flex;align-items:center;gap:8px;font-size:var(--fs-label);font-weight:600;
  text-transform:uppercase;letter-spacing:.07em;color:var(--blue-pale);
}
/* Author `display` beats the UA [hidden] rule, and there is no global reset
   here, so the badge needs its own — otherwise a JS-off visitor gets an empty
   green dot and no label. Same pattern as .field-err[hidden] above. */
.live-dot[hidden]{display:none}
.live-dot i{width:8px;height:8px;border-radius:50%;background:#5FD08A;box-shadow:0 0 0 4px rgba(95,208,138,.22)}
/* Closed state. The badge is authored hidden and unhidden by JS once it has
   read the real Eastern-time clock, so "open" is never a guess. */
.live-dot.is-closed{color:rgba(255,255,255,.72)}
.live-dot.is-closed i{background:#C9A227;box-shadow:0 0 0 4px rgba(201,162,39,.2)}
/* Appended by JS for visitors outside Eastern: 9-5 ET in their own clock. */
.js-local-hours{
  justify-content:flex-start;margin-top:var(--s-1);
  font-size:var(--fs-label);color:rgba(255,255,255,.62);
}

/* Intent routing: three lanes, each ending in a person. */
.route-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-6)}
@media (max-width:940px){.route-grid{grid-template-columns:minmax(0,1fr)}}
.route{
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-card);
  padding:clamp(24px,2.6vw,32px);display:flex;flex-direction:column;
  transition:border-color var(--t-med),transform var(--t-med) var(--ease),box-shadow var(--t-med);
}
.route:hover{border-color:rgba(8,106,174,.45);transform:translateY(-3px);box-shadow:var(--shadow-2)}
.route-ico{
  width:44px;height:44px;border-radius:12px;display:grid;place-items:center;
  background:rgba(8,106,174,.09);color:var(--blue);margin-bottom:var(--s-4);
}
.route-ico svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.route h3{font-size:1.15rem;margin-bottom:var(--s-2)}
.route p{font-size:var(--fs-sm);color:var(--ink-700);line-height:1.6}
.route-links{margin-top:var(--s-5);display:grid;gap:var(--s-2);justify-items:start}
.route-links a{font-weight:600;font-size:.95rem}
.route-meta{
  margin-top:auto;padding-top:var(--s-6);font-size:var(--fs-label);color:var(--ink-500);
  display:flex;align-items:center;gap:7px;
}
.route-meta::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--azure);flex:none}

/* Form beside reassurance, not above a wall of text. */
.contact-form-layout{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(32px,4.5vw,64px);align-items:start}
@media (max-width:940px){.contact-form-layout{grid-template-columns:minmax(0,1fr)}}
.form-card{
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-sheet);
  padding:clamp(26px,3vw,40px);box-shadow:var(--shadow-1);
}
.form-stack{display:grid;gap:var(--s-5)}          /* single column: lower cognitive load */
.form-stack .row-2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-5)}
@media (max-width:620px){.form-stack .row-2{grid-template-columns:minmax(0,1fr)}}

.aside-card{background:var(--ink-50);border:1px solid var(--ink-100);border-radius:var(--r-card);padding:clamp(22px,2.4vw,30px)}
.aside-card + .aside-card{margin-top:var(--s-5)}
.next-steps{display:grid;gap:var(--s-5);margin-top:var(--s-4);counter-reset:ns}
.next-steps li{list-style:none;display:grid;grid-template-columns:28px 1fr;gap:var(--s-4);align-items:start;counter-increment:ns}
.next-steps li::before{
  content:counter(ns);width:28px;height:28px;border-radius:50%;background:var(--blue);color:#fff;
  font-size:.8rem;font-weight:700;display:grid;place-items:center;
}
.next-steps strong{display:block;font-size:.95rem;color:var(--navy)}
.next-steps span{font-size:var(--fs-sm);color:var(--ink-700);line-height:1.55}

/* Office */
.office-grid{display:grid;grid-template-columns:.9fr 1.1fr;gap:clamp(28px,4vw,56px);align-items:center}
@media (max-width:860px){.office-grid{grid-template-columns:minmax(0,1fr)}}
.office-visual{
  border-radius:var(--r-sheet);overflow:hidden;border:1px solid var(--ink-100);
  background:var(--ink-50);aspect-ratio:16/10;
}
.office-visual svg{width:100%;height:100%;display:block}
.addr-line{display:flex;gap:var(--s-4);align-items:flex-start;margin-top:var(--s-5)}
.addr-line svg{width:20px;height:20px;flex:none;margin-top:2px;fill:none;stroke:var(--blue);stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.addr-line div{font-size:.95rem;color:var(--ink-700);line-height:1.6}

/* ══════════════════════════════════════════════════════════════════
   INVESTOR RELATIONS
   A small-cap IR page earns trust by being legible, not decorated:
   the number first, the comparison beside it, the filing one click away.
   ══════════════════════════════════════════════════════════════════ */

/* The IR hero is a single column now: the page opens on the story, and the
   share data it used to carry in a black panel is on about + the filings below.
   No measure on the block itself — .display sets its own break and .lede is
   already capped at 56ch, so a wrapper limit only re-broke the headline. */

/* Results headline numbers */
.ir-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s-5)}
@media (max-width:940px){.ir-stats{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:520px){.ir-stats{grid-template-columns:minmax(0,1fr)}}
.ir-stat{
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-card);
  padding:clamp(20px,2.2vw,26px);
}
.ir-stat .k{font-size:var(--fs-label);text-transform:uppercase;letter-spacing:.07em;color:var(--ink-500);font-weight:600}
.ir-stat .v{
  margin-top:var(--s-3);font-size:clamp(1.7rem,2.7vw,2.15rem);font-weight:700;
  color:var(--navy);letter-spacing:-.02em;font-variant-numeric:tabular-nums;line-height:1;
}
.ir-stat .d{margin-top:var(--s-3);font-size:var(--fs-sm);color:var(--ink-700);line-height:1.5}
.delta{display:inline-flex;align-items:center;gap:5px;font-weight:700;font-size:var(--fs-sm)}
.delta.up{color:#1B7A3D}
.delta.up::before{content:"▲";font-size:.7em}
.delta.flat{color:var(--ink-500)}

/* Document library */
.doc-group + .doc-group{margin-top:var(--s-9)}
.doc-list{display:grid;gap:var(--s-3);margin-top:var(--s-5)}
.doc{
  display:flex;align-items:center;gap:var(--s-5);padding:var(--s-5) var(--s-6);
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-ctl);
  text-decoration:none;transition:border-color var(--t-fast),background var(--t-fast),transform var(--t-fast) var(--ease);
}
.doc:hover{border-color:rgba(8,106,174,.45);background:var(--ink-50);transform:translateX(2px)}
.doc-ico{
  width:38px;height:44px;flex:none;border-radius:5px;background:var(--ink-50);
  border:1px solid var(--ink-100);display:grid;place-items:center;
  font-family:var(--mono);font-size:.6rem;font-weight:700;color:var(--blue);letter-spacing:.04em;
}
.doc-body{flex:1;min-width:0}
.doc-body strong{display:block;color:var(--navy);font-weight:600;font-size:1rem}
.doc-body span{display:block;margin-top:3px;font-size:var(--fs-sm);color:var(--ink-500)}
.doc-go{flex:none;color:var(--blue);font-weight:600;font-size:var(--fs-sm)}

/* Which fiscal period a results release reports. The library above it groups
   by fiscal year and this list groups by calendar year of issue, so the same
   four digits mean two different things within one screen; this is the label
   that tells them apart. */
.doc-tag{
  flex:none;align-self:center;padding:4px 10px;border-radius:var(--r-pill);
  background:var(--ink-50);border:1px solid var(--ink-100);
  font-size:var(--fs-label);font-weight:600;color:var(--ink-700);
  letter-spacing:.03em;white-space:nowrap;
}
@media (max-width:640px){.doc-tag{display:none}}

/* Compact variant. Five full-width rows of meeting materials pushed the AGM
   deck and everything under it a screen down, for documents that a handful of
   voting shareholders open once a year. Same rows, two-up and tighter, so the
   set stays visible — it has to be — without owning the section. */
.doc-list.is-compact{gap:var(--s-2)}
@media (min-width:760px){.doc-list.is-compact{grid-template-columns:1fr 1fr}}
.doc-list.is-compact .doc{padding:var(--s-4) var(--s-5);gap:var(--s-4);align-items:center}
.doc-list.is-compact .doc-ico{width:30px;height:34px;font-size:.52rem}
.doc-list.is-compact .doc-body strong{font-size:.92rem;line-height:1.35}
.doc-list.is-compact .doc-body span{margin-top:1px;font-size:.78rem}
.doc-list.is-compact .doc-go{display:none}

.ir-note{
  background:var(--ink-50);border:1px solid var(--ink-100);border-left:3px solid var(--blue);
  border-radius:var(--r-ctl);padding:var(--s-6);font-size:var(--fs-sm);color:var(--ink-700);line-height:1.65;
}

/* On-page jump nav. The IR page is long and its visitors arrive hunting for
   one specific document, so give them the shortcuts up front. */
.ir-jump{display:flex;flex-wrap:wrap;gap:var(--s-3);margin-top:var(--s-6)}
.ir-jump a{
  display:inline-flex;align-items:center;padding:9px 16px;border-radius:var(--r-pill);
  background:var(--paper);border:1px solid var(--ink-100);text-decoration:none;
  font-size:var(--fs-sm);font-weight:500;color:var(--navy);
  transition:border-color var(--t-fast),background var(--t-fast),color var(--t-fast);
}
.ir-jump a:hover{border-color:rgba(8,106,174,.45);background:var(--ink-50);color:var(--blue-deep)}

/* Fiscal-year filing block. A year is four quarters; each quarter has one
   MD&A, one set of financial statements, and one results release. The year
   also carries its AGM presentation. That shape is the whole component. */
.fy{border:1px solid var(--ink-100);border-radius:var(--r-card);background:var(--paper);overflow:hidden}
/* The CMS region holds only the data-driven years; the static "and earlier"
   archive card sits outside it by design, so a sibling selector could not
   reach across that boundary and the last card sat flush against the one
   above it while every other pair had 20px. */
.fy + .fy,
[data-cms-view="years"] + .fy{margin-top:var(--s-5)}
/* The intro paragraph sat straight on top of the first year card. */
[data-cms-view="years"]{margin-top:var(--s-7)}

/* Archive. Holds years rather than quarters. Nesting bordered cards inside a
   bordered card gave the years an inset that lined up with nothing, and the
   first one sat flush against the summary while its siblings had 20px — the
   .fy-body shorthand below resets padding, so a padding-top here never
   applied. Flattened instead: inside the archive a year is a flush row on a
   hairline, the same shape the press-release groups use, so opening the
   archive changes the depth of the list and not the kind of thing in it. */
.fy-archive > summary .fy-yr{color:var(--ink-700)}
.fy-archive[open] > .fy-body{padding:0}
.fy-archive-body > .fy,
.fy-archive-body > .fy-sedar{
  margin-top:0;border:0;border-top:1px solid var(--ink-100);
  border-radius:0;background:transparent;
}
.fy-archive-body > .fy > summary{background:transparent}
.fy-archive-body > .fy[open] > summary{background:var(--ink-50)}
.fy-archive-body > .fy-sedar .qtr{border-bottom:none;padding-left:var(--s-6);padding-right:var(--s-6)}
.fy-sedar-bare{
  margin-top:var(--s-5);border:1px solid var(--ink-100);
  border-radius:var(--r-card);background:var(--paper);padding:0 var(--s-6);
}
.fy-sedar-bare .qtr{border-bottom:none}
.fy > summary{
  display:flex;align-items:baseline;gap:var(--s-4);flex-wrap:wrap;
  --disc-inset:var(--s-6);
  padding:var(--s-5) calc(var(--disc-size) + var(--s-6) + 12px) var(--s-5) var(--s-6);
  cursor:pointer;list-style:none;
  background:var(--paper);border-bottom:1px solid transparent;
}
/* The tint belongs to the open header, where it separates the header from the
   white body beneath it. A collapsed year is nothing but its summary, so
   tinting it unconditionally painted the whole card --ink-50 — the same value
   as the section behind it — and three of the four years showed up as a bare
   1px outline on an identical background. */
.fy[open] > summary{background:var(--ink-50);border-bottom-color:var(--ink-100)}
.fy > summary::-webkit-details-marker{display:none}
.spec-group > summary .count{color:var(--ink-500);font-weight:400}
.fy-yr{font-size:1.15rem;font-weight:650;color:var(--navy);letter-spacing:-.01em}
.fy-when{font-size:var(--fs-sm);color:var(--ink-500)}
.fy-body{padding:0 var(--s-6) var(--s-5)}

.qtr{
  display:grid;grid-template-columns:150px minmax(0,1fr);gap:var(--s-3) var(--s-5);
  align-items:center;padding:var(--s-5) 0;border-bottom:1px solid var(--ink-100);
}
.qtr:last-child{border-bottom:none}
.qtr-when strong{display:block;color:var(--navy);font-weight:650;font-size:.95rem}
.qtr-when span{
  display:block;margin-top:3px;font-size:var(--fs-label);color:var(--ink-500);
  text-transform:uppercase;letter-spacing:.06em;font-weight:600;
}
.qtr-docs{display:flex;flex-wrap:wrap;gap:var(--s-3)}
.qtr.is-annual .qtr-when strong{color:var(--blue-deep)}

/* Document pill. The badge is content, not decoration, so it rides in on a
   data attribute rather than a wrapper element. */
.dl{
  display:inline-flex;align-items:center;gap:8px;padding:8px 14px;border-radius:var(--r-ctl);
  border:1px solid var(--ink-100);background:var(--paper);text-decoration:none;
  font-size:var(--fs-sm);font-weight:500;color:var(--navy);
  transition:border-color var(--t-fast),background var(--t-fast),color var(--t-fast);
}
.dl:hover{border-color:rgba(8,106,174,.45);background:var(--ink-50);color:var(--blue-deep)}
.dl::before{
  content:attr(data-kind);flex:none;font-family:var(--mono);font-size:.58rem;font-weight:700;
  letter-spacing:.04em;color:var(--blue);border:1px solid var(--ink-100);border-radius:4px;
  padding:2px 5px;background:var(--ink-50);
}
.dl.is-pending{color:var(--ink-500);border-style:dashed;cursor:default}
.dl.is-pending::before{color:var(--ink-500)}

@media (max-width:640px){
  .qtr{grid-template-columns:minmax(0,1fr);padding:var(--s-5) 0}
  .dl{min-height:44px}
  .fy > summary{min-height:44px}
}

/* ══════════════════════════════════════════════════════════════════
   PRODUCT CATALOG
   Ecommerce-shaped: sticky facet rail, result count + sort, rich cards,
   and a quote list standing in for a cart (AirIQ does not publish price).
   ══════════════════════════════════════════════════════════════════ */

/* Both columns open with a header row and then a rule, and the two rules
   have to land on the same line or the grid reads crooked. They used to be
   free-running: the left one sat wherever "Filter" plus a margin put it
   (38px) and the right one wherever the sort <select> put it (60px), which
   is a platform font metric, not a decision. So the row height is a token
   both columns are built from, and the select is held to it. */
.catalog{
  --catalog-hdr:40px;
  display:grid;grid-template-columns:260px minmax(0,1fr);gap:clamp(28px,3.4vw,48px);align-items:start;
}
@media (max-width:940px){.catalog{grid-template-columns:minmax(0,1fr)}}

/* The rail runs about 1000px of filters and a laptop viewport is 700-900,
   so sticky on its own pinned the last group below the fold: the only way
   to reach "Capability" was to scroll the whole page past all fifteen
   products, by which point the rail was on its way out too. So the rail is
   capped to the viewport and scrolls itself.

   The head is held out of the scroller — "Clear all" is the one control you
   want reachable from anywhere in the list — and only the groups move. */
.facets{
  position:sticky;top:92px;
  display:flex;flex-direction:column;
  max-height:calc(100vh - 92px - var(--s-6));
}
.facet-head{flex:none}
.facets [data-facet-groups]{
  /* No overscroll-behavior:contain here. The rail is part of the document,
     not a chrome panel, so chaining stays on: run out of filters with the
     pointer over the rail and the page carries on rather than trapping you. */
  min-height:0;overflow-y:auto;
  /* a checkbox focus ring is drawn 4px outside its box and a scroller clips
     it, so the padding buys the ring room and the negative margin gives it
     back to the column, leaving the options on the same line as the head */
  padding-inline:12px;margin-inline:-12px;
  /* tabbing into an option off-screen scrolls it flush to the edge, which
     is exactly where the fade below sits; keep it clear of both fades */
  scroll-padding-block:24px;
  scrollbar-width:thin;
  /* A shadow at whichever edge has more list behind it. The two cover
     layers scroll with the content (background-attachment:local) and sit on
     top of the shadows, so each shadow is only visible while there is
     something in that direction to scroll to. No JS, no extra element. */
  background:
    linear-gradient(var(--paper) 45%, rgba(255,255,255,0)) 0 0 / 100% 22px no-repeat local,
    linear-gradient(rgba(255,255,255,0), var(--paper) 55%) 0 100% / 100% 22px no-repeat local,
    radial-gradient(farthest-side at 50% 0, rgba(10,25,41,.13), rgba(10,25,41,0)) 0 0 / 100% 11px no-repeat scroll,
    radial-gradient(farthest-side at 50% 100%, rgba(10,25,41,.13), rgba(10,25,41,0)) 0 100% / 100% 11px no-repeat scroll;
}
@media (max-width:940px){.facets{top:100px}}
@media (max-width:940px){
  /* Stacked and folded behind a toggle down here: there is no rail to cap,
     and a scroller nested in the page scroll is a trap under a thumb. */
  .facets{position:static;display:block;max-height:none}
  .facets [data-facet-groups]{
    overflow:visible;padding-inline:0;margin-inline:0;background:none;
  }
}
.facet-head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
  min-height:var(--catalog-hdr);margin-bottom:var(--s-5);
}
.facet-head h2{font-size:1.05rem}
.facet-clear{font-size:var(--fs-sm);color:var(--blue);background:none;border:0;cursor:pointer;font-weight:600;padding:0}
.facet-clear[hidden]{display:none}
.facet-group{border-top:1px solid var(--ink-100);padding:var(--s-5) 0}
.facet-group > h3{font-size:var(--fs-label);text-transform:uppercase;letter-spacing:.07em;color:var(--ink-500);font-weight:700;margin-bottom:var(--s-4)}
.facet-opt{display:flex;align-items:center;gap:10px;padding:6px 0;cursor:pointer;font-size:.92rem;color:var(--ink-700)}
.facet-opt:hover{color:var(--navy)}
.facet-opt input{
  appearance:none;width:18px;height:18px;flex:none;border:1.5px solid var(--ink-300);
  border-radius:5px;background:var(--paper);display:grid;place-items:center;cursor:pointer;
  transition:border-color var(--t-fast),background var(--t-fast);
}
.facet-opt input:checked{background:var(--blue);border-color:var(--blue)}
.facet-opt input:checked::after{content:"";width:9px;height:5px;border:2px solid #fff;border-top:0;border-right:0;transform:rotate(-45deg) translate(1px,-1px)}
.facet-opt input:focus-visible{outline:none;box-shadow:var(--ring)}
.facet-opt .n{margin-left:auto;font-size:.78rem;color:var(--ink-300);font-variant-numeric:tabular-nums}

.catalog-bar{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-5);flex-wrap:wrap;
  min-height:var(--catalog-hdr);
  padding-bottom:var(--s-5);border-bottom:1px solid var(--ink-100);margin-bottom:var(--s-6);
}
.catalog-count{font-size:var(--fs-sm);color:var(--ink-500)}
.catalog-count b{color:var(--navy);font-weight:700}
.catalog-sort{display:flex;align-items:center;gap:10px;font-size:var(--fs-sm);color:var(--ink-500)}
.catalog-sort select{
  /* min-height, not height: the control still grows if someone zooms text
     only. padding-block goes to zero so the token is what sets the height */
  border:1px solid var(--ink-100);border-radius:var(--r-ctl);
  min-height:var(--catalog-hdr);padding:0 12px;
  font-family:inherit;font-size:.9rem;color:var(--navy);background:var(--paper);cursor:pointer;
}
.chips-active{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:var(--s-5)}
.chip-x{
  display:inline-flex;align-items:center;gap:7px;padding:6px 12px;border-radius:var(--r-pill);
  background:rgba(8,106,174,.08);color:var(--blue-deep);border:1px solid rgba(8,106,174,.2);
  font-size:.82rem;font-weight:500;cursor:pointer;
}
/* Drawn, not typed. This was the "×" glyph at 1.05rem, and a multiplication
   sign is centred on the font's math axis rather than in its line box, so
   align-items:center lined up the box and left the mark itself sitting high
   against the label. Same trap as the old circled "+" in the disclosure
   marker above, and the same fix: two bars crossed at 45°, symmetric about
   the centre of a square box, so there is no font metric to fight and it
   stays centred at any size or weight. */
.chip-x::after{
  content:"";flex:none;width:9px;height:9px;transform:rotate(45deg);
  background:
    linear-gradient(currentColor,currentColor) center/100% 1.5px no-repeat,
    linear-gradient(currentColor,currentColor) center/1.5px 100% no-repeat;
  opacity:.75;
}
.chip-x:hover::after{opacity:1}

/* ── Industry and solution cards ─────────────────────────────────
   These reused .pcard with inline height overrides, which put the title
   on top of the photo and dropped it entirely on hover. The title now
   sits below the image, and the whole card is one link rather than a
   cover anchor stacked over a button. */
.icard{
  display:flex;flex-direction:column;background:var(--paper);
  border:1px solid var(--ink-100);border-radius:var(--r-card);overflow:hidden;
  text-decoration:none;color:inherit;
  transition:border-color var(--t-med),transform var(--t-med) var(--ease),box-shadow var(--t-med);
}
.icard:hover{border-color:rgba(8,106,174,.4);transform:translateY(-4px);box-shadow:var(--shadow-2)}
.icard:focus-visible{outline:3px solid var(--blue);outline-offset:3px}
.icard-media{
  position:relative;aspect-ratio:16/10;overflow:hidden;
  background:linear-gradient(160deg,var(--ink-50),#fff);
  border-bottom:1px solid var(--ink-100);
}
.icard-media img{
  width:100%;height:100%;object-fit:cover;display:block;
  transition:transform var(--t-slow) var(--ease);
}
.icard:hover .icard-media img{transform:scale(1.045)}
/* a product shot sits on a plate rather than bleeding to the edges */
.icard-media.is-plate{background:linear-gradient(155deg,#123048,var(--navy))}
.icard-media.is-plate img{
  object-fit:contain;width:auto;height:auto;max-width:64%;max-height:70%;
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  filter:drop-shadow(0 12px 22px rgba(0,0,0,.42));
}
.icard:hover .icard-media.is-plate img{transform:translate(-50%,-50%) scale(1.05)}
.icard-body{padding:var(--s-6);display:flex;flex-direction:column;flex:1}
.icard-body h2,.icard-body h3{font-size:1.1rem;margin:0;line-height:1.3}
.icard-body p{margin-top:var(--s-3);font-size:var(--fs-sm);color:var(--ink-700);line-height:1.6}
.icard-meta{
  margin-top:var(--s-4);font-size:.78rem;color:var(--ink-500);font-family:var(--mono);
}
.icard-go{
  margin-top:auto;padding-top:var(--s-5);color:var(--blue);font-weight:600;
  font-size:var(--fs-sm);display:inline-flex;align-items:center;gap:8px;
}
.icard-go svg{
  width:16px;height:16px;stroke:currentColor;stroke-width:2;fill:none;
  stroke-linecap:round;stroke-linejoin:round;transition:transform var(--t-fast) var(--ease);
}
.icard:hover .icard-go svg{transform:translateX(4px)}

.pgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-6)}
@media (max-width:1100px){.pgrid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:620px){.pgrid{grid-template-columns:minmax(0,1fr)}}

.pcard{
  display:flex;flex-direction:column;background:var(--paper);border:1px solid var(--ink-100);
  border-radius:var(--r-card);overflow:hidden;position:relative;
  transition:border-color var(--t-med),transform var(--t-med) var(--ease),box-shadow var(--t-med);
}
.pcard:hover{border-color:rgba(8,106,174,.4);transform:translateY(-4px);box-shadow:var(--shadow-2)}
/* A percentage max-height on a centered grid item did not hold once the
   media band shrank on a phone, and the device photo spilled up to 75px
   over the product name. Flex plus min-height:0 bounds it properly. */
.pcard-media{
  height:clamp(150px,15vw,190px);background:linear-gradient(160deg,var(--ink-50),#fff);
  display:flex;align-items:center;justify-content:center;overflow:hidden;
  padding:var(--s-6);border-bottom:1px solid var(--ink-100);
}
.pcard-media img{max-width:100%;max-height:100%;min-height:0;width:auto;height:auto;
  object-fit:contain;transition:transform var(--t-med) var(--ease)}
.pcard:hover .pcard-media img{transform:scale(1.05)}
/* The whole card is the click target. The quote button and the details
   link sit above the cover so both still work. */
.pcard > a.card-cover{position:absolute;inset:0;z-index:1}
.pcard-foot{position:relative;z-index:2}
.pcard:focus-within{border-color:rgba(8,106,174,.4);box-shadow:var(--shadow-2)}
.pcard-badge{
  position:absolute;top:14px;left:14px;z-index:2;font-size:.68rem;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;color:#fff;background:var(--blue);
  padding:5px 10px;border-radius:var(--r-pill);
}
.pcard-body{padding:var(--s-6);display:flex;flex-direction:column;flex:1}
.pcard-body h3{font-size:1.15rem}
.pcard-tag{margin-top:6px;font-size:var(--fs-sm);color:var(--blue);font-weight:600}
.pcard-body > p{margin-top:var(--s-3);font-size:var(--fs-sm);color:var(--ink-700);line-height:1.6}
.pspecs{margin-top:var(--s-5);display:grid;gap:7px}
.pspecs li{list-style:none;display:flex;gap:9px;font-size:.82rem;color:var(--ink-700);line-height:1.45}
.pspecs li::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--azure);flex:none;margin-top:7px}
/* The spec sheet sits above the card cover so it stays clickable in its
   own right: some buyers want the PDF, not the page. */
/* Models on a catalog card. IQ-CAM and IQ-Wired are families, not single
   devices, and the card is where that first becomes visible: each model
   is a real link out, above the card's own cover link. */
.pcard-models{margin-top:var(--s-5);position:relative;z-index:2;display:grid;gap:6px}
.pcard-models-label{
  font-size:.68rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-300);
}
.pcard-models a{
  display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;
  padding:7px 10px;border-radius:8px;background:var(--ink-50);
  font-size:.84rem;font-weight:600;color:var(--navy);
  transition:background var(--t-fast),color var(--t-fast);
}
.pcard-models a span{font-weight:400;color:var(--ink-500);font-size:.8rem}
.pcard-models a:hover{background:rgba(8,106,174,.09);color:var(--blue-deep)}
@media (max-width:640px){.pcard-models a{min-height:44px;align-items:center}}

.pcard-sheet{margin-top:var(--s-5);position:relative;z-index:2}
.pcard-sheet a{
  display:inline-flex;align-items:center;gap:7px;font-size:.82rem;font-weight:600;
  color:var(--blue);text-decoration:none;
}
.pcard-sheet a::before{
  content:"PDF";font-family:var(--mono);font-size:.55rem;font-weight:700;letter-spacing:.04em;
  border:1px solid var(--ink-100);border-radius:4px;padding:2px 5px;background:var(--ink-50);
}
.pcard-sheet a:hover{color:var(--blue-deep);text-decoration:underline}
@media (max-width:640px){.pcard-sheet a{min-height:44px}}
.pcard-foot{margin-top:auto;padding-top:var(--s-6);display:grid;gap:10px;position:relative;z-index:2}
.pcard-foot .btn{justify-content:center;white-space:nowrap}
.pcard-foot .btn-quote{width:100%}
.btn-quote{
  border:1px solid var(--ink-100);background:var(--paper);color:var(--navy);
  padding:12px 14px;border-radius:var(--r-ctl);font-size:.86rem;font-weight:600;cursor:pointer;
  white-space:nowrap;transition:border-color var(--t-fast),background var(--t-fast),color var(--t-fast);
}
.btn-quote:hover{border-color:var(--blue);color:var(--blue)}
.btn-quote[aria-pressed="true"]{background:rgba(8,106,174,.09);border-color:var(--blue);color:var(--blue-deep)}
.btn-quote[aria-pressed="true"]::before{content:"✓ ";}

.catalog-empty{
  grid-column:1/-1;text-align:center;padding:var(--s-11) var(--s-6);
  border:1px dashed var(--ink-100);border-radius:var(--r-card);color:var(--ink-500);
}

/* Quote tray */
.quote-tray{
  position:fixed;left:50%;bottom:22px;transform:translate(-50%,140%);z-index:70;
  display:flex;align-items:center;gap:var(--s-5);padding:14px 16px 14px 22px;
  background:var(--navy);color:#fff;border-radius:var(--r-pill);box-shadow:var(--shadow-3);
  transition:transform var(--t-med) var(--ease);max-width:calc(100vw - 32px);
}
.quote-tray.is-in{transform:translate(-50%,0)}
.quote-tray span{font-size:var(--fs-sm)}
.quote-tray b{font-weight:700}
.quote-tray .btn{padding:10px 18px}
@media (prefers-reduced-motion: reduce){.quote-tray{transition:none}}

/* ══════════════════════════════════════════════════════════════════
   REQUEST A DEMO
   Multi-step by design. Research is consistent that progressive
   disclosure beats one long form on the same field count, and that
   qualified volume beats raw volume. Step 1 asks for zero personal
   information: it is all tappable, which suits the primary buyer,
   who arrives on a phone from Facebook.
   Without JS every step renders stacked and the form still submits.
   ══════════════════════════════════════════════════════════════════ */

.demo-layout{display:grid;grid-template-columns:1.1fr .9fr;gap:clamp(32px,4.5vw,68px);align-items:start}
@media (max-width:940px){.demo-layout{grid-template-columns:minmax(0,1fr)}}

.demo-card{
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-sheet);
  padding:clamp(24px,3vw,40px);box-shadow:var(--shadow-1);
}

/* Progress */
.steps-bar{display:flex;align-items:center;gap:var(--s-3);margin-bottom:var(--s-7)}
.steps-bar li{list-style:none;flex:1;display:grid;gap:8px}
.steps-bar .rail{height:4px;border-radius:2px;background:var(--ink-100);overflow:hidden}
.steps-bar .rail i{display:block;height:100%;width:0;background:var(--blue);transition:width var(--t-med) var(--ease)}
.steps-bar li[data-done] .rail i{width:100%}
.steps-bar li[data-current] .rail i{width:100%}
.steps-bar .lbl{font-size:var(--fs-label);font-weight:600;color:var(--ink-300);letter-spacing:.02em}
.steps-bar li[data-current] .lbl,.steps-bar li[data-done] .lbl{color:var(--blue-deep)}
@media (max-width:520px){.steps-bar .lbl{display:none}}

.step-panel[hidden]{display:none}
.step-q{font-size:clamp(1.3rem,2.4vw,1.7rem);letter-spacing:-.02em;line-height:1.2}
.step-q:focus{outline:none}
.step-help{margin-top:var(--s-3);color:var(--ink-700);font-size:.95rem;line-height:1.6}

/* Big tappable option tiles: no typing on step one */
.opt-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-3);margin-top:var(--s-6)}
.opt-grid.cols-1{grid-template-columns:minmax(0,1fr)}
@media (max-width:520px){.opt-grid{grid-template-columns:minmax(0,1fr)}}
.opt{
  position:relative;display:flex;align-items:center;gap:var(--s-4);cursor:pointer;
  padding:var(--s-5) var(--s-5);border:1.5px solid var(--ink-100);border-radius:var(--r-ctl);
  background:var(--paper);transition:border-color var(--t-fast),background var(--t-fast),transform var(--t-fast);
  min-height:60px;
}
.opt:hover{border-color:var(--ink-300);background:var(--ink-50)}
.opt input{position:absolute;opacity:0;width:1px;height:1px;margin:0}
.opt .mark{
  width:22px;height:22px;flex:none;border:1.5px solid var(--ink-300);border-radius:50%;
  display:grid;place-items:center;transition:border-color var(--t-fast),background var(--t-fast);
}
.opt input[type=checkbox] ~ .mark{border-radius:6px}
.opt .mark::after{content:"";width:9px;height:9px;border-radius:50%;background:#fff;transform:scale(0);transition:transform var(--t-fast) var(--ease)}
.opt input[type=checkbox] ~ .mark::after{width:9px;height:5px;border:2px solid #fff;border-top:0;border-right:0;border-radius:0;background:none;transform:rotate(-45deg) scale(0)}
.opt input:checked ~ .mark{background:var(--blue);border-color:var(--blue)}
.opt input:checked ~ .mark::after{transform:scale(1)}
.opt input[type=checkbox]:checked ~ .mark::after{transform:rotate(-45deg) scale(1) translate(1px,-1px)}
.opt:has(input:checked){border-color:var(--blue);background:rgba(8,106,174,.05)}
.opt:has(input:focus-visible){box-shadow:var(--ring)}
.opt .txt{display:grid;gap:2px}
.opt .txt b{font-weight:600;color:var(--navy);font-size:.98rem}
.opt .txt span{font-size:var(--fs-sm);color:var(--ink-500)}

.step-nav{display:flex;align-items:center;gap:var(--s-4);margin-top:var(--s-7);flex-wrap:wrap}
.step-back{background:none;border:0;color:var(--ink-500);font-weight:600;cursor:pointer;font-size:.95rem;padding:8px 0}
.step-back:hover{color:var(--navy)}
.step-nav .btn{min-width:170px;justify-content:center}
.step-skip{font-size:var(--fs-sm);color:var(--ink-500)}

.field-err{color:#B3261E;font-size:var(--fs-sm);margin-top:var(--s-2)}
.field-err[hidden]{display:none}
/* Validation is per field: the line sits directly under the input it is
   about and the input carries the state, so the eye lands on the problem
   rather than on a summary at the bottom of the form. On the dark bands the
   red has to lift or it disappears into the blue. */
.is-invalid,
.field .is-invalid{border-color:#B3261E!important;box-shadow:0 0 0 3px rgba(179,38,30,.14)!important}
.cta-final .field-err,.cta-form .field-err{color:#FFD4D0}
.footer .field-err{color:#FFC9C4}
.signup-row + .field-err{margin-top:var(--s-2)}

/* Reassurance rail */
.demo-aside .aside-card:first-child{background:var(--paper);border-color:var(--ink-100)}
.demo-proof{display:grid;gap:var(--s-5);margin-top:var(--s-4)}
.demo-proof div{display:grid;grid-template-columns:22px 1fr;gap:var(--s-4);align-items:start}
.demo-proof svg{width:22px;height:22px;fill:none;stroke:var(--blue);stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round;margin-top:1px}
.demo-proof b{display:block;font-size:.95rem;color:var(--navy)}
.demo-proof span{font-size:var(--fs-sm);color:var(--ink-700);line-height:1.55}

.demo-success{text-align:center;padding:var(--s-9) var(--s-6)}
.demo-success .tick{
  width:60px;height:60px;border-radius:50%;background:rgba(27,122,61,.1);color:#1B7A3D;
  display:grid;place-items:center;margin:0 auto var(--s-6);
}
.demo-success .tick svg{width:30px;height:30px;fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}

/* ══════════════════════════════════════════════════════════════════
   EXIT-COST CALCULATOR
   The category's interactive lead magnets (Verizon's ROI calculator,
   Geotab's Fleet Safety Grader) all model savings. This one deliberately
   does not: it multiplies the visitor's own contract numbers, so nothing
   here is an AirIQ outcome claim, and it lands the one differentiator
   competitors cannot answer.
   ══════════════════════════════════════════════════════════════════ */
/* One surface, two panels. Previously the result sat on a card and the inputs
   sat on bare section background, so the two halves read as different kinds of
   object rather than two sides of one control — and with no gap to close, the
   inputs' content ended 165px above the card's floor and left an empty
   rectangle. Both panels now share the card and fill it edge to edge. */
.calc{
  display:grid;grid-template-columns:1.05fr .95fr;align-items:stretch;
  background:var(--paper);border:1px solid var(--ink-100);
  border-radius:var(--r-sheet);overflow:hidden;box-shadow:var(--shadow-2);
}
@media (max-width:860px){.calc{grid-template-columns:minmax(0,1fr)}}
.calc-inputs{
  display:flex;flex-direction:column;gap:var(--s-7);
  padding:clamp(26px,3vw,38px);
}
/* The fine print anchors to the floor of the panel so the two columns end on
   the same line instead of the inputs stopping short. */
.calc-note{margin-top:auto;padding-top:var(--s-5);font-size:.8rem;color:var(--ink-500);line-height:1.6}
/* Label and its value share one line above the track. Sitting the value at the
   far end of the slider row instead left a dead gap between the track's end and
   the number, and separated the reading ("Months left … 24 months") from the
   control that sets it. */
.calc-head{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-4);
  margin-bottom:var(--s-3);
}
.calc-field label{font-size:var(--fs-sm);font-weight:600;color:var(--ink-700)}
.calc-out{
  font-variant-numeric:tabular-nums;font-weight:650;color:var(--navy);
  font-size:var(--fs-sm);letter-spacing:-.01em;white-space:nowrap;
}

/* Range control. accent-color gives the platform's own slider — a hairline
   track under an outsized solid thumb — which reads as an unstyled form
   control next to the rest of the page. Drawn here instead: an even 4px
   track, the filled half in brand blue, and a white thumb that sits on the
   track like a physical knob. --pct is written by the calculator on input. */
.calc-field input[type=range]{
  -webkit-appearance:none;appearance:none;
  display:block;width:100%;height:22px;background:transparent;cursor:pointer;
}
.calc-field input[type=range]:focus{outline:none}
.calc-field input[type=range]::-webkit-slider-runnable-track{
  height:6px;border-radius:999px;
  background:
    linear-gradient(var(--blue),var(--blue)) 0/var(--pct,50%) 100% no-repeat,
    var(--ink-100);
}
.calc-field input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;appearance:none;
  width:22px;height:22px;border-radius:50%;background:#fff;
  border:.5px solid rgba(10,25,41,.08);
  box-shadow:0 3px 8px rgba(10,25,41,.15), 0 1px 1px rgba(10,25,41,.16);
  margin-top:-8px;                      /* (6px track - 22px thumb) / 2 */
  transition:transform var(--t-fast) var(--ease-soft),
             box-shadow var(--t-fast) var(--ease-soft);
}
.calc-field input[type=range]:active::-webkit-slider-thumb{
  transform:scale(1.1);box-shadow:0 5px 14px rgba(10,25,41,.22), 0 1px 1px rgba(10,25,41,.16);
}
.calc-field input[type=range]:focus-visible::-webkit-slider-thumb{
  box-shadow:0 0 0 4px rgba(30,156,227,.32), 0 3px 8px rgba(10,25,41,.15);
}
/* Firefox draws the filled portion itself, so no --pct is needed there. */
.calc-field input[type=range]::-moz-range-track{height:6px;border-radius:999px;background:var(--ink-100)}
.calc-field input[type=range]::-moz-range-progress{height:6px;border-radius:999px;background:var(--blue)}
.calc-field input[type=range]::-moz-range-thumb{
  width:22px;height:22px;border-radius:50%;background:#fff;
  border:.5px solid rgba(10,25,41,.08);
  box-shadow:0 3px 8px rgba(10,25,41,.15), 0 1px 1px rgba(10,25,41,.16);
  transition:transform var(--t-fast) var(--ease-soft);
}
.calc-field input[type=range]:active::-moz-range-thumb{transform:scale(1.1)}
.calc-field input[type=range]:focus-visible::-moz-range-thumb{
  box-shadow:0 0 0 4px rgba(30,156,227,.32), 0 3px 8px rgba(10,25,41,.15);
}
@media (prefers-reduced-motion: reduce){
  .calc-field input[type=range]::-webkit-slider-thumb,
  .calc-field input[type=range]::-moz-range-thumb{transition:none}
}
.calc-result{
  background:var(--grad-hero);color:#fff;
  padding:clamp(26px,3vw,38px);display:flex;flex-direction:column;justify-content:center;
  position:relative;overflow:hidden;
}
.calc-result::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(75% 65% at 88% 4%, rgba(158,210,242,.22), transparent 70%);
}
.calc-result .eyebrow{color:var(--blue-soft);position:relative}
.calc-big{
  font-size:clamp(2.4rem,5.4vw,3.4rem);font-weight:700;letter-spacing:-.03em;line-height:1;
  margin-top:var(--s-4);font-variant-numeric:tabular-nums;position:relative;
}
.calc-cap{margin-top:var(--s-4);color:rgba(255,255,255,.85);font-size:.95rem;line-height:1.6;position:relative}
.calc-vs{
  margin-top:var(--s-6);padding-top:var(--s-5);border-top:1px solid rgba(255,255,255,.22);
  display:grid;gap:var(--s-3);position:relative;
}
.calc-vs div{display:flex;justify-content:space-between;gap:var(--s-4);font-size:var(--fs-sm)}
.calc-vs span:first-child{color:rgba(255,255,255,.7)}
.calc-vs span:last-child{font-weight:700}

/* ══════════════════════════════════════════════════════════════════
   BLOG / EDITORIAL
   Posts may or may not have artwork, so the imageless card is designed
   rather than merely tolerated: it becomes a typographic tile that
   reads as an editorial choice next to the photographic ones.
   ══════════════════════════════════════════════════════════════════ */

.post-filters{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:var(--s-8)}
.post-filter{
  padding:8px 16px;border-radius:var(--r-pill);border:1px solid var(--ink-100);
  background:var(--paper);color:var(--ink-700);font-size:.86rem;font-weight:500;cursor:pointer;
  transition:border-color var(--t-fast),background var(--t-fast),color var(--t-fast);
}
.post-filter:hover{border-color:var(--ink-300);color:var(--navy)}
.post-filter[aria-pressed="true"]{background:var(--navy);border-color:var(--navy);color:#fff}

/* Featured */
.post-feature{
  display:grid;grid-template-columns:1.05fr .95fr;gap:0;margin-bottom:var(--s-9);
  border:1px solid var(--ink-100);border-radius:var(--r-sheet);overflow:hidden;background:var(--paper);
  transition:border-color var(--t-med),box-shadow var(--t-med),transform var(--t-med) var(--ease);
  position:relative;
}
.post-feature:hover{border-color:rgba(8,106,174,.4);box-shadow:var(--shadow-2);transform:translateY(-3px)}
@media (max-width:820px){.post-feature{grid-template-columns:minmax(0,1fr)}}
.post-feature .pf-media{position:relative;min-height:300px;background:var(--ink-50)}
.post-feature .pf-media img{width:100%;height:100%;object-fit:cover;display:block}
.post-feature .pf-body{padding:clamp(28px,3.4vw,48px);display:flex;flex-direction:column;justify-content:center}
.post-feature h2{font-size:clamp(1.5rem,2.8vw,2.1rem);margin-top:var(--s-4)}
.post-feature p{margin-top:var(--s-4);color:var(--ink-700);line-height:1.65}

.post-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-6)}
@media (max-width:1000px){.post-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:620px){.post-grid{grid-template-columns:minmax(0,1fr)}}

.post{
  display:flex;flex-direction:column;background:var(--paper);border:1px solid var(--ink-100);
  border-radius:var(--r-card);overflow:hidden;position:relative;
  transition:border-color var(--t-med),transform var(--t-med) var(--ease),box-shadow var(--t-med);
}
.post:hover{border-color:rgba(8,106,174,.4);transform:translateY(-4px);box-shadow:var(--shadow-2)}
.post-media{aspect-ratio:16/10;overflow:hidden;background:var(--ink-50)}
.post-media img{width:100%;height:100%;object-fit:cover;display:block;transition:transform var(--t-slow) var(--ease)}
.post:hover .post-media img{transform:scale(1.04)}

/* Imageless: a typographic tile, not an empty box */
.post-media.is-type{
  display:grid;grid-template-columns:100%;grid-template-rows:auto auto;padding:var(--s-6);
  aspect-ratio:auto;
  background:linear-gradient(150deg,var(--blue-mid) 0%,var(--blue) 62%,var(--blue-deep) 100%);
  position:relative;overflow:hidden;
}
/* 16:10 like a photo tile, but never shorter than chip + headline. The ratio
   is held by an invisible sizer spanning both rows rather than by
   aspect-ratio on the box: with overflow hidden, an aspect-ratio box will not
   grow for its content, and at the narrow end of the three-column range a
   three-line title either ran up into the chip or clipped at the bottom.
   The negative margin makes the sizer the tile's full width, so the ratio
   is measured on the tile, not on its padding box. */
.post-media.is-type::after{
  content:"";grid-row:1/3;grid-column:1;margin:calc(-1 * var(--s-6));
  aspect-ratio:16/10;pointer-events:none;
}
/* The featured post's tile takes its height from the body beside it. */
.post-feature .pf-media.is-type::after{aspect-ratio:auto}
.post-media.is-type::before{
  content:"";position:absolute;inset:0;
  background:
    radial-gradient(60% 70% at 84% 8%, rgba(158,210,242,.28), transparent 70%),
    repeating-linear-gradient(115deg, rgba(255,255,255,.06) 0 2px, transparent 2px 22px);
}
.post-media.is-type .glyph{grid-row:2;grid-column:1;position:relative;display:block;align-self:end}
/* The typographic tile already carries the headline, so the card body must not
   repeat it. Category, summary, and meta still read normally. */
/* Matched by tag alone, a card using <h2 class="h3"> kept its heading and
   printed the title twice, once on the tile and once underneath. */
.post.is-typeset .post-body h3,
.post.is-typeset .post-body h2,
.post-feature.is-typeset .pf-body h2{display:none}
.post.is-typeset .post-body p{margin-top:var(--s-4)}
.post-media.is-type .gt{
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
  font-size:clamp(1.25rem,2.1vw,1.6rem);font-weight:650;color:#fff;
  letter-spacing:-.025em;line-height:1.18;text-wrap:balance;
}
.post-media.is-type .glyph small{display:block;font-size:.7rem;letter-spacing:.13em;
  text-transform:uppercase;color:rgba(255,255,255,.78);margin-bottom:10px;font-weight:700}

/* The tile's icon: a chip top-left, and the same drawing again as a faint
   watermark bleeding off the top-right, so the tile reads as composed rather
   than as an empty image slot with a caption. Both are one <use> of a symbol
   in /assets/img/hub-icons.svg — stroke and fill are set here, not in the
   sprite, which is what lets one drawing serve both roles.
   The chip is the .outcome-ico pattern from the home page, sized up for a
   16:10 tile: 56px chip, 30px glyph, the same 1.8 stroke. */
.post-media.is-type .glyph-ico{
  grid-row:1;grid-column:1;position:relative;align-self:start;margin-bottom:var(--s-3);
  width:56px;height:56px;border-radius:14px;
  background:rgba(255,255,255,.14);display:grid;place-items:center;
}
.post-media.is-type .glyph-ico svg{
  width:30px;height:30px;stroke:#fff;stroke-width:1.8;fill:none;stroke-linecap:round;stroke-linejoin:round;
}
.post-media.is-type .glyph-wm{
  position:absolute;right:-28px;top:-30px;width:180px;height:180px;
  stroke:#fff;stroke-width:.55;fill:none;stroke-linecap:round;stroke-linejoin:round;
  opacity:.14;pointer-events:none;
}
/* The featured post's tile is taller and has no aspect ratio, so the
   watermark can sit further in. */
.post-feature .pf-media.is-type .glyph-wm{right:-10px;top:-10px;width:220px;height:220px}

.post-body{padding:var(--s-6);display:flex;flex-direction:column;flex:1}
.post-cat{
  align-self:flex-start;font-size:.7rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--blue-deep);background:rgba(8,106,174,.09);padding:5px 10px;border-radius:var(--r-pill);
}
.post-body h3{margin-top:var(--s-4);font-size:1.1rem;line-height:1.3}
.post-body p{margin-top:var(--s-3);font-size:var(--fs-sm);color:var(--ink-700);line-height:1.6}
.post-meta{
  margin-top:auto;padding-top:var(--s-5);font-size:.78rem;color:var(--ink-500);
  display:flex;gap:8px;align-items:center;font-variant-numeric:tabular-nums;
}
.post-meta .dot{width:3px;height:3px;border-radius:50%;background:var(--ink-300)}
.post-cover{position:absolute;inset:0;z-index:1}
.post-empty{grid-column:1/-1;text-align:center;padding:var(--s-10);color:var(--ink-500);
  border:1px dashed var(--ink-100);border-radius:var(--r-card)}


/* ── Mobile usability ───────────────────────────────────────────────
   Inputs below 16px make iOS zoom the viewport on focus, which throws the
   user out of the form. Tap targets follow the 44px minimum. */
@media (max-width: 940px){
  .field input,.field select,.field textarea,
  .cta-form input,.cta-form select,.lp-form input,.lp-form select,.form-card input,.form-card textarea,.form-card select{font-size:16px}
  .nav-actions .btn{min-height:44px}
  .nav-logo{min-height:44px;align-items:center}
  .footer-bottom a{display:inline-block;padding:10px 4px}
  .terms a{padding:10px 4px}
  .post-filter,.facet-clear{min-height:44px}
  .facet-opt{padding:13px 0}
  .btn,.skip-link,.btn-quote,.step-back{min-height:44px}
  .catalog-count a{min-height:44px;display:inline-flex;align-items:center}
  main p > a.btn-arrow{min-height:44px;display:inline-flex;align-items:center}
  .catalog-sort select{min-height:44px}
  .hb-sol a{min-height:44px;display:inline-flex;align-items:center}
  .route-links a,.addr-line a,.cta-phone a{min-height:44px;display:inline-flex;align-items:center}
  .ir-jump a{min-height:44px}
  .footnote,.quotes-source,.source{font-size:12px}
  .pcard-badge,.card-tag{font-size:12px}
  .band .footnote{font-size:12px;color:rgba(255,255,255,.62)}
  main a[href^="tel:"],main a[href^="mailto:"],
  .footer a[href^="tel:"],.footer a[href^="mailto:"]{min-height:44px;display:inline-flex;align-items:center}
  .breadcrumbs a,.breadcrumbs span{display:inline-block;padding:11px 2px}
  .breadcrumbs{margin-bottom:var(--s-3)}
  .footer-bottom a,.terms a{padding:12px 4px}
  .doc,.route-links a{min-height:44px}
  .route-links a{display:inline-flex;align-items:center}
  /* Footer columns are navigation, so each link needs a real tap area. */
  .footer-links a{display:block;padding:12px 0;min-height:44px}
  .footer-links li{margin:0}
  .footer-logo{min-height:44px;display:inline-flex;align-items:center}
  .cta-phone a{display:inline-block;padding:12px 4px}
  .nav-mobile a:not(.btn){min-height:44px}
}


/* ── Mobile legibility and touch, second pass ───────────────────────
   Small uppercase labels sit at 10 to 11px on desktop, which is legible at
   that size beside larger type but not on a phone. Floor them at 12px and
   give the remaining link lists a real tap area. The hero chips are white on
   a translucent fill over the road scene, so they get a solid backing here. */
@media (max-width: 940px){
  .pcard-badge,.post-cat,.eyebrow,.route-meta,.doc-ico,
  .spec-table th,.chip-x,.catalog-count,.facet-opt .n,
  .model-kicker,.model-specs span,.realities-label,
  .device-hero .device-inset .cap,.pcard-models-label,
  .post-media.is-type .glyph small{font-size:12px}
  .catalog-sort select{font-size:16px}
  .pspecs li,.post-meta,.hero-micro,.field .hint{font-size:13px}
  .hb-sol a{font-size:13px;background:rgba(4,26,46,.55);border-color:rgba(255,255,255,.4)}
  .index-list a,.help-list a,.link-list a{display:block;padding:12px 0;min-height:44px}
  /* A contents list is navigation, same as the ones above it. The two
     standalone "more" links are inline runs of text, so they need the tap
     area without becoming full-width blocks. */
  .legal-toc a{display:block;padding:12px 0;min-height:44px}
  .logo-wall-foot a,.sheet-more a{display:inline-flex;align-items:center;min-height:44px;padding:6px 2px}
  .learn,.card-body .learn{display:inline-flex;align-items:center;min-height:44px}
}

/* The IQ-CAM hero media sits in a fixed-width inset that overflowed a 390px
   viewport by 40px, scrolling the section sideways. */
@media (max-width: 940px){
  .hero{overflow-x:clip}
  .device-inset,.device-hero{max-width:100%;min-width:0}
  .device-hero{grid-template-columns:minmax(0,1fr)}
  .hero-grid{grid-template-columns:minmax(0,1fr)}
}


/* ── Catalog filters on mobile ──────────────────────────────────────
   Stacked, the facet rail pushed every product below four groups and about
   twenty options. On a phone the filters collapse behind a toggle so the
   products are the first thing in view. */
@media (max-width: 940px){
  .facets{border:1px solid var(--ink-100);border-radius:var(--r-card);padding:var(--s-5) var(--s-6);margin-bottom:var(--s-6)}
  .facet-head{margin-bottom:0}
  .facet-toggle{
    display:inline-flex;align-items:center;gap:8px;background:none;border:0;
    padding:0 calc(var(--disc-size) + 12px) 0 0;
    font:inherit;font-weight:700;color:var(--navy);cursor:pointer;min-height:44px;
  }
  .facets [data-facet-groups]{display:none}
  .facets[data-open] [data-facet-groups]{display:block}
  .facet-count-pill{
    margin-left:6px;font-size:12px;font-weight:700;background:var(--blue);color:#fff;
    border-radius:var(--r-pill);padding:2px 8px;
  }
}
@media (min-width: 941px){ .facet-toggle{display:none} }
@media (max-width: 940px){ .facet-title-desktop{display:none} }

/* ══════════════════════════════════════════════════════════════════
   PRODUCT SHOWCASE
   Apple's product-page grammar: a confident stage, then one idea per
   full-width section with the type doing the work, then the full
   specification set folded away at the bottom for the people who want it.
   ══════════════════════════════════════════════════════════════════ */

/* Stage: the product presented, not decorated */
.stage{
  position:relative;overflow:hidden;text-align:center;
  padding:clamp(24px,3.4vw,44px) 0 0;
  background:linear-gradient(180deg,#F7F9FC 0%,#EDF3F9 42%,#E4EDF6 100%);
}
/* The stage centres its copy; the breadcrumb above it does not follow. It gets
   its own full-width row so it lands top-left like every other page. */
.stage-crumbs{
  max-width:var(--container);margin-inline:auto;padding-inline:var(--gutter);
  position:relative;z-index:2;text-align:left;margin-bottom:clamp(18px,2.6vw,32px);
}
.stage-crumbs .breadcrumbs{margin-bottom:0}
.stage-copy{max-width:820px;margin-inline:auto;padding-inline:var(--gutter);position:relative;z-index:2}
.stage .eyebrow{color:var(--blue-deep)}
.stage h1{
  margin-top:var(--s-4);font-size:clamp(2.6rem,6.4vw,4.6rem);letter-spacing:-.035em;
  line-height:1.02;font-weight:650;
}
.stage h1 .accent{color:var(--blue)}
.stage-sub{
  margin-top:var(--s-5);font-size:clamp(1.05rem,1.8vw,1.32rem);line-height:1.5;
  color:var(--ink-700);max-width:620px;margin-inline:auto;
}
.stage-ctas{margin-top:var(--s-7);display:flex;gap:var(--s-4);justify-content:center;flex-wrap:wrap}
.stage-media{
  margin-top:clamp(28px,4vw,52px);position:relative;z-index:1;
  display:grid;place-items:center;padding:0 var(--gutter) clamp(36px,5vw,64px);
}
.stage-media img{
  max-width:min(720px,92%);height:auto;display:block;
  filter:drop-shadow(0 40px 60px rgba(10,25,41,.22));
}
.stage-media svg{max-width:min(560px,90%);height:auto}

/* One idea per band */
.feature{padding:clamp(64px,8vw,120px) 0;overflow:hidden}
.feature.dark{background:var(--navy);color:#fff}
.feature.dark h2,.feature.dark h3{color:#fff}
.feature.dark p{color:rgba(255,255,255,.76)}
/* A dark band for .section, matching .feature.dark. The timeline is a
   white-on-navy component and had nowhere to live before this. */
.section.dark{background:var(--navy);color:#fff}
.section.dark h2,.section.dark h3{color:#fff}
.section.dark p,.section.dark .lede{color:rgba(255,255,255,.76)}
.section.dark .eyebrow{color:var(--blue-soft)}
.section.dark .btn-secondary{
  background:transparent;border-color:rgba(255,255,255,.28);color:#fff;
}
.section.dark .btn-secondary:hover{border-color:#fff;background:rgba(255,255,255,.08)}
.feature.tinted{background:var(--ink-50)}
.feature-grid{
  max-width:var(--container);margin-inline:auto;padding-inline:var(--gutter);
  display:grid;grid-template-columns:1fr 1fr;gap:clamp(32px,5vw,80px);align-items:center;
}
.feature.flip .feature-grid > *:first-child{order:2}
@media (max-width:860px){
  .feature-grid{grid-template-columns:minmax(0,1fr);gap:var(--s-8)}
  .feature.flip .feature-grid > *:first-child{order:0}
}
.feature-copy .eyebrow{color:var(--blue)}
.feature.dark .feature-copy .eyebrow{color:var(--blue-soft)}
.feature-copy h2{
  margin-top:var(--s-4);font-size:clamp(1.9rem,3.6vw,2.9rem);letter-spacing:-.028em;line-height:1.08;
}
.feature-copy p{margin-top:var(--s-5);font-size:1.05rem;line-height:1.62;color:var(--ink-700)}
.feature-list{margin-top:var(--s-6);display:grid;gap:var(--s-4)}
.feature-list li{list-style:none;display:grid;grid-template-columns:20px 1fr;gap:var(--s-4);align-items:start}
.feature-list li::before{
  content:"";width:20px;height:20px;border-radius:50%;margin-top:1px;
  background:rgba(8,106,174,.12) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23086AAE' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M20 6L9 17l-5-5'/></svg>") center/12px no-repeat;
}
.feature.dark .feature-list li::before{
  background:rgba(255,255,255,.14) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ED2F2' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M20 6L9 17l-5-5'/></svg>") center/12px no-repeat;
}
.feature-list span{font-size:.98rem;line-height:1.55;color:var(--ink-700)}
.feature.dark .feature-list span{color:rgba(255,255,255,.8)}
.feature-media{border-radius:var(--r-sheet);overflow:hidden;background:var(--ink-50)}
.feature-media img{width:100%;height:100%;object-fit:cover;display:block;aspect-ratio:4/3}
.feature.dark .feature-media{background:rgba(255,255,255,.05)}

/* Big number strip */
.figures{
  max-width:var(--container);margin-inline:auto;padding-inline:var(--gutter);
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s-6);
}
@media (max-width:860px){.figures{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* Values here are short specs, but the cell is narrow and the type is large,
   so one long unbreakable word is wider than its column. Nothing clipped it,
   so "Weatherproof" ran straight out over the "48,000" beside it. A value long
   enough to overrun now breaks instead of overlapping its neighbour. */
.figure-k{
  font-size:clamp(2rem,4vw,3rem);font-weight:700;letter-spacing:-.035em;line-height:1;
  color:var(--navy);font-variant-numeric:tabular-nums;
  overflow-wrap:break-word;hyphens:auto;
}
.feature.dark .figure-k{color:#fff}
.figure-l{margin-top:var(--s-3);font-size:var(--fs-sm);color:var(--ink-500);line-height:1.5}
.feature.dark .figure-l{color:rgba(255,255,255,.65)}

/* Tech specs, folded */
.specs-wrap{max-width:920px;margin-inline:auto;padding-inline:var(--gutter)}
.spec-group{border-top:1px solid var(--ink-100)}
.spec-group:last-of-type{border-bottom:1px solid var(--ink-100)}
.spec-group > summary{
  list-style:none;cursor:pointer;display:flex;align-items:center;
  justify-content:space-between;gap:var(--s-5);
  padding:var(--s-6) calc(var(--disc-size) + 18px) var(--s-6) 0;
  font-weight:650;color:var(--navy);font-size:1.06rem;
}
.spec-group > summary::-webkit-details-marker{display:none}
.spec-rows{padding-bottom:var(--s-7);display:grid;gap:0}
.spec-row{
  display:grid;grid-template-columns:minmax(160px,.42fr) 1fr;gap:var(--s-6);
  padding:var(--s-4) 0;border-top:1px solid var(--ink-50);
}
.spec-row:first-child{border-top:0}
.spec-row dt{color:var(--ink-500);font-size:.92rem}
.spec-row dd{color:var(--navy);font-size:.98rem;line-height:1.55}
@media (max-width:620px){
  .spec-row{grid-template-columns:minmax(0,1fr);gap:4px}
  .spec-row dt{font-size:.82rem;text-transform:uppercase;letter-spacing:.05em}
}
.spec-note{
  margin-top:var(--s-7);font-size:.82rem;color:var(--ink-500);line-height:1.7;
  padding-top:var(--s-5);border-top:1px solid var(--ink-100);
}

/* The outcome is the heading; the spec that backs it sits underneath in a
   quieter voice. Keeps the claim checkable without leading with a figure
   nobody outside the factory reads for pleasure. */
.payoff-spec{
  margin-top:10px;font-size:var(--fs-sm);color:var(--ink-500);line-height:1.65;
  padding-top:12px;border-top:1px solid var(--ink-100);
}

/* ── Capability cards ───────────────────────────────────────────────
   Six near-identical text blocks are read one after another; an icon per
   card gives the eye something to aim at, so you find "maintenance"
   without reading the five cards above it. */
.cap{position:relative}
.cap-ico{
  display:grid;place-items:center;width:40px;height:40px;margin-bottom:14px;
  border-radius:var(--r-ctl);background:var(--ink-50);color:var(--blue);
  border:1px solid var(--ink-100);
}
.cap-ico svg{width:22px;height:22px;display:block}
/* The whole card is the target, not just the link at the bottom of it. The
   cover is not a second tab stop; the visible link is the one you focus. */
.cap > a.card-cover{position:absolute;inset:0;z-index:1}
.cap .sheet-foot{position:relative;z-index:2}
.cap:hover{border-color:var(--ink-300)}
.cap:focus-within{outline:2px solid var(--blue);outline-offset:2px}
/* Five of these jump down this page and one leaves it. Same arrow for both
   would promise the same thing twice and deliver two different results. */
.btn-arrow.is-jump::after{content:"↓"}

/* A family hub stacks one spec block per model. The device shot and the
   name ride above the rows so a figure can never be read against the wrong
   camera, which is what a single family-wide table invited. */
.spec-model{margin-top:var(--s-9)}
.spec-model:first-of-type{margin-top:var(--s-7)}
.spec-model-head{
  display:flex;align-items:center;gap:var(--s-5);flex-wrap:wrap;
  padding-bottom:var(--s-5);
}
.spec-model-shot{
  width:64px;height:52px;flex-shrink:0;display:grid;place-items:center;
  background:var(--ink-50);border-radius:var(--r-ctl);padding:6px;
}
.spec-model-shot img{width:100%;height:100%;object-fit:contain;display:block}
.spec-model-head h3{margin:0;font-size:1.25rem}
.spec-model-role{margin:2px 0 0;color:var(--ink-500);font-size:.9rem}
.spec-model-go{
  margin-left:auto;font-weight:600;font-size:.92rem;white-space:nowrap;
  display:inline-flex;align-items:center;min-height:44px;
}
.spec-model .spec-note{margin-top:var(--s-5)}
@media (max-width:560px){
  .spec-model-go{margin-left:0;width:100%}
}

/* ── Comparison table ───────────────────────────────────────────────
   Apple's compare grammar: the products sit in a quiet sticky header, the
   rows are hairlines rather than boxes, group labels are small caps in the
   margin, and the eye runs down a column without furniture in the way. */
.cmp-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;margin-inline:calc(var(--gutter)*-1);padding-inline:var(--gutter)}
.cmp{border-collapse:collapse;width:100%;min-width:820px;font-size:.95rem;table-layout:fixed}
.cmp col.cmp-label{width:220px}

/* No background here: the header has to sit in whatever section the table
   lands in. A hard --paper fill read as a white slab on .section.tint. */
.cmp thead th{
  padding:0 var(--s-5) var(--s-6);text-align:left;vertical-align:bottom;
  border-bottom:1px solid var(--ink-100);
}
.cmp-head{display:grid;gap:var(--s-3);justify-items:start}
.cmp-head .shot{
  width:100%;height:96px;overflow:hidden;
  background:linear-gradient(160deg,var(--ink-50),#fff);
  border-radius:var(--r-card);border:1px solid var(--ink-100);padding:12px;
}
.cmp-head .shot img{width:100%;height:100%;object-fit:contain;display:block}
.cmp-head b{font-size:1.05rem;color:var(--navy);letter-spacing:-.015em;line-height:1.2}
.cmp-head .role{font-size:.8rem;color:var(--ink-500);line-height:1.35}
.cmp-head .go{font-size:.82rem;font-weight:600;color:var(--blue);
  min-height:44px;display:inline-flex;align-items:center}

/* Group label: a quiet rule, not a filled band */
.cmp-section th,.cmp-section td{
  padding:var(--s-9) var(--s-5) var(--s-4);border:0;
  font-size:.72rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-500);
}
.cmp-section th{border-bottom:1px solid var(--navy)}
.cmp-section td{border-bottom:1px solid var(--ink-100)}
.cmp tbody tr:first-child.cmp-section th,
.cmp tbody tr:first-child.cmp-section td{padding-top:var(--s-8)}

.cmp tbody th{
  text-align:left;font-weight:400;color:var(--ink-500);font-size:.9rem;
  padding:var(--s-5);vertical-align:top;border-bottom:1px solid var(--ink-50);
}
.cmp tbody td{
  padding:var(--s-5);vertical-align:top;color:var(--navy);line-height:1.5;
  border-bottom:1px solid var(--ink-50);
}
.cmp tbody tr:hover th,.cmp tbody tr:hover td{background:rgba(8,106,174,.028)}

.cmp-yes{
  display:inline-grid;place-items:center;width:24px;height:24px;border-radius:50%;
  background:rgba(8,106,174,.12);color:var(--blue);font-size:.8rem;font-weight:700;
}
.cmp-no{color:var(--ink-300);font-size:1.05rem;line-height:1}
/* Bool rows used to be centred here, on the reasoning that a column of marks
   scans well. It does, but the column header above them and every text value
   around them are left-aligned, so each ✓ sat off the edge everything else
   shared. The table is fixed-layout, so left-aligned marks still line up with
   each other — and now with the product name above them too. */

/* ── Comparison table on a phone ────────────────────────────────────
   The table cannot become a stack: reading two products against each
   other IS the page, and a stack puts the values it is comparing on
   different screens. So it stays a table and scrolls sideways, with the
   two things that makes bearable:

   the row label pins to the left edge, so you always know which line you
   are reading once the products have scrolled past it, and

   a fade on the right edge says there is more to the right, since a
   phone shows no scrollbar until you touch it.

   The fade is painted by the wrapper and sits above the table, so it
   needs the wrapper to be a positioning context; .cmp-wrap is the
   scroller, so the fade goes on a sticky child that rides the viewport
   instead (a fixed pseudo-element on a scroller scrolls away with it). */
@media (max-width:620px){
  .cmp{min-width:560px;font-size:.9rem}
  .cmp col.cmp-label{width:132px}
  .cmp-head .shot{height:64px}
  .cmp-head b{font-size:.95rem}
  .cmp thead th{padding:var(--s-4) var(--s-3) var(--s-5)}
  .cmp tbody th,.cmp tbody td{padding:var(--s-4) var(--s-3)}
  .cmp-section th,.cmp-section td{padding:var(--s-7) var(--s-3) var(--s-3);letter-spacing:.05em}

  /* the label column rides along */
  .cmp thead th:first-child,
  .cmp tbody th:first-child,
  .cmp-section th:first-child{
    position:sticky;left:0;z-index:2;
    background:var(--paper);
    box-shadow:1px 0 0 var(--ink-100);
  }
  .section.tint .cmp thead th:first-child,
  .section.tint .cmp tbody th:first-child,
  .section.tint .cmp-section th:first-child{background:var(--ink-25,#F7F9FC)}
  /* the hover tint would otherwise show through the pinned cell */
  .cmp tbody tr:hover th:first-child{background:var(--paper)}

  /* A shadow at whichever edge has more table behind it. The two cover
     layers scroll with the content (background-attachment:local) and sit
     on top of the shadows, so each shadow is only visible while there is
     something in that direction to scroll to. No JS, no extra element. */
  .cmp-wrap{
    background:
      radial-gradient(farthest-side at 0 50%, rgba(10,25,41,.13), rgba(10,25,41,0)) 0 0 / 13px 100% no-repeat scroll,
      radial-gradient(farthest-side at 100% 50%, rgba(10,25,41,.13), rgba(10,25,41,0)) 100% 0 / 13px 100% no-repeat scroll;
  }
}

/* ── The model chooser on a family hub ──────────────────────────────
   A family page has one job before any other: get you to the right
   device. So the card leads with the sentence that decides it, the
   device sits above that sentence at a size you can actually tell
   apart, and the button is a real button rather than a cover link. */
.mdl-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s-6)}
.mdl-card{
  display:flex;flex-direction:column;
  border:1px solid var(--ink-100);border-radius:var(--r-card);
  background:var(--paper);padding:var(--s-6);
  transition:border-color var(--t-fast),box-shadow var(--t-fast);
}
/* --shadow-card was never a token, so this hover only moved the border and
   the card's box-shadow transition had nothing to animate. */
.mdl-card:hover{border-color:var(--ink-300);box-shadow:var(--shadow-2)}
.mdl-shot{
  height:150px;overflow:hidden;margin-bottom:var(--s-5);
  background:linear-gradient(160deg,var(--ink-50),#fff);
  border-radius:var(--r-card);padding:var(--s-4);
}
/* The three devices arrive at wildly different native sizes, so the frame
   does the containing rather than trusting each image to behave. */
.mdl-shot img{width:100%;height:100%;object-fit:contain;display:block}
.mdl-body{flex:1}
.mdl-body h3{font-size:1.25rem;letter-spacing:-.02em}
.mdl-role{
  margin-top:4px;font-size:.72rem;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--blue);
}
/* The line that decides it: heavier than body copy, quieter than a heading. */
.mdl-pitch{
  margin-top:var(--s-5);font-size:1.05rem;line-height:1.45;font-weight:600;
  color:var(--navy);letter-spacing:-.01em;
}
.mdl-card .check-list{margin-top:var(--s-5)}
.mdl-card .sheet-foot{margin-top:var(--s-5)}
.mdl-go{margin-top:var(--s-6);width:100%;justify-content:center}

@media (max-width:980px){.mdl-grid{grid-template-columns:1fr;gap:var(--s-5)}
  .mdl-shot{height:130px}}
/* Small caps hold at .72rem beside larger type but fall under the 12px
   floor on a phone. These sit after the block above, so they win. */
@media (max-width:940px){.mdl-role,.cmp-pick-label{font-size:12px}}

/* ── The difference table on a model page ───────────────────────────
   Same table, one job narrower: it shows only the rows where a family
   disagrees, and it tints the column you are currently reading so the
   comparison is anchored rather than abstract. */
.cmp-models{min-width:720px}
.cmp-models thead th.is-this{background:rgba(8,106,174,.06)}
.cmp-models thead th.is-this .cmp-head b{color:var(--blue-deep)}
.cmp-models tbody td.is-this{background:rgba(8,106,174,.05);font-weight:600}
.cmp-models tbody tr:hover td.is-this{background:rgba(8,106,174,.08)}
.mdl-this{
  display:inline-flex;align-items:center;
  font-size:.72rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  color:var(--blue);
}
@media (max-width:620px){.cmp-models{min-width:600px}}
@media (max-width:940px){.mdl-this{font-size:12px}}

/* Remove-a-column button, tucked under the header card. It only earns
   its keep once several products are up, so it stays quiet until hover. */
.cmp-drop{
  border:0;background:none;padding:0;cursor:pointer;font:inherit;
  font-size:.78rem;color:var(--ink-300);text-decoration:underline;
  text-underline-offset:3px;min-height:32px;
  transition:color var(--t-fast);
}
.cmp-drop:hover{color:var(--blue)}
.cmp-drop:focus-visible{box-shadow:var(--ring);border-radius:4px}
@media (max-width:940px){.cmp-drop{min-height:44px}}

/* ── Compare controls: category first, then the picker ──────────────
   Comparison is usually a within-category act, so the category tabs
   lead and the picker sits under them. The picker never disables a
   choice: past the column limit the oldest pick steps aside instead. */
.cmp-controls{
  border:1px solid var(--ink-100);border-radius:var(--r-card);
  background:var(--paper);padding:var(--s-6);
}
.cmp-tabs{
  display:flex;flex-wrap:wrap;gap:6px;
  border-bottom:1px solid var(--ink-100);padding-bottom:var(--s-5);
}
.cmp-tab{
  border:0;background:none;cursor:pointer;font:inherit;
  padding:9px 15px;border-radius:var(--r-pill);min-height:44px;
  font-size:.92rem;font-weight:600;color:var(--ink-500);
  transition:background var(--t-fast),color var(--t-fast);
}
.cmp-tab:hover{background:var(--ink-50);color:var(--navy)}
.cmp-tab.is-on{background:var(--navy);color:#fff}
.cmp-tab:focus-visible{box-shadow:var(--ring)}

.cmp-blurb{
  margin:var(--s-5) 0 0;color:var(--ink-500);font-size:.95rem;line-height:1.55;
  max-width:62ch;
}

/* Compare picker */
.cmp-picker{margin-top:var(--s-5)}
.cmp-pick-row{display:flex;flex-wrap:wrap;gap:10px}
.cmp-pick-group + .cmp-pick-group{margin-top:var(--s-5)}
.cmp-pick-label{
  display:block;margin-bottom:10px;
  font-size:.72rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-300);
}

/* Tools row: the differences switch and the column count. */
.cmp-tools{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:var(--s-4);margin-top:var(--s-6);padding-top:var(--s-5);
  border-top:1px solid var(--ink-50);
}
.cmp-count{font-size:.85rem;color:var(--ink-300)}
.cmp-switch{
  display:inline-flex;align-items:center;gap:10px;cursor:pointer;
  font-size:.9rem;color:var(--ink-700);min-height:44px;
}
.cmp-switch input{position:absolute;opacity:0;width:1px;height:1px}
.cmp-switch .track{
  width:40px;height:23px;flex:none;border-radius:var(--r-pill);
  background:var(--ink-100);position:relative;
  transition:background var(--t-fast);
}
.cmp-switch .track::after{
  content:"";position:absolute;top:3px;left:3px;width:17px;height:17px;
  border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(10,25,41,.28);
  transition:transform var(--t-fast);
}
.cmp-switch input:checked ~ .track{background:var(--blue)}
.cmp-switch input:checked ~ .track::after{transform:translateX(17px)}
.cmp-switch:has(input:focus-visible) .track{box-shadow:var(--ring)}

.cmp-pick{
  display:inline-flex;align-items:center;gap:10px;padding:10px 16px;cursor:pointer;
  border:1.5px solid var(--ink-100);border-radius:var(--r-pill);background:var(--paper);
  font-size:.9rem;color:var(--ink-700);min-height:44px;
  transition:border-color var(--t-fast),background var(--t-fast),color var(--t-fast);
}
.cmp-pick:hover{border-color:var(--ink-300);color:var(--navy)}
.cmp-pick input{position:absolute;opacity:0;width:1px;height:1px}
.cmp-pick .box{
  width:18px;height:18px;flex:none;border:1.5px solid var(--ink-300);border-radius:5px;
  display:grid;place-items:center;transition:background var(--t-fast),border-color var(--t-fast);
}
.cmp-pick input:checked ~ .box{background:var(--blue);border-color:var(--blue)}
.cmp-pick input:checked ~ .box::after{content:"";width:9px;height:5px;border:2px solid #fff;border-top:0;border-right:0;transform:rotate(-45deg) translate(1px,-1px)}
.cmp-pick:has(input:checked){border-color:var(--blue);background:rgba(8,106,174,.05);color:var(--navy)}
.cmp-pick:has(input:focus-visible){box-shadow:var(--ring)}

@media (max-width:620px){
  .cmp-controls{padding:var(--s-5)}
  .cmp-tab{padding:9px 12px;font-size:.86rem}
  .cmp-tools{justify-content:flex-start}
}

/* ══════════════════════════════════════════════════════════════════
   SWITCHER — the 36-month trap (homepage + /lp/no-contract)
   Dark cinematic band: verbatim competitor reviews, then the
   commitment math. Blue-family only, per the design mandate.
   ══════════════════════════════════════════════════════════════════ */
.switcher{
  background:linear-gradient(180deg,var(--blue-mid) 0%,var(--navy) 100%);
  padding:var(--sec-pad) 0;
}
.switcher h2{color:#fff}
.switcher .lede{color:rgba(255,255,255,.78)}

.switch-quotes{
  display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s-5);
  margin-top:var(--s-8);
}
.switch-quote{
  background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);
  border-radius:var(--r-card);padding:var(--s-6);
}
.switch-quote blockquote{
  color:rgba(255,255,255,.9);line-height:1.6;font-size:var(--fs-body);
}
.switch-quote figcaption{
  margin-top:var(--s-4);font-size:var(--fs-label);color:rgba(255,255,255,.5);
  text-transform:uppercase;letter-spacing:.07em;
}

.switch-math{margin-top:var(--s-9)}
.switch-ask h3{color:#fff;margin-bottom:var(--s-5)}
.switch-slider{
  display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:var(--s-5);
  background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);
  border-radius:var(--r-card);padding:var(--s-5) var(--s-6);
}
.switch-slider label{color:rgba(255,255,255,.85);font-size:var(--fs-sm);font-weight:500}
.switch-slider output{
  color:#fff;font-weight:600;font-variant-numeric:tabular-nums;
  min-width:7.5ch;text-align:right;
}
.switch-slider input[type="range"]{
  -webkit-appearance:none;appearance:none;width:100%;height:6px;border-radius:3px;
  background:rgba(255,255,255,.22);outline:none;
}
.switch-slider input[type="range"]::-webkit-slider-thumb{
  -webkit-appearance:none;appearance:none;width:24px;height:24px;border-radius:50%;
  background:var(--azure);border:3px solid #fff;cursor:pointer;
  box-shadow:0 2px 8px rgba(10,25,41,.4);
}
.switch-slider input[type="range"]::-moz-range-thumb{
  width:24px;height:24px;border-radius:50%;background:var(--azure);
  border:3px solid #fff;cursor:pointer;box-shadow:0 2px 8px rgba(10,25,41,.4);
}
.switch-slider input[type="range"]:focus-visible{box-shadow:var(--ring)}

.switch-panels{
  display:grid;grid-template-columns:1fr 1fr;gap:var(--s-5);margin-top:var(--s-5);
}
.switch-panel{border-radius:var(--r-sheet);padding:var(--s-7)}
.switch-them{
  background:rgba(255,255,255,.045);border:1px solid rgba(255,255,255,.12);
}
.switch-us{
  background:linear-gradient(135deg,var(--blue-deep) 0%,var(--blue) 100%);
  border:1px solid rgba(255,255,255,.18);box-shadow:var(--shadow-3);
}
.switch-kicker{
  font-size:var(--fs-label);font-weight:600;text-transform:uppercase;
  letter-spacing:.09em;color:rgba(255,255,255,.6);
}
.switch-us .switch-kicker{color:var(--blue-pale)}
.switch-num{
  margin-top:var(--s-4);font-size:clamp(2.4rem,4.6vw,3.4rem);font-weight:650;
  letter-spacing:-.03em;line-height:1;color:rgba(255,255,255,.55);
  font-variant-numeric:tabular-nums;
}
.switch-us .switch-num{color:#fff}
.switch-cap{margin-top:var(--s-3);font-size:var(--fs-sm);color:rgba(255,255,255,.65);max-width:34ch}
.switch-us .switch-cap{color:rgba(255,255,255,.85)}
/* These lists carry no class, so the global ul[class] reset misses them and
   the native bullets show up next to the ✕/✓ marks. */
.switch-panel ul{margin-top:var(--s-6);display:grid;gap:var(--s-3);list-style:none}
.switch-panel li{
  position:relative;padding-left:26px;font-size:var(--fs-sm);
  color:rgba(255,255,255,.72);line-height:1.5;
}
.switch-them li::before{content:"✕";position:absolute;left:0;color:rgba(255,255,255,.35);font-weight:700}
.switch-us li::before{content:"✓";position:absolute;left:0;color:var(--blue-pale);font-weight:700}
.switch-us li{color:rgba(255,255,255,.92)}

.switch-footnote{
  margin-top:var(--s-8);font-size:.72rem;line-height:1.6;color:rgba(255,255,255,.45);
  max-width:78ch;
}
.switch-footnote p + p{margin-top:var(--s-2)}
.switch-footnote a{color:rgba(255,255,255,.65)}
.switch-footnote a:hover{color:#fff}

@media (max-width:900px){
  .switch-quotes{grid-template-columns:1fr}
  .switch-panels{grid-template-columns:1fr}
  .switch-slider{grid-template-columns:1fr;gap:var(--s-3)}
  .switch-slider output{text-align:left}
  .switch-footnote{font-size:12px}
}

/* ══════════════════════════════════════════════════════════════════
   CAMPAIGN LANDING PAGES (/lp/*)
   One hook, one form, no nav to wander off into. Reuses the hero
   gradient and form grammar from the homepage final CTA.
   ══════════════════════════════════════════════════════════════════ */
.lp-header{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
  padding:14px var(--gutter);max-width:var(--container);margin-inline:auto;
}
.lp-header > a,.lp-header .lp-brand{display:flex;align-items:center;min-height:44px}
.lp-header img{width:110px;height:auto}
.lp-header-phone{
  display:flex;align-items:center;gap:10px;color:var(--navy);font-weight:600;min-height:44px;
}
.lp-header-phone small{display:block;font-weight:400;font-size:.75rem;color:var(--ink-500)}
.lp-header-phone:hover{color:var(--blue)}

.lp-hero{
  background:var(--grad-hero);color:#fff;
  padding:clamp(48px,7vw,96px) 0 clamp(56px,7vw,96px);
}
.lp-hero h1{color:#fff;font-size:var(--fs-display);font-weight:650;max-width:21ch}
.lp-hero .lede{color:rgba(255,255,255,.85);margin-top:var(--s-5)}
.lp-hero .hero-micro{margin-top:var(--s-6);color:rgba(255,255,255,.75)}
.lp-hero .hero-micro span::before{color:var(--blue-pale)}

.lp-form{
  display:flex;gap:10px;margin-top:var(--s-7);max-width:520px;flex-wrap:wrap;
}
.lp-form input[type="email"]{
  flex:1;min-width:230px;padding:15px 18px;font-size:1rem;font-family:inherit;
  border:1px solid rgba(255,255,255,.35);border-radius:var(--r-ctl);
  background:rgba(255,255,255,.95);color:var(--ink-900);
}
.lp-form input[type="email"]:focus-visible{box-shadow:var(--ring);outline:none}
.lp-phone-line{margin-top:var(--s-5);font-size:var(--fs-sm);color:rgba(255,255,255,.75)}
.lp-phone-line a{color:#fff;font-weight:600}

.lp-steps{counter-reset:lp-step;display:grid;gap:var(--s-6);max-width:640px}
.lp-step{position:relative;padding-left:56px}
.lp-step::before{
  counter-increment:lp-step;content:counter(lp-step);
  position:absolute;left:0;top:0;width:38px;height:38px;border-radius:50%;
  display:grid;place-items:center;font-weight:650;color:var(--blue);
  background:rgba(8,106,174,.1);
}
.lp-step h3{margin-bottom:var(--s-2)}
.lp-step p{color:var(--ink-700)}
.lp-step .step-kicker{font-family:var(--mono);font-size:.75rem;color:var(--ink-500);margin-bottom:var(--s-2)}

.lp-footer{
  padding:var(--s-8) var(--gutter);text-align:center;
  font-size:var(--fs-sm);color:var(--ink-500);border-top:1px solid var(--ink-100);
}
.lp-footer a{
  display:inline-flex;align-items:center;min-height:44px;padding-inline:var(--s-2);
  color:var(--ink-500);text-decoration:underline;
}
.lp-footer a:hover{color:var(--navy)}
.lp-footer .lp-legal{margin-top:var(--s-2)}

@media (max-width:640px){
  .lp-form{flex-direction:column}
  .lp-form .btn{width:100%}
  .lp-form select{width:100%}
}

/* Fleet size, sitting next to the email. Two fields is the ceiling, and
   this is the one worth the friction: it tags every lead with a size band
   for the creative-intelligence table, and the options themselves answer
   "is this for someone my size?" before the visitor reads another word. */
.lp-form.has-size{max-width:700px}
/* Says what happens to the address, next to the button that asks for it. */
.lp-hero .form-note{margin-top:var(--s-4);font-size:var(--fs-sm);color:rgba(255,255,255,.7);max-width:52ch}
.cta-final .form-note{margin-top:var(--s-4);font-size:var(--fs-sm);color:rgba(255,255,255,.7)}

/* Thank-you page: the hero carries the next step instead of a form. */
.thanks-next{
  margin-top:var(--s-7);padding:var(--s-6);max-width:640px;
  border:1px solid rgba(255,255,255,.22);border-radius:var(--r-lg,14px);
  background:rgba(255,255,255,.08);
}
.thanks-next h2{color:#fff;margin-bottom:var(--s-3)}
.thanks-next p{color:rgba(255,255,255,.85)}
.thanks-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-4);margin-top:var(--s-5)}
.thanks-or{font-size:var(--fs-sm);color:rgba(255,255,255,.75);max-width:34ch}
.thanks-or a{color:#fff;font-weight:600;white-space:nowrap}
.lp-form select{
  padding:15px 16px;font-size:1rem;font-family:inherit;
  border:1px solid rgba(255,255,255,.35);border-radius:var(--r-ctl);
  background:rgba(255,255,255,.95);color:var(--ink-900);
}
.lp-form select:focus-visible{box-shadow:var(--ring);outline:none}
.cta-form.has-size{grid-template-columns:1fr auto auto;max-width:680px}
.cta-form select{
  padding:15px 16px;border-radius:var(--r-ctl);border:1px solid rgba(255,255,255,.3);
  background:rgba(255,255,255,.12);color:#fff;font-size:.95rem;font-family:inherit;
}
.cta-form select option{color:var(--ink-900)}
.cta-form select:focus{outline:none;background:rgba(255,255,255,.18);border-color:var(--azure);box-shadow:var(--ring)}

/* Sticky CTA, phones only. The form is at the top and at the bottom, and
   between them is a long scroll where the only route back was the thumb.
   Hidden while either real form is on screen (see airiq.js). */
.lp-sticky{
  position:fixed;left:0;right:0;bottom:0;z-index:60;display:none;gap:10px;align-items:center;
  padding:10px var(--gutter);padding-bottom:calc(10px + env(safe-area-inset-bottom));
  background:rgba(255,255,255,.97);border-top:1px solid var(--ink-100);
  box-shadow:0 -6px 24px rgba(10,25,41,.12);
  transform:translateY(110%);transition:transform var(--t-med,.28s) ease;
}
.lp-sticky .btn{flex:1;min-height:48px}
.lp-sticky-call{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-height:48px;padding-inline:16px;border-radius:var(--r-ctl);
  border:1px solid var(--ink-100);color:var(--navy);font-weight:600;white-space:nowrap;
}
.lp-sticky-call svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
@media (max-width:640px){
  /* after the shared 16px rule in source order, so it has to repeat it */
  .cta-form select,.lp-form select{font-size:16px}
  .lp-sticky{display:flex}
  .lp-sticky.is-on{transform:translateY(0)}
  .lp-footer{padding-bottom:calc(96px + env(safe-area-inset-bottom))}
}
@media (prefers-reduced-motion:reduce){
  .lp-sticky{transition:none}
}

/* ══════════════════════════════════════════════════════════════════
   PROOF GRID — one bento for the recovery story and the testimonials.
   The story is the featured dark tile (stat leads), quotes are quiet
   cards whose attributions bottom-align, and the last tile is the path
   to the full customer-stories page.
   ══════════════════════════════════════════════════════════════════ */
.proof-grid{
  display:grid;grid-template-columns:1.25fr 1fr 1fr;grid-auto-rows:auto;
  gap:var(--s-5);margin-top:var(--s-8);
}
.proof-card{
  display:flex;flex-direction:column;gap:var(--s-4);
  background:var(--paper);border:1px solid var(--ink-100);border-radius:var(--r-sheet);
  padding:var(--s-7);
  transition:transform var(--t-med) var(--ease),box-shadow var(--t-med),border-color var(--t-med);
}
.proof-card:hover{transform:translateY(-3px);box-shadow:var(--shadow-1);border-color:var(--ink-300)}
.proof-card blockquote{
  flex:1;color:var(--ink-700);line-height:1.65;font-size:1.02rem;
}
.proof-card blockquote::before{
  content:"\201C";display:block;font-size:2.4rem;line-height:1;font-weight:700;
  color:var(--blue);margin-bottom:var(--s-2);
}
.proof-card .quote-attrib{margin-top:0}

/* featured story tile */
.proof-feature{
  grid-row:span 2;
  background:linear-gradient(165deg,var(--blue-mid) 0%,var(--navy) 100%);
  border-color:rgba(255,255,255,.14);
}
.proof-kicker{
  font-size:var(--fs-label);font-weight:600;text-transform:uppercase;
  letter-spacing:.09em;color:var(--blue-soft);
}
.proof-stat{
  font-size:clamp(2.8rem,4.8vw,3.8rem);font-weight:650;letter-spacing:-.03em;
  line-height:1;color:var(--azure);
}
.proof-stat-cap{margin-top:calc(var(--s-2)*-1);font-size:var(--fs-sm);color:rgba(255,255,255,.65)}
.proof-feature h3{color:#fff}
.proof-feature p,
.proof-feature blockquote{flex:1;color:rgba(255,255,255,.86);line-height:1.65}
.proof-feature blockquote::before{color:var(--blue-soft)}
.proof-feature .avatar{background:rgba(255,255,255,.14);color:#fff}
.proof-feature .quote-name{color:#fff}
.proof-feature .quote-role{color:rgba(255,255,255,.6)}

/* path to the full stories page */
.proof-more{
  flex-direction:row;align-items:center;justify-content:space-between;
  font-weight:600;color:var(--blue);background:rgba(8,106,174,.05);
}
.proof-more:hover{color:var(--blue-deep);border-color:var(--blue)}
.proof-more-arrow{font-size:1.3rem;transition:transform var(--t-fast) var(--ease-soft)}
.proof-more:hover .proof-more-arrow{transform:translateX(4px)}

@media (max-width:1020px){
  .proof-grid{grid-template-columns:1fr 1fr}
  .proof-feature{grid-column:span 2;grid-row:auto}
}
@media (max-width:640px){
  .proof-grid{grid-template-columns:1fr}
  .proof-feature{grid-column:auto}
  .proof-card{padding:var(--s-6)}
}

/* Co-brand band: the AirIQ + Phillips Connect lockup is a light-on-dark
   asset, so it gets its own dark strip.

   It is never a full-width banner: on the trailer hub it is a hero pill
   (.cobrand-hero) and on a product page a badge above the headline
   (.cobrand-stage). Both size to their own contents. */
.cobrand-band{
  display:flex;align-items:center;gap:var(--s-4) var(--s-5);flex-wrap:wrap;
  justify-content:space-between;
  background:linear-gradient(120deg,var(--blue-mid) 0%,var(--navy) 100%);
  border-radius:var(--r-card);padding:var(--s-4) var(--s-5);
}
.cobrand-lockup{display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap}
.cobrand-band img{height:40px;width:auto;display:block}
/* The Phillips lockup ships as artwork already built for this dark band. A bare
   AirIQ mark does not, so it takes the same reversal the footer logo uses. */
.cobrand-band img.on-dark{filter:brightness(0) invert(1)}
.cobrand-band span{
  color:rgba(255,255,255,.85);font-weight:500;letter-spacing:.01em;
  font-size:.95rem;line-height:1.35;
}
/* Divider between the lockup and its line, so the two read as one unit. */
.cobrand-band .cobrand-line{
  padding-left:var(--s-5);border-left:1px solid rgba(255,255,255,.22);
}
/* In a hero the band replaces the usual eyebrow pill, so it sizes to its
   contents rather than spanning the column. That is what kills the empty
   right-hand half the full-width version had at desktop widths. */
.cobrand-hero{
  display:inline-flex;width:auto;max-width:100%;
  padding:9px var(--s-5);margin-bottom:var(--s-6);
}
/* One row, so the line sits beside the lockup rather than wrapping under it
   and leaving the top-right of the bar empty. */
.cobrand-hero .cobrand-lockup{flex-wrap:nowrap}
.page-hero .cobrand-hero img{height:32px}
.page-hero .cobrand-hero span{font-size:.86rem}
.page-hero.is-cobrand h1{margin-top:0}

/* On a product page the co-brand is a badge, not a band: it replaces the
   eyebrow at the top of the stage so the device reads as a Phillips Connect
   product before the headline, and it sizes to its own text instead of
   stretching the full column. The whole pill links to the partnership. */
.cobrand-stage{
  display:inline-flex;width:auto;max-width:100%;
  padding:7px 16px 7px 12px;margin-bottom:var(--s-5);
  text-decoration:none;transition:transform .18s ease,box-shadow .18s ease;
}
.cobrand-stage .cobrand-lockup{gap:12px}
.cobrand-stage img{height:26px}
.cobrand-stage .cobrand-line{
  padding-left:12px;font-size:.82rem;font-weight:500;
  border-left:1px solid rgba(255,255,255,.22);
}
.cobrand-stage:hover{transform:translateY(-1px);box-shadow:0 8px 18px rgba(10,25,41,.18)}
.cobrand-stage:hover .cobrand-line{color:#fff}
.stage-copy .cobrand-stage + h1{margin-top:0}

@media (max-width:720px){
  .cobrand-band{padding:var(--s-4)}
  .cobrand-band .cobrand-line{padding-left:0;border-left:0}
  .cobrand-hero{display:flex;padding:var(--s-4)}
  /* The stage badge stays a badge on phones — it just wraps to two lines. */
  .cobrand-stage{display:inline-flex;padding:7px 14px 7px 10px}
  .cobrand-stage .cobrand-lockup{flex-wrap:nowrap}
  .cobrand-stage .cobrand-line{padding-left:10px;border-left:1px solid rgba(255,255,255,.22)}
}

/* Sensor coverage. A smart trailer is a list of things that can now report
   for themselves, so the list itself is the visual: one pill per sensor. */
.sensor-grid{display:flex;flex-wrap:wrap;gap:10px;margin-top:var(--s-6)}
.sensor{
  display:inline-flex;align-items:center;gap:8px;padding:9px 15px;border-radius:var(--r-pill);
  background:var(--paper);border:1px solid var(--ink-100);
  font-size:var(--fs-sm);font-weight:500;color:var(--navy);
}
.sensor::before{
  content:"";flex:none;width:7px;height:7px;border-radius:50%;background:var(--blue);
}

/* ══════════════════════════════════════════════════════════════════
   SIGNUP FORMS — newsletter (footer) and press-release alerts (IR)

   The footer one is a footer utility, so it is built from the footer's
   own parts: its heading is a plain <h2>, which picks up `.footer h2`
   like PRODUCTS and COMPANY do, and it sits on the same rhythm and the
   same 280px column width as `.footer-about` above it.

   The IR one borrows `.cta-final` and `.cta-form` wholesale, so it is
   the same closing band as every other page with an email field in
   place of the demo request.
   ══════════════════════════════════════════════════════════════════ */

.footer-signup{margin-top:var(--s-7);max-width:280px}

.signup-row{display:grid;grid-template-columns:1fr 44px;gap:var(--s-2)}
.signup-row input[type=email]{
  width:100%;min-width:0;height:44px;padding:0 var(--s-4);
  border-radius:var(--r-ctl);border:1px solid rgba(255,255,255,.18);
  background:rgba(255,255,255,.06);color:#fff;
  font-family:inherit;font-size:var(--fs-sm);line-height:1;
  transition:border-color var(--t-fast),background var(--t-fast),box-shadow var(--t-fast);
}
.signup-row input[type=email]::placeholder{color:rgba(255,255,255,.45)}
.signup-row input[type=email]:focus{
  outline:none;background:rgba(255,255,255,.12);border-color:var(--azure);box-shadow:var(--ring);
}
.signup-go{
  width:44px;height:44px;padding:0;border-radius:var(--r-ctl);
  border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.06);color:#fff;
  font-size:1rem;line-height:1;cursor:pointer;display:grid;place-items:center;
  transition:background var(--t-fast),border-color var(--t-fast),color var(--t-fast);
}
.signup-go:hover{background:#fff;border-color:#fff;color:var(--blue-deep)}
.signup-go:focus-visible{outline:none;border-color:var(--azure);box-shadow:var(--ring)}

/* Bot trap. Off-screen rather than display:none, so bots that skip hidden
   fields still fill it in. The block also carries this inline, so a stale
   stylesheet can never expose it. 16px keeps the iOS-zoom check honest. */
.signup-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.signup-hp input{font-size:16px}

.consent-opt{display:flex;gap:10px;align-items:flex-start;font-size:.86rem;line-height:1.5;color:var(--ink-700);cursor:pointer}
.consent-opt input{margin-top:3px;flex:none}
.form-msg{margin-top:14px;font-size:.95rem;line-height:1.5;color:var(--ink-700)}
.form-msg.is-error{color:#B3261E}
.cta-final .form-msg{color:#fff}
.cta-final .form-msg.is-error{color:#FFD4D0}

/* A sent form has nothing left in it but its confirmation, so the line
   has to carry the weight the fields used to. A tick and the accent
   colour say "this worked" at a glance; without them the reply to a
   submission is one grey sentence where a form used to be, which is
   thin enough to read as a failure. Errors keep their own colour — the
   tick is only ever shown for success. */
.is-sent .signup-msg,
.is-sent .form-msg{
  display:flex;gap:9px;align-items:flex-start;margin-top:0;
  font-size:1rem;font-weight:600;color:var(--blue-deep);
}
.is-sent .signup-msg::before,
.is-sent .form-msg::before{
  content:"";flex:none;width:20px;height:20px;margin-top:1px;border-radius:50%;
  background:var(--blue-deep);
  /* tick, knocked out of the disc */
  -webkit-mask:var(--tick) center/11px 11px no-repeat;mask:var(--tick) center/11px 11px no-repeat;
  background-image:none;box-shadow:inset 0 0 0 20px var(--blue-deep);
}
/* A sent form is a one-line block, so the grid it used to be has to go.
   .cta-form is `1fr auto` for field-then-button: left as a grid, the
   consent line and the confirmation end up side by side in those two
   tracks, reading as a broken layout rather than a reply. */
.is-sent{display:block!important}
/* Everything but the confirmation goes. Hiding only the controls left their
   wrapper rows, grid gaps and the consent note behind — which put the
   message at the bottom of a card that was still the height of the form.
   The footer keeps its NEWSLETTER heading: it labels the column. */
.is-sent > :not(.signup-msg):not(.form-msg):not(h2){display:none!important}

.is-sent .form-msg.is-error,
.is-sent .signup-msg.is-error{color:#B3261E}
.is-sent .form-msg.is-error::before,
.is-sent .signup-msg.is-error::before{display:none}
/* On the dark bands — the footer newsletter and the closing CTA — the
   same state has to invert or it disappears into the gradient. */
.footer .is-sent .signup-msg,
.cta-final .is-sent .form-msg,
.cta-final .is-sent .signup-msg{color:#fff}
.footer .is-sent .signup-msg::before,
.cta-final .is-sent .form-msg::before,
.cta-final .is-sent .signup-msg::before{box-shadow:inset 0 0 0 20px #fff}
.form-consent{margin-top:14px;font-size:.8rem;line-height:1.5;color:var(--ink-500)}
.form-consent a{color:inherit;text-decoration:underline}
.cta-final .form-consent{color:rgba(255,255,255,.62)}
.cta-final .form-consent a{color:rgba(255,255,255,.8)}
.signup-consent{margin-top:10px;font-size:.78rem;line-height:1.45;color:rgba(255,255,255,.55)}
.signup-consent a{color:rgba(255,255,255,.75);text-decoration:underline}
.signup-consent a:hover{color:#fff}
.signup-msg{margin-top:var(--s-3);font-size:var(--fs-sm);line-height:1.5;color:#fff;font-weight:600}
.signup-msg[hidden]{display:none}
.signup-msg.is-error{color:#FFC9C4;font-weight:500}

/* Inside the two-column .cta-form the message has to span both tracks
   rather than squeeze in beside the button. */
.cta-form .signup-msg,
.cta-form .form-msg{grid-column:1/-1;text-align:center;margin-top:var(--s-1)}
/* The CTA band is centred, so its confirmation centres too — the tick and
   the line as one centred row, not a left-aligned row in a centred band. */
.cta-form.is-sent .signup-msg,
.cta-form.is-sent .form-msg{justify-content:center;text-align:left;max-width:none}
/* The 520px cap fits a field and a button; it wraps a one-line
   confirmation into two and the block then reads as off-centre. */
.cta-form.is-sent{max-width:none}
/* The grid is `field | button`. The consent line and a field error are
   neither, and without a span they land in the first column, wrapped
   narrow beside the button. */
.cta-form .form-consent{grid-column:1/-1;text-align:center;margin-top:6px}
.cta-form .field-err{grid-column:1/-1;margin-top:-4px}

@media (max-width:720px){
  .signup-row input[type=email]{font-size:16px}   /* stops iOS zooming on focus */
  .footer-signup{max-width:none}
}

/* ══════════════════════════════════════════════════════════════════
   EVIDENCE FOOTAGE — real dashcam clips, self-hosted.
   These are silent by nature (the cameras record no audio), so they
   carry no audio track at all and the controls stay minimal. Nothing
   autoplays: a crash loop running behind a claims argument reads as
   theatre, and the visitor should choose to watch. The poster does the
   selling until they do.
   ══════════════════════════════════════════════════════════════════ */
.evidence-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s-5)}
.evidence-grid.cols-2{grid-template-columns:repeat(2,1fr);max-width:820px}
.evidence-grid.cols-1{grid-template-columns:1fr;max-width:720px}

.evidence{margin:0;display:flex;flex-direction:column}
.evidence video,.evidence .js-embed{
  /* width/height attributes on the element are presentational hints, so the
     height sticks at 720px and kills aspect-ratio unless it is reset here. */
  width:100%;height:auto;aspect-ratio:16/9;display:block;background:var(--ink-900);
  border-radius:var(--r-card);border:1px solid var(--ink-100);
  object-fit:cover;
}
/* The caption is the evidentiary bit — what the camera caught and why it
   matters. It carries more weight than a normal figcaption, so it sits at
   body size rather than shrinking away. */
.evidence .js-embed{border:0}
.evidence figcaption{
  margin-top:var(--s-3);font-size:var(--fs-sm);line-height:1.6;
  color:var(--ink-700);
}
.evidence figcaption strong{display:block;color:var(--ink-900);font-weight:650;margin-bottom:2px}

@media (max-width:900px){
  .evidence-grid,.evidence-grid.cols-2{grid-template-columns:1fr 1fr}
}
@media (max-width:640px){
  .evidence-grid,.evidence-grid.cols-2{grid-template-columns:1fr}
}

/* The click-to-load poster for an embedded video. It has to sit at the
   same size and radius as a self-hosted clip, because the two appear
   side by side in the same grid and must not read as different things. */
.yt-facade{
  position:relative;display:block;width:100%;padding:0;border:1px solid var(--ink-100);
  border-radius:var(--r-card);overflow:hidden;background:var(--ink-900);
  aspect-ratio:16/9;cursor:pointer;
}
.yt-facade img{width:100%;height:100%;object-fit:cover;display:block;opacity:.86;
  transition:opacity var(--t-med) var(--ease),transform var(--t-slow) var(--ease)}
.yt-facade:hover img{opacity:1;transform:scale(1.02)}
/* A play target big enough to hit on a phone, drawn rather than imported
   so it needs no asset and inherits the brand blue. */
.yt-facade::after{
  content:"";position:absolute;top:50%;left:50%;width:68px;height:68px;
  transform:translate(-50%,-50%);border-radius:50%;
  background:var(--blue) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23fff'%3E%3Cpath d='M8 5v14l11-7z'/%3E%3C/svg%3E") no-repeat 54% 50%/26px 26px;
  box-shadow:0 6px 24px rgba(0,0,0,.35);transition:transform var(--t-med) var(--ease)
}
.yt-facade:hover::after{transform:translate(-50%,-50%) scale(1.08)}
.yt-facade:focus-visible{outline:none;box-shadow:var(--ring)}
