/* ============================================================
   AquaCore theme stylesheet
   Palette and type come from theme.json presets. This file owns
   layout, section styling, and every piece of motion.
   All motion degrades to static under prefers-reduced-motion.
   ============================================================ */

:root{
  --abyss:var(--wp--preset--color--abyss,#03101a);
  --deep:var(--wp--preset--color--deep,#071c29);
  --mid:var(--wp--preset--color--mid,#0b2739);
  --navy:var(--wp--preset--color--navy,#0f2f56);
  --blue:var(--wp--preset--color--blue,#3d9bd7);
  --blue-lt:var(--wp--preset--color--blue-light,#7ec4ee);
  --orange:var(--wp--preset--color--orange,#e8492b);
  --orange-lt:var(--wp--preset--color--orange-light,#ff7d55);
  --ink:var(--wp--preset--color--ink,#eaf4f8);
  --muted:var(--wp--preset--color--muted,#8dafbe);
  --dim:var(--wp--preset--color--dim,#5c7f8f);
  --line:rgba(160,205,225,.13);
  --mono:'IBM Plex Mono',ui-monospace,Menlo,monospace;
  --sans:Outfit,system-ui,sans-serif;
  --pad:clamp(20px,5vw,72px);
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  /* Reveals only. --ease-out is expo-out: gorgeous on a 300ms hover, but over
     half a second it spends the tail barely moving, which is the part that
     reads as lag. This lands. */
  --ease-rv:cubic-bezier(.22,.72,.28,1);
  --max:1240px;
  --navh:77px;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
/* set by smoothScroll(): the engine drives scrollTo every frame and the
   browser's own easing fights it */
html.ac-lerp{scroll-behavior:auto}
body{margin:0;background:var(--abyss);color:var(--ink);font-family:var(--sans);font-weight:300;
  line-height:1.62;overflow-x:hidden;-webkit-font-smoothing:antialiased}
img{max-width:100%;height:auto;display:block}
a{color:inherit;text-decoration:none}
h1,h2,h3,h4,h5{margin:0;font-weight:700;letter-spacing:-.02em;line-height:1.12}
p,ul,ol{margin:0}

/* focus: visible, never removed */
:focus-visible{outline:2px solid var(--orange-lt);outline-offset:3px;border-radius:2px}
.skip{position:absolute;left:-9999px;top:0;z-index:200;background:var(--orange);color:#fff;
  padding:12px 20px;font:600 14px/1 var(--sans);border-radius:0 0 4px 0}
.skip:focus{left:0}

/* ---------- ambient depth ---------- */
.depth-field{position:fixed;inset:0;z-index:-3;pointer-events:none;
  background:radial-gradient(120% 80% at 78% -10%,rgba(61,155,215,.20) 0%,rgba(3,16,26,0) 58%),
    linear-gradient(180deg,var(--deep) 0%,var(--mid) 22%,var(--deep) 55%,var(--abyss) 100%)}
.depth-veil{position:fixed;inset:0;z-index:-2;pointer-events:none;background:var(--abyss);
  opacity:calc(var(--scroll,0) * .82);transition:opacity .18s linear}
/* Surface light, and it has to go away as you go down. This was a flat .5
   for the whole page, so the deepest section was lit exactly like the top and
   the descent had no destination. It now fades out with --scroll, which the
   scroll handler already publishes: by the bottom of a page there is almost
   nothing left of it. Together with .depth-veil below, scrolling the site is
   losing the sun. */
.caustics{position:fixed;inset:-20%;z-index:-2;pointer-events:none;
  opacity:calc(.58 - var(--scroll,0) * .5);
  transition:opacity .18s linear;
  mix-blend-mode:screen;filter:blur(58px)}
.caustics i{position:absolute;display:block;border-radius:50%;
  background:radial-gradient(circle,rgba(126,196,238,.30) 0%,rgba(126,196,238,0) 68%)}
.caustics i:nth-child(1){width:52vw;height:52vw;left:-8%;top:-6%;animation:drift1 34s var(--ease) infinite alternate}
.caustics i:nth-child(2){width:40vw;height:40vw;right:-6%;top:18%;animation:drift2 44s var(--ease) infinite alternate}
.caustics i:nth-child(3){width:60vw;height:60vw;left:22%;bottom:-18%;
  background:radial-gradient(circle,rgba(232,73,43,.14) 0%,rgba(232,73,43,0) 68%);
  animation:drift3 52s var(--ease) infinite alternate}
@keyframes drift1{to{transform:translate3d(14vw,10vh,0) scale(1.22)}}
@keyframes drift2{to{transform:translate3d(-12vw,16vh,0) scale(.84)}}
@keyframes drift3{to{transform:translate3d(10vw,-12vh,0) scale(1.15)}}
.glow{position:fixed;left:0;top:0;width:640px;height:640px;margin:-320px 0 0 -320px;z-index:-1;
  pointer-events:none;opacity:0;transition:opacity .6s var(--ease);will-change:transform;
  background:radial-gradient(circle,rgba(61,155,215,.13) 0%,transparent 62%)}
.glow.on{opacity:1}

/* The depth rail lived here. Removed: a permanent HUD gauge down the edge
   of the page is cockpit decoration, not navigation. */

/* ---------- announcement ---------- */
.announce{position:relative;z-index:70;display:flex;justify-content:center;align-items:center;gap:10px;
  background:var(--orange);color:#fff;font:500 11.5px/1.4 var(--mono);letter-spacing:.1em;
  padding:9px 16px;text-align:center}
.announce i{width:5px;height:5px;border-radius:50%;background:#fff;flex:none;animation:blip 2.4s infinite}
@keyframes blip{0%,100%{opacity:1}50%{opacity:.25}}

/* ---------- header ---------- */
/* Sticky, and it must stay legible over whatever scrolls under it. It used to
   be fully transparent until .stuck landed, so against a dark section it read
   as having disappeared. It now carries a faint ground at rest and a solid
   one once the page moves. */
/* The sticky element has to be the wrapper WordPress puts around the header
   template part, not the nav inside it. Sticky positions against the nearest
   scrolling ancestor but is bounded by its own parent, and that wrapper is
   exactly as tall as the nav, so a sticky .nav scrolled straight out of view
   the moment the header's own box did. The static preview injects the part
   with no wrapper, which is why this only ever showed up in WordPress. */
.hdr{position:sticky;top:0;z-index:65}
.nav{position:relative;z-index:65;display:flex;align-items:center;justify-content:space-between;
  gap:24px;padding:16px var(--pad);border-bottom:1px solid transparent;
  background:linear-gradient(180deg,rgba(3,16,26,.55),rgba(3,16,26,0));
  transition:background .35s var(--ease),padding .35s var(--ease),border-color .35s var(--ease)}
.nav.stuck{background:rgba(3,16,26,.94);backdrop-filter:blur(20px) saturate(160%);
  border-bottom-color:var(--line);padding-top:11px;padding-bottom:11px}
/* The announcement bar scrolls away; the nav must not go with it. */
.announce{position:relative}
.navprog{position:absolute;left:0;bottom:-1px;height:1.5px;width:100%;transform-origin:left;
  transform:scaleX(var(--scroll,0));opacity:0;transition:opacity .3s;
  background:linear-gradient(90deg,var(--orange),var(--blue))}
.nav.stuck .navprog{opacity:1}
.brand{display:flex;align-items:center;gap:11px;flex:none}
.foot .brand svg,.foot .brand img{width:34px;height:34px;flex:none}
/* The two lockups are exported at identical geometry from the same artwork,
   so stacking them and crossfading cannot shift a pixel. The orange sits on
   top at zero opacity rather than swapping src, which would flash on first
   hover while the second file downloads. */
.brand__logo{position:relative;display:block;width:clamp(178px,15.5vw,236px);flex:none}
.brand__logo img{display:block;width:100%;height:auto}
.brand__base{transition:opacity .34s var(--ease)}
.brand__hov{position:absolute;inset:0;opacity:0;transition:opacity .34s var(--ease)}
.brand:hover .brand__hov,.brand:focus-visible .brand__hov{opacity:1}
/* Cross-fade rather than fade-in-over. The two glyph sets differ by a hair
   of antialiasing, so leaving the white one lit underneath left a pale rim
   around the orange SOLUTIONS. */
.brand:hover .brand__base,.brand:focus-visible .brand__base{opacity:0}
@media (prefers-reduced-motion:reduce){.brand__base,.brand__hov{transition:none}}
/* legacy text lockup, kept for the drawer and any pattern still using it */
.brand__wm{display:flex;flex-direction:column;line-height:1}
.brand__wm b{font:700 24px/1 var(--sans);letter-spacing:-.015em}
.brand__wm b i{font-style:normal;color:var(--blue)}
.brand__wm small{font:500 9px/1 var(--mono);letter-spacing:.44em;color:var(--dim);margin-top:4px}
.nav__links{display:flex;gap:26px;font:400 14px/1 var(--sans);color:var(--muted);
  list-style:none;margin:0;padding:0}
.nav__links a{position:relative;padding:6px 0;transition:color .25s;display:inline-block}
.nav__links a::after{content:'';position:absolute;left:0;bottom:0;width:100%;height:1px;
  background:var(--orange);transform:scaleX(0);transform-origin:right;
  transition:transform .4s var(--ease-out)}
.nav__links a:hover,.nav__links a[aria-current]{color:var(--ink)}
.nav__links a:hover::after,.nav__links a[aria-current]::after{transform:scaleX(1);transform-origin:left}
.nav__end{display:flex;align-items:center;gap:12px}

/* products dropdown. Hover opens it on desktop, but focus does too, and the
   parent link still navigates: nothing here is only reachable by hovering. */
.has-sub{position:relative}
.has-sub>a{display:inline-flex;align-items:center;gap:6px}
.caret{width:9px;height:6px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;transition:transform .28s var(--ease-out)}
.has-sub:hover>a .caret,.has-sub:focus-within>a .caret{transform:rotate(180deg)}
.sub{position:absolute;top:100%;left:50%;transform:translate(-50%,8px);margin-top:12px;
  width:320px;padding:9px;border-radius:9px;border:1px solid var(--line);
  background:rgba(7,28,41,.97);backdrop-filter:blur(18px) saturate(150%);
  box-shadow:0 26px 60px rgba(0,0,0,.55);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .24s var(--ease-out),transform .28s var(--ease-out),visibility .24s}
.has-sub:hover .sub,.has-sub:focus-within .sub{
  opacity:1;visibility:visible;pointer-events:auto;transform:translate(-50%,0)}
/* keeps the pointer inside the hover target while it travels down */
.sub::before{content:'';position:absolute;left:0;right:0;top:-14px;height:14px}
.sub a{display:block;padding:11px 13px;border-radius:6px;transition:background .2s}
.sub a:hover,.sub a:focus-visible{background:rgba(126,196,238,.09)}
.sub b{display:block;font-size:14px;font-weight:600;color:var(--ink);line-height:1.3}
.sub a:hover b{color:var(--blue-lt)}
.sub span{display:block;margin-top:3px;font-size:12px;line-height:1.45;color:var(--muted)}
.drawer__sub{padding-left:22px !important;font-size:15px !important;color:var(--muted) !important}
.drawer__sub::before{content:'';position:absolute;left:2px;top:50%;width:11px;height:1px;
  background:rgba(160,205,225,.28)}

/* mobile menu */
.burger{display:none;width:42px;height:42px;align-items:center;justify-content:center;
  background:transparent;border:1px solid var(--line);border-radius:4px;cursor:pointer;padding:0}
.burger span{display:block;width:17px;height:1.5px;background:var(--ink);position:relative;
  transition:background .2s}
.burger span::before,.burger span::after{content:'';position:absolute;left:0;width:17px;height:1.5px;
  background:var(--ink);transition:transform .32s var(--ease-out)}
.burger span::before{top:-5.5px}.burger span::after{top:5.5px}
.burger[aria-expanded="true"] span{background:transparent}
.burger[aria-expanded="true"] span::before{transform:translateY(5.5px) rotate(45deg)}
.burger[aria-expanded="true"] span::after{transform:translateY(-5.5px) rotate(-45deg)}
.drawer{position:fixed;inset:0;z-index:64;background:rgba(3,16,26,.985);
  backdrop-filter:blur(22px);display:flex;flex-direction:column;justify-content:center;
  gap:6px;padding:0 var(--pad);opacity:0;visibility:hidden;transition:opacity .35s var(--ease),visibility .35s}
.drawer.open{opacity:1;visibility:visible}
.drawer a{font:600 clamp(24px,7vw,38px)/1.25 var(--sans);letter-spacing:-.02em;color:var(--ink);
  padding:9px 0;border-bottom:1px solid var(--line);opacity:0;transform:translateY(14px);
  transition:opacity .5s var(--ease-out),transform .5s var(--ease-out)}
.drawer.open a{opacity:1;transform:none}
.drawer.open a:nth-child(1){transition-delay:.06s}.drawer.open a:nth-child(2){transition-delay:.11s}
.drawer.open a:nth-child(3){transition-delay:.16s}.drawer.open a:nth-child(4){transition-delay:.21s}
.drawer.open a:nth-child(5){transition-delay:.26s}.drawer.open a:nth-child(6){transition-delay:.31s}
.drawer.open a:nth-child(7){transition-delay:.36s}
@media(max-width:1080px){.nav__links{display:none}.burger{display:flex}}
body.locked{overflow:hidden}

/* ---------- buttons ---------- */
.btn{position:relative;display:inline-flex;align-items:center;gap:9px;overflow:hidden;
  font:500 14px/1 var(--sans);padding:15px 24px;border-radius:3px;white-space:nowrap;border:0;
  cursor:pointer;transition:transform .3s var(--ease-out),box-shadow .3s var(--ease),
  background .3s,border-color .3s,color .3s}
.btn--solid{background:var(--orange);color:#fff;--sheen:-30%}
.btn--solid::before{content:'';position:absolute;top:0;bottom:0;width:55%;left:var(--sheen);opacity:0;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.34),transparent);
  transition:left .75s var(--ease-out),opacity .2s}
.btn--solid:hover{box-shadow:0 10px 34px rgba(232,73,43,.36);--sheen:105%}
.btn--solid:hover::before{opacity:1}
.btn--wire{border:1px solid var(--line);color:var(--ink);background:transparent}
.btn--wire:hover{border-color:var(--blue);color:var(--blue-lt);background:rgba(61,155,215,.07)}
.btn--sm{font-size:12.5px;padding:11px 18px;gap:0}
.btn__ar{transition:transform .35s var(--ease-out)}
.btn:hover .btn__ar{transform:translateX(4px)}
@media(max-width:620px){.nav .btn--sm{font-size:11.5px;padding:10px 14px}.nav .btn--sm .long{display:none}}
@media(max-width:430px){.nav .btn--sm{display:none}}

/* ---------- section furniture ---------- */
/* Was clamp(58px,8vw,104px). On a page with nine sections that padding alone
   accounted for well over a screen of scroll, and the sections have enough
   internal structure to separate themselves without it. */
.sec{position:relative;padding:clamp(46px,5.4vw,74px) var(--pad)}
.sec--flush{padding-top:0}
.sec::before{content:'';position:absolute;inset:0;pointer-events:none;z-index:0;
  background-image:linear-gradient(rgba(126,196,238,.05) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(126,196,238,.05) 1px,transparent 1px);
  background-size:72px 72px;
  -webkit-mask-image:radial-gradient(130% 80% at 50% 0%,#000 0%,transparent 70%);
          mask-image:radial-gradient(130% 80% at 50% 0%,#000 0%,transparent 70%)}
.sec>*{position:relative;z-index:1}
.wrap{max-width:var(--max);margin:0 auto}
.eyebrow{display:inline-flex;align-items:center;gap:9px;font:500 11px/1.4 var(--mono);
  letter-spacing:.26em;color:var(--orange-lt);margin:0 0 18px;text-transform:uppercase}
.eyebrow::before{content:'';width:24px;height:1px;background:var(--orange);flex:none}
/* The hero eyebrow carries the positioning line, not a section label, so it
   runs larger than the section eyebrows and its rule grows to match. Scoped to
   the hero on purpose: bumping .eyebrow globally would enlarge the small
   labels on every section of every page. */
.hero .eyebrow{font-size:13px;letter-spacing:.28em;gap:11px;margin-bottom:20px}
.hero .eyebrow::before{width:30px}
@media(max-width:600px){.hero .eyebrow{font-size:11.5px;letter-spacing:.22em}}
.h2{font-size:clamp(27px,3.6vw,42px)}
.lede{font-size:clamp(15px,1.35vw,17.5px);line-height:1.68;color:var(--muted);font-weight:300}
/* The section scan line used to live here: a 1.5px orange gradient injected
   into every section and animated top to bottom over 1.25s on first sight. It
   read as a rendering artifact rather than an effect, because that is what a
   coloured line crossing static content looks like. Removed, along with the
   observer in motion.js that drove it. */

/* ---------- reveal ----------
   Retuned for arrival rather than performance. The old values were 26px of
   travel over 850ms on an expo-out curve, fired at 18% visibility with the
   trigger line pulled 8% up the viewport: by the time an element started
   moving you were already reading it, so the motion registered as the page
   lagging behind the scroll rather than as anything deliberate.

   Now it starts before the element reaches the fold, travels a third as far,
   and is done in half the time. The effect you want is content that is
   already settled when your eye lands on it, which means most of the motion
   should happen just off screen. */
.rv{opacity:0;transform:translateY(10px);
  transition:opacity .46s var(--ease-rv),transform .52s var(--ease-rv);
  transition-delay:calc(var(--i,0) * 55ms)}
.rv.in{opacity:1;transform:none}

/* ---------- scroll-linked reveals ----------
   The block above is the fallback. It is scroll TRIGGERED: an observer spots
   an element crossing a line, flips a class, and the element then animates on
   its own clock. That is the pattern every template has used since about
   2014, and it is why the page reads as older than it looks. Scroll one pixel
   and either nothing happens or a whole 500ms animation fires.

   This is scroll LINKED. Progress is a function of where the element sits in
   the viewport, so it resolves as you scroll, reverses if you scroll back,
   and never pops. --i staggers by scroll distance rather than by milliseconds,
   which is what makes a row of tiles resolve in sequence under your thumb
   instead of on a timer that started before you got there.

   Chrome and Edge 115+, Safari 26+. Firefox has not shipped it, so it keeps
   the transition above: same design, one generation older, nothing broken. */
@supports (animation-timeline: view()) {
  .rv{
    opacity:1;transform:none;transition:none;
    animation:acrv linear both;
    animation-timeline:view();
    /* cover, not entry. An "entry" range only completes once the element is
       FULLY inside the viewport, so anything taller than the screen never
       gets there and its reveal stalls part-way, permanently. A cover range
       is a share of the element's whole pass across the screen and completes
       at any height. */
    animation-range:cover calc(0% + var(--i,0) * 2.5%) cover calc(30% + var(--i,0) * 2.5%);
  }
  /* ---------- how a thing arrives ----------
     Not a fade and a slide. Those are the same motion a document makes, and
     they were why this still read as scrolling past something that had
     already been placed.

     Underwater, an object does not travel to a spot and stop on it. It rises,
     goes a little past, and settles back down as the water takes the momentum
     out of it. That overshoot is the entire difference, and because these are
     scrubbed against the scroll rather than played on a timer, scrolling
     slowly lets you watch a heading drift up, hang, and sink into its line.

     --dx gives each element a small sideways component so a row does not move
     in lockstep. Nothing in water falls in a straight line. */
  @keyframes acrv{
    0%{opacity:0;transform:translate3d(var(--dx,0px),32px,0) scale(.972) rotate(var(--dr,0deg))}
    55%{opacity:1}
    78%{transform:translate3d(calc(var(--dx,0px) * .12),-5px,0) scale(1.005) rotate(calc(var(--dr,0deg) * .12))}
    100%{opacity:1;transform:none}}

  /* The drift, varied so it never looks patterned. Tiny: at these distances
     the eye reads it as things not being rigid, not as things moving apart. */
  main :is(.wrap,.g2,.g3,.g4,.shotgrid,.numrow,.prowset,.faq,.split,.anat__steps,.esn__flow) > *:nth-child(3n+1){--dx:-9px;--dr:-.5deg}
  main :is(.wrap,.g2,.g3,.g4,.shotgrid,.numrow,.prowset,.faq,.split,.anat__steps,.esn__flow) > *:nth-child(3n+2){--dx:7px;--dr:.4deg}
  main :is(.wrap,.g2,.g3,.g4,.shotgrid,.numrow,.prowset,.faq,.split,.anat__steps,.esn__flow) > *:nth-child(3n+3){--dx:-4px;--dr:.28deg}

  /* The hero is on screen before any scrolling happens, so a scroll timeline
     would render it finished and there would be no arrival at all. It keeps
     the timed entrance, which is the one place a timed entrance belongs. */
  .hero .rv,.phero .rv{
    opacity:0;transform:translateY(10px);transition:opacity .46s var(--ease-rv),transform .52s var(--ease-rv);
    transition-delay:calc(var(--i,0) * 55ms);animation:none}
  .hero .rv.in,.phero .rv.in{opacity:1;transform:none}

  /* Headlines unmask against the scroll rather than against a stopwatch. */
  .lines:not(.hero .lines):not(.phero .lines) span i{
    transform:none;transition:none;
    animation:acline linear both;
    animation-timeline:view();
    animation-range:cover 0% cover 27%;
  }
  @keyframes acline{
    0%{transform:translateY(118%)}
    76%{transform:translateY(-3.5%)}
    100%{transform:none}}

  /* ---------- depth ----------
     Foreground media drifts slower than the page. Two planes moving at
     different rates is the cheapest real depth there is, and it is the thing
     a flat fade-up can never give you. */
  .esn__media,.phero__obj,.esn__shot,.wlb__shot,.dsc__media img,.shot img,.ui img{
    animation:acdrift linear both;
    animation-timeline:view();
    animation-range:cover 0% cover 100%;
  }
  @keyframes acdrift{from{transform:translateY(26px)}to{transform:translateY(-26px)}}

  /* the analyzer already has a scroll-driven transform of its own from
     esnScene(); two engines writing the same property is a fight */
  .esn__shot{animation:none}

  /* ---------- the same reveal, on everything ----------
     .rv only reaches what somebody remembered to tag, which is most of the
     prose and almost none of the furniture: tiles, spec tables, FAQ rows,
     figures and grid cells all just existed, fully formed, waiting to be
     scrolled past. That is what made the site read as a document. Every
     block-level thing in a section now arrives the same way, whether anyone
     tagged it or not.

     Two levels, deliberately. A section's own children (eyebrow, heading,
     lede, the grid as one object) reveal together; the cells inside a grid
     reveal individually and in sequence. Doing both to the same element
     would fade it twice, so the grids are excluded from the first rule and
     handled by the second.

     No fallback needed. This is additive: where view() is unsupported these
     selectors simply do nothing and the content renders normally, so unlike
     .rv there is no opacity:0 waiting to strand anyone. */
  main .wrap > *:not(.g2):not(.g3):not(.g4):not(.shotgrid):not(.numrow):not(.prowset):not(.faq):not(.split):not(.anat):not(.esn__in):not(.anat__steps),
  main :is(.g2,.g3,.g4,.shotgrid,.numrow,.prowset,.faq,.split,.anat__steps,.esn__flow) > *{
    animation:acrv linear both;
    animation-timeline:view();
    animation-range:cover 0% cover 30%;
  }
  /* Cells in a row resolve left to right rather than all at once. Six is as
     far as any grid here goes before it wraps. */
  main :is(.g2,.g3,.g4,.shotgrid,.numrow,.prowset,.faq) > *:nth-child(2){animation-range:cover 3% cover 33%}
  main :is(.g2,.g3,.g4,.shotgrid,.numrow,.prowset,.faq) > *:nth-child(3){animation-range:cover 6% cover 36%}
  main :is(.g2,.g3,.g4,.shotgrid,.numrow,.prowset,.faq) > *:nth-child(4){animation-range:cover 9% cover 39%}
  main :is(.g2,.g3,.g4,.shotgrid,.numrow,.prowset,.faq) > *:nth-child(5){animation-range:cover 12% cover 42%}
  main :is(.g2,.g3,.g4,.shotgrid,.numrow,.prowset,.faq) > *:nth-child(n+6){animation-range:cover 15% cover 45%}

  /* Never inside something that is one composed picture. A dashboard whose
     tiles fade in one by one is not a dashboard, it is a slideshow of a
     dashboard, and the same goes for the map, the pinned descent scene, the
     analyzer readout and the anatomy stage. */
  main :is(.fleet,.hubui,.escr,.cap,.dsc,.anat,.wl,.wlb,.chart,.spec,.ui,.card,.pnav) *,
  main :is(.fleet,.hubui,.escr,.cap,.dsc,.wl,.chart,.spec){
    animation:none}
  /* except the analyzer's own scroll transform, which is not a reveal */
  .esn__media{animation:acdrift linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
}

@media (prefers-reduced-motion: reduce){
  @supports (animation-timeline: view()){
    .rv,.lines span i,.esn__media,.phero__obj,.wlb__shot,.dsc__media img,.shot img,.ui img{
      animation:none;opacity:1;transform:none}
  }
}
/* overflow:hidden is the reveal mask, and it clips at the line box, so any
   descender hanging below it got sliced: the g in "Change" lost its tail.
   Pad the mask downward and pull the same amount back as negative margin, so
   the glyph has room while the layout spacing is unchanged. The slide
   distance goes past that padding, otherwise the incoming line peeks into it
   before it is meant to arrive. */
.lines span{display:block;overflow:hidden;padding-bottom:.18em;margin-bottom:-.18em}
.lines span i{display:block;font-style:normal;transform:translateY(115%);
  transition:transform .62s var(--ease-rv);transition-delay:calc(var(--i,0) * 70ms)}
.lines.in span i{transform:none}

/* ---------- hero ---------- */
/* The nav is transparent and sticky, so the hero is pulled up underneath it
   and pays the height back as padding. Without this the background stopped at
   the nav's lower edge and the header read as a separate bar bolted on top.
   overflow stays hidden: it is what keeps the bleeding panel from adding a
   horizontal scrollbar. */
.hero{position:relative;
  /* The hero owns the whole first screen. Capped shorter than the viewport it
     always left a slice of the next section showing under the fold, which read
     as a stray band no matter what colour that section was. Full height puts
     the water surface exactly at the fold. Phones opt out below. */
  /* Filling the viewport is right up to about 1400px tall. Past that the
     composition is 537px of content in a 2281px box and no amount of centring
     hides it, so the hero caps and the water below the fold becomes the
     invitation instead of a field of empty photograph. Screens 1080 and
     shorter are unaffected: they never reach the cap. */
  /* Height is 100vh, not 100vh + navh. The hero is already pulled up by navh
     (see the margin rule below), so its top sits at the top of the page and a
     100vh box ends exactly on the fold. Adding navh a second time put the
     bottom that far past the fold, and every pixel of that overshoot showed
     up as empty photograph under the stat row. */
  /* Just short of the fold. The surface moved below the e-Sens beat, so the
     two sections share one ground and the slice showing under the fold is the
     analyzer rising into frame rather than a stray band of another colour.
     Only a slice, though: at -112px the hero gave up enough height that the
     instrument climbed into it and crowded the Hub panel. */
  /* --annh is the announcement bar, measured in JS: it is optional, its text
     is editable, and it wraps to two or three lines on a phone, so its height
     is not a constant this stylesheet can know. The hero is pulled up under
     the nav but not under that bar, so every pixel of it pushes the fold down
     and eats the slice of the e-Sens section that is supposed to show. */
  min-height:min(calc(100vh - 84px - var(--annh,0px)),calc(var(--navh) + 1400px));
  /* Load-bearing. Neither .hero nor the section below it creates a stacking
     context on its own, so the hero's internal layers competed in the root
     context: .hero__grid sits at z-index 3 and the e-Sens content at 1, which
     put the hero's panels and their very large drop shadows OVER the analyzer
     wherever it bleeds up into the hero. The hero clips at its own bottom
     edge, so the result was a hard line across the device with everything
     above it a stop darker. Isolating keeps the hero's stack to itself. */
  isolation:isolate;
  display:flex;align-items:flex-start;
  /* Centring the copy inside a full-height hero pushed it into the lower half
     and left a dead band of photograph under the nav. The hero is taller than
     the composition on purpose, so the composition is anchored below the nav
     with a measured gap instead of floating in the middle of the box. */
  /* The gap under the nav scales harder with height than it used to. The
     composition cannot grow to fill a tall hero (the headline goes to three
     lines and pushes the stat row under the fold), so on a tall screen the
     slack is better spent above the copy than dumped below it. */
  padding:calc(var(--navh) + clamp(32px,12vh,180px)) var(--pad) clamp(64px,6vw,92px);
  overflow:hidden}
/* This has to outrank WordPress. The block layout stylesheet ships
   `.is-layout-flow > :first-child{margin-block-start:0 !important}` at
   specificity (0,2,0), which silently zeroed the pull-up and dropped the hero
   below the header, so the photograph began under the nav instead of behind
   it. The selector deliberately names no ancestor: wp:post-content renders
   inside its own .entry-content wrapper, so the hero is not a child of
   .site-main and a descendant selector matched nothing. Repeating the class
   clears the specificity bar on its own, whatever WordPress nests it in. */
.hero.hero.hero{
  margin-top:calc(var(--navh) * -1) !important;
  margin-block-start:calc(var(--navh) * -1) !important}
/* Home hero photograph. The source is 1000px wide, so it is treated as an
   atmospheric layer rather than a picture: scaled up, held well back in
   opacity, and masked out before the fold ends. At that weight the softness
   reads as depth of field instead of a low-resolution image. Swap in a
   larger original and it sharpens with no CSS change. */
/* The photograph and the dashboard are both "water plus data", so at any
   real opacity they say the same thing twice and cancel out: mid-tones come
   up everywhere, nothing has contrast, and the eye lands nowhere. The panel
   is the product, so it carries the frame and the photo drops to texture:
   held back, blurred past the point of competing for detail, desaturated so
   its stray orange bokeh stops fighting the CTA. inset is negative because
   blurring a background samples past the box and would feather the edges.
   The top of the mask fades to nothing so there is no seam under the nav. */
.hero::before{content:'';position:absolute;inset:-24px;z-index:0;pointer-events:none;
  background:url(../img/home-bg.webp) 72% center/cover no-repeat;
  opacity:.72;filter:blur(1.5px) saturate(.92);
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 52%,rgba(0,0,0,.55) 78%,transparent 97%);
  mask-image:linear-gradient(180deg,#000 0%,#000 52%,rgba(0,0,0,.55) 78%,transparent 97%)}
/* Scrim. The headline owns the left third, so the ground goes nearly solid
   there and only opens up out past the panels. */
.hero::after{content:'';position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    linear-gradient(97deg,var(--abyss) 18%,rgba(3,16,26,.9) 36%,rgba(3,16,26,.58) 62%,rgba(3,16,26,.24) 100%),
    linear-gradient(180deg,rgba(3,16,26,.3) 0%,transparent 26%,rgba(3,16,26,.5) 100%)}
/* Stacking only. Do not touch `position` here: .scrollcue is absolutely
   placed at the bottom-left and relative-ing it dropped it into flow, where
   it landed in the middle of the dashboard. */
.hero__canvas{z-index:2}
.hero__grid{z-index:3}
.hero .scrollcue{z-index:4}
/* screen blend so the light rings add to the photograph underneath instead of
   sitting on it as flat semi-transparent line work. Full opacity: the fade is
   handled per-ring in the canvas, where it can be timed. */
.hero__canvas{position:absolute;inset:0;width:100%;height:100%;z-index:0;opacity:1;
  mix-blend-mode:screen}
.hero__grid{position:relative;z-index:2;max-width:1560px;margin:0 auto;width:100%;display:grid;
  /* minmax(0,…) is load-bearing. heroFit gives the panel an explicit pixel
     width, and without the zero floor that width feeds back into the track
     as intrinsic size: the media column grows, the copy column shrinks, the
     lede picks up a line and the stat row wraps. */
  grid-template-columns:minmax(0,1.02fr) minmax(0,1.2fr);
  gap:clamp(28px,3vw,48px);align-items:center}

.hero h1{font-size:clamp(33px,4.6vw,78px);letter-spacing:-.036em;line-height:1.03}
.hero h1 em{font-style:normal;color:var(--orange)}
.hero__sub{margin-top:24px;max-width:48ch;font-size:clamp(15px,1.45vw,19px)}
.hero__stat{max-width:660px}
.hero__cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:28px}
.hero__stat{display:flex;gap:clamp(16px,2vw,28px);margin-top:30px;padding-top:19px;
  border-top:1px solid var(--line);flex-wrap:wrap}
.hero__stat div{display:flex;flex-direction:column;gap:3px}
/* <b> and <small> were the only way to mark these up by hand, which forced
   the whole stat row into a Custom HTML block. Class-based equivalents let
   the row be ordinary paragraphs the editor can reach; the element selectors
   stay so the hand-written rows still render. */
.hero__stat b,.hero__stat .stat__n{margin:0;
  font:600 clamp(17px,1.5vw,21px)/1 var(--sans);color:var(--ink);white-space:nowrap}
.hero__stat small,.hero__stat .stat__l{margin:0;
  font:500 8.5px/1.35 var(--mono);letter-spacing:.14em;color:var(--dim);text-transform:uppercase}
/* the e-Sens lockup above the page title */
.phero__logo{margin:0 0 18px}
.phero__logo img{display:block;width:132px;height:auto}
/* The nav collapses at 1080, and below that the dashboard drops its perspective
   and stacks its tiles, going from 434px tall to about 840. Two columns still
   applied down to 1000, so the short copy column was being centred against that
   and floated 215px below the nav with a field of photograph above it. Stack at
   the same width the nav does. */
@media(max-width:1080px){.hero__grid{grid-template-columns:1fr;gap:44px}}
/* Just above the stack point the panel is squeezed to its minimum width and
   runs two tile columns, so it is still taller than the copy. Top-align there
   so the copy stays put instead of floating to the panel's midpoint. Above
   1240 the copy is the taller column and centring does nothing to it, so that
   range is left exactly as it is. */
@media(min-width:1081px) and (max-width:1240px){.hero__grid{align-items:start}}
/* HERO MEDIA
   One composition, committed to, rather than two half-hidden rectangles.
   The dashboard is the deep plane: big, in real perspective, bleeding off
   the right edge so it reads as a window into something larger. The live
   card is the near plane: small, sharp, counter-rotated, clearly in front.
   Both drift with the pointer at different rates, so the depth is real
   rather than drawn. */
/* Hero geometry, in one place.
   --fleet-x  where the fleet panel starts, as a share of the media column
   --card-w   width of the West Tower card
   --kiss     how far the card is allowed to overlap the panel. The Hub's nav
              pill starts 18px in from the panel edge, so this has to stay
              under that or the card clips it.
   The card's left is derived from those three, so the overlap is always
   exactly --kiss. It used to be a hand-tuned percentage, which meant the card
   sat 136px into a 160px sidebar and cut the Hub's nav in half. */
.hero__media{position:relative;min-width:0;perspective:1700px;transform-style:preserve-3d;
  --fleet-x:33%; --card-w:268px; --kiss:6px;}

/* hero deep plane: the fleet panel in perspective, bleeding off the right */
.hero__media .hero__fleet{
  --tx:0; --ty:0;
  /* A hard 980px ran the panel 300px off-screen on a wide monitor, so the
     Ask Amphy corner was never reachable. heroFit() in motion.js sizes this
     to land a fixed gap short of the window edge: it cannot be done in CSS
     alone because the perspective magnifies the right edge by an amount that
     depends on the width you are trying to solve for. This value is the
     no-JS fallback and the first paint before heroFit measures. */
  width:min(760px,150%);
  min-width:0;
  transform:rotateY(-9deg) rotateX(3deg)
    translate3d(calc(var(--tx) * 8px), calc(var(--ty) * 6px), 0);
  transform-origin:left center;
  margin-left:var(--fleet-x);
  box-shadow:0 44px 92px rgba(0,0,0,.6);
  transition:transform .5s cubic-bezier(.22,.61,.36,1)}
/* The panel runs off the right edge on purpose, so its own bottom-right
   corner is off-screen. --edge is how much of it the window cuts off, measured
   in JS on load and resize; pulling the pill back by that much parks it on the
   visible corner instead of somewhere out in the dark. */
.hero__fleet .fleet__amphy{left:auto;right:calc(var(--edge, 0px) + 18px);bottom:16px}
/* Set by heroFit() when the panel had to fall back to bleeding: three tile
   columns in the remaining width would be unreadable, so run two and show
   eight tiles instead of eleven. */
.hero__fleet.is-tight .fleet__grid{grid-template-columns:repeat(2,minmax(0,1fr))}
.hero__fleet.is-tight .ft:nth-child(n+9){display:none}
/* it is the backdrop here, so it runs quieter than on the Hub page */
.hero__fleet .fleet__body{grid-template-columns:158px 1fr}
.hero__fleet .fleet__grid{gap:7px}
.hero__fleet .ft{padding:10px 11px}
.hero__fleet .ft__t b{font-size:14px}

/* Opaque in the hero. At 90% the panel let the photograph through and
   stopped reading as a solid window: the wave ran across the tiles and the
   whole composition went soft. */
.hero__fleet{background:linear-gradient(170deg,#0b2739,#051520)}
.hero__fleet .fleet__side{background:rgba(3,16,26,.55)}

.hero__media .hero__card{position:absolute;bottom:-26px;z-index:3;
  width:var(--card-w);
  left:calc(var(--fleet-x) - var(--card-w) + var(--kiss));
  --tx:0; --ty:0;
  transform:rotateY(4deg) rotateX(-1deg)
    translate3d(calc(var(--tx) * -16px), calc(var(--ty) * -11px), 40px);
  /* Near-opaque on purpose. At 31% alpha the fleet panel behind it read
     straight through the glass: its sidebar border became a hard line across
     the sensor grid and "Water Treater / Admin" ghosted over the readings.
     Glass works over an empty background, not over a second dashboard. */
  background:linear-gradient(165deg,rgba(18,54,77,.975),rgba(7,26,39,.975));
  backdrop-filter:blur(22px) saturate(140%);
  box-shadow:0 40px 90px rgba(0,0,0,.62),inset 0 1px 0 rgba(160,205,225,.1);
  transition:transform .5s cubic-bezier(.22,.61,.36,1)}
.hero__card .hubui__top{padding-bottom:12px}
.hero__card .chart{height:72px;margin-top:12px}
/* ---------- the hero e-Sens card ----------
   It is a .ro tile grid like every other dashboard mock here. An earlier pass
   used rows with filled status chips and it read as an alert list: four
   coloured blocks stacked down one edge, no even shapes for the eye to settle
   on. The status is the tile's own caption now, tinted rather than boxed. */
.hubui__flask{width:19px;height:19px;flex:none;fill:none;stroke:var(--orange-lt);
  stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.hubui__grid--quad{grid-template-columns:1fr 1fr;gap:8px}
/* the device this instrument is watching, under its own name. Slightly
   brighter than the caption below it: one is identity, the other is a label,
   and two dim mono lines running together read as one muddy block. */
.hubui__sub{margin:8px 0 0;font:500 8.5px/1.5 var(--mono);letter-spacing:.16em;
  color:var(--muted)}
/* The rule sits under the identity block, not under the title, so the name
   and the device it watches read as one unit before the readings start. */
.hero__card .hubui__top{padding-bottom:0;border-bottom:0}
.hero__card .hubui__sub{padding-bottom:13px;border-bottom:1px solid var(--line)}
/* Title, flask and pill on one 228px line is tight. The title truncates
   rather than shoving the pill off the card, which is what a longer device
   name would otherwise do. */
.hero__card .hubui__dev{gap:8px}
.hero__card .hubui__dev b{font-size:14.5px;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.hero__card .hubui__pill{font-size:8px;padding:4px 7px;letter-spacing:.12em}
/* two per row at every width; the card is never wide enough for more and a
   single column turns four readings into a scroll */
.hubui__grid--quad{grid-template-columns:1fr 1fr !important}
/* Taller on purpose. At 268 square the card read as a widget; a portrait
   rectangle reads as a panel, and the tiles can breathe at the same time. */
.hero__card .hubui__cap{margin:16px 0 11px}
.hero__card .ro{padding:14px 14px 15px}
.hero__card .ro small{margin-bottom:10px}
.hero__card .ro b{font-size:22px}
.hero__card .ro i{margin-top:9px}
/* amber only where a probe disagrees with the lab; everything else stays the
   ordinary caption grey, so two warm captions in a calm card actually read */
.hubui .ro i.is-off{color:#dda758}
.hubui__foot{margin:13px 0 0;font:500 8px/1.5 var(--mono);letter-spacing:.16em;
  color:var(--dim)}

.hubui__grid--three{grid-template-columns:1fr 1fr;gap:7px}
.hubui__grid--three .ro:first-child{grid-column:1 / -1}

/* The copy column keeps its width as the window narrows, so the gutter the
   card lives in shrinks. Give back width from the card, not from the gap. */
@media(max-width:1340px){
  .hero__media{--fleet-x:35%; --card-w:238px}
}
@media(max-width:1200px){
  .hero__media{--fleet-x:37%; --card-w:224px}
  .hero__media .hero__card{bottom:-22px}
}
/* Below this the copy column and two overlapping panels cannot share a row:
   the card starts cutting into the lede. Stack instead of squeezing. */
@media(max-width:1150px){
  .hero__media{perspective:none}
  /* stacked, so it stops being a bleeding backdrop and becomes a plain panel:
     full width, no perspective, and the component's own responsive rules
     (which are less specific) get to apply again */
  .hero__media .hero__fleet{width:auto;transform:none;margin-right:0;margin-left:0}
  .hero__media .hero__card{position:static;left:auto;width:100%;margin-top:16px;transform:none}
}
@media(max-width:900px){
  /* On a wide screen the West Tower card is a foreground layer overlapping the
     fleet panel: one composition, read as a single object beside the copy.
     Stacked on a phone it becomes a second full-width mockup directly under
     the first, and two big interface screenshots back to back is clutter. It
     also says the same thing as the alarm card one screen further down in the
     descent, so nothing is lost by dropping it here. */
  .hero__media .hero__card{display:none}
  .hero__fleet .fleet__body{grid-template-columns:1fr}
  .hero__fleet .fleet__grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .hero__fleet .fleet__amphy{left:auto;right:14px}
}
@media(max-width:560px){
  /* eleven stacked tiles is a long scroll before the reader reaches anything
     else, so on a phone the hero shows the first six in two columns */
  .hero__fleet .fleet__grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}
  .hero__fleet .ft{padding:9px 9px;gap:8px}
  .hero__fleet .ft__i{width:26px;height:26px}
  .hero__fleet .ft__t b{font-size:14px}
  .hero__fleet .ft:nth-child(n+7){display:none}
}
@media (prefers-reduced-motion:reduce){
  .hero__media .hero__fleet,.hero__media .hero__card{transition:none}
}

.scrollcue{position:absolute;left:var(--pad);bottom:22px;z-index:3;display:flex;align-items:center;
  gap:11px;font:500 9.5px/1 var(--mono);letter-spacing:.24em;color:var(--dim)}
.scrollcue i{display:block;width:1px;height:34px;
  background:linear-gradient(180deg,var(--orange),transparent);animation:cue 2.1s var(--ease) infinite}
@keyframes cue{0%{transform:scaleY(0);transform-origin:top}45%{transform:scaleY(1);transform-origin:top}
  55%{transform:scaleY(1);transform-origin:bottom}100%{transform:scaleY(0);transform-origin:bottom}}

/* ---------------------------------------------------------------
   THE TETHER  ·  the hero card, drawn down onto the analyzer.

   The hero card is an e-Sens lab result and the very next section is the
   instrument that produced it, so a line is drawn between the two objects
   themselves rather than a paragraph claiming they are related. It draws as
   you scroll, with a lit head at the tip, and lands in a target ring on the
   analyzer. Nothing separates the two beats now: the water surface moved
   below the e-Sens section, so the hero and the analyzer are one stretch of
   page above the waterline and the descent starts after them.

   Geometry is set in tether() in motion.js. It has to reach an element in a
   different section, at a distance that depends on the viewport, so there is
   nothing to hard-code here: the stylesheet owns look, JS owns coordinates.
   --------------------------------------------------------------- */
.tether{position:fixed;left:0;top:0;width:100vw;height:100vh;z-index:6;
  pointer-events:none;overflow:visible;display:none}
.tether.is-ready{display:block}
.tether path{fill:none;stroke-linecap:round}
/* Bright enough to survive crossing the analyzer, which is a white object
   under a spotlight and swallowed a 45% line whole. */
.tether__p{stroke:rgba(255,120,60,.8);stroke-width:1.3}
/* The lit head. A 34px dash parked at the drawing tip, so the line does not
   just lengthen, it is being drawn by something. */
.tether__glow{stroke:var(--orange-lt);stroke-width:2.4;opacity:0;
  filter:drop-shadow(0 0 7px rgba(255,138,86,.8));transition:opacity .28s linear}
.tether.is-live .tether__glow{opacity:1}

.tether__end{opacity:0;transition:opacity .4s ease}
.tether.is-landed .tether__end{opacity:1}
.tether__ring{fill:none;stroke:var(--orange);stroke-width:1.3}
.tether__dot{fill:var(--orange-lt);stroke:none}
.tether__pulse{fill:none;stroke:var(--orange-lt);stroke-width:1.2;opacity:0;
  transform-box:fill-box;transform-origin:center}
.tether.is-landed .tether__pulse{animation:tetherpulse 2.6s cubic-bezier(.22,.61,.36,1) infinite}
@keyframes tetherpulse{
  0%{transform:scale(.5);opacity:.8}
  72%{transform:scale(2.05);opacity:0}
  100%{transform:scale(2.05);opacity:0}}

/* Below this the hero composition stacks and the two objects are no longer
   in the same relationship, so there is nothing to draw between. tether()
   pulls the element out of the layout at this width. */
@media(prefers-reduced-motion:reduce){
  .tether__glow,.tether__pulse{display:none}
  .tether__end{opacity:1}
}

/* page hero for inner pages */
.phero{position:relative;padding:clamp(56px,8vw,104px) var(--pad) clamp(36px,5vw,60px)}
.phero__in{max-width:var(--max);margin:0 auto}
.phero h1{max-width:900px;font-size:clamp(27px,3.3vw,44px);
  letter-spacing:-.03em;line-height:1.09}
.phero .lede{margin-top:20px;max-width:760px}
.crumbs{display:flex;gap:9px;font:500 10px/1 var(--mono);letter-spacing:.16em;color:var(--dim);
  margin-bottom:20px;flex-wrap:wrap}
.crumbs a:hover{color:var(--blue-lt)}
.crumbs span{color:rgba(160,205,225,.3)}

/* ---------- HUD + telemetry card ---------- */
.hud{position:relative}
.hud::before,.hud::after{content:'';position:absolute;width:16px;height:16px;pointer-events:none;
  border-color:var(--orange);opacity:.85}
.hud::before{left:0;top:0;border-left:1.5px solid;border-top:1.5px solid}
.hud::after{right:0;bottom:0;border-right:1.5px solid;border-bottom:1.5px solid}
.card{position:relative;border:1px solid var(--line);border-radius:8px;padding:24px;
  background:linear-gradient(165deg,rgba(11,39,57,.92),rgba(7,28,41,.96));
  box-shadow:0 30px 80px rgba(0,0,0,.5),inset 0 1px 0 rgba(160,205,225,.07)}
.card__top{display:flex;justify-content:space-between;align-items:center;gap:14px;
  padding-bottom:15px;border-bottom:1px solid var(--line)}
.card__id{font:500 10.5px/1 var(--mono);letter-spacing:.15em;color:var(--muted)}
.live{display:flex;align-items:center;gap:7px;font:600 10px/1 var(--mono);letter-spacing:.15em;color:#4ade9a}
.live i{width:6px;height:6px;border-radius:50%;background:#4ade9a;animation:pulse 2s infinite;
  box-shadow:0 0 0 0 rgba(74,222,154,.6)}
@keyframes pulse{70%{box-shadow:0 0 0 8px rgba(74,222,154,0)}100%{box-shadow:0 0 0 0 rgba(74,222,154,0)}}
.readouts{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;margin-top:16px}
.ro{background:rgba(3,16,26,.62);border:1px solid rgba(160,205,225,.08);border-radius:5px;padding:12px 13px}
.ro small{display:block;font:500 8.5px/1 var(--mono);letter-spacing:.16em;color:var(--dim);margin-bottom:8px}
.ro b{font:600 clamp(17px,2vw,23px)/1 var(--sans);letter-spacing:-.01em;
  font-variant-numeric:tabular-nums;transition:color .3s}
.ro b u{text-decoration:none;font:400 10px/1 var(--mono);color:var(--dim);margin-left:3px}
.ro b span{display:inline-block}
.ro.tick b{color:var(--blue-lt)}
.ro.tick b span{animation:flip .38s var(--ease-out)}
@keyframes flip{0%{transform:translateY(-45%);opacity:.35;filter:blur(2px)}100%{transform:none;opacity:1;filter:blur(0)}}
.ro.alarm{border-color:rgba(232,73,43,.5);background:rgba(232,73,43,.09)}
.ro.alarm b{color:var(--orange-lt)}
.chart{margin-top:15px;position:relative;height:88px}
.chart svg{width:100%;height:100%;overflow:visible}
.chart .thresh{stroke:var(--orange);stroke-width:1;stroke-dasharray:3 4;opacity:.55}
.chart .trace{fill:none;stroke:var(--blue-lt);stroke-width:1.8;stroke-linejoin:round;
  stroke-linecap:round;filter:drop-shadow(0 0 6px rgba(126,196,238,.45))}
.chart .fillarea{fill:url(#acgrad);opacity:.9}
.chart .lab{font:500 8px var(--mono);fill:var(--dim);letter-spacing:.12em}
.drafted{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:15px;
  background:rgba(232,73,43,.09);border:1px solid rgba(232,73,43,.28);border-radius:5px;padding:12px 14px}
.drafted p{font:400 12.5px/1.45 var(--sans);color:#f6dcd4;min-height:19px}
.drafted p::after{content:'▌';color:var(--orange);animation:caret 1s steps(1) infinite}
.drafted.done p::after{content:''}
@keyframes caret{50%{opacity:0}}
.drafted b{font:600 10px/1 var(--mono);letter-spacing:.12em;color:var(--orange-lt);flex:none}

/* alarm toast */
.toast{position:fixed;right:20px;bottom:20px;z-index:80;display:flex;align-items:center;gap:11px;
  background:rgba(8,26,37,.95);backdrop-filter:blur(14px);border:1px solid rgba(232,73,43,.45);
  border-left:2px solid var(--orange);border-radius:6px;padding:13px 17px;max-width:300px;
  box-shadow:0 20px 50px rgba(0,0,0,.55);
  transform:translateX(calc(100% + 30px));transition:transform .6s var(--ease-out)}
.toast.on{transform:none}
.toast i{width:7px;height:7px;border-radius:50%;background:var(--orange);flex:none;
  box-shadow:0 0 10px var(--orange);animation:blip 1.1s infinite}
.toast b{display:block;font:600 9.5px/1 var(--mono);letter-spacing:.16em;color:var(--orange-lt);margin-bottom:5px}
.toast p{font:400 12.5px/1.45 var(--sans);color:var(--ink)}
@media(max-width:860px){.toast{left:16px;right:16px;max-width:none;
  bottom:calc(74px + env(safe-area-inset-bottom,0px))}}

/* ---------- wave divider ----------
   Lisa's motif, and the right one for this company. Kept as a shallow swell
   rather than the scalloped edge on the mock: at this scale a deep scallop
   reads as decoration, a shallow one reads as a waterline. */
.wave{display:block;width:100%;height:clamp(34px,4vw,58px);position:relative;z-index:2;
  margin-bottom:-1px}
.wave path{fill:var(--band,rgba(20,58,82,.72))}
.wave--flip{transform:rotate(180deg);margin:-1px 0 0}

/* Sections butt against each other. Each one carries its own padding, so
   WordPress's 24px margin between top-level blocks does nothing but expose a
   stripe of page background at every seam. Five stacked bands between the hero
   and the descent was that, three times over. Blog prose still needs its flow
   margins, so this names the section blocks rather than every child. */
.hero,.phero,.dsc,.sec,.whofor,.esn,.wlb,.ph{
  margin-block-start:0 !important;margin-block-end:0 !important}
.wave{margin-block-start:0 !important;margin-block-end:-1px !important}
.wave--flip{margin-block-start:-1px !important;margin-block-end:0 !important}

/* The wave only works if it is painted the colour of whatever it introduces,
   so it reads as that section's edge rising rather than a band of its own.
   Above the descent it is the water surface. */
.wave--surface path{fill:#0e3350}

/* ---------- what AquaCore is, and who it is for ----------
   Lisa put the definition and the audience in two centred bands with a pair
   of calls to action. She is right that a cold visitor needs a plain answer
   before the product detail, and right that this is the natural place to ask
   for the meeting. Folded into one band, in our palette. */
.whofor{--band:#11334b;background:var(--band);position:relative;z-index:2;
  padding:clamp(38px,4.4vw,62px) var(--pad)}
.whofor__in{max-width:820px;margin-inline:auto;text-align:center}
.whofor__in .eyebrow{justify-content:center}
.whofor__in .h2{font-size:clamp(23px,2.5vw,34px);margin-inline:auto;max-width:22ch}
.whofor__in .lede{margin-top:18px;font-size:15.5px}
.whofor__cta{display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-top:30px}

/* ---------- slogan band ---------- */
.band{position:relative;z-index:2;display:grid;grid-template-columns:repeat(4,1fr);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);background:rgba(11,39,57,.35)}
.band a{padding:clamp(20px,2.6vw,30px) 10px;text-align:center;
  font:600 clamp(12px,1.5vw,18px)/1 var(--mono);letter-spacing:.3em;color:var(--ink);
  border-right:1px solid var(--line);position:relative;overflow:hidden;
  transition:background .4s var(--ease),letter-spacing .5s var(--ease)}
.band a:last-child{border-right:0;color:var(--orange)}
.band a:hover{background:rgba(61,155,215,.09);letter-spacing:.38em}
.band a::after{content:'';position:absolute;left:0;bottom:0;height:2px;width:100%;background:var(--orange);
  transform:scaleX(0);transform-origin:left;transition:transform .5s var(--ease-out)}
.band a:hover::after{transform:scaleX(1)}
@media(max-width:700px){.band{grid-template-columns:repeat(2,1fr)}
  .band a:nth-child(2){border-right:0}.band a:nth-child(-n+2){border-bottom:1px solid var(--line)}}

/* ---------- pinned story ---------- */
.pin{display:grid;grid-template-columns:.95fr 1.05fr;gap:clamp(30px,5vw,74px);
  max-width:var(--max);margin:0 auto;align-items:start}
.pin__stick{position:sticky;top:104px}
.pin__steps{display:flex;flex-direction:column;gap:clamp(38px,8vh,72px);padding:2vh 0}
.step{border-left:2px solid var(--line);padding-left:26px;transition:border-color .5s var(--ease)}
.step.on{border-left-color:var(--orange)}
.step b{display:block;font:500 10px/1 var(--mono);letter-spacing:.24em;color:var(--dim);
  margin-bottom:12px;transition:color .5s}
.step.on b{color:var(--orange-lt)}
.step h3{font-size:clamp(20px,2.4vw,28px);margin-bottom:11px}
@media(max-width:940px){.pin{grid-template-columns:1fr;gap:34px}
  .pin__stick{position:relative;top:0}.pin__steps{padding:0;gap:36px}}

/* ---------- grids, tiles, rows ---------- */
/* WordPress gives every child of a flow group a 24px top margin and exempts
   the first one. Inside a grid or flex row that margin is not spacing, it is a
   shove: siblings two onward sit 24px lower in their cells while the stretched
   first cell keeps its full height, so tile one reads taller than the rest of
   the row. Every container below spaces itself with gap, so the margins are
   pure damage. Zero them. */
.g2>*,.g3>*,.g4>*,.shotgrid>*,.faq>*,.split>*,.numrow>*,.hero__stat>*,
.esn__flow>*,.wlb__marks>*,.prowset>*,.pin__steps>*,.foot__grid>*{
  margin-block-start:0 !important;margin-block-end:0 !important}

.g2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
@media(max-width:760px){.g2{grid-template-columns:1fr}}
/* the qualifier under a spec tile: the part or limit, in the vendor's terms */
.tile__note{margin-top:11px;padding-top:10px;border-top:1px solid var(--line);
  font:500 10px/1.45 var(--mono);letter-spacing:.08em;color:var(--dim)}
.g3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
@media(max-width:900px){.g3{grid-template-columns:1fr}}
/* Benefit pillars. The claim is the heading, the proof is the list: three
   short items a reader can scan without committing to a paragraph. */
.ticks{list-style:none;margin:16px 0 0;padding:0;display:flex;flex-direction:column;gap:8px}
.ticks li{position:relative;padding-left:20px;font-size:14.5px;line-height:1.45;color:var(--muted)}
.ticks li::before{content:'';position:absolute;left:0;top:.52em;width:9px;height:9px;
  border-radius:50%;border:1.5px solid var(--blue);background:rgba(61,155,215,.18)}
.g4{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}
@media(max-width:1000px){.g4{grid-template-columns:repeat(2,1fr)}}
@media(max-width:780px){.g3,.g2{grid-template-columns:1fr}}
@media(max-width:560px){.g4{grid-template-columns:1fr}}

.tile,.numrow{--mx:50%;--my:0%}
.tile{position:relative;background:rgba(11,39,57,.42);border:1px solid var(--line);border-radius:7px;
  padding:26px 22px;overflow:hidden;
  transition:transform .5s var(--ease-out),border-color .4s,background .4s}
.tile::before{content:'';position:absolute;left:0;top:0;height:2px;width:100%;background:var(--orange);
  transform:scaleX(0);transform-origin:left;transition:transform .55s var(--ease-out)}
.tile::after,.numrow::after{content:'';position:absolute;inset:0;pointer-events:none;opacity:0;
  transition:opacity .45s var(--ease);
  background:radial-gradient(320px circle at var(--mx) var(--my),rgba(126,196,238,.13),transparent 68%)}
.tile:hover{transform:translateY(-5px);border-color:rgba(126,196,238,.3);background:rgba(11,39,57,.72)}
.tile:hover::before{transform:scaleX(1)}
.tile:hover::after,.numrow:hover::after{opacity:1}
.tile__n{display:block;font:600 10.5px/1.4 var(--mono);letter-spacing:.18em;color:var(--orange);
  margin-bottom:14px;text-transform:uppercase}
.tile h3{font-size:17.5px;margin-bottom:9px;font-weight:600}
.tile p{font-size:13.5px;line-height:1.62;color:var(--muted)}
/* WordPress zeroes block margins inside these, so two paragraphs in one tile
   ran together as a single wall. */
.tile p + p{margin-top:11px}

.tile--link:hover h3{color:var(--blue-lt)}

.numrow{position:relative;overflow:hidden;display:flex;gap:16px;align-items:flex-start;
  background:rgba(3,16,26,.5);border:1px solid var(--line);border-radius:6px;padding:19px 21px;
  transition:border-color .4s,transform .4s var(--ease-out)}
.numrow:hover{border-color:rgba(232,73,43,.34);transform:translateX(5px)}
.numrow>b{font:600 13px/1 var(--mono);color:var(--orange);padding-top:3px;flex:none}
.numrow h4{font-size:15.5px;font-weight:600;margin-bottom:5px}
.numrow p{font-size:13.5px;line-height:1.6;color:var(--muted)}

/* minmax(0,1fr), not 1fr. A bare 1fr floors at min-content, so one wide
   child (a full-size product photo, a spec table) pushes its column past half
   the row and the page scrolls sideways. Same trap that put a scrollbar on
   .hero__stat. */
.split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:clamp(28px,4.5vw,62px);align-items:center}
.split--top{align-items:flex-start}
/* A two-up grid inside one half of a split has about 290px per cell, which is
   narrower than the tiles were drawn for: the headings wrap to three lines and
   the row ends up as tall as it is wide. Stack it. */
.split .g2,.split .g3{grid-template-columns:minmax(0,1fr)}
@media(max-width:900px){.split{grid-template-columns:minmax(0,1fr);gap:34px}}

.checks{list-style:none;display:flex;flex-direction:column;gap:13px}
.checks li{position:relative;padding-left:26px;font-size:14.5px;line-height:1.62;color:var(--muted)}
.checks li::before{content:'';position:absolute;left:0;top:8px;width:9px;height:9px;border-radius:2px;
  background:var(--orange);opacity:.85}
.checks li b{color:var(--ink);font-weight:600}

.note-box{border-left:3px solid var(--orange);padding:4px 0 4px 18px;font-size:14.5px;line-height:1.62;
  color:var(--muted);max-width:700px}
.bracket{display:inline-block;font:500 11.5px/1.5 var(--mono);letter-spacing:.06em;color:var(--dim);
  background:rgba(232,73,43,.08);border:1px dashed rgba(232,73,43,.35);border-radius:4px;padding:9px 13px}

/* ---------- FAQ ----------
   Deliberately not an accordion. Answers stay visible so both readers and AI
   answer engines get self-contained Q&A pairs, and every part stays editable
   with core blocks. */
.faq{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;max-width:1240px}
@media(max-width:820px){.faq{grid-template-columns:1fr}}
.faqitem{position:relative;background:rgba(3,16,26,.5);border:1px solid var(--line);
  border-radius:6px;padding:22px 24px;transition:border-color .4s,background .4s}
.faqitem:hover{border-color:rgba(232,73,43,.3);background:rgba(11,39,57,.55)}
.faqitem h3{font-size:15.5px;font-weight:600;line-height:1.45;margin-bottom:10px;padding-left:22px;
  position:relative}
.faqitem h3::before{content:'Q';position:absolute;left:0;top:1px;
  font:600 11px/1.5 var(--mono);color:var(--orange)}
.faqitem p{font-size:13.5px;line-height:1.68;color:var(--muted);padding-left:22px}

/* ---------- CTA ---------- */
.cta{position:relative;overflow:hidden;border-radius:10px;padding:clamp(34px,5.5vw,62px);
  background:linear-gradient(125deg,var(--navy) 0%,#0a2438 62%);
  border:1px solid rgba(126,196,238,.16);display:flex;justify-content:space-between;
  align-items:center;gap:32px;flex-wrap:wrap}
.cta::after{content:'';position:absolute;width:520px;height:520px;right:-140px;top:-220px;
  border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,rgba(232,73,43,.30),transparent 66%)}
.cta h2{font-size:clamp(23px,3.1vw,34px);margin-bottom:11px;position:relative}
.cta p{position:relative;max-width:560px}
.cta .btn{position:relative}

/* ---------- form ---------- */
.form{display:grid;grid-template-columns:1fr 1fr;gap:16px;max-width:760px}
.form label{display:flex;flex-direction:column;gap:7px;font:500 10.5px/1 var(--mono);
  letter-spacing:.16em;color:var(--dim);text-transform:uppercase}
.form .full{grid-column:1/-1}
/* Honeypot. Off-screen rather than display:none, because some bots skip hidden
   inputs but happily fill a positioned one. aria-hidden and tabindex keep it
   away from anyone using a keyboard or a screen reader. */
.hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}

/* Result of a submission, above the form. */
.formnote{margin:0 0 22px;padding:14px 18px;border-radius:6px;
  font-size:14.5px;line-height:1.55;border:1px solid}
.formnote--ok{background:rgba(61,155,215,.10);border-color:rgba(126,196,238,.34);color:var(--ink)}
.formnote--bad{background:rgba(232,73,43,.10);border-color:rgba(232,73,43,.42);color:var(--ink)}
.formnote b{color:var(--ink)}

.form input,.form select,.form textarea{background:rgba(3,16,26,.6);border:1px solid var(--line);
  border-radius:4px;padding:13px 14px;color:var(--ink);font:300 15px/1.4 var(--sans);width:100%;
  transition:border-color .3s,background .3s}
/* The global :focus-visible ring was being overridden away here, on the
   demo form specifically. A border-colour change alone is a weak affordance
   and fails 2.4.11 focus appearance. Keep the ring, keep the border tint. */
.form input:focus,.form select:focus,.form textarea:focus{border-color:var(--blue);
  background:rgba(11,39,57,.7);outline:2px solid var(--orange-lt);outline-offset:2px}
.form textarea{min-height:120px;resize:vertical}
.form option{background:var(--deep)}
.form__warn{grid-column:1/-1;display:none;margin:0;padding:13px 16px;border-radius:6px;
  border:1px solid rgba(232,73,43,.45);background:rgba(232,73,43,.1);
  font-size:13.5px;line-height:1.55;color:var(--ink)}
.form__warn.on{display:block}
@media(max-width:700px){.form{grid-template-columns:1fr}}

/* ============================================================
   PAGE TRANSITIONS
   Same-origin navigation is a descent, not a reload. The outgoing
   page rises out of frame, the incoming one surfaces from below
   behind a rising waterline. The nav and announce bar are named, so
   they hold still while the water moves around them.
   No blur anywhere in here: it reads as eye strain, not depth.
   ============================================================ */
/* The waterline. A wave front sweeps down the viewport on every internal
   click, the page below it goes cool and deep, bubbles rise past. This is
   the one moment on the site that is unambiguously water, which is why it
   is spent on navigation rather than sprinkled everywhere. */
.dive{position:fixed;inset:0;z-index:9997;pointer-events:none;opacity:0;
  transform:translateY(-102%);will-change:transform}
.dive.on{opacity:1}
.dive.in{animation:acdivein .42s cubic-bezier(.34,.02,.2,1) forwards}
.dive.out{animation:acdiveout .62s cubic-bezier(.3,.5,.2,1) forwards}
/* The crest. It used to be #0b2739 on a #03101a page: dark navy sliding over
   dark navy, so the whole transition ran with nothing visible on screen and
   every click spent 370ms looking frozen. The waterline is now lit, and it is
   the only bright thing in the effect, so what you see is a surface arriving
   rather than a panel flashing. */
/* Original colours, restored. The one thing kept from the brighter pass is
   top:100% instead of bottom:100%: the overlay travels downward in both
   directions, so the edge you watch is its lower one, and the crest used to
   be pinned to the upper edge where it was off screen for the whole
   animation. Same palette as before, wave now actually in frame. */
.dive__wave{position:absolute;left:0;top:100%;width:100%;height:14vh;
  fill:#0b2739;filter:drop-shadow(0 6px 26px rgba(126,196,238,.28))}
.dive__body{position:absolute;inset:0;
  background:linear-gradient(180deg,#0b2739 0%,#071c29 42%,#03101a 100%)}
.dive__caust{position:absolute;inset:0;overflow:hidden;opacity:.5;mix-blend-mode:screen}
.dive__caust i{position:absolute;inset:-40% -20%;
  background:radial-gradient(48% 34% at 30% 28%,rgba(126,196,238,.4),transparent 62%),
    radial-gradient(40% 30% at 72% 62%,rgba(126,196,238,.3),transparent 60%);
  animation:accaust 2.6s linear infinite}
.dive__caust i:nth-child(2){animation-duration:3.6s;animation-direction:reverse;opacity:.6}
.dive__bub{position:absolute;inset:0;overflow:hidden}
.dive__bub i{position:absolute;bottom:-8%;width:7px;height:7px;border-radius:50%;
  border:1px solid rgba(160,205,225,.5);background:rgba(160,205,225,.1);
  animation:acbub 1s linear forwards}
.dive__bub i:nth-child(1){left:12%;animation-delay:.04s;transform:scale(.7)}
.dive__bub i:nth-child(2){left:29%;animation-delay:.16s;transform:scale(1.25)}
.dive__bub i:nth-child(3){left:46%;animation-delay:.02s}
.dive__bub i:nth-child(4){left:63%;animation-delay:.22s;transform:scale(.8)}
.dive__bub i:nth-child(5){left:79%;animation-delay:.1s;transform:scale(1.1)}
.dive__bub i:nth-child(6){left:91%;animation-delay:.28s;transform:scale(.65)}
@keyframes acdivein{from{transform:translateY(-102%)}to{transform:translateY(0)}}
@keyframes acdiveout{from{transform:translateY(0)}to{transform:translateY(102%)}}
@keyframes accaust{
  0%{transform:translate(0,0) scale(1)}
  50%{transform:translate(4%,-3%) scale(1.12)}
  100%{transform:translate(0,0) scale(1)}
}
@keyframes acbub{to{transform:translateY(-108vh) scale(.5);opacity:0}}
/* The page underneath gives way. Previously only the overlay moved, which is
   why even a visible version would have read as a panel dropped on top of a
   photograph rather than as one view handing over to the next. The nav and
   the announcement bar deliberately hold still: they are the frame, and a
   frame that moves with the content makes the whole window feel loose. */
.is-diving main,.is-diving .foot{
  transform:translateY(-16px);opacity:.55;
  transition:transform .42s cubic-bezier(.34,.02,.2,1),opacity .34s linear}

@media (prefers-reduced-motion: reduce){
  .dive{display:none}
  .is-diving main,.is-diving .foot{transform:none;opacity:1;transition:none}
}

/* ============================================================
   THE DESCENT
   A pinned stage. Scroll drives one continuous fall from the tower
   down to the signed report. Beats cross-fade through the same
   physical space rather than scrolling past each other, which is
   what makes it read as depth instead of as a page.
   ============================================================ */
/* --travel was 78vh, set when this ran six beats. At three it just meant a
   long empty push between them: the beat is already fully read well before
   the next one starts arriving. */
.dsc{position:relative;--beats:3;--travel:44vh;
  /* one travel per beat, because each beat is now centred in its own slot */
  height:calc((var(--beats) - 1) * var(--travel) + 100vh)}
.dsc__sticky{position:sticky;top:0;height:100vh;overflow:hidden;isolation:isolate}

/* the water column itself: surface light at the top of the section,
   abyss by the bottom. --p is written by motion.js. */
.dsc__bg{position:absolute;inset:0;z-index:0;
  background:linear-gradient(180deg,#0e3350 0%,#0b2739 45%,#071c29 100%)}
.dsc__bg::after{content:'';position:absolute;inset:0;opacity:var(--p,0);
  background:linear-gradient(180deg,#061a27 0%,#03101a 60%,#01080e 100%)}
.dsc__bg::before{content:'';position:absolute;inset:0;opacity:calc(.5 - var(--p,0) * .5);
  background:
    radial-gradient(120% 70% at 50% -20%,rgba(126,196,238,.22),transparent 60%),
    repeating-linear-gradient(96deg,rgba(126,196,238,.05) 0 2px,transparent 2px 46px)}

/* rising particulate. Cheap, and it sells the medium. */
.dsc__bub{position:absolute;inset:0;z-index:1;pointer-events:none;
  opacity:calc(.25 + var(--p,0) * .5)}
.dsc__bub i{position:absolute;bottom:-14px;width:4px;height:4px;border-radius:50%;
  background:rgba(160,205,225,.4);animation:dscrise linear infinite}
.dsc__bub i:nth-child(1){left:12%;animation-duration:13s;animation-delay:-2s;transform:scale(.7)}
.dsc__bub i:nth-child(2){left:27%;animation-duration:17s;animation-delay:-9s}
.dsc__bub i:nth-child(3){left:44%;animation-duration:11s;animation-delay:-5s;transform:scale(.5)}
.dsc__bub i:nth-child(4){left:63%;animation-duration:19s;animation-delay:-13s;transform:scale(.8)}
.dsc__bub i:nth-child(5){left:78%;animation-duration:15s;animation-delay:-1s;transform:scale(.6)}
.dsc__bub i:nth-child(6){left:91%;animation-duration:21s;animation-delay:-7s;transform:scale(.45)}
@keyframes dscrise{
  0%{transform:translateY(0) translateX(0);opacity:0}
  12%{opacity:.55}
  88%{opacity:.35}
  100%{transform:translateY(-102vh) translateX(16px);opacity:0}
}

/* depth HUD */
.dsc__hud{position:absolute;z-index:4;left:var(--pad);top:50%;transform:translateY(-50%);
  display:flex;flex-direction:column;align-items:center;gap:12px;
  font:500 10px/1 var(--mono);letter-spacing:.18em;color:var(--dim)}
.dsc__depth{color:var(--blue-lt);font-variant-numeric:tabular-nums}
.dsc__depth b{font-weight:600;font-size:15px;color:var(--ink)}
.dsc__track{position:relative;width:2px;height:min(240px,26vh);border-radius:2px;
  background:rgba(126,196,238,.16);overflow:hidden}
.dsc__fill{position:absolute;inset:0;transform-origin:top;transform:scaleY(0);
  background:linear-gradient(180deg,var(--blue),var(--orange))}
.dsc__cap{writing-mode:vertical-rl;letter-spacing:.3em;font-size:8.5px;opacity:.7}

/* the beats share one centred frame and pass through it */
.dsc__beats{position:absolute;inset:0;z-index:3}
.dsc__beat{position:absolute;top:50%;left:50%;
  width:min(var(--max),calc(100% - var(--pad) * 2));
  display:grid;grid-template-columns:minmax(0,.95fr) minmax(0,1.05fr);
  align-items:center;gap:clamp(26px,4.5vw,68px);
  opacity:var(--o,0);pointer-events:none;
  transform:translate(-50%,-50%) translateY(calc(var(--u,0) * -70vh)) scale(var(--s,1));
  will-change:transform,opacity}
.dsc__beat.is-on{pointer-events:auto}

/* "01 · The gap" was 10.5px sitting under a 124px numeral, so the word doing
   the actual work was the smallest thing on the screen and readers went
   straight past it. Up to a size that can be read at a glance, with tighter
   tracking so it does not sprawl, and a longer rule to anchor it. */
.dsc__n{display:inline-flex;align-items:center;gap:12px;margin-bottom:18px;
  font:600 clamp(13px,1.15vw,16px)/1.3 var(--mono);letter-spacing:.14em;
  text-transform:uppercase;color:var(--orange-lt)}
.dsc__n::before{content:'';width:34px;height:2px;background:var(--orange);flex:none}
.dsc__copy h2{font-size:clamp(24px,3.1vw,40px);line-height:1.14;font-weight:600;
  color:var(--ink);max-width:15ch}
.dsc__copy p:not(.dsc__n){margin-top:16px;max-width:44ch;
  font-size:clamp(14px,1.25vw,16px);line-height:1.7;color:var(--muted);font-weight:300}

.dsc__media{display:flex;align-items:center;justify-content:center;min-width:0}
/* a beat with no object: the statement takes the whole frame, centred, so the
   first real thing the visitor ever sees is a photograph of your hardware */
.dsc__beat--solo{grid-template-columns:1fr;max-width:min(880px,88vw)}
.dsc__beat--solo .dsc__copy{text-align:left}
.dsc__beat--solo h2{font-size:clamp(28px,4vw,54px);max-width:17ch}
.dsc__beat--solo>.dsc__copy>p:last-child{max-width:52ch}
.dsc__media>img{max-height:52vh;width:auto;max-width:100%;
  filter:drop-shadow(0 30px 60px rgba(0,0,0,.6))}
.dsc__shot{position:relative;margin:0;border-radius:10px;overflow:hidden;
  border:1px solid var(--line);max-width:100%;
  box-shadow:0 34px 70px rgba(0,0,0,.55)}
.dsc__shot img{display:block;width:100%;max-height:52vh;object-fit:cover;
  filter:saturate(.88) contrast(1.04)}
.dsc__shot--ui img{object-fit:contain;background:rgba(3,16,26,.6)}
/* cut-out product renders float on the water: no frame, no crop */
.dsc__render{margin:0;display:flex;flex-direction:column;align-items:center;gap:14px}
.dsc__render img{max-height:54vh;width:auto;max-width:100%;
  filter:drop-shadow(0 30px 58px rgba(0,0,0,.62))}
.dsc__render figcaption{font:500 9px/1 var(--mono);letter-spacing:.18em;color:var(--dim)}
.dsc__shot figcaption{position:absolute;left:16px;bottom:12px;
  font:500 9px/1 var(--mono);letter-spacing:.18em;color:var(--ink);
  background:rgba(3,16,26,.66);padding:6px 9px;border-radius:3px;
  backdrop-filter:blur(4px)}
.dsc__media .card{width:min(430px,100%)}
/* the report is the payoff beat: let it run wider than the telemetry cards */
.dsc__media .rpt{width:min(600px,100%)}


/* the report writes itself as the last beat plays */
.rpt{width:min(470px,100%)}
.rpt__body{padding:4px 0 2px}
.rpt .rl{font-size:12.5px;line-height:1.62;color:var(--muted);
  padding:7px 0;border-bottom:1px solid rgba(126,196,238,.08);
  opacity:0;transform:translateY(7px);
  transition:opacity .34s ease,transform .34s cubic-bezier(.16,1,.3,1)}
.rpt .rl.on{opacity:1;transform:none}
.rpt .rl b{color:var(--ink);font-weight:600}
.rpt .rl:last-child{border-bottom:0}
.rpt .rl--sig{margin-top:6px;font:500 10px/1.5 var(--mono);letter-spacing:.13em;
  text-transform:uppercase;color:var(--dim)}
.rpt .rl--sig span{color:var(--orange)}

@media(max-width:900px){
  .dsc{--travel:70vh}
  .dsc__beat{grid-template-columns:1fr;gap:22px;
    transform:translate(-50%,-50%) translateY(calc(var(--u,0) * -58vh)) scale(var(--s,1))}
  /* Reading order matches desktop. On a wide screen the picture sits to the
     right of the copy, so it is read after it; hoisting it above the text on a
     phone reversed that, and buried the beat number and the 700 that the whole
     beat is built around. */
  .dsc__media{order:0}
  .dsc__media>img,.dsc__shot img{max-height:34vh}
  .dsc__copy h2{max-width:none;font-size:clamp(21px,5.4vw,28px)}
  .dsc__copy p:not(.dsc__n){max-width:none;font-size:14px;margin-top:12px}
  .dsc__media .card,.rpt{width:100%}
  .rpt .rl{font-size:11.5px;padding:5px 0}
  /* HUD goes horizontal, clear of the fixed nav */
  .dsc__hud{left:var(--pad);right:var(--pad);top:74px;transform:none;
    flex-direction:row;align-items:center;gap:10px}
  .dsc__track{width:auto;flex:1;height:2px}
  .dsc__fill{transform-origin:left;transform:scaleX(0)}
  .dsc__cap{writing-mode:horizontal-tb;letter-spacing:.2em}
  }

/* Pinning buys nothing on a phone. There is no hover, the beats stack anyway,
   and holding the viewport for 2,160px of scroll is exactly the "why has the
   page stopped responding" feeling, on the device least able to absorb it.
   Below 900px the three beats are three ordinary blocks you scroll past. */
@media (max-width:900px){
  .dsc{height:auto}
  .dsc__sticky{position:static;height:auto;overflow:visible}
  /* The water stays. The wave above this section is painted the colour of the
     descent's surface, so hiding the gradient left it introducing forty pixels
     of blue and then nothing: a strip, not a transition. .dsc is positioned,
     so inset:0 covers the whole stacked section once the sticky frame is
     released. The bubbles and the depth rail are the parts that needed the
     pinning, and those stay off. */
  .dsc__bub,.dsc__hud{display:none}
  .dsc__bg{position:absolute;inset:0}
  .dsc__bg::after{opacity:.55}
  .dsc__beats{position:static}
  .dsc__beat{position:static;width:auto;max-width:var(--max);
    margin:0 auto clamp(44px,7vw,72px);
    opacity:1 !important;transform:none !important;filter:none !important}
  .rpt .rl{opacity:1 !important;transform:none !important}
}

@media (prefers-reduced-motion: reduce){
  /* no pinning, no fall: the same beats as a plain vertical list */
  .dsc{height:auto}
  .dsc__sticky{position:static;height:auto;overflow:visible}
  .dsc__bg,.dsc__bub,.dsc__hud{display:none}
  .dsc__beats{position:static}
  .dsc__beat{position:static;width:auto;max-width:var(--max);
    margin:0 auto clamp(48px,7vw,86px);
    opacity:1 !important;transform:none !important;filter:none !important}
  .rpt .rl{opacity:1 !important;transform:none !important}
}

/* ============================================================
   THE LIGHT BEAT
   One full-bleed statement on a lighter ground, mid-page. The rest
   of the site is one continuous dark surface, so this is the only
   place the value changes. It doubles as the mid-page conversion
   point, which the page previously did not have for seven screens.
   ============================================================ */
.beat{position:relative;z-index:2;overflow:hidden;
  padding:clamp(50px,6.5vw,84px) var(--pad);
  background:linear-gradient(160deg,#12395f 0%,#0f2f56 46%,#0a2540 100%);
  border-top:1px solid rgba(126,196,238,.16);
  border-bottom:1px solid rgba(126,196,238,.16)}
.beat::before{content:'';position:absolute;inset:0;pointer-events:none;opacity:.5;
  background:
    radial-gradient(90% 120% at 88% -10%,rgba(126,196,238,.22),transparent 62%),
    repeating-linear-gradient(96deg,rgba(255,255,255,.035) 0 1px,transparent 1px 44px)}
.beat__in{position:relative;z-index:1;max-width:var(--max);margin:0 auto;
  display:flex;align-items:center;justify-content:space-between;
  gap:clamp(22px,4vw,60px);flex-wrap:wrap}
.beat h2{font-size:clamp(21px,2.7vw,34px);line-height:1.2;font-weight:600;
  color:var(--ink);max-width:20ch;margin:0}
.beat h2 em{font-style:normal;color:var(--blue-lt)}
.beat .btn{flex:none}
@media(max-width:700px){
  .beat__in{flex-direction:column;align-items:flex-start;gap:22px}
  .beat h2{max-width:none}
}

/* ---------- the four elements, as rows ----------
   The products page used to be four small cards that did not read as
   clickable. Each element now gets a full row: its real image, three lines,
   and a link target the size of the whole row. */
/* Full product rows. These are the /products page: a row per product, big
   product shot, the argument spelled out. */
.prow{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);
  align-items:center;gap:clamp(24px,4vw,64px);
  padding:clamp(30px,4vw,52px) 0;border-top:1px solid var(--line);
  position:relative}
.prow:last-of-type{border-bottom:1px solid var(--line)}
.prow:nth-child(even) .prow__media{order:-1}
@media(max-width:860px){
  .prow{grid-template-columns:1fr;gap:22px;padding:26px 0}
  .prow:nth-child(even) .prow__media{order:0}
}
.prow__n{font:600 10.5px/1 var(--mono);letter-spacing:.2em;color:var(--orange);
  display:block;margin-bottom:13px}
.prow h2{font-size:clamp(21px,2.4vw,30px);line-height:1.18;font-weight:600;margin:0}
.prow h2 a{color:var(--ink);text-decoration:none}
.prow:hover h2 a{color:var(--blue-lt)}
.prow p{margin-top:13px;font-size:15px;line-height:1.66;color:var(--muted);max-width:46ch}
.prow__go{display:inline-flex;align-items:center;gap:9px;margin-top:18px;
  font:600 12.5px/1 var(--mono);letter-spacing:.13em;text-transform:uppercase;
  color:var(--blue-lt)}
/* The arrow was an inline SVG, which forced the whole row to be raw HTML and
   put it out of the block editor's reach. Drawn in CSS the row becomes an
   ordinary link that marketing can edit. */
.prow__go::after{content:'';width:15px;height:7px;flex:none;
  border-right:1.7px solid currentColor;border-top:1.7px solid currentColor;
  transform:rotate(45deg) scale(.62);transform-origin:70% 30%;
  transition:transform .32s var(--ease-out)}
.prow:hover .prow__go::after{transform:rotate(45deg) scale(.62) translate(4px,-4px)}
.prow__go svg{width:16px;height:9px;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;transition:transform .32s var(--ease-out)}
.prow:hover .prow__go svg{transform:translateX(5px)}
.prow__go:has(svg)::after{display:none}
.prow__media{display:flex;align-items:center;justify-content:center;min-width:0}
.prow__media img{max-height:280px;width:auto;max-width:100%;
  filter:drop-shadow(0 24px 48px rgba(0,0,0,.55));
  transition:transform .5s cubic-bezier(.16,1,.3,1)}
.prow:hover .prow__media img{transform:translateY(-6px) scale(1.015)}
.prow__media .ui{width:100%;max-width:520px}
@media(max-width:860px){.prow__media img{max-height:200px}}

/* ---------- the same four products, compacted for the home page ----------
   Everything below is scoped to .prowset. The full rows above are the
   /products page and must stay long: that page exists to make the case.
   The home page only needs to name the four pieces and let people click, and
   at full height they were 1,840px, the longest block on the page. */
.prowset{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px;margin-top:34px}
.prowset .prow{display:flex;flex-direction:column;align-items:flex-start;gap:0;
  padding:0;border:1px solid var(--line);border-radius:11px;overflow:hidden;
  background:linear-gradient(170deg,rgba(11,39,57,.5),rgba(5,21,32,.5));
  transition:border-color .3s var(--ease),transform .3s var(--ease)}
.prowset .prow:last-of-type{border-bottom:1px solid var(--line)}
.prowset .prow:hover{border-color:rgba(126,196,238,.28);transform:translateY(-3px)}
.prowset .prow > div:first-child{order:2;padding:16px 18px 20px}
.prowset .prow__n{font-size:9.5px;letter-spacing:.18em;margin-bottom:9px}
.prowset .prow h2{font-size:17px;line-height:1.22}
/* the blurb is what makes a row tall; the product page carries the argument */
.prowset .prow p{display:none}
.prowset .prow__go{margin-top:11px;font-size:10.5px;gap:8px}
.prowset .prow__media{order:1;width:100%;height:132px;overflow:hidden;
  background:radial-gradient(closest-side,rgba(61,155,215,.14),transparent 74%)}
.prowset .prow__media img{max-height:118px;max-width:86%;
  filter:drop-shadow(0 14px 26px rgba(0,0,0,.5))}
.prowset .prow:hover .prow__media img{transform:translateY(-4px) scale(1.03)}
.prowset .prow__media .ui{width:100%;max-width:320px}
.prowset .prow:nth-child(even) .prow__media{order:1}
@media(max-width:1100px){.prowset{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:860px){
  .prowset .prow{flex-direction:row;align-items:center}
  .prowset .prow > div:first-child{padding:14px 16px}
  .prowset .prow__media{order:0;width:132px;height:104px;flex:none}
  .prowset .prow__media img{max-height:88px}
}
@media(max-width:560px){.prowset{grid-template-columns:1fr}}

/* ---------- e-Sens product hero, two columns ----------
   The unit on the right of its own page hero. Hidden below 900px, where the
   e-Sens logo at the top of the copy already carries the identity and a second
   large image would just be scroll. */
.phero--split .phero__in{display:grid;
  grid-template-columns:minmax(0,1.06fr) minmax(0,.78fr);
  gap:clamp(24px,4vw,64px);align-items:center}
.phero__obj{margin:0}
.phero__obj img{display:block;width:100%;max-width:360px;height:auto;margin-inline:auto;
  filter:drop-shadow(0 46px 76px rgba(0,0,0,.7)) drop-shadow(0 0 66px rgba(61,155,215,.2))}
@media(max-width:900px){
  .phero--split .phero__in{grid-template-columns:1fr}
  .phero__obj{display:none}
}

/* ---------- the e-Sens result screen ----------
   A real product surface rather than a picture of one. Everything the reader
   needs to be impressed is the density of it, so the chrome stays quiet and
   the tiles do the talking. Values step through three successive samples. */
.escr__lead{max-width:70ch}
/* Beside the copy rather than under it, so the section is one screen instead
   of two. Everything in the panel tightens: smaller tiles, three to a row, and
   the "on target" caption dropped, because a flag that appears on eighteen of
   twenty three tiles is noise. Only the ones with something to say keep a
   line, which also makes them easier to find. */
.split > .escr{margin-top:0}
.split .escr__top{padding:14px 16px}
.split .escr__id b{font-size:15px}
.split .escr__id svg{width:18px;height:18px}
.split .escr__meta{gap:16px;font-size:8px}
.split .escr__meta b{font-size:10.5px;margin-top:3px}
.split .escr__body{padding:14px 16px}
.split .escr__g{margin-top:16px}
.split .escr__gh{margin-bottom:8px !important}
.split .escr__gh span{font-size:8.5px;letter-spacing:.17em}
.split .escr__t{grid-template-columns:repeat(3,minmax(0,1fr));gap:6px}
.split .escr__c{padding:9px 10px 10px}
.split .escr__c small{font-size:7px;letter-spacing:.1em;margin-bottom:6px;min-height:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.split .escr__c b{font-size:16px}
.split .escr__c i{margin-top:6px;font-size:7px}
.split .escr__c i:not(.off):not(.cyc){display:none}
.split .escr__foot{padding:12px 16px;font-size:8px;letter-spacing:.13em}
@media(max-width:1100px){.split .escr__t{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:900px){
  .split .escr__t{grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media(max-width:560px){.split .escr__t{grid-template-columns:1fr 1fr}}
.escr{margin-top:clamp(28px,3.6vw,44px);border:1px solid var(--line);border-radius:14px;
  background:linear-gradient(168deg,rgba(12,42,61,.72),rgba(5,21,32,.86));
  box-shadow:0 46px 92px rgba(0,0,0,.5);overflow:hidden}
.escr__top{display:flex;align-items:center;justify-content:space-between;gap:18px;
  flex-wrap:wrap;padding:19px clamp(16px,2.2vw,26px);
  border-bottom:1px solid var(--line);background:rgba(4,18,29,.5)}
.escr__id{display:flex;align-items:center;gap:11px;min-width:0}
.escr__id svg{width:22px;height:22px;flex:none;fill:none;stroke:var(--orange-lt);
  stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.escr__id b{font:600 18px/1.2 var(--sans)}
.escr__meta{display:flex;gap:26px;font:500 9px/1.5 var(--mono);letter-spacing:.15em;
  text-transform:uppercase;color:var(--dim)}
.escr__meta b{display:block;margin-top:4px;font:600 12px/1 var(--sans);
  letter-spacing:0;text-transform:none;color:var(--ink)}
.escr__body{padding:clamp(16px,2.2vw,26px)}
.escr__g{margin-top:24px}
.escr__g:first-child{margin-top:0}
.escr__gh{display:flex;align-items:center;gap:12px;margin:0 0 11px !important}
.escr__gh span{font:600 9.5px/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;
  color:var(--dim)}
.escr__gh i{flex:1;height:1px;background:var(--line)}
.escr__gh em{font:500 9px/1 var(--mono);letter-spacing:.14em;font-style:normal;color:var(--dim)}
.escr__t{display:grid;grid-template-columns:repeat(auto-fill,minmax(166px,1fr));gap:8px}
.escr__c{padding:12px 13px 13px;border-radius:6px;background:rgba(3,16,26,.6);
  border:1px solid rgba(160,205,225,.08)}
.escr__c--flag{border-color:rgba(221,167,88,.34)}
.escr__c small{display:block;font:500 8px/1.35 var(--mono);letter-spacing:.13em;
  text-transform:uppercase;color:var(--dim);margin-bottom:9px}
.escr__c b{font:600 21px/1 var(--sans);letter-spacing:-.015em;
  font-variant-numeric:tabular-nums;transition:color .3s}
.escr__c b u{text-decoration:none;margin-left:4px;font:500 9px/1 var(--mono);color:var(--dim)}
.escr__c b span{display:inline-block}
.escr__c i{display:block;margin-top:8px;font:500 8px/1 var(--mono);letter-spacing:.1em;
  font-style:normal;color:var(--dim)}
.escr__c i.off{color:#dda758}
.escr__c i.cyc{color:#5fd6a0}
/* same flip the rest of the site's readouts use */
.escr__c.tick b{color:var(--blue-lt)}
.escr__c.tick b span{animation:flip .38s var(--ease-out)}
.escr__foot{display:flex;justify-content:space-between;gap:18px;flex-wrap:wrap;
  margin:0 !important;padding:15px clamp(16px,2.2vw,26px);border-top:1px solid var(--line);
  background:rgba(4,18,29,.4);
  font:500 9px/1.5 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--dim)}
.escr__foot b{color:var(--orange-lt);font-weight:600}
@media(max-width:560px){
  .escr__t{grid-template-columns:1fr 1fr;gap:6px}
  .escr__c{padding:10px}
  .escr__c b{font-size:17px}
  .escr__meta{gap:16px}
}
@media(prefers-reduced-motion:reduce){.escr__c.tick b span{animation:none}}

/* ---------- the bus under the four elements ----------
   Panels, connectivity and e-Sens gather onto one line that turns up into the
   Hub card above them. It says "the fourth one connects the other three"
   without a diagram section of its own, and at 70px it takes no screen from
   anything that was already working. */
.bus{position:relative;margin-top:-1px;line-height:0}
.bus svg{display:block;width:100%;height:70px;fill:none}
.bus__l{stroke:rgba(126,196,238,.18);stroke-width:1.4;vector-effect:non-scaling-stroke}
.bus__f{stroke:rgba(126,196,238,.85);stroke-width:1.6;vector-effect:non-scaling-stroke;
  stroke-linecap:round;stroke-dasharray:26 900;stroke-dashoffset:926;
  animation:busrun 3.4s linear infinite}
.bus__f--b{animation-delay:1.13s}
/* e-Sens is the warm feed, same as its card */
.bus__f--c{stroke:rgba(255,138,86,.9);animation-delay:2.26s}
@keyframes busrun{to{stroke-dashoffset:0}}
.bus__cap{margin:6px 0 0 !important;text-align:center;
  font:500 10px/1.6 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim)}
@media(max-width:1100px){.bus{display:none}}
@media(prefers-reduced-motion:reduce){.bus__f{animation:none;stroke-dasharray:none;
  stroke-dashoffset:0;opacity:.5}}

/* e-Sens is the one a competitor cannot answer, so its card runs warm while
   the other three stay blue. No badge: a card that has to announce it is the
   important one is not the important one. */
.prowset .prow--star{border-color:rgba(255,106,45,.3)}
.prowset .prow--star:hover{border-color:rgba(255,106,45,.55)}
.prowset .prow--star .prow__media{background:radial-gradient(closest-side,rgba(255,106,45,.13),transparent 74%)}

/* ---------- product pager ---------- */
.pnav{position:relative;z-index:2;padding:0 var(--pad) clamp(34px,5vw,58px)}
.pnav__in{display:grid;grid-template-columns:1fr 1fr;gap:14px;
  padding-top:clamp(28px,4vw,44px);border-top:1px solid var(--line)}
@media(max-width:700px){.pnav__in{grid-template-columns:1fr}}
.pnav__link{display:block;padding:18px 20px;border:1px solid var(--line);border-radius:7px;
  background:rgba(11,39,57,.4);transition:border-color .3s,background .3s,transform .3s}
.pnav__link:hover{border-color:rgba(126,196,238,.32);background:rgba(11,39,57,.72);
  transform:translateY(-3px)}
.pnav__link--next{text-align:right}
@media(max-width:700px){.pnav__link--next{text-align:left}}
.pnav__link--empty{border:0;background:none;padding:0}
@media(max-width:700px){.pnav__link--empty{display:none}}
.pnav__k{display:block;font:500 9.5px/1 var(--mono);letter-spacing:.2em;
  text-transform:uppercase;color:var(--orange);margin-bottom:9px}
.pnav__link b{display:block;font-size:16.5px;font-weight:600;color:var(--ink);line-height:1.3}
.pnav__link:hover b{color:var(--blue-lt)}
.pnav__b{display:block;margin-top:5px;font-size:13px;line-height:1.55;color:var(--muted)}

/* ---------- blog index ---------- */
/* .posts is the core post-template <ul>; each <li> is a grid cell holding a tile */
.posts{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;list-style:none;
  margin:0;padding:0}
@media(max-width:820px){.posts{grid-template-columns:1fr}}
.posts>li{display:flex;margin:0;list-style:none}
.posts>li>.tile{width:100%}
.post{display:flex;flex-direction:column;gap:11px}
.post .tile__n{margin-bottom:0}
.post h3{font-size:19px;line-height:1.3}
.post h3 a{color:inherit;text-decoration:none}
.post:hover h3 a{color:var(--blue-lt)}
.post .meta{font:500 10px/1 var(--mono);letter-spacing:.16em;color:var(--dim)}
.post p{font-size:13.5px;line-height:1.62;color:var(--muted)}

/* featured image, edge to edge inside the tile */
.post__img{margin:-22px -22px 4px;border-radius:6px 6px 0 0;overflow:hidden;
  position:relative;background:rgba(3,16,26,.6)}
.post__img img{display:block;width:100%;aspect-ratio:16/9;object-fit:cover;
  filter:saturate(.85) contrast(1.05);transition:transform .6s cubic-bezier(.16,1,.3,1)}
.post:hover .post__img img{transform:scale(1.03)}
.post__img::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(3,16,26,0) 45%,rgba(3,16,26,.5))}

.byline{font:500 10px/1.5 var(--mono);letter-spacing:.16em;color:var(--dim);
  text-transform:uppercase;margin-top:auto;padding-top:4px}
.byline a{color:inherit;text-decoration:none}

/* pagination */
.pager{display:flex;align-items:center;gap:14px;margin-top:34px;
  font:500 11px/1 var(--mono);letter-spacing:.14em}
.pager a{color:var(--muted);text-decoration:none;transition:color .25s}
.pager a:hover{color:var(--blue-lt)}
.pager .page-numbers.current{color:var(--orange)}
.pager .wp-block-query-pagination-numbers{display:flex;gap:11px}

/* ---------- single post ---------- */
.phero--post .post__h1{max-width:16ch;font-size:clamp(28px,3.4vw,46px);line-height:1.14}
.phero--post .lede{margin-top:18px;max-width:660px}
.byline--lg{display:flex;flex-wrap:wrap;align-items:center;gap:9px 16px;margin-top:26px;
  font:500 10.5px/1 var(--mono);letter-spacing:.16em;color:var(--dim);text-transform:uppercase}
.byline--lg .wp-block-post-author-name{color:var(--ink)}
.byline--lg .wp-block-post-author-name::before{content:'By ';color:var(--dim)}
.byline--lg .rt{color:var(--orange)}
.byline--lg>*+*{position:relative;padding-left:17px}
.byline--lg>*+*::before{content:'';position:absolute;left:0;top:50%;width:5px;height:1px;
  background:rgba(160,205,225,.3)}

.post__hero{margin:0 0 clamp(30px,4vw,48px);border-radius:10px;overflow:hidden;
  border:1px solid var(--line);position:relative}
.post__hero img{display:block;width:100%;aspect-ratio:16/9;object-fit:cover;
  filter:saturate(.85) contrast(1.05)}
.post__hero::after{content:'';position:absolute;inset:0;pointer-events:none;
  box-shadow:inset 0 -70px 90px -50px rgba(3,16,26,.9)}

/* article measure: prose sits narrower than the page grid */
.article{max-width:none}
.article>*{max-width:720px}
.article>p{font-size:16px;line-height:1.78;color:var(--muted);margin-bottom:20px}
.article>p.lede{font-size:clamp(17px,1.5vw,19.5px);line-height:1.66;color:var(--ink);
  font-weight:300;margin-bottom:30px}
.article>h2{font-size:clamp(22px,2.5vw,29px);line-height:1.24;font-weight:600;
  margin:clamp(40px,5vw,58px) 0 15px;color:var(--ink);position:relative}
.article>h2::before{content:'';position:absolute;left:-20px;top:.42em;width:9px;height:2px;
  background:var(--orange);opacity:0}
@media(min-width:1000px){.article>h2::before{opacity:1}}
.article>h3{font-size:18px;font-weight:600;margin:32px 0 11px;color:var(--ink)}
.article>ul,.article>ol{margin:0 0 22px 0;padding-left:20px}
.article>ul li,.article>ol li{font-size:15.5px;line-height:1.72;color:var(--muted);
  margin-bottom:9px}
.article>ul.checks{padding-left:0}
.article>p a,.article>ul li a,.article>ol li a{color:var(--blue-lt);
  text-decoration:underline;text-underline-offset:3px;
  text-decoration-color:rgba(126,196,238,.35);transition:text-decoration-color .25s}
.article>p a:hover{text-decoration-color:var(--blue-lt)}
.article>.note-box{margin:30px 0;font-size:15.5px}
.article>blockquote{margin:32px 0;padding-left:22px;border-left:2px solid var(--blue);
  font-size:17px;line-height:1.66;color:var(--ink);font-weight:300}
.article>figure{margin:34px 0;max-width:960px}
.article>figure img{width:100%;display:block;border-radius:8px;border:1px solid var(--line)}
.article>figure figcaption{margin-top:9px;font:500 10px/1.5 var(--mono);letter-spacing:.14em;
  color:var(--dim);text-transform:uppercase}
.article>.faq{margin-top:26px;max-width:none}
.article hr{border:0;border-top:1px solid var(--line);margin:40px 0}

/* previous / next */
.postnav{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:clamp(44px,6vw,70px);
  padding-top:26px;border-top:1px solid var(--line)}
@media(max-width:700px){.postnav{grid-template-columns:1fr}}
.postnav a{display:block;font-size:14.5px;line-height:1.5;color:var(--muted);
  text-decoration:none;transition:color .25s}
.postnav a:hover{color:var(--blue-lt)}
.postnav .post-navigation-link-next{text-align:right}
@media(max-width:700px){.postnav .post-navigation-link-next{text-align:left}}

/* editor-only guidance paragraph: invisible on the front end */
.editor-note{display:none}

/* The scrolling data ticker lived here. Removed with the rest of the HUD. */

/* ---------- touch targets ----------
   WCAG 2.5.8 asks for 24x24 CSS pixels on anything you tap. The footer links
   sit on a 16px line, the breadcrumbs on 10px and the contact links on 19px,
   which is fine for a mouse and mean on a thumb. Padding grows the hit area
   without touching the line box, so nothing reflows and the desktop layout is
   untouched: this only applies where the pointer is actually coarse. */
@media(pointer:coarse){
  .foot ul a,.foot__bot a{display:inline-block;padding-block:5px}
  .crumbs{gap:4px}
  .crumbs a{display:inline-block;padding:7px 5px;margin:-7px -5px}
  .cblock a,.cinline a{display:inline-block;padding-block:4px}
  .spec button,.faqitem button{min-height:32px}
}

/* ---------- footer ---------- */
.foot{position:relative;z-index:2;background:rgba(3,16,26,.72);
  padding:clamp(44px,6vw,68px) var(--pad) 26px;border-top:1px solid var(--line)}
.foot__grid{display:grid;grid-template-columns:1.5fr repeat(3,1fr);gap:34px;
  max-width:var(--max);margin:0 auto}
@media(max-width:860px){.foot__grid{grid-template-columns:1fr 1fr}}
.foot .foot__h{font:500 9.5px/1 var(--mono);letter-spacing:.22em;color:var(--orange-lt);margin:0 0 15px}
.foot ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:9px;
  font-size:13.5px;color:var(--muted)}
.foot ul a:hover{color:var(--blue-lt)}
.foot__bot{max-width:var(--max);margin:38px auto 0;padding-top:20px;border-top:1px solid var(--line);
  display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;
  font:400 11.5px/1.5 var(--mono);color:var(--dim)}
.foot__bot a{color:inherit;text-decoration:none;border-bottom:1px solid var(--line)}
.foot__bot a:hover{color:var(--blue-lt);border-bottom-color:var(--blue-lt)}
.gate{max-width:var(--max);margin:0 auto;padding:16px var(--pad) 30px;
  font:400 11px/1.7 var(--mono);color:var(--dim)}
.gate b{color:var(--orange-lt)}

/* ---------- media ---------- */
.shot{position:relative;display:flex;align-items:center;justify-content:center;
  background:radial-gradient(70% 70% at 50% 40%,rgba(61,155,215,.16),transparent 70%);
  border:1px solid var(--line);border-radius:10px;padding:26px;overflow:hidden}
.shot img{max-height:340px;width:auto;filter:drop-shadow(0 24px 46px rgba(0,0,0,.55))}
.shot figcaption{position:absolute;left:20px;bottom:16px;font:500 9.5px/1 var(--mono);
  letter-spacing:.2em;color:var(--dim)}
.shot--tall img{max-height:440px}
.shotgrid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media(max-width:780px){.shotgrid{grid-template-columns:1fr}}

.photo{position:relative;border-radius:10px;overflow:hidden;border:1px solid var(--line)}
.photo img{width:100%;display:block;filter:saturate(.85) contrast(1.05)}
.photo::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(3,16,26,.05),rgba(3,16,26,.55))}
.photo figcaption{position:absolute;left:20px;bottom:16px;z-index:2;
  font:500 9.5px/1 var(--mono);letter-spacing:.2em;color:var(--muted)}

/* full-bleed image band */
.band-photo{position:relative;height:clamp(220px,30vw,380px);overflow:hidden;z-index:2}
.band-photo img{width:100%;height:100%;object-fit:cover;filter:saturate(.8) contrast(1.05)}
.band-photo::after{content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(3,16,26,.75),rgba(3,16,26,.35) 45%,rgba(3,16,26,.9))}
.band-photo .cap{position:absolute;inset:auto 0 0 0;z-index:2;padding:0 var(--pad) 26px;
  max-width:var(--max);margin:0 auto;font:500 10px/1.6 var(--mono);letter-spacing:.2em;color:var(--muted)}

/* brand texture behind a hero. Set --bg inline on the section.
   Masked so it fades out before the copy, and kept under the content layer. */
.hasbg::before{content:'';position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:var(--bg);background-size:cover;background-position:center;
  opacity:var(--bgo,.22);
  -webkit-mask-image:linear-gradient(180deg,#000 0%,rgba(0,0,0,.6) 62%,transparent 96%);
          mask-image:linear-gradient(180deg,#000 0%,rgba(0,0,0,.6) 62%,transparent 96%)}
.hasbg::after{content:'';position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(100deg,var(--abyss) 22%,rgba(3,16,26,.72) 48%,rgba(3,16,26,.18) 100%)}
/* Lift only the content wrappers. A blanket rule on children would clobber
   the absolutely positioned hero canvas and drop it into flow. */
.hasbg .hero__grid,.hasbg .phero__in,.hasbg .scrollcue{position:relative;z-index:2}

/* full lockup, used where the brand should speak rather than label */
.lockup{display:block;width:clamp(150px,15vw,190px);height:auto;opacity:.95}

/* screenshot: light product UI needs a frame to sit on a dark ground */
.ui{position:relative;border-radius:12px;overflow:hidden;border:1px solid rgba(160,205,225,.22);
  box-shadow:0 34px 90px rgba(0,0,0,.62);background:var(--mid);
  transition:transform .6s var(--ease-out),box-shadow .6s var(--ease)}
.ui img{width:100%;display:block}
.ui:hover{transform:translateY(-4px);box-shadow:0 44px 110px rgba(0,0,0,.7)}
.ui--float{max-width:340px;margin-left:auto}
.ui figcaption{padding:11px 16px;font:500 9.5px/1 var(--mono);letter-spacing:.2em;color:var(--dim);
  background:rgba(3,16,26,.7);border-top:1px solid var(--line)}

/* placeholder: photography we do not have yet.
   Pure CSS so there is no file to swap, just replace the element with an <img>. */
.ph{position:relative;display:grid;place-items:center;text-align:center;gap:8px;
  min-height:var(--phh,300px);padding:34px;border-radius:10px;
  border:2px dashed rgba(232,73,43,.45);
  background:
    linear-gradient(rgba(126,196,238,.045) 1px,transparent 1px) 0 0/38px 38px,
    linear-gradient(90deg,rgba(126,196,238,.045) 1px,transparent 1px) 0 0/38px 38px,
    rgba(7,28,41,.6)}
.ph::before,.ph::after{content:'';position:absolute;width:20px;height:20px;border-color:var(--orange)}
.ph::before{left:14px;top:14px;border-left:2px solid;border-top:2px solid}
.ph::after{right:14px;bottom:14px;border-right:2px solid;border-bottom:2px solid}
.ph b{display:block;font:600 10.5px/1 var(--mono);letter-spacing:.26em;color:var(--orange-lt)}
.ph span{display:block;max-width:380px;font:400 13.5px/1.6 var(--sans);color:var(--muted)}
.ph--wide{--phh:340px}
.ph--band{--phh:clamp(220px,28vw,340px);border-radius:0;border-left:0;border-right:0}

/* spec table */
.spec{width:100%;border-collapse:collapse;font-size:14px}
.spec caption{text-align:left;font:500 10px/1 var(--mono);letter-spacing:.22em;
  color:var(--orange-lt);padding-bottom:14px}
.spec th,.spec td{text-align:left;padding:13px 16px;border-bottom:1px solid var(--line);vertical-align:top}
.spec thead th{font:500 9.5px/1 var(--mono);letter-spacing:.18em;color:var(--dim);
  border-bottom-color:rgba(232,73,43,.35)}
.spec tbody th{font-weight:600;color:var(--ink);width:34%}
.spec td{color:var(--muted)}
.spec tbody tr:hover td,.spec tbody tr:hover th{background:rgba(61,155,215,.05)}
.spec tbody th{font-weight:600;color:var(--ink)}
.spec .num{font-variant-numeric:tabular-nums;white-space:nowrap}
/* group header row inside a long spec table */
.spec .grp th{background:rgba(232,73,43,.07);border-bottom-color:rgba(232,73,43,.3);
  font:600 9.5px/1 var(--mono);letter-spacing:.22em;color:var(--orange-lt);
  text-transform:uppercase;padding:14px 16px}
.spec-wrap{position:relative;overflow-x:auto;border:1px solid var(--line);border-radius:8px;
  background:rgba(3,16,26,.4)}
.spec-note{margin-top:12px;font:400 11.5px/1.6 var(--mono);color:var(--dim)}

/* ---------- disclosure ----------
   The parameter table is 23 rows and the spec table is another six, and both
   sat open: two screens of reference data wedged between the argument above
   them and the argument below. A buyer scrolling the page wants to know the
   list exists, not read it. Native <details>, so it costs no JavaScript,
   takes keyboard focus for free, is announced correctly, and still gets
   indexed and found by the browser's own find-in-page. */
.disc{border:1px solid var(--line);border-radius:8px;background:rgba(3,16,26,.4);
  overflow:hidden;transition:border-color .4s}
.disc:hover{border-color:rgba(126,196,238,.24)}
/* The bar has to look like a control at rest, not like a line of text that
   happens to be clickable: it carries a tint of its own, the label is phrased
   as the action rather than as a title, and the chevron is the accent colour
   rather than inherited grey. Hiding something and then not signposting it is
   worse than leaving it open. */
.disc > summary{list-style:none;cursor:pointer;
  display:flex;align-items:center;gap:16px;padding:17px 19px;
  font:500 10.5px/1.3 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--blue-lt);background:rgba(61,155,215,.055);
  transition:background .3s,color .3s}
.disc > summary::-webkit-details-marker{display:none}
.disc > summary:hover{background:rgba(61,155,215,.13);color:var(--ink)}
.disc > summary:focus-visible{outline:2px solid var(--blue);outline-offset:-2px}
/* the count, so the label says what is inside before it is opened */
.disc__n{margin-left:auto;flex:none;font-style:normal;
  font-size:9px;letter-spacing:.16em;color:var(--dim)}
/* chevron, drawn rather than shipped */
.disc > summary::after{content:'';flex:none;width:9px;height:9px;
  border-right:1.6px solid var(--orange);border-bottom:1.6px solid var(--orange);
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform .34s var(--ease-out)}
.disc[open] > summary{border-bottom:1px solid var(--line)}
.disc[open] > summary::after{transform:rotate(-135deg) translate(-3px,-3px)}
/* the panel owns the horizontal scroll now, so the table box inside it drops
   its own frame rather than drawing a second border inside the first */
.disc__body{overflow-x:auto;padding:0 0 16px}
.disc .spec-wrap{border:0;border-radius:0;background:none;overflow:visible}
.disc .spec-note{padding:0 19px}
/* the caption repeats the summary on screen, but a table still wants one */
.disc .spec caption{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap}
/* On a phone the label already takes two lines; the count then fights it for
   the same row. The label is the part that has to survive. */
@media(max-width:560px){.disc__n{display:none}
  /* the count carried the auto margin that pinned the chevron right; with the
     count gone the chevron has to carry it itself */
  .disc > summary::after{margin-left:auto}
  .disc > summary{padding:15px 16px;letter-spacing:.16em}}
@media(prefers-reduced-motion:reduce){.disc > summary::after{transition:none}}

@media(max-width:760px){
  .spec{font-size:12.5px;min-width:640px}
  .spec th,.spec td{padding:10px 12px}
}



/* brand mark in header/footer */
.brand img.mark{width:34px;height:34px;object-fit:contain}
.foot .brand img.mark{width:38px;height:38px}

/* ============================================================
   MOBILE LAYER
   Phone-only behaviour. Desktop is untouched by everything below.
   ============================================================ */

/* Sticky conversion bar. Without it there is no CTA on screen anywhere
   between the hero and the footer, which is most of the page. */
.mcta{position:fixed;left:0;right:0;bottom:0;z-index:75;display:none;align-items:center;gap:12px;
  padding:10px 14px calc(10px + env(safe-area-inset-bottom,0px));
  background:rgba(3,16,26,.97);backdrop-filter:blur(18px) saturate(160%);
  border-top:1px solid var(--line);box-shadow:0 -14px 40px rgba(0,0,0,.5);
  transform:translateY(115%);transition:transform .45s var(--ease-out)}
.mcta.on{transform:none}
.mcta__t{flex:1;min-width:0}
.mcta__t b{display:block;font:600 13.5px/1.25 var(--sans);color:var(--ink)}
.mcta__t span{display:block;font:500 8.5px/1.3 var(--mono);letter-spacing:.14em;color:var(--dim);margin-top:3px}
.mcta .btn{flex:none;padding:12px 16px;font-size:13px}
@media(max-width:860px){
  .mcta{display:flex}
  /* the bar measures 80px at 390, so 74 left the last few pixels of the page
     tucked under it. Clear its real height plus the home-indicator inset. */
  body{padding-bottom:calc(92px + env(safe-area-inset-bottom,0px))}
}

/* Desktop keeps reading as one block: a claim, then its elaboration, with a
   smaller gap than a full paragraph break would give. */
.lede--aside{margin-top:10px}

@media(max-width:760px){
  /* An elaboration that exists to balance the desktop two-column split. On a
     phone there is no empty column beside it, and the checklist underneath
     already says the same thing, so it is pure scroll. The claim sentence
     above it stays. */
  .lede--aside{display:none}
  /* the checklist is 60% of that section's height; a tighter rhythm gives
     back most of a screen across the page without shrinking the type */
  .checks{gap:11px}
  .checks li{line-height:1.52}
}

@media(max-width:560px){
  /* three lines of orange ate the top of the first screen */
  .announce{font-size:10px;letter-spacing:.03em;padding:8px 14px;gap:8px}
  /* flex-wrap left the stat row ragged, 2 then 1 at different widths.
     A grid puts the third directly under the first. */
  /* minmax(0,1fr), not 1fr. A bare 1fr floors at the content's min-content
     width, so one long stat label ("Panels field-proven") pushed its column
     to 309px, left 100px for the other, and overflowed the page: the panels
     page scrolled sideways on a phone. */
  .hero__stat{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:15px 16px}
  /* the Ask Amphy pill sat on top of the last tile row */
  .fleet__main{padding-bottom:44px}
  .hero__fleet .fleet__amphy,.fleet__amphy{right:12px;bottom:10px}
  /* 6.5px labels in a 35px cell collided into each other; the figures alone
     read as a dashboard at thumbnail size */
  .prowset .hubmini__t{font-size:0;gap:0}
  .prowset .hubmini__t b{font-size:15px}
}

/* ---------- very large displays ----------
   Everything in the hero is capped for a 1440-1920 screen: the grid stops at
   1560px and the headline at 78px. Above that the composition simply stops
   growing while the frame keeps going, so at 2560 the gutters are 500px a side
   and at 4K they are 1140px with the content filling about a tenth of the
   hero. Let the composition keep scaling, and once the hero is much taller
   than the content, centre it rather than hanging it from the top. Nothing
   here applies at or below 1920 x 1080, which is the size already signed off. */
@media(min-width:1921px){
  .hero__grid{max-width:min(84vw,2200px)}
  .hero h1{font-size:clamp(78px,4.4vw,112px)}
  .hero__sub{font-size:clamp(19px,1.05vw,25px);max-width:50ch}
  .hero__stat{max-width:min(44vw,880px)}
  .hero__stat b{font-size:clamp(21px,1.35vw,32px)}
  .hero__cta .btn{font-size:clamp(14px,.8vw,18px);padding:19px 32px}
}
@media(min-height:1100px) and (min-width:1001px){
  /* the void was growing one-for-one with viewport height: 323px at 900 tall,
     1493px at 2160. Centred, the space falls either side of the composition
     instead of all of it underneath. */
  .hero{align-items:center;
    padding-bottom:calc(var(--navh) + clamp(32px,6vh,120px))}
}

@media(max-width:1080px){
  /* the depth rail is hidden on small screens, so keep the progress line up */
  .navprog{opacity:1}
}

@media(max-width:760px){
  /* tighter rhythm: the desktop padding made the home page 12 screens tall */
  .sec{padding-top:clamp(42px,9vw,62px);padding-bottom:clamp(42px,9vw,62px)}
  .hero{min-height:auto;padding-bottom:52px}
  .phero{padding-bottom:28px}
  .pin__steps{gap:28px}
  .hero__stat{gap:18px}

  /* card grids become swipeable rows. The partial next card is the affordance. */
  .g3,.g4{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:80%;
    overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;
    margin-inline:calc(var(--pad) * -1);padding:2px var(--pad) 16px;
    scrollbar-width:none;-ms-overflow-style:none}
  .g3::-webkit-scrollbar,.g4::-webkit-scrollbar{display:none}
  .g3>*,.g4>*{scroll-snap-align:start}
  .g3+.swipe,.g4+.swipe{display:flex}

  /* one product shot per row: two of them at 390px is unreadable */
  .shotgrid{grid-template-columns:1fr;gap:12px}
  .shot{padding:18px 18px 40px}
  .shot img,.shot--tall img{max-height:260px}

  /* article: shorter measure, so keep the type honest at 390px */
  .article>p{font-size:15.5px;line-height:1.72}
  .article>h2{margin-top:36px}
  .byline--lg{gap:7px 12px;font-size:9.5px}
  .post__hero{margin-bottom:28px}
  .postnav .post-navigation-link-next{text-align:left}
}

/* swipe hint, injected next to any grid that became a carousel */
.swipe{display:none;align-items:center;gap:8px;margin-top:-4px;
  font:500 8.5px/1 var(--mono);letter-spacing:.2em;color:var(--dim)}
.swipe i{display:block;width:26px;height:2px;border-radius:2px;background:rgba(160,205,225,.2);
  position:relative;overflow:hidden}
.swipe i::after{content:'';position:absolute;inset:0 auto 0 0;width:40%;border-radius:2px;
  background:var(--orange);transform:translateX(var(--sw,0%));transition:transform .2s linear}

/* whole card is the tap target where it links */
.tile--link{cursor:pointer}

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;scroll-behavior:auto !important}
  .rv{opacity:1;transform:none}
  .lines span i{transform:none}
  .caustics,.glow,.hero__canvas{display:none}
  .toast{transform:none;position:static;max-width:none;margin:0 var(--pad) 20px}
  .mcta{transform:none}
}

/* ============================================================
   WHITE LABEL
   The differentiator, demonstrated rather than claimed. The login is
   rebuilt in markup rather than screenshotted, so the visitor's own
   company name can land in it live.
   ============================================================ */

/* The stage set its own height from a 330px portrait plate beside a 720px
   window, which made this the second-tallest block on the page for what is
   really one interactive demo. Narrower plate, tighter gap, and the window
   caps its own height. */
.wl__stage{display:grid;grid-template-columns:minmax(0,1fr) 270px;gap:18px;
  align-items:stretch;margin-top:22px}
.wl__win{max-height:none}
.wl__plate .wl__panelimg{max-width:216px;margin-inline:auto}
@media(max-width:860px){.wl__stage{grid-template-columns:1fr}}

/* browser window */
.wl__win{max-width:720px;border-radius:11px;overflow:hidden;border:1px solid var(--line);
  box-shadow:0 34px 70px rgba(0,0,0,.5);background:#fff}
.wl__bar{display:flex;align-items:center;gap:7px;padding:10px 14px;background:#12232e}
.wl__bar i{width:9px;height:9px;border-radius:50%;background:#33505f;flex:none}
.wl__bar i:first-child{background:#e8492b}
.wl__url{margin-left:12px;font:500 10px/1 var(--mono);letter-spacing:.08em;color:#7d9aa8;
  transition:color .4s}
.wl__body{display:grid;grid-template-columns:1fr 1fr}
@media(max-width:700px){.wl__body{grid-template-columns:1fr}.wl__right{display:none}}

/* light side */
.wl__left{background:#fff;color:#12232e;padding:22px 26px 18px;font-family:var(--sans)}
.wl__mark{font:700 14px/1 var(--sans);color:var(--wl,#0f6b7d);letter-spacing:-.01em;
  transition:color .4s}
.wl__kick{margin-top:26px;font:600 8.5px/1 var(--mono);letter-spacing:.18em;
  color:var(--wl,#0f6b7d);transition:color .4s}
.wl__left h3{font-size:20px;font-weight:700;color:#12232e;margin:9px 0 3px}
.wl__sub{font-size:11.5px;color:#6b8494;margin-bottom:16px}
.wl__lab{display:block;font-size:9.5px;font-weight:600;color:#42606f;margin:11px 0 5px}
.wl__field{display:flex;align-items:center;gap:8px;background:#eef4f8;border-radius:5px;
  padding:9px 11px;font-size:11px;color:#42606f}
.wl__ico{opacity:.5;font-size:10px}
.wl__dots{letter-spacing:.12em;font-size:9px}
.wl__row{display:flex;justify-content:space-between;align-items:center;margin:12px 0 14px;
  font-size:10px;color:#6b8494}
.wl__check::before{content:'';display:inline-block;width:10px;height:10px;border-radius:2px;
  background:var(--wl,#0f6b7d);margin-right:7px;vertical-align:-1px;transition:background .4s}
.wl__link{color:var(--wl,#0f6b7d);transition:color .4s}
.wl__btn{background:var(--wl,#0f6b7d);color:#fff;text-align:center;border-radius:5px;
  padding:11px;font-size:12px;font-weight:600;transition:background .4s}
.wl__or{position:relative;text-align:center;margin:15px 0}
.wl__or::before{content:'';position:absolute;left:0;right:0;top:50%;height:1px;background:#e2ebf1}
.wl__or span{position:relative;background:#fff;padding:0 10px;
  font:600 8px/1 var(--mono);letter-spacing:.16em;color:#9db3bf}
.wl__sso{border:1px solid #e2ebf1;border-radius:5px;padding:9px;text-align:center;
  font-size:11px;font-weight:600;color:#42606f}
.wl__foot{display:flex;justify-content:space-between;margin-top:18px;
  font-size:8.5px;color:#9db3bf}

/* accent side */
.wl__right{position:relative;overflow:hidden;align-items:center;
  background:linear-gradient(150deg,var(--wl,#0f6b7d),var(--wl2,#0a3f52));
  transition:background .5s;display:flex;align-items:flex-end;min-height:300px}
.wl__rings{position:absolute;right:-18%;top:-14%;width:112%;aspect-ratio:1;opacity:.5}
.wl__rings i{position:absolute;inset:0;border:1px solid rgba(255,255,255,.34);border-radius:50%}
.wl__rings i:nth-child(2){inset:14%}
.wl__rings i:nth-child(3){inset:29%}
.wl__rin{position:relative;padding:26px;color:#fff;width:100%}
.wl__reyebrow{font:600 8px/1 var(--mono);letter-spacing:.2em;opacity:.75}
.wl__rin h4{font-size:19px;font-weight:700;margin:10px 0 7px;line-height:1.2}
.wl__rsub{font-size:11px;line-height:1.55;opacity:.78;max-width:30ch}
.wl__stats{display:flex;gap:22px;margin-top:20px}
.wl__stats b{display:block;font-size:15px;font-weight:700}
.wl__stats span{font-size:8.5px;opacity:.72}

/* the panel, beside the login */
.wl__plate{margin:0;grid-column:2;background:rgba(11,39,57,.4);border:1px solid var(--line);
  border-radius:9px;padding:16px;display:flex;flex-direction:column;gap:12px;align-items:center}
.wl__plate .wl__panelimg{margin:auto auto}
.wl__plate figcaption{align-self:flex-start;font:500 9px/1 var(--mono);
  letter-spacing:.18em;color:var(--dim)}
@media(max-width:860px){.wl__plate{grid-column:1}}

.wl__win{position:relative}




/* ---------- white label: type your own name ---------- */
.wl__ctl{display:flex;align-items:flex-end;gap:20px;flex-wrap:wrap;margin-top:32px}
.wl__inp{display:block}
.wl__inp span{display:block;font:500 9.5px/1 var(--mono);letter-spacing:.18em;
  text-transform:uppercase;color:var(--dim);margin-bottom:9px}
.wl__inp input{width:min(300px,72vw);background:rgba(11,39,57,.5);color:var(--ink);
  border:1px solid var(--line);border-bottom:2px solid var(--wl,#1a6fa8);
  border-radius:6px 6px 0 0;padding:12px 15px;font:400 16px/1 var(--sans);
  transition:border-color .3s,background .3s}
.wl__inp input::placeholder{color:var(--dim)}
.wl__inp input:focus{outline:none;background:rgba(11,39,57,.8);
  border-color:rgba(126,196,238,.4);border-bottom-color:var(--wl,#1a6fa8)}
.wl__sw{display:flex;gap:9px;padding-bottom:4px}
.wl__sw button{width:26px;height:26px;border-radius:50%;background:var(--sw);cursor:pointer;
  border:2px solid transparent;box-shadow:0 0 0 1px rgba(255,255,255,.14) inset;
  transition:transform .25s var(--ease-out),border-color .25s}
.wl__sw button:hover{transform:scale(1.12)}
.wl__sw button.is-on{border-color:var(--ink);transform:scale(1.12)}


/* ---------- connectivity: the blank faceplate ---------- */
.faceone{margin:30px 0 0;padding:22px 22px 0;border:1px solid var(--line);border-radius:10px;
  background:radial-gradient(120% 90% at 50% 0%,rgba(15,47,86,.5),rgba(3,16,26,.5))}
.faceone img{display:block;width:100%;height:auto;
  filter:drop-shadow(0 18px 32px rgba(0,0,0,.5))}
.faceone figcaption{padding:15px 0;font:500 9px/1.4 var(--mono);letter-spacing:.16em;
  color:var(--dim);text-align:center}

/* the panel sits beside the login, unbranded and static: nothing here
   follows the colour picker */
.wl__panelimg{display:block;width:100%;height:auto;
  filter:drop-shadow(0 20px 38px rgba(0,0,0,.55))}


/* ---------- real photography ---------- */
.photo--tall img{max-height:640px;object-fit:cover;object-position:center}
.proof .photo img{width:100%;height:auto;display:block}
.facepair{display:grid;grid-template-columns:1.35fr 1fr;gap:18px;margin-top:30px;
  align-items:start}
@media(max-width:820px){.facepair{grid-template-columns:1fr}}
.facepair .faceone{margin:0}
.facepair .photo img{width:100%;height:auto;display:block}

/* ---------- photo sizing ----------
   The on-site shots carry retouched areas (blanked faceplates and labels).
   Held small they read as proof; blown up they invite inspection. */
.photo--sm{max-width:420px}
.photo--sm img{width:100%;height:auto;display:block}
.photo--sm.photo--tall img{max-height:420px;object-fit:cover;object-position:center}
.photo--sm figcaption{left:14px;bottom:12px;font-size:8.5px}
@media(max-width:820px){.photo--sm{max-width:100%}}
/* connectivity: the installed shot is a supporting note, not the subject */
.facepair{grid-template-columns:1.6fr 1fr}
.facepair .photo{max-width:300px}

/* ---------- the report card, structured like the real export ---------- */
.rpt{width:min(600px,100%)}
.rpt .rl{font-size:13.5px}
.rpt__hd b{font-size:17px}
.rpt__flag{font-size:13.5px;padding:11px 13px}
.rpt__item b{font-size:14px}
.rpt__item p{font-size:12.5px}
.rpt__row{font-size:13px}
.rpt__hd{display:flex;flex-direction:column;gap:3px}
.rpt__hd b{font-size:15px}
.rpt__hd span{font:500 9.5px/1 var(--mono);letter-spacing:.13em;color:var(--dim)}
.rpt__flags{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.rpt__flag{display:flex;flex-direction:column;gap:4px;padding:9px 11px;border-radius:5px;
  font-size:12.5px;font-weight:600;color:var(--ink);background:rgba(3,16,26,.45)}
.rpt__flag small{font:600 8px/1 var(--mono);letter-spacing:.16em;color:var(--dim)}
.rpt__flag--watch{border-left:3px solid #e0a83a}
.rpt__flag--act{border-left:3px solid var(--orange)}
.rpt__cap{font:700 8.5px/1 var(--mono) !important;letter-spacing:.18em;color:var(--dim) !important}
.rpt__item{display:grid;grid-template-columns:auto 1fr auto;gap:10px;align-items:start}
.rpt__item p{font-size:11.5px;line-height:1.5;color:var(--muted);margin-top:3px}
.rpt__item em{font:500 8.5px/1.4 var(--mono);font-style:normal;letter-spacing:.1em;
  color:var(--dim);text-align:right;white-space:nowrap}
.rpt__tag{font:700 7.5px/1 var(--mono);letter-spacing:.12em;padding:5px 7px;border-radius:3px;
  white-space:nowrap}
.rpt__tag--act{background:rgba(232,73,43,.18);color:var(--orange-lt)}
.rpt__tag--mon{background:rgba(224,168,58,.16);color:#e8bd6a}
.rpt__row{display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:baseline}
.rpt__row i{font-style:normal;font-size:10.5px;color:#5fae7a}
.rpt__row b{font-variant-numeric:tabular-nums;min-width:88px;text-align:right}
.rpt__row--bad i{color:var(--dim)}
.rpt__row--bad b{color:var(--orange-lt)}
@media(max-width:760px){
  .rpt__flags{grid-template-columns:1fr}
  .rpt__item{grid-template-columns:auto 1fr}
  .rpt__item em{grid-column:2;text-align:left}
}

/* ---------- descent: the opening number ---------- */
/* An 11px label beside a 190px numeral is not a pairing, it is a typo: the
   unit vanished and the stat read as a bare number floating in dead space.
   The number comes down, the label goes up, and they align on the cap height
   instead of the baseline so they read as one lockup. */
.dsc__huge{display:flex;align-items:center;gap:16px;margin:2px 0 14px}
.dsc__huge b{font:700 clamp(62px,8.4vw,124px)/0.86 var(--sans);letter-spacing:-.035em;
  color:var(--ink);font-variant-numeric:tabular-nums}
.dsc__huge span{font:600 clamp(12px,1.05vw,15px)/1.35 var(--mono);letter-spacing:.16em;
  color:var(--orange);text-transform:uppercase}
.dsc__beat--solo .dsc__copy{max-width:900px}

/* ---------- the catch: the card itself reacts ---------- */
.card--alarm{border-color:rgba(232,73,43,.55) !important;
  box-shadow:0 0 0 1px rgba(232,73,43,.25),0 22px 50px rgba(0,0,0,.5);
  animation:acalarm 1.6s ease-in-out infinite}
@keyframes acalarm{0%,100%{box-shadow:0 0 0 1px rgba(232,73,43,.22),0 22px 50px rgba(0,0,0,.5)}
  50%{box-shadow:0 0 0 4px rgba(232,73,43,.14),0 22px 50px rgba(0,0,0,.5)}}
@media (prefers-reduced-motion:reduce){.card--alarm{animation:none}}


/* ---------- the analyst screenshot ----------
   The PNG is transparent around the panel, so a border and a box-shadow drew
   a rectangle around empty space. A drop-shadow filter follows the panel's
   real silhouette instead, and the whole thing sits on the page rather than
   in a frame. */
.amphyshot{margin:0;display:flex;justify-content:flex-end}
.amphyshot img{display:block;width:100%;height:auto;max-width:392px;
  filter:drop-shadow(0 26px 52px rgba(0,0,0,.55));
  transition:transform .55s var(--ease-out),filter .55s var(--ease-out)}
.amphyshot:hover img{transform:translateY(-12px) scale(1.03);
  filter:drop-shadow(0 42px 74px rgba(0,0,0,.6))
         drop-shadow(0 0 38px rgba(61,155,215,.26))}
@media(max-width:900px){
  .amphyshot{justify-content:center}
  .amphyshot img{max-width:300px}
}
@media (prefers-reduced-motion:reduce){
  .amphyshot img{transition:none}
  .amphyshot:hover img{transform:none}
}

/* ============================================================
   HUB UI, RESTATED IN THE SITE PALETTE
   The invented three-readout card implied a product that does not
   exist. This mirrors the real device page: status strip, sensor
   snapshot, the actual sensor names and units, analogue tiles that
   read a value and digital tiles that read a state.
   ============================================================ */
/* The eyebrow beside it is an inline-flex, so half-leading pushes its box
   10px down the column while the card starts at 0: the card read as sitting
   higher than the section it belongs to, and its top edge landed exactly on a
   background grid line. 12px lines the card's edge up with the letterforms.
   The !important is answering the grid-margin reset higher up this file,
   which zeroes margin-block-start on every .split child to stop WordPress's
   flow margins shoving grid cells around. */
.split--top > .hubui{margin-block-start:12px !important}
.hubui{padding:20px}
.hubui__top{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;
  padding-bottom:14px;border-bottom:1px solid var(--line)}
.hubui__dev{display:flex;align-items:center;gap:9px;min-width:0}
.hubui__dev b{font:600 16px/1.2 var(--sans);color:var(--ink)}
.hubui__drop{width:22px;height:22px;border-radius:6px;flex:none;
  background:rgba(61,155,215,.16);position:relative}
.hubui__drop::after{content:'';position:absolute;inset:6px;border-radius:50% 50% 50% 0;
  transform:rotate(-45deg);background:var(--blue-lt)}
.hubui__pill{font:600 8.5px/1 var(--mono);letter-spacing:.14em;padding:5px 8px;border-radius:99px;
  background:rgba(74,222,154,.14);color:#5fd6a0;flex:none}
.hubui__meta{font:500 8.5px/1.6 var(--mono);letter-spacing:.13em;color:var(--dim);
  text-align:right;flex:none}
.hubui__cap{font:700 8px/1 var(--mono);letter-spacing:.2em;color:var(--dim);margin:15px 0 10px}

.hubui__grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.hubui .ro{padding:10px 11px}
.hubui .ro small,.hs small{display:block;font:500 8px/1.3 var(--mono);letter-spacing:.11em;
  color:var(--dim);margin-bottom:7px}
.hubui .ro b{font-size:clamp(15px,1.6vw,19px)}
.hubui .ro i,.hs i{display:block;font-style:normal;margin-top:6px;
  font:500 7.5px/1 var(--mono);letter-spacing:.13em;color:rgba(160,205,225,.42)}

/* digital channels report a state, not a number */
.hs{background:rgba(3,16,26,.62);border:1px solid rgba(160,205,225,.08);border-radius:5px;
  padding:10px 11px}
.hs b{font:600 13px/1 var(--sans);color:var(--muted)}
.hs--on b{color:#5fd6a0}

@media(max-width:760px){
  .hubui__grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .hubui__meta{display:none}
}

/* ---------- analytics: the multi-chart view ---------- */
.hubch__grid{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-top:12px}
.hubch{background:rgba(3,16,26,.62);border:1px solid rgba(160,205,225,.08);
  border-radius:5px;padding:11px 12px 8px}
.hubch p{font:500 8px/1.3 var(--mono);letter-spacing:.11em;color:var(--dim)}
.hubch p b{display:block;font:600 10px/1.4 var(--sans);letter-spacing:0;color:var(--ink)}
.hubch svg{display:block;width:100%;height:52px;margin-top:7px;overflow:visible}
.hubch svg path.l{fill:none;stroke-width:1.5;stroke-linejoin:round;stroke-linecap:round}
.hubch svg path.a{opacity:.16}
.hubch__ax{display:flex;justify-content:space-between;margin-top:5px;
  font:500 7px/1 var(--mono);letter-spacing:.08em;color:rgba(160,205,225,.32)}
@media(max-width:760px){.hubch__grid{grid-template-columns:1fr}}
.hubui__pill--alarm{background:rgba(232,73,43,.16);color:var(--orange-lt)}

/* hero card runs denser than the same component elsewhere: it has to carry
   the sensor detail without becoming the tallest thing on the page */
.hero__card{padding:16px 16px 18px}
.hero__card .hubui__grid{gap:6px}
.hero__card .ro,.hero__card .hs{padding:8px 9px}
.hero__card .ro small,.hero__card .hs small{font-size:7.5px;margin-bottom:5px}
.hero__card .ro b{font-size:16px}
.hero__card .hs b{font-size:11.5px}
.hero__card .ro i,.hero__card .hs i{margin-top:4px;font-size:6.5px}
.hero__card .hubui__cap{margin:11px 0 8px}

/* ============================================================
   FLEET DASHBOARD, IN THE SITE PALETTE
   A full-width light screenshot was a slab of white in the middle
   of a dark page. Rebuilt in markup so it sits in the palette,
   stays crisp, and carries the white-label story: the sidebar
   reads "Your Company", not ours.

   Balance note: this is deliberately NOT flat dark. The semantic
   icon colours (alarm red, attention amber, healthy green) are the
   only bright things, so the eye lands on state rather than chrome.
   ============================================================ */
.fleet{margin:0;border-radius:11px;overflow:hidden;border:1px solid var(--line);
  background:linear-gradient(170deg,rgba(11,39,57,.9),rgba(5,21,32,.96));
  box-shadow:0 40px 90px rgba(0,0,0,.55)}
.fleet__bar{display:flex;align-items:center;gap:7px;padding:11px 15px;
  background:rgba(3,16,26,.72);border-bottom:1px solid var(--line)}
.fleet__bar i{width:9px;height:9px;border-radius:50%;background:#31505f;flex:none}
.fleet__bar i:first-child{background:#e8492b}
.fleet__bar i:nth-child(2){background:#e0a83a}
.fleet__bar i:nth-child(3){background:#4ade9a}
.fleet__bar span{margin-left:12px;font:500 9.5px/1 var(--mono);letter-spacing:.14em;color:var(--dim)}

.fleet__body{display:grid;grid-template-columns:186px 1fr}
.fleet__side{border-right:1px solid var(--line);padding:18px 14px;
  background:rgba(3,16,26,.4);display:flex;flex-direction:column;gap:16px}
.fleet__brand{font:700 14px/1.2 var(--sans);color:var(--ink)}
.fleet__nav{display:flex;flex-direction:column;gap:1px}
.fn{font:400 12px/1 var(--sans);color:var(--muted);padding:8px 10px;border-radius:5px}
.fn.is-on{background:rgba(61,155,215,.16);color:var(--blue-lt);font-weight:600}
.fleet__user{margin-top:auto;display:flex;align-items:center;gap:9px;padding-top:14px;
  border-top:1px solid var(--line)}
.fleet__av{width:26px;height:26px;border-radius:50%;flex:none;
  background:linear-gradient(140deg,#2f6fb5,#123a63)}
.fleet__user b{display:block;font:600 11.5px/1.2 var(--sans);color:var(--ink)}
.fleet__user small{font:500 8.5px/1 var(--mono);letter-spacing:.12em;color:var(--dim)}

.fleet__main{padding:18px 18px 20px;position:relative}
.fleet__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
.fleet__head b{font:700 17px/1 var(--sans);color:var(--ink)}
/* floats bottom-right, where the real widget lives */
.fleet__amphy{position:absolute;right:18px;bottom:18px;z-index:2;
  display:inline-flex;align-items:center;gap:7px;font:600 10.5px/1 var(--sans);
  color:var(--blue-lt);background:rgba(61,155,215,.16);border:1px solid rgba(126,196,238,.26);
  border-radius:99px;padding:8px 13px;box-shadow:0 8px 22px rgba(0,0,0,.35)}
.fleet__amphy::before{content:'';width:6px;height:6px;border-radius:50%;background:#4ade9a}

.fleet__grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:9px}
.ft{display:flex;align-items:center;gap:11px;padding:12px 13px;border-radius:7px;
  background:rgba(3,16,26,.5);border:1px solid rgba(160,205,225,.07);
  transition:border-color .3s,background .3s}
.ft:hover{border-color:rgba(126,196,238,.22);background:rgba(11,39,57,.66)}
.ft__i{width:30px;height:30px;border-radius:8px;flex:none;display:grid;place-items:center}
.ft__i svg{width:15px;height:15px}
.ft__i svg path,.ft__i svg circle,.ft__i svg rect{fill:currentColor;stroke:currentColor}
.ft__i svg [fill="none"]{fill:none}
.ft__i--red{background:rgba(232,73,43,.15);color:#ff8368}
.ft__i--amber{background:rgba(224,168,58,.15);color:#eab863}
.ft__i--green{background:rgba(74,222,154,.14);color:#5fd6a0}
.ft__i--blue{background:rgba(61,155,215,.16);color:#7ec4ee}
.ft__i--violet{background:rgba(169,140,240,.15);color:#b39cf3}
.ft__i--grey{background:rgba(160,205,225,.09);color:#8dafbe}
.ft__t small{display:block;font:500 9.5px/1.2 var(--sans);color:var(--muted);margin-bottom:4px}
.ft__t b{font:700 16px/1 var(--sans);color:var(--ink);font-variant-numeric:tabular-nums}

.fleet figcaption{padding:11px 16px;font:500 8.5px/1.4 var(--mono);letter-spacing:.16em;
  color:var(--dim);border-top:1px solid var(--line);background:rgba(3,16,26,.5)}

@media(max-width:900px){
  .fleet__body{grid-template-columns:1fr}
  .fleet__side{flex-direction:row;align-items:center;border-right:0;
    border-bottom:1px solid var(--line);overflow:hidden}
  .fleet__nav{flex-direction:row;gap:4px;overflow:hidden}
  .fleet__user{display:none}
  .fleet__grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media(max-width:560px){.fleet__grid{grid-template-columns:1fr}.fleet__nav{display:none}}

/* five sensors: the lead reading spans, the rest pair off */
.hubui__grid--five{grid-template-columns:1fr 1fr;gap:6px}
.hubui__grid--five .ro:first-child{grid-column:1 / -1}

/* the device name must not wrap: the card is narrow by design */
.hero__card .hubui__dev b{white-space:nowrap;font-size:15px}
.hero__card .hubui__top{flex-direction:column;align-items:flex-start;gap:6px}
.hero__card .hubui__meta{text-align:left}

/* ---------- e-Sens: its own beat on the home page ----------
   The analyzer was buried as item three in a product list and one tile in a
   benefits grid, which is the wrong weight for the piece of the package
   nobody else can match. It gets a full section, and the 23-parameter figure
   carries it: it is the only hard number on the page. */
/* ================= e-SENS, THE HOME BEAT =================
   Written as the hero's next frame, not as a new page. The hero has a
   photograph, light, depth and a live interface; the section under it used to
   be a flat blue wall with a product cut-out on it, and the drop in production
   value at the boundary was the whole reason it read as basic. So: no seam at
   the top, the same dark ground carried down, a pool of light behind the
   instrument, technical circles at a scale the hero never had room for, and
   grain over all of it. */
/* No isolation:isolate here. It was added so the grain's soft-light would
   blend only against this section, and it printed a hard luminance step
   straight across the analyzer at the section's top edge: the device bleeds
   into the hero, and a stacking context on the section composited the two
   halves of it differently. The grain's own mask does that job instead. */
/* overflow-x:clip, not hidden. The circles are wider than the window on a
   laptop and would otherwise put a horizontal scrollbar on the page; hidden
   would force the other axis to a scroll container and guillotine the device
   where it bleeds up into the hero. clip on one axis leaves the other visible. */
/* No background on the section at all, and that is the point. Any gradient
   painted into this box is clipped by the box, so wherever it is not already
   fully transparent at the top edge it prints a hard horizontal line across
   the whole page: a radial centred at 44% is at half strength by the time it
   reaches y=0. Every bit of light in this section therefore lives on
   .esn__media::before, which is a child of the instrument and bleeds past the
   boundary with it. The ground here is just the page's, carried down. */
.esn{position:relative;overflow-x:clip;
  padding-bottom:clamp(38px,4vw,64px)}

/* Every other section masks its grid from the top edge, which draws exactly
   the "a new section starts here" line this one must not have. Here the grid
   is pulled around the instrument instead. */
.esn::before{
  -webkit-mask-image:radial-gradient(52% 60% at 72% 48%,#000 0%,transparent 76%);
          mask-image:radial-gradient(52% 60% at 72% 48%,#000 0%,transparent 76%)}

/* Grain. Two things it buys: the big soft gradients stop banding on cheap
   panels, and the whole frame picks up the texture of a photograph instead of
   the flatness of a fill. Inline SVG turbulence, so it costs no request. */
/* Masked in from the top. Any full-coverage overlay that starts at the
   section's first pixel draws the exact seam this section is trying not to
   have: at overlay blend even a faint one stepped the ground lighter and put a
   hard line across the analyzer where it crosses the boundary. */
.esn::after{content:'';position:absolute;inset:0;z-index:0;pointer-events:none;
  opacity:.34;mix-blend-mode:soft-light;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 34%,#000 74%,transparent 100%);
          mask-image:linear-gradient(180deg,transparent 0,#000 34%,#000 74%,transparent 100%);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.86' numOctaves='3'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='.42'/%3E%3C/svg%3E")}

.esn__in{position:relative;z-index:1;
  display:grid;grid-template-columns:minmax(0,1.04fr) minmax(0,.96fr);
  gap:clamp(28px,3.4vw,60px);align-items:center}
.esn__copy .h2{max-width:24ch}
.esn__copy .lede{margin-top:16px;max-width:53ch}

/* The four steps are one process, so a rule runs behind them and the numbers
   sit on it. Rows on narrow screens, where a horizontal run would crush. */
.esn__flow{position:relative;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:18px;margin-top:clamp(28px,3vw,38px);padding-top:30px}
.esn__flow::before{content:'';position:absolute;left:11px;right:11px;top:11px;height:1px;
  background:linear-gradient(90deg,var(--blue),rgba(61,155,215,.18))}
.esn__step{position:relative}
.esn__k{position:absolute;top:-30px;left:0;width:23px;height:23px;border-radius:50%;
  display:grid;place-items:center;margin:0;
  font:600 9.5px/1 var(--mono);letter-spacing:.02em;color:var(--blue-lt);
  background:var(--deep);border:1px solid rgba(126,196,238,.4)}
.esn__t{margin:0;font:600 14.5px/1.3 var(--sans);color:var(--ink)}
.esn__d{margin:6px 0 0;font-size:13px;line-height:1.5;color:var(--muted)}
.esn .btn{margin-top:clamp(24px,2.8vw,34px)}

/* ---- the instrument ----
   Larger than its track and running past the top of the section into the hero.
   It is the one object on this page nobody has seen before, and a product that
   unfamiliar sitting politely inside a column reads as an illustration of the
   argument rather than the subject of it.

   --enter is the scroll-driven arrival, set by esnScene() in motion.js: the
   device rises and levels off as the section takes the frame. --drift is the
   slow parallax it keeps afterwards. Both default to their resting values so
   the composition is correct with no JS at all. */
.esn__media{position:relative;width:fit-content;margin-inline:auto;
  /* Enough to cross the boundary and no more. At -116px the analyzer climbed
     into the hero's lower right and sat under the Hub panel, and two large
     objects in the same corner read as congestion rather than depth. */
  margin-block:clamp(-64px,-4.4vw,-30px) clamp(-40px,-3vw,-18px);
  --enter:1; --drift:0}

/* Technical circles, at a size the hero never had room for. Uneven radii and
   one heavier ring, so they read as an instrument scale rather than a halo. */
.esn__media::before{content:'';position:absolute;z-index:0;pointer-events:none;
  left:50%;top:48%;width:min(1020px,96vw);aspect-ratio:1;
  transform:translate3d(-50%,calc(-50% + var(--drift) * -26px),0);
  border-radius:50%;
  background:
    /* the pool it casts on the floor */
    radial-gradient(46% 9% at 50% 88%,rgba(61,155,215,.2),transparent 72%),
    /* the light it is standing in */
    radial-gradient(closest-side,rgba(61,155,215,.3),rgba(61,155,215,.09) 50%,transparent 72%),
    repeating-radial-gradient(closest-side,transparent 0 61px,rgba(126,196,238,.075) 61px 62px),
    repeating-radial-gradient(closest-side,transparent 0 244px,rgba(126,196,238,.11) 244px 246px);
  -webkit-mask-image:radial-gradient(closest-side,#000 26%,rgba(0,0,0,.55) 62%,transparent 88%);
          mask-image:radial-gradient(closest-side,#000 26%,rgba(0,0,0,.55) 62%,transparent 88%)}

.esn__shot{position:relative;z-index:1;margin:0;
  transform:translate3d(0,calc((1 - var(--enter)) * 120px + var(--drift) * -30px),0)
            rotate(calc((1 - var(--enter)) * 6deg));
  opacity:calc(.55 + var(--enter) * .45);
  transform-origin:60% 100%;will-change:transform}
.esn__shot img{display:block;width:clamp(300px,27vw,430px);height:auto;
  filter:drop-shadow(0 48px 76px rgba(0,0,0,.7)) drop-shadow(0 0 70px rgba(61,155,215,.14))}

/* The button lights once, when the device has finished arriving. It is the
   only thing on the instrument that looks like it does something. */
.esn__media::after{content:'';position:absolute;z-index:2;pointer-events:none;
  left:44.5%;top:57%;width:52px;height:52px;margin:-26px 0 0 -26px;border-radius:50%;
  border:1px solid rgba(126,196,238,.8);opacity:0}
.esn__media.is-set::after{animation:esnping 1.9s cubic-bezier(.22,.61,.36,1) .15s 2}
@keyframes esnping{
  0%{transform:scale(.34);opacity:0}
  18%{opacity:.85}
  100%{transform:scale(1.5);opacity:0}}

.esn__big{position:absolute;z-index:2;left:-6%;bottom:5%;margin:0;
  display:flex;align-items:center;gap:12px;
  padding:14px 20px;border-radius:12px;
  background:linear-gradient(160deg,rgba(18,54,77,.97),rgba(7,26,39,.97));
  border:1px solid rgba(126,196,238,.22);
  box-shadow:0 26px 56px rgba(0,0,0,.55);
  transform:translate3d(0,calc(var(--drift) * -6px),0)}
.esn__big b{font:700 clamp(38px,4vw,56px)/0.86 var(--sans);letter-spacing:-.03em;color:var(--orange-lt)}
.esn__big span{font:600 10px/1.35 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--muted)}

/* The Hub panel is the thing the reader is leaving; the e-Sens card is the
   thing they are following. So the panel lifts and dims on the way out and the
   card does not move at all. --out is set by esnScene(). */
.hero__fleet{--out:0}
.hero__media .hero__fleet{
  transform:rotateY(-9deg) rotateX(3deg)
    translate3d(calc(var(--tx) * 8px), calc(var(--ty) * 6px - var(--out) * 80px), 0);
  opacity:calc(1 - var(--out) * .8)}

@media(prefers-reduced-motion:reduce){
  .esn__shot,.esn__big,.esn__media::before{transform:none !important}
  .esn__shot{opacity:1}
  .esn__media.is-set::after{animation:none}
  .hero__media .hero__fleet{opacity:1}
}

@media(max-width:1000px){
  .esn__in{grid-template-columns:1fr}
  /* Same order as the desktop composition reads in: the section introduces
     itself, then the instrument. Reversing it on narrow screens meant the
     photograph arrived before the words that name it, and the page told the
     story backwards depending on the window. */
  .esn__media{margin:14px auto 0}
  .esn__shot img{width:min(330px,64vw)}
  .esn__media::before{width:min(760px,132vw)}
  .esn__big{left:auto;right:-6%}
}
@media(max-width:720px){
  .esn__flow{grid-template-columns:1fr;gap:26px;padding-top:0;padding-left:34px}
  .esn__flow::before{left:11px;right:auto;top:6px;bottom:6px;width:1px;height:auto;
    background:linear-gradient(180deg,var(--blue),rgba(61,155,215,.18))}
  .esn__k{top:0;left:-34px}
  .esn__big{right:auto;left:50%;transform:translateX(-50%);bottom:-14px;top:auto;padding:11px 16px}
  .esn__big b{font-size:34px}
}

/* ============================================================
   PANEL ANATOMY
   The one place on the site where scrolling assembles something
   instead of uncovering it. The photograph pins; the parts list
   walks past it; a ring steps from component to component as each
   one comes up. A visitor who scrolls this has been given a tour
   of the hardware without clicking anything, which is the whole
   argument the page is trying to make.
   ============================================================ */
.anat{display:grid;grid-template-columns:minmax(0,1.02fr) minmax(0,.98fr);
  gap:clamp(30px,4vw,68px);align-items:start}

.anat__stage{position:sticky;top:calc(var(--navh) + 26px);margin:0;
  align-self:start;justify-self:center;width:100%;max-width:520px}
.anat__stage img{display:block;width:100%;height:auto;
  filter:drop-shadow(0 40px 70px rgba(0,0,0,.6))}

/* The ring. One element that travels, rather than nine that light up:
   a single moving marker reads as attention going somewhere, nine
   reads as a diagram with its labels switched on. */
.anat__ring{position:absolute;z-index:2;left:var(--x,50%);top:var(--y,50%);
  width:clamp(54px,7vw,78px);aspect-ratio:1;margin:0;
  transform:translate(-50%,-50%) scale(.6);opacity:0;
  border:1.5px solid var(--orange);border-radius:50%;
  box-shadow:0 0 0 1px rgba(3,16,26,.55),0 0 30px 6px rgba(232,73,43,.3);
  transition:left .58s var(--ease-out),top .58s var(--ease-out),
    opacity .3s linear,transform .45s var(--ease-out)}
.anat__stage.is-live .anat__ring{opacity:1;transform:translate(-50%,-50%) scale(1)}
.anat__ring::after{content:'';position:absolute;inset:-7px;border-radius:50%;
  border:1px solid rgba(255,138,86,.35);animation:anatping 2.4s var(--ease-out) infinite}
@keyframes anatping{0%{transform:scale(.86);opacity:.9}70%{transform:scale(1.5);opacity:0}100%{opacity:0}}

/* The bottom padding is runway, not whitespace. The stage stays pinned only
   while the grid row is taller than it is, so the list has to keep going past
   its last step or the photograph slides away while step nine is still being
   read. This is the shortest value that holds it through the end. */
.anat__steps{display:flex;flex-direction:column;gap:clamp(34px,7vh,74px);
  padding:6vh 0 34vh}
.anat__step{position:relative;padding-left:46px;
  opacity:.32;transition:opacity .45s var(--ease-out)}
.anat__step.is-on{opacity:1}
.anat__k{position:absolute;left:0;top:2px;margin:0;
  width:29px;height:29px;border-radius:50%;display:grid;place-items:center;
  font:600 10px/1 var(--mono);color:var(--blue-lt);
  background:var(--deep);border:1px solid rgba(126,196,238,.4);
  transition:color .4s linear,border-color .4s linear,background .4s linear}
.anat__step.is-on .anat__k{color:#fff;background:var(--orange);border-color:var(--orange)}
.anat__step h3{margin:0;font:600 clamp(17px,1.5vw,20px)/1.28 var(--sans);color:var(--ink)}
.anat__step p{margin:9px 0 0;font-size:14.5px;line-height:1.6;color:var(--muted)}
.anat__step .tile__note{margin-top:9px}

/* Marker coordinates, as a share of the photograph. Read off panel-basic.webp:
   01 controller, 02 modem, 03 e-Sens, 04 flow switch, 05 strainer,
   06 isolation valve, 07 sensor tees, 08 the assembled backboard,
   09 the branded faceplate. Replace the photograph and these move with it. */
.anat__step:nth-child(1){--x:37%;--y:23%}
.anat__step:nth-child(2){--x:79%;--y:26%}
.anat__step:nth-child(3){--x:75%;--y:56%}
.anat__step:nth-child(4){--x:45%;--y:8%}
.anat__step:nth-child(5){--x:53%;--y:92%}
.anat__step:nth-child(6){--x:22%;--y:83%}
.anat__step:nth-child(7){--x:45%;--y:61%}
.anat__step:nth-child(8){--x:46%;--y:44%}
.anat__step:nth-child(9){--x:79%;--y:32%}

@media(max-width:1000px){
  /* Sticky needs a tall viewport and a column to scroll past. On a phone the
     photograph would eat the screen and the list would crawl underneath it,
     so it becomes an ordinary figure and the steps become an ordinary list. */
  .anat{grid-template-columns:1fr;gap:26px}
  .anat__stage{position:static;max-width:340px;margin-inline:auto}
  .anat__ring{display:none}
  .anat__steps{padding:0;gap:26px}
  .anat__step{opacity:1}
  .anat__step .anat__k{color:#fff;background:var(--orange);border-color:var(--orange)}
}
@media(prefers-reduced-motion:reduce){
  .anat__step{opacity:1}
  .anat__ring::after{animation:none}
}

/* ---------- white label, brief (home page) ----------
   The full interactive demo was running on the home page and the Hub page,
   the same 1,069px block twice. The claim is what a first-time visitor needs
   and it needs to land fast; the type-your-own-name stage rewards somebody
   already evaluating, so it stays on the Hub page and the home page states
   the case and points there. */
.wlb__in{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(28px,4vw,58px);align-items:center}
.wlb__copy .h2{max-width:16ch}
.wlb__copy .lede{margin-top:16px;max-width:50ch}
.wlb__copy .btn{margin-top:26px}
.wlb__marks{display:grid;grid-template-columns:1fr 1fr;gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:11px;overflow:hidden}
.wlb__mark{padding:20px 22px;background:linear-gradient(170deg,rgba(11,39,57,.62),rgba(5,21,32,.62))}
.wlb__t{margin:0;font:600 10px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--blue-lt)}
.wlb__d{margin:10px 0 0;font-size:14px;line-height:1.5;color:var(--muted)}
@media(max-width:900px){
  .wlb__in{grid-template-columns:1fr}
}
@media(max-width:520px){
  .wlb__marks{grid-template-columns:1fr}
}

/* ---------- Hub card thumbnail ----------
   The other three cards show a physical object, so a screenshot was the
   obvious move for the software one. It does not survive the size: a 1400px
   light-mode capture at 118px tall is unreadable mush, and the white slab
   fights three products photographed on dark. A dashboard needs a symbol at
   thumbnail scale, not a miniature of itself, so this is a purpose-built
   fragment in the site's own palette carrying the numbers from the real Hub.
   Decorative: the card's heading and link already name the product. */
/* Light, not dark. The other three cards are pale objects lit against a dark
   ground, and a dark fragment read as a hole in the row rather than a fourth
   product sitting in it. The accent colours are the darker end of the brand
   palette, because the light versions used on the dark UI do not hold their
   contrast on white. */
.hubmini{width:90%;border-radius:8px;overflow:hidden;
  border:1px solid rgba(20,50,70,.14);background:#fff;
  box-shadow:0 16px 30px rgba(0,0,0,.5)}
.hubmini__bar{display:flex;gap:4px;padding:6px 9px;
  background:#eef2f5;border-bottom:1px solid rgba(20,50,70,.1)}
.hubmini__bar i{width:5px;height:5px;border-radius:50%;background:#c3ced6}
.hubmini__bar i:first-child{background:#e8492b}
.hubmini__bar i:nth-child(2){background:#e0a83a}
.hubmini__bar i:nth-child(3){background:#38c98a}
.hubmini__grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px;padding:8px}
.hubmini__t{display:flex;flex-direction:column;align-items:center;gap:3px;
  padding:8px 4px;border-radius:5px;
  background:#f5f8fa;border:1px solid rgba(20,50,70,.09);
  font:500 6.5px/1.2 var(--mono);letter-spacing:.1em;text-transform:uppercase;color:#6b8090}
.hubmini__t b{font:700 16px/1 var(--sans);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.hubmini__t--alarm b{color:#d63c1e}
.hubmini__t--ok b{color:#0f8a5c}
.hubmini__t--dev b{color:#1a6fa8}
.prowset .prow:hover .hubmini{transform:translateY(-4px);transition:transform .5s cubic-bezier(.16,1,.3,1)}

/* The header search was removed. It was on the marketing mock, but on a
   nine-page site where every page is one nav click away it answered a
   question nobody had, and with the blog unpublished it searched nothing.
   Bring it back when the blog has real volume, and build a search.html
   results template with it. */



/* ---------- expandable parameter rows ----------
   The table answered "what does it measure" but not "why would I care",
   which is the question a treater is actually asking. Each row opens into
   the meaning of the reading. Detail rows ship visible and JS hides them, so
   the content is never trapped behind a script. */
.prm__b{display:flex;align-items:center;gap:9px;width:100%;
  background:none;border:0;padding:0;margin:0;cursor:pointer;
  font:inherit;color:inherit;text-align:left}
.prm__cv{flex:none;width:13px;height:13px;position:relative}
.prm__cv::before,.prm__cv::after{content:'';position:absolute;background:var(--blue-lt);
  transition:transform .28s var(--ease),opacity .28s var(--ease)}
.prm__cv::before{left:0;top:6px;width:13px;height:1.5px}
.prm__cv::after{left:5.75px;top:0;width:1.5px;height:13px}
.prm.is-open .prm__cv::after{transform:scaleY(0);opacity:0}
.prm__b:hover{color:var(--blue-lt)}
.prm.is-open > th{color:var(--blue-lt)}
.prm__b:focus-visible{outline:2px solid var(--blue-lt);outline-offset:3px;border-radius:3px}
.prmd > td{padding:0 !important;border-top:0 !important}
.prmd__in{padding:2px 16px 18px 38px;max-width:78ch}
.prmd__k{margin:0 0 6px;font:600 9px/1 var(--mono);letter-spacing:.18em;
  text-transform:uppercase;color:var(--orange)}
.prmd__in p:last-child{margin:0;font-size:14px;line-height:1.62;color:var(--muted)}
@media(max-width:760px){.prmd__in{padding:2px 12px 16px 12px}}

/* ---------- rep capacity model ----------
   The whole argument in one screen: claim and controls left, one rep's service
   area right, the mechanism as a strip underneath.

   Distance rings rather than a map of anywhere. The argument is a week of
   driving, so distance from a base is the only geography that matters, and a
   multi-state map would tell a regional water treater that the goal is to
   become a national one. A 25-mile grid gives it the texture of a map without
   claiming a location.

   Emphasis, not categorical: today's book is the de-emphasis slot and reads
   gray, the accent carries the one thing the section is about. The legend
   names all three marks, so identity never rests on colour. Validated against
   the #03101a ground: CVD separation 15.4 deutan, 23.2 normal, both marks over
   3:1 contrast. */
/* The map is square, so whatever caps its width caps the section's height.
   The cap goes on the grid rather than on the figure: an auto margin or a
   justify-self on a grid item switches off stretch, and the figure collapses
   to the SVG's 300px intrinsic fallback. Scaling the whole grid keeps the two
   columns in proportion and keeps the section inside the screen: 1.78 is the
   grid width divided by the map track. */
/* The width used to be purely a function of viewport HEIGHT, so that the
   whole model fit one screen. On a wide, short window (1620x820 is the common
   laptop) that squeezed a two-column layout with a map, a slider, a headline,
   a number and a four-item list into about 980px while the page had 1240 to
   give: every part correct, the whole thing cramped. It still tracks height,
   but it will not go below a width the composition can actually breathe in. */
.cap{--cap-now:#8ba1b1;--cap-with:var(--orange);
  max-width:min(var(--max),max(1140px,(100vh - 234px) * 1.68));margin-inline:auto;
  display:grid;column-gap:clamp(26px,3vw,48px);row-gap:clamp(20px,2.2vw,30px);
  grid-template-columns:minmax(0,1fr) minmax(0,1.02fr);
  grid-template-areas:"txt map" "ctl map" "why why";
  grid-template-rows:auto 1fr auto;align-items:start}
/* the section is core blocks, so WordPress's flow margins have to go and the
   rhythm gets set here, where it can be reasoned about */
.cap>*{margin-block-start:0 !important;margin-block-end:0 !important}
.cap__txt{grid-area:txt}
/* the controls stretch the height of the map and let the money sink to its
   bottom edge, so the column has a top and a bottom instead of trailing off */
.cap__ctl{grid-area:ctl;align-self:stretch;display:flex;flex-direction:column}
.cap__fig{grid-area:map}
.cap__why{grid-area:why}

/* tighter than the page default: this section has to fit a screen */
.cap__txt>*{margin-block-start:0 !important;margin-block-end:0 !important}
.cap__txt .eyebrow{margin:0 0 12px !important}
.cap__txt .h2{font-size:clamp(25px,2.5vw,37px);line-height:1.1}
.cap__txt .lede{margin-top:13px !important;font-size:clamp(14px,1.05vw,15.5px);
  line-height:1.6;max-width:52ch}

/* ---- the editable sentence ---- */
.cap__say{margin:clamp(18px,2vw,26px) 0 0;
  font:500 clamp(17px,1.5vw,22px)/1.5 var(--sans);letter-spacing:-.014em;color:var(--ink)}
.cap__n{appearance:none;-webkit-appearance:none;background:none;border:0;border-radius:0;
  border-bottom:2px solid rgba(126,196,238,.42);
  color:var(--ink);font:inherit;font-variant-numeric:tabular-nums;
  padding:0 2px;margin:0;text-align:center;cursor:text;
  transition:border-color .2s,background .2s}
.cap__n:hover{background:rgba(126,196,238,.1);border-bottom-color:rgba(126,196,238,.75)}
.cap__n:focus{outline:none;background:rgba(61,155,215,.16);border-bottom-color:var(--blue)}
/* the currency mark belongs inside the field's underline, not beside it */
.cap__amt{display:inline-flex;align-items:baseline;padding:0 2px;
  border-bottom:2px solid rgba(126,196,238,.42);transition:border-color .2s,background .2s}
.cap__amt:hover{background:rgba(126,196,238,.1);border-bottom-color:rgba(126,196,238,.75)}
.cap__amt:focus-within{background:rgba(61,155,215,.16);border-bottom-color:var(--blue)}
.cap__amt .cap__n{border-bottom:0;padding:0}
.cap__amt .cap__n:hover,.cap__amt .cap__n:focus{background:none}
.cap__pre{color:var(--dim)}
/* the with-figure is the accent everywhere it appears, including here */
.cap__n--out{color:var(--orange-lt);border-bottom-color:rgba(255,106,45,.7)}
.cap__n--out:hover{background:rgba(255,106,45,.14);border-bottom-color:var(--orange)}
.cap__n--out:focus{background:rgba(255,106,45,.18);border-bottom-color:var(--orange)}
/* off-screen ruler the inline inputs are measured against */
.cap__mirror{position:absolute;left:-9999px;top:0;white-space:pre;
  visibility:hidden;pointer-events:none;font-variant-numeric:tabular-nums}

/* ---- the slider ----
   Dragging a number inside a sentence was never discoverable; a track with a
   thumb is. The floor is today's count, set in JS, so the orange fill is
   exactly the gain AquaCore is being credited with. The fill is a gradient
   stop driven from JS because no browser styles a range's lower half. */
.cap__slider{margin-top:clamp(16px,1.7vw,22px);margin-bottom:clamp(18px,2vw,26px)}
.cap__srow{display:flex;align-items:baseline;justify-content:space-between;
  gap:14px;margin:0 0 10px}
.cap__sl{font:500 9.5px/1.4 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:var(--dim);white-space:nowrap}
.cap__sl b{font-weight:600;margin-left:5px;font-size:12px}
.cap__sl--now{cursor:pointer}
.cap__sl--now b{color:var(--cap-now)}
.cap__sl--with b{color:var(--orange-lt)}
.cap__slider input[type=range]{-webkit-appearance:none;appearance:none;
  display:block;width:100%;height:9px;border-radius:5px;border:0;margin:0;
  background:linear-gradient(90deg,var(--orange) 0 var(--f,0%),
             rgba(226,238,246,.88) var(--f,0%) 100%);
  cursor:ew-resize;touch-action:pan-y}
.cap__slider input[type=range]:focus-visible{outline:2px solid var(--blue);outline-offset:4px}
.cap__slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
  width:19px;height:19px;border-radius:50%;background:var(--orange);border:0;cursor:ew-resize;
  box-shadow:0 0 0 3px rgba(3,16,26,.85),0 2px 8px rgba(0,0,0,.4)}
.cap__slider input[type=range]::-moz-range-thumb{width:19px;height:19px;border-radius:50%;
  background:var(--orange);border:0;cursor:ew-resize;
  box-shadow:0 0 0 3px rgba(3,16,26,.85),0 2px 8px rgba(0,0,0,.4)}
.cap__slider input[type=range]::-moz-range-track{background:transparent;height:9px}

/* ---- the callout ---- */
.cap__pan{margin-top:auto;padding-top:clamp(16px,1.7vw,22px);
  border-top:1px solid var(--line)}
.cap__pl{margin:0;font:500 9.5px/1.4 var(--mono);letter-spacing:.14em;
  text-transform:uppercase;color:var(--dim)}
.cap__pn{display:flex;align-items:baseline;flex-wrap:wrap;gap:4px 16px;margin:12px 0 0}
/* This number is the argument. At 50px it was four points bigger than the
   headline above it and lost to the map beside it, so a glance landed on
   nothing in particular and the section had to be studied to be understood. */
.cap__pn span{font:700 clamp(46px,5.4vw,86px)/.9 var(--sans);letter-spacing:-.045em;
  color:var(--ink);font-variant-numeric:tabular-nums}
.cap__pn em{font-style:normal;font:600 13px/1.4 var(--mono);letter-spacing:.05em;color:var(--orange-lt)}
/* a prompt is not a result: it should not shout in the accent colour */
.cap__pn em.is-flat{color:var(--dim)}
.cap__pnote{margin:12px 0 0;font:400 13.5px/1.6 var(--sans);color:var(--muted)}
.cap__pnote span:not(:empty){display:block;margin-top:5px;font:400 11px/1.6 var(--mono);color:var(--dim)}

/* ---- the map ---- */
/* No card. A bordered panel made the figure read as a widget dropped onto the
   page; the disc is its own shape and needs no rectangle around it. The depth
   the panel was providing moves inside the SVG as a radial glow on the disc,
   so the figure still has a body without having an edge. */
.cap__fig{position:relative;margin:0;border:0;background:none}
/* A soft filled field, not a ring: it gives the figure a body without a hard
   edge, and it mutes the section's own background grid so the map is not read
   through a second grid. */
.cap__glow{fill:url(#capGlow)}
/* Cartography, generated rather than drawn by hand.
   Two perfect concentric circles read as air-traffic control; abstract
   crossing lines read as scribble. What makes a picture a map is structure:
   water that rivers run into, roads that connect towns to each other, county
   lines that ignore both. Towns are placed with a spacing rule, the road
   network is a minimum spanning tree over them plus a few loops, and every
   line is midpoint-displaced and Catmull-Rom smoothed so nothing is straight.
   The place is invented on purpose: real geography would put AquaCore
   somewhere, and this argument is about drive time, not about Wisconsin. */
/* Contrast is the whole design here. The first pass drew the map at the same
   weight as the data and the section stopped being a chart: 45 roads, 17 river
   segments and 18 town names competed with the one thing the reader is meant
   to see, which is gray pins inside a boundary and orange pins outside it.
   So the cartography runs at roughly a third of its natural strength and every
   piece of map text is off. It is texture. The map's job is to say "this is
   ground a truck drives across"; the moment it says anything else it is
   stealing attention from the argument. */
.cap__geo path,.cap__geo use{fill:none;vector-effect:non-scaling-stroke}
/* Water is the lighter surface and land is the darker one, which is the
   single decision that makes a shape read as a coast rather than as a blob.
   The sea is painted across the whole field and the landmass is knocked back
   out of it, so the coastline is a by-product of the land shape instead of a
   line somebody drew. */
.cap__geo .sea{fill:rgba(86,164,214,.17);stroke:none}
.cap__geo .land{fill:rgba(3,16,26,.94)}
.cap__geo .coastglow{stroke:rgba(126,196,238,.10);stroke-width:5}
.cap__geo .coast{stroke:rgba(126,196,238,.5);stroke-width:1.2}
/* Contours are the texture that fills the land. They carry no information the
   reader is meant to extract, which is exactly why they work: they read as
   terrain at a glance and as nothing at all on inspection. */
.cap__geo .ctr path{stroke:rgba(126,196,238,.085);stroke-width:.65}
.cap__geo .riv path{stroke:rgba(110,180,230,.22);stroke-width:1;stroke-linecap:round}
.cap__geo .rd path{stroke:rgba(226,240,250,.07);stroke-width:.8;stroke-linecap:round}
.cap__geo .hwy path{stroke:rgba(226,240,250,.16);stroke-width:1.3;stroke-linecap:round}
.cap__geo .grd path{stroke:rgba(226,240,250,.09);stroke-width:.55}
.cap__title{fill:var(--dim);font:500 10px var(--mono);letter-spacing:.2em;
  text-transform:uppercase}
.cap__map{display:block;width:100%;height:auto;aspect-ratio:1}
/* No knockout stroke on any of the map's text. The disc is lighter than the
   page, so a near-black halo does not disappear into it, it reads as a dark
   blob around every label. Every label now sits clear of the marks instead. */
/* the reach ring closes just outside the farthest account being served */
/* The without state gets an edge of its own; the with state closes outside it.
   Both are the same irregular boundary drawn once at full size and resized by
   transform, because an SVG path's `d` cannot be transitioned but a transform
   can. Stroke width is held constant against the scale so the outline does not
   thicken as the territory grows. */
/* No transform transition. The boundary changes shape as well as size now, so
   the tween happens in JS on the path data; a CSS transform on top of it would
   fight the frame it is drawing. */
.cap__ringnow,.cap__reach{vector-effect:non-scaling-stroke;stroke-linejoin:round}
/* No fill on either boundary. A translucent fill laid over the map flattens
   everything under it, and it was what made the first cartographic pass read
   as mud: the map was fine, it was being looked at through two sheets of
   tinted glass. Outlines only, the way a territory is drawn on a real map. */
.cap__ringnow{fill:none;stroke:rgba(139,161,177,.75);stroke-width:1.4;
  stroke-dasharray:4 5}
.cap__reach{fill:none;stroke:var(--cap-with);stroke-width:2.4}
/* The pins carry a hairline of the disc's own ground colour. Not a knockout
   halo: at this width it reads as separation, and without it a gray pin
   landing on a highway loses its edge. */
/* No radius here. Setting a circle's r from CSS is legal and Chrome honours
   it, but Safari does not apply it to these nodes, and the circles carry no r
   attribute of their own, so on iOS every account rendered at radius zero and
   the whole book vanished. Only the rep marker survived, because its radius
   is an attribute. The radius is set as an attribute in motion.js now, which
   also lets it grow on a phone; this rule only animates it. */
.cap__pin{fill:var(--cap-now);stroke:rgba(4,18,30,.55);stroke-width:.9;
  transition:r .35s var(--ease-out),fill .3s}
.cap__pin.is-gain{fill:var(--cap-with)}
.cap__base{fill:var(--ink)}
.cap__baset{fill:var(--dim);font:500 9px var(--mono);letter-spacing:.16em}
.cap__scale line{stroke:rgba(126,196,238,.4);stroke-width:1}
.cap__scale text{fill:var(--dim);font:500 9px var(--mono);letter-spacing:.16em}

/* Each state is named on the ring it describes rather than in a key. A key
   placed after the figure gets read after the reader has already given up
   decoding it, and a label in a corner is too far from a ring to read as
   belonging to it. Count first, state second: the eye lands on the number. */
.cap__lab text{text-anchor:middle}
.cap__labn{font:600 17px/1 var(--sans);letter-spacing:-.01em}
.cap__labp{font:500 9.5px/1 var(--mono);letter-spacing:.17em}
.cap__lab--now .cap__labn{fill:var(--cap-now)}
.cap__lab--now .cap__labp{fill:var(--dim)}
.cap__lab--with .cap__labn{fill:var(--ink)}
.cap__lab--with .cap__labp{fill:var(--orange-lt)}

/* ---- the mechanism, as a numbered strip rather than a claim ---- */
.cap__why{list-style:none;margin:clamp(6px,1vw,14px) 0 0 !important;padding:0;counter-reset:cw;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  gap:clamp(16px,2vw,30px);
  background:none;border:0;border-radius:0}
/* Four dense grey paragraphs in a hard-edged band were the least attractive
   thing in the frame and sat directly under the payoff, competing with it.
   They are footnotes to the number, so they read as footnotes. */
.cap__why li{counter-increment:cw;margin:0;padding:14px 0 0;
  border-top:1px solid var(--line);
  background:none;font:400 12.5px/1.55 var(--sans);color:var(--dim)}
.cap__why li::before{content:'0' counter(cw);display:block;margin-bottom:6px;
  font:500 9px/1 var(--mono);letter-spacing:.14em;color:var(--orange)}

/* Short viewports: the section is meant to land in one screen, so the type
   scale and the map cap both come down rather than letting it run past the
   fold. Keyed off height, because height is the constraint. */
@media(max-height:900px){
  .cap{row-gap:18px}
  #capacity{padding-bottom:clamp(28px,3vw,46px)}
  .cap__txt .h2{font-size:clamp(23px,2.1vw,31px)}
  .cap__txt .lede{font-size:14px;max-width:46ch;margin-top:11px !important}
  .cap__say{margin-top:16px;font-size:clamp(16px,1.3vw,19px)}
  .cap__slider{margin-top:14px}
  .cap__slider label{margin-bottom:8px}
  .cap__pan{padding-top:14px}
  /* Still the loudest thing in the section on a short screen. It was 40px
     here, nine points off the headline, which is not a hierarchy. */
  .cap__pn span{font-size:clamp(40px,4.4vw,68px)}
  .cap__pnote{margin-top:9px;font-size:12.5px}
  .cap__more{margin-top:12px}
  .cap__why li{padding:12px 0 0;font-size:12px}
}
@media(max-height:760px){
  .cap__txt .lede{display:none}
}

/* one column below this: the map and the argument each need the full width */
@media(max-width:900px){
  .cap{max-width:none;grid-template-columns:1fr;grid-template-areas:"txt" "map" "ctl" "why";
    grid-template-rows:auto auto auto auto;row-gap:24px}
  /* No auto margin here. An auto margin on a grid item switches off stretch,
     the figure falls back to the SVG's 300px intrinsic width, and the map
     renders at half the column it was given. */
  .cap__fig{max-width:none}
  .cap__say{margin-top:0}
}
/* One column but plenty of it: left alone the map runs to the full tablet
   width, and its type scales with the SVG, so the state labels end up larger
   than the headline above them. */
@media(min-width:501px) and (max-width:900px){
  .cap__fig{max-width:500px}
}
/* Below this the column is narrow enough that the SVG's type scales down to
   8px and 5px and the state labels stop being readable. Sizes are viewBox
   units, picked so the rendered result lands near 16px and 9px on a 390px
   screen. */
@media(max-width:500px){
  .cap__labn{font-size:26px}
  .cap__labp{font-size:15px}
  /* at this size the title runs into the centred state label, so it drops
     below it rather than sharing the line */
  /* clear of the WITHOUT AQUACORE label, which is centred just above it */
  .cap__title{font-size:16px;transform:translateY(62px)}
  .cap__baset{font-size:14px}
  .cap__scale text{font-size:14px}
  /* the bigger type needs room the original positions did not leave */
  .cap__lab--now{transform:translateY(11px)}
  .cap__lab--with{transform:translateY(-5px)}
}
@media(prefers-reduced-motion:reduce){
  .cap__reach,.cap__pin{transition:none}
}

/* ---------- benefit cards that lead somewhere ----------
   A real link on the heading, so it is keyboard reachable and announced as a
   link, with the anchor's own box stretched over the whole card so the card is
   the target. No arrow and no colour shift: the cards were signed off looking
   exactly as they do, and the affordance is the cursor. */
.tile--jump h3 a{color:inherit;text-decoration:none}
.tile--jump h3 a::after{content:'';position:absolute;inset:0;z-index:1}
.tile--jump:hover{border-color:rgba(126,196,238,.34)}
.tile--jump h3 a:focus-visible{outline:none}
.tile--jump:focus-within{border-color:var(--blue);
  box-shadow:0 0 0 2px rgba(61,155,215,.35)}
/* the sticky header would otherwise cover the heading a jump lands on */
#capacity{scroll-margin-top:calc(var(--navh) + 24px)}


/* ---------- legal pages ----------
   One measured column of prose. Nothing here is a marketing surface, so it
   gets none of the section furniture: no tiles, no reveal choreography, just
   a comfortable line length and enough air between headings that somebody can
   find the paragraph their lawyer asked about. */
.phero--slim{padding-bottom:clamp(20px,2.4vw,30px)}
.phero--slim h1{font-size:clamp(28px,3.2vw,42px)}
/* The measure goes on the children, not the wrap. Narrowing the wrap itself
   re-centres it and the whole policy drifts right of the H1 above it. */
.legal>*{margin-block-start:0;max-width:74ch}
.legal p{margin-top:15px;font-size:15px;line-height:1.72;color:var(--muted)}
.legal p a{color:var(--blue-lt)}
.legal .h3{margin-top:44px;padding-top:22px;border-top:1px solid var(--line);
  font-size:clamp(19px,1.8vw,23px);line-height:1.25;color:var(--ink)}
.legal h3{margin-top:26px;font-size:15.5px;line-height:1.35;color:var(--ink)}
.legal .h3+p,.legal h3+p{margin-top:9px}
.legal__date{margin-top:0 !important;font:500 10px/1 var(--mono);
  letter-spacing:.16em;text-transform:uppercase;color:var(--dim)}
.legal .checks{margin-top:15px}
/* The contact block reads as a record rather than a sentence, so it gets the
   mono face the rest of the site uses for anything that is data. */
.legal__addr{margin-top:16px !important;padding-left:15px;
  border-left:2px solid var(--orange);
  font:400 13px/1.85 var(--mono) !important;color:var(--ink) !important;
  letter-spacing:.01em}
.legal__addr a{color:var(--blue-lt);text-decoration:none}
.legal__addr a:hover{text-decoration:underline}
@media(max-width:600px){.legal p{font-size:14.5px}}

/* ============================================================
   PHONE, 390 AND DOWN
   The first four screens were composed in a desktop window and
   then left to reflow. This block treats a phone as its own
   layout rather than a narrow version of that one. Scoped to the
   top of the page: hero, stat row, Hub panel, e-Sens instrument.
   Appended last on purpose, so it wins against the earlier
   breakpoints without raising anyone's specificity.
   ============================================================ */

@media(max-width:600px){

  /* --- the Hub panel --------------------------------------------
     On a wide screen the panel is rotated and bleeds off the right
     edge by design, and heroFit() writes an inline width to land it
     a fixed gap short of the glass. Stacked, all of that is supposed
     to drop. An inline width beats a stylesheet, so the only thing
     that can guarantee the panel never reaches past a phone screen
     is !important here. Cheap insurance on the first thing a
     visitor sees. */
  .hero__media .hero__fleet{
    width:auto !important;
    max-width:100% !important;
    margin-left:0 !important;
    margin-right:0 !important;
    transform:none !important}
  .hero__media{--fleet-x:0; perspective:none}
  /* and a hard floor: nothing in the hero may scroll the page sideways */
  .hero{overflow-x:clip}

  /* Clipped tile labels were the symptom of the panel overflowing.
     Let them wrap instead of running out of their cell, so the
     reading survives even if something else gets wide later. */
  .fleet__grid .ft{gap:9px;padding:10px 10px}
  .ft__t{min-width:0}
  .ft__t small{white-space:normal;overflow-wrap:anywhere;line-height:1.25}

  /* --- the first screen ------------------------------------------
     Everything above was a desktop composition reaching its clamp()
     floor: a 78px headline bottoming out at 33, a 19px lede bottoming
     out at 15, and two auto-width buttons sitting side by side because
     there happened to be room. The result reads as a laptop page seen
     through a letterbox. These rules set phone values on purpose
     rather than inheriting the smallest desktop ones.

     The headline grows and the body shrinks, so the ratio between them
     goes from 2.2:1 to roughly 2.9:1. Hierarchy is what a small screen
     has instead of space. */
  .hero h1{font-size:clamp(38px,10.8vw,46px);line-height:1.0;letter-spacing:-.042em}
  .hero .eyebrow{font-size:9.5px;letter-spacing:.2em;margin-bottom:14px;gap:7px}
  .hero .eyebrow::before{width:16px}
  .hero__sub{margin-top:16px;font-size:15px;line-height:1.58;max-width:36ch}

  /* Full width and stacked. Two auto-width buttons on one line is a
     desktop habit; a phone wants one unambiguous target per row, at a
     size a thumb hits without aiming. This single change does more to
     make the page feel native than anything else here. */
  .hero__cta{display:grid;grid-template-columns:1fr;gap:10px;margin-top:26px}
  .hero__cta .btn{width:100%;justify-content:center;padding:17px 20px;font-size:15px}

  /* --- the stat row ---------------------------------------------
     A wrapping flex row gave two stats then one, which reads as an
     accident. Three equal columns divided by hairlines reads as a
     spec strip, which is what it is, and it costs one line instead
     of two. Columns are minmax(0,1fr) so a long label wraps inside
     its cell rather than pushing the row off the screen. */
  .hero__stat{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
    gap:0;margin-top:24px;padding-top:16px}
  .hero__stat > *{padding-inline:11px;border-left:1px solid var(--line)}
  .hero__stat > *:first-child{padding-left:0;border-left:0}
  .hero__stat > *:last-child{padding-right:0}
  .hero__stat b,.hero__stat .stat__n{font-size:18px;letter-spacing:-.02em;white-space:normal}
  .hero__stat small,.hero__stat .stat__l{font-size:7.5px;letter-spacing:.09em;line-height:1.3}

  /* 12vh of empty photograph above the eyebrow is a laptop gesture.
     On a 686px screen it was 150px, a fifth of the view, spent before
     the page says anything. */
  .hero{padding-top:calc(var(--navh) + clamp(16px,4.5vh,52px))}

  /* --- the sticky bar -------------------------------------------
     FLEET VIEW / ANALYTICS / AI REPORT wrapped to a second line at
     390, which took the bar to roughly 95px and charged that to
     every screen on the site. Hold it on one line and give the
     page back the difference. */
  .mcta__t span{white-space:nowrap;font-size:8px;letter-spacing:.07em}
  .mcta__t b{font-size:13px}
  .mcta{padding:9px 13px calc(9px + env(safe-area-inset-bottom,0px))}
  body{padding-bottom:calc(80px + env(safe-area-inset-bottom,0px))}
}

@media(max-width:560px){

  /* --- the analyzer ---------------------------------------------
     Stacked, the photograph ran 250px wide and about 370 tall with
     the callout floating under it: a full screen of scroll that
     makes a single point. Side by side it makes the same point in
     half the height, and the number sits next to the instrument
     instead of drifting below it. The badge loses its card here
     because at this size a bordered panel beside a photograph is
     two objects competing, where plain figures read as a caption. */
  .esn__media{display:flex;align-items:center;justify-content:center;
    gap:18px;width:auto;max-width:340px;
    margin-block:clamp(-30px,-5vw,-14px) 0;margin-inline:auto}
  .esn__shot{flex:none}
  .esn__shot img{width:128px;
    filter:drop-shadow(0 22px 34px rgba(0,0,0,.65)) drop-shadow(0 0 40px rgba(61,155,215,.14))}
  .esn__big{position:static;transform:none;left:auto;right:auto;bottom:auto;
    flex-direction:column;align-items:flex-start;gap:2px;
    padding:0;background:none;border:0;box-shadow:none}
  .esn__big b{font-size:46px;line-height:.85}
  .esn__big span{font-size:9px;letter-spacing:.13em}
  /* the ring was sized around a 250px photograph */
  .esn__media::before{width:min(430px,112vw);top:50%}
  /* the button ping was aimed at the old photograph's centre */
  .esn__media::after{display:none}
}
