/* ==========================================================================
   Nxtcell Mobility, flow layer
   One idea, carried the whole height of every page: signal moving downward,
   and nothing on screen ever completely still.

     .spine       a rail flush against the left edge of the viewport, drawn
                  by scroll, with packets running down it continuously
     section tick a mark that extends from the edge as each section arrives
     .flowlines   soft descending light inside the dark bands
     drift        headings, media, cards and cells move against each other
                  as the page scrolls, at different rates

   Everything here animates translate, scale, opacity or clip-path. Nothing
   runs a scroll listener. All of it stops under prefers-reduced-motion.

   NOTE ON PROPERTIES: this layer uses the independent `translate` and
   `scale` properties rather than `transform`, so it composes with the
   arrival animations in elevate.css instead of replacing them. A card can
   rise in on arrival AND drift on scroll at the same time.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. MOTION SCALE
   Every keyframe below reads these, so the whole system rescales in one
   place. A phone screen is a third the height of a desktop one, and the
   same travel that reads as depth on a monitor reads as a lurch on a
   handset, so the amplitudes come down rather than the motion coming off.
   -------------------------------------------------------------------------- */
:root{
  --d-head:30px;
  --d-head-up:24px;
  --d-media:36px;
  --d-settle:1.11;
  --d1:26px;
  --d2:18px;
  --d2b:24px;
  --d3:40px;
  --d4:62px;
  --d-row:26px;
  --spine-w:3px;
  --tick-w:16px;
}
@media (max-width:900px){
  :root{
    --d-head:15px;
    --d-head-up:12px;
    --d-media:18px;
    --d-settle:1.06;
    --d1:13px;
    --d2:9px;
    --d2b:12px;
    --d3:20px;
    --d4:28px;
    --d-row:18px;
    --spine-w:2px;
    --tick-w:10px;
  }
}

/* --------------------------------------------------------------------------
   1. THE SPINE
   Flush to the viewport edge, so it can never cross a word or a product.
   -------------------------------------------------------------------------- */
.spine{
  position:fixed;
  top:0;bottom:0;left:0;
  width:var(--spine-w);
  z-index:2;
  pointer-events:none;
}

.spine i{display:block}

.spine__rail{
  position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 0%,rgba(10,24,38,.09) 6%,rgba(10,24,38,.09) 94%,transparent 100%);
}
.spine__draw{
  position:absolute;inset:0;
  background:linear-gradient(180deg,var(--blue) 0%,var(--cyan) 100%);
  box-shadow:0 0 12px -2px rgba(29,156,215,.55);
  transform-origin:0 0;
  transform:scaleY(0);
}
/* A blurred shadow on an element that moves every frame is paint a phone
   cannot spare, and at 2px wide it is not visible there anyway. */
@media (max-width:900px){
  .spine__draw,.spine__pk{box-shadow:none}
}
@supports (animation-timeline:scroll()){
  .spine__draw{
    animation:spineDraw linear both;
    animation-timeline:scroll(root block);
  }
  @keyframes spineDraw{from{transform:scaleY(0)}to{transform:scaleY(1)}}
}

/* the packets: the part that never stops */
.spine__pk{
  position:absolute;left:0;top:0;
  width:var(--spine-w);height:70px;
  background:linear-gradient(180deg,rgba(111,198,236,0),rgba(140,214,246,.9) 46%,#fff 74%,rgba(255,255,255,0));
  box-shadow:0 0 14px 1px rgba(111,198,236,.55);
  opacity:0;
  animation:spineRun 5.6s linear infinite;
}
.spine__pk--b{animation-duration:8.1s;animation-delay:-2.4s;height:42px;opacity:.75}
.spine__pk--c{animation-duration:10.4s;animation-delay:-6.2s;height:26px}
@keyframes spineRun{
  0%{translate:0 -90px;opacity:0}
  7%{opacity:1}
  90%{opacity:1}
  100%{translate:0 100vh;opacity:0}
}

/* --------------------------------------------------------------------------
   2. SECTION TICKS
   Each section extends a short mark from the edge rail as it arrives. A mark
   rather than a dot, because a dot on the rail would be half off screen.
   -------------------------------------------------------------------------- */
.section{position:relative}
.section::before{
  content:"";
  position:absolute;z-index:2;
  left:0;
  top:clamp(72px,7vw,110px);
  width:var(--tick-w);height:2px;
  background:var(--blue);
  transform:scaleX(0);
  transform-origin:0 50%;
  transition:transform .55s var(--ease-drawer);
}
.section.in-view::before{transform:scaleX(1)}
.band::before,.globe::before,.upcoming::before,.teaser::before{background:var(--cyan)}

/* --------------------------------------------------------------------------
   3. DESCENDING LIGHT
   A hairline carrying a bright packet that falls the height of the band. The
   line is held at 7% alpha so it reads as structure behind the copy rather
   than as a rule across it. The product rail is excluded in site.js: its
   panels are translucent, so anything behind them cuts across the product.
   -------------------------------------------------------------------------- */
.flowlines{
  position:absolute;inset:0;
  overflow:hidden;
  pointer-events:none;
  z-index:0;
}
.flowlines i{
  position:absolute;top:0;bottom:0;width:1px;
  background:linear-gradient(180deg,transparent 0%,rgba(111,198,236,.07) 16%,rgba(111,198,236,.07) 84%,transparent 100%);
}
.flowlines i::after{
  content:"";
  position:absolute;left:-1px;top:0;
  width:3px;height:88px;border-radius:2px;
  background:linear-gradient(180deg,rgba(111,198,236,0),rgba(111,198,236,.42) 46%,rgba(150,222,248,.72) 74%,rgba(111,198,236,0));
  animation:lineFall var(--dur,7s) linear var(--delay,0s) infinite;
  opacity:0;
}
@keyframes lineFall{
  0%{translate:0 -130px;opacity:0}
  9%{opacity:1}
  88%{opacity:1}
  100%{translate:0 var(--fall,1100px);opacity:0}
}

/* --------------------------------------------------------------------------
   4. DRIFT
   The whole page moves as you scroll, in layers at different rates. This is
   what makes a section feel like it is travelling past rather than sitting
   still while the window slides over it.
   -------------------------------------------------------------------------- */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){

    /* headings lead, media trails */
    .section-head,.pagehead .wrap{
      animation:driftUp linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes driftUp{
      from{translate:0 var(--d-head)}
      to{translate:0 calc(var(--d-head-up) * -1)}
    }

    .split__media{
      animation:driftDown linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes driftDown{
      from{translate:0 calc(var(--d-media) * -1)}
      to{translate:0 var(--d-media)}
    }

    /* the picture settles into its frame as it arrives, then holds */
    .split__media img,.bracket img{
      animation:settle linear both;
      animation-timeline:view();
      animation-range:entry 0% contain 22%;
    }
    @keyframes settle{
      from{scale:var(--d-settle)}
      to{scale:1}
    }

    /* the eyebrow leads its own heading in by a hair */
    .eyebrow{
      animation:driftLead linear both;
      animation-timeline:view();
      animation-range:entry 10% cover 40%;
    }
    @keyframes driftLead{
      from{opacity:0;translate:0 calc(var(--d2) * .9)}
      to{opacity:1;translate:0 0}
    }

    /* --- LAYERED DRIFT --------------------------------------------------
       Whole groups move, never the pieces inside them. A card grid has a
       gap and the NXTPAPER points share a top border: offsetting siblings
       against each other closes the gap in one and breaks the line in the
       other. So the container carries the motion and everything inside it
       stays exactly as laid out. */
    .grid,.nxtp,.meta,.feat{
      animation:flowA linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes flowA{
      from{translate:0 var(--d1)}
      to{translate:0 calc(var(--d1) * -1)}
    }
    @keyframes flowB{
      from{translate:0 calc(var(--d2) * -1)}
      to{translate:0 var(--d2b)}
    }
    @keyframes flowC{
      from{translate:0 var(--d3)}
      to{translate:0 calc(var(--d3) * -1)}
    }
    /* the bento travels the other way from the heading above it */
    .feat{animation-name:flowB}
    /* and its copy column travels against its own picture */
    .feat__cell--lead .feat__body{
      animation:flowC linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }

    /* the dot field sits behind the page and moves slower than it */
    .band .sigfield,.globe .sigfield,.upcoming .sigfield,.teaser .sigfield{
      animation:fieldPar linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes fieldPar{
      from{translate:0 -7%}
      to{translate:0 7%}
    }
    @media (max-width:900px){
      @keyframes fieldPar{
        from{translate:0 -4%}
        to{translate:0 4%}
      }
    }

    /* the logo panel and the teaser art travel furthest of all */
    .nxpanel,.teaser__media{
      animation:flowFar linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes flowFar{
      from{translate:0 calc(var(--d4) * -1)}
      to{translate:0 var(--d4)}
    }
  }
}

/* --------------------------------------------------------------------------
   5. LISTS AND FORM FIELDS RESOLVE DOWNWARD
   Scrubbed by scroll rather than fired on a timer, so the sequence follows
   the reader's own pace down the page.
   -------------------------------------------------------------------------- */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .checks li,.form .field,.form .form-row{
      animation:rowIn linear both;
      animation-timeline:view();
      animation-range:entry 4% cover 30%;
    }
    @keyframes rowIn{
      from{opacity:0;transform:translate3d(0,var(--d-row),0)}
      to{opacity:1;transform:none}
    }
    .checks li:nth-child(2),.form > *:nth-child(2){animation-range:entry 9% cover 35%}
    .checks li:nth-child(3),.form > *:nth-child(3){animation-range:entry 14% cover 40%}
    .checks li:nth-child(4),.form > *:nth-child(4){animation-range:entry 19% cover 45%}
    .checks li:nth-child(5),.form > *:nth-child(5){animation-range:entry 24% cover 50%}
    .checks li:nth-child(6),.form > *:nth-child(6){animation-range:entry 29% cover 55%}
  }
}

/* --------------------------------------------------------------------------
   6. POINTER TRACKING
   Fine pointers only, because there is nothing to track on a touchscreen.
   --px / --py are written by one delegated pointermove in site.js.

   .track  a panel leans toward the cursor and catches a highlight
   .glow   a dark band carries a soft light that follows the cursor
   -------------------------------------------------------------------------- */
.band.glow,.globe.glow,.upcoming.glow{isolation:isolate}
.glow__light{
  position:absolute;inset:0;
  z-index:0;
  pointer-events:none;
  opacity:0;
  transition:opacity .5s var(--ease-out);
}

@media (hover:hover) and (pointer:fine) and (prefers-reduced-motion:no-preference){
  .track{
    transform-style:preserve-3d;
    transition:transform .5s var(--ease-out);
  }
  .track.is-tracking{
    transform:perspective(1000px)
              rotateX(calc(var(--ty,.5) * -5.4deg + 2.7deg))
              rotateY(calc(var(--tx,.5) * 5.4deg - 2.7deg))
              translateZ(10px);
    transition:transform .12s linear;
    z-index:2;
  }
  .track::after{
    content:"";
    position:absolute;inset:0;
    pointer-events:none;
    z-index:3;
    opacity:0;
    background:radial-gradient(400px circle at calc(var(--tx,.5) * 100%) calc(var(--ty,.5) * 100%),
      rgba(120,200,240,.20),rgba(120,200,240,.07) 40%,transparent 66%);
    transition:opacity .4s var(--ease-out);
  }
  .track.is-tracking::after{opacity:1}
  .glow__light{
    background:radial-gradient(540px circle at calc(var(--px,.5) * 100%) calc(var(--py,.5) * 100%),
      rgba(111,198,236,.16),rgba(111,198,236,.06) 38%,transparent 66%);
  }
  .glow.is-tracking .glow__light{opacity:1}
}

/* --------------------------------------------------------------------------
   6b. THE SAME TWO IDEAS ON TOUCH
   A phone has no cursor, so neither effect can follow one. The light in the
   dark bands follows the scroll instead, travelling down the band as the
   reader does; the panels answer a press rather than a hover. The effect is
   the same idea in the language the device actually has.
   -------------------------------------------------------------------------- */
@media (hover:none),(pointer:coarse){
  .glow__light{
    opacity:1;
    background:radial-gradient(520px circle at 64% 50%,
      rgba(111,198,236,.15),rgba(111,198,236,.055) 38%,transparent 66%);
  }
  @supports (animation-timeline:view()){
    @media (prefers-reduced-motion:no-preference){
      .glow__light{
        animation:glowSweep linear both;
        animation-timeline:view();
        animation-range:cover 0% cover 100%;
      }
      @keyframes glowSweep{
        from{translate:0 -34%}
        to{translate:0 34%}
      }
    }
  }
  .track{transition:transform .32s var(--ease-out),border-color .32s var(--ease-out)}
  .track:active{transform:scale(.985)}
}

/* --------------------------------------------------------------------------
   7. REDUCED MOTION
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  .spine__pk{display:none;animation:none}
  .spine__draw{transform:scaleY(1);animation:none;opacity:.3}
  .flowlines{display:none}
  .section-head,.pagehead .wrap,.split__media,.split__media img,.bracket img,.eyebrow,
  .grid,.nxtp,.meta,.feat,.feat__body,
  .nxpanel,.teaser__media,.band .sigfield,.globe .sigfield,.upcoming .sigfield,.teaser .sigfield{
    animation:none!important;translate:none!important;scale:none!important;opacity:1!important;
  }
  .checks li,.form .field,.form .form-row{animation:none!important;opacity:1!important;transform:none!important}
  .section::before{transform:scaleX(1);transition:none}
  .track{transform:none!important}
  .track::after{display:none}
  .glow__light{animation:none;translate:none}
}

/* ==========================================================================
   8. TOUCH ERGONOMICS
   Everything a thumb has to hit is given room to be hit. The rules are
   scoped to coarse pointers so a mouse still gets the tighter desktop
   rhythm, and they add padding rather than changing type size, so nothing
   about the design shifts.
   ========================================================================== */
@media (pointer:coarse){
  .site-footer ul li a{
    display:flex;
    align-items:center;
    min-height:44px;
  }

  .meta a{
    display:inline-flex;
    align-items:center;
    min-height:44px;
  }

  .arrowlink{
    display:inline-flex;
    align-items:center;
    min-height:44px;
  }

  /* The carousel dots are small by design; the hit area is not. The button
     box itself carries it (40x44), so nothing is painted here: the ::after
     on these buttons is the dwell rail, and a hit-area box drawn on top of
     it used to fight that rail for geometry. */
  .banner__dots button{position:relative}

  .nav__links a{min-height:48px;display:flex;align-items:center}
}

/* the page never runs under a notch or a home indicator */
@supports (padding:max(0px)){
  .wrap{
    padding-left:max(var(--pad),env(safe-area-inset-left));
    padding-right:max(var(--pad),env(safe-area-inset-right));
  }
  .site-footer .footer__bar{padding-bottom:max(0px,env(safe-area-inset-bottom))}
}
