/* ==========================
   MOTION LAYER
   Loads after styles.css. Works with motion.js (pointer glow, panel tilt, scroll line)
   and globe.js (the live hero globe). Every effect is decorative: without JS, WebGL or
   motion, the page keeps the finished look from styles.css.
   ========================== */

/* ---- Hero globe (globe.js) ----
   --globe-d is the sphere's diameter; the canvas is 1.55x wider for the halo and orbit.
   --gcx / --gcy place the sphere's centre, measured from the hero's left and top edges.
   The whole sphere stays in view at every size. */
.hero-globe {
  --globe-d: 46rem;
  --gcx: calc(100% - 16rem);
  --gcy: 26rem;
  position: absolute;
  left: calc(var(--gcx) - var(--globe-d) * 0.775);
  top: calc(var(--gcy) - var(--globe-d) * 0.775);
  width: calc(var(--globe-d) * 1.55);
  height: calc(var(--globe-d) * 1.55);
  z-index: -1;
  pointer-events: none;
}

/* The live globe replaces the CSS orb and its halo. With JavaScript on, the orb is held back
   from the first paint, so its 266 KB texture (assets/earth-texture.jpg) is never downloaded
   (display: none, because Chrome still downloads the background of a content: none pseudo-element).
   globe.js adds .no-globe to the hero when WebGL2 is missing or fails, which brings the orb back. */
.js .hero:not(.no-globe)::before,
.js .hero:not(.no-globe)::after {
  content: none;
  display: none;
}

/* Two columns: the globe sits behind the service panel, a little larger than it and a little
   to the right, so its round edge shows on both sides and it glows through the glass */
@media (width > 1080px) {
  .hero-globe {
    --globe-d: clamp(30rem, calc(100vh - 13rem), 46rem);
    /* A little right of the panel's centre, so Indonesia shows past the panel's right edge */
    --gcx: calc(var(--panel-cx, calc(100% - 16rem)) + var(--globe-d) * 0.22);
    --gcy: var(--panel-cy, 26rem);
  }

  .has-globe .hero-panel .panel-surface {
    background:
      linear-gradient(160deg, rgba(4, 17, 47, 0.56), rgba(18, 70, 196, 0.36)),
      radial-gradient(circle at top right, rgba(34, 208, 234, 0.16), transparent 40%);
    -webkit-backdrop-filter: blur(6px) saturate(140%);
    backdrop-filter: blur(6px) saturate(140%);
  }
}

/* One column with room beside the heading: the whole globe sits to the right of it */
@media (821px <= width <= 1080px) {
  .hero-globe {
    --globe-d: min(26rem, 40vw);
    --gcx: calc(100% - var(--globe-d) / 2 - 0.25rem);
    --gcy: calc(5rem + var(--globe-d) / 2);
    opacity: 0.85;
  }
}

/* Phones: the globe sits centred behind the heading, dimmed so the copy stays readable */
@media (width <= 820px) {
  .hero-globe {
    --globe-d: min(84vw, 24rem);
    --gcx: 50%;
    --gcy: calc(var(--globe-d) / 2 + 1.5rem);
    opacity: 0.5;
  }
}

/* The globe sinks and fades as you scroll away (desktop) */
@keyframes globe-recede {
  to { translate: 0 6rem; scale: 0.9; opacity: 0.45; }
}

@media (prefers-reduced-motion: no-preference) and (width > 1080px) {
  @supports (animation-timeline: scroll()) {
    .hero-globe {
      animation: globe-recede linear both;
      animation-timeline: scroll(root);
      animation-range: 0px 80vh;
    }
  }
}

/* ---- Headings: words rise out of a soft blur ----
   A word gets a compositor layer of its own only while it moves (the browser does that for
   every running transition), not for the life of the page: about 60 fewer layers to update
   on every frame. */
[data-split-text] .split-word {
  will-change: auto;
}

@media (prefers-reduced-motion: no-preference) {
  [data-split-text] .split-word {
    transform: translateY(0.6em) rotate(2.5deg);
    transform-origin: 0 100%;
    filter: blur(10px);
    transition:
      opacity 0.9s cubic-bezier(0.16, 1, 0.3, 1),
      transform 0.9s cubic-bezier(0.16, 1, 0.3, 1),
      filter 0.9s cubic-bezier(0.16, 1, 0.3, 1);
    transition-delay: var(--split-delay, 0ms);
  }

  [data-split-text].is-revealed .split-word,
  .is-revealed [data-split-text] .split-word {
    transform: none;
    filter: none;
  }
}

/* ---- "operate": a light sweep travels through the gradient word ---- */
@media (prefers-reduced-motion: no-preference) {
  h1 .accent-word {
    background-image: linear-gradient(100deg,
      #2f3ad8 0%, #1e86ee 26%, #22d0ea 40%, #d2f8ff 50%, #22d0ea 60%, #1e86ee 74%, #2f3ad8 100%);
    background-size: 280% 100%;
    background-position: 100% 0;
    animation: accent-sheen 6.5s cubic-bezier(0.45, 0, 0.55, 1) 1.6s infinite;
  }

  @media (max-width: 820px) {
    h1 .accent-word {
      background-image: linear-gradient(100deg,
        #4aa4ff 0%, #3fb6f2 26%, #22d0ea 40%, #d2f8ff 50%, #22d0ea 60%, #3fb6f2 74%, #4aa4ff 100%);
    }
  }
}

@keyframes accent-sheen {
  0% { background-position: 100% 0; }
  45%, 100% { background-position: 0% 0; }
}

/* ---- Pointer glow on cards (motion.js adds .spot and a .spot-glow layer) ----
   The border lights up near the pointer, even on neighbouring cards; the surface
   glows only under the pointer. */
.spot > .spot-glow {
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
  /* Neon bloom along the lit edge */
  filter: drop-shadow(0 0 6px rgba(34, 208, 234, 0.75));
}

.spot-live .spot > .spot-glow {
  opacity: 1;
}

.spot > .spot-glow::before,
.spot > .spot-glow::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.spot > .spot-glow::before {
  background: radial-gradient(24rem circle at var(--mx, -999px) var(--my, -999px), rgba(34, 208, 234, 0.17), rgba(47, 58, 216, 0.06) 45%, transparent 70%);
  opacity: 0;
  transition: opacity 0.35s ease;
}

.spot:hover > .spot-glow::before {
  opacity: 1;
}

.spot > .spot-glow::after {
  padding: 2px;
  background: radial-gradient(30rem circle at var(--mx, -999px) var(--my, -999px),
    rgba(190, 246, 255, 1), rgba(74, 164, 255, 0.75) 42%, transparent 78%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}

@media (hover: none), (prefers-reduced-motion: reduce) {
  .spot > .spot-glow {
    display: none;
  }
}

/* ---- Hero panel leans towards the pointer ----
   The two angles only matter on the panel itself, so they don't pass down to its content:
   a pointer move restyles the panel and nothing inside it. */
@property --tilt-x {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@property --tilt-y {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) and (width > 1080px) {
  .hero .panel-surface {
    transform: perspective(1400px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
    transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
    will-change: transform;
  }
}

/* ---- Slow aurora behind the page ---- */
@media (prefers-reduced-motion: no-preference) {
  body::after {
    display: block;
    position: fixed;
    inset: -15%;
    z-index: 0;
    pointer-events: none;
    background:
      radial-gradient(38% 32% at 18% 28%, rgba(47, 58, 216, 0.16), transparent 70%),
      radial-gradient(34% 30% at 82% 72%, rgba(34, 208, 234, 0.08), transparent 70%),
      radial-gradient(30% 28% at 62% 12%, rgba(30, 134, 238, 0.1), transparent 70%);
    animation: aurora-drift 26s ease-in-out infinite alternate;
  }
}

/* ---- Reading progress line along the top edge ---- */
.scroll-progress {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  height: 2px;
  pointer-events: none;
  transform-origin: 0 50%;
  transform: scaleX(var(--sp, 0));
  background: linear-gradient(90deg, #2f3ad8, #1e86ee 45%, #22d0ea);
  box-shadow: 0 0 12px rgba(34, 208, 234, 0.6);
}

@media (prefers-reduced-motion: reduce) {
  .scroll-progress {
    display: none;
  }
}

/* ---- Icons: no tiles. Each icon floats on the card with a soft glow instead ---- */
.ico-tile,
.ico-tile--slate,
.section-slate .ico-tile,
.signal-list--flow .ico-tile,
.deliverables-strip .ico-tile,
.service-card:hover > .ico-tile,
.services-scroller.is-pinned .service-card.is-current > .ico-tile {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.ico-tile > .ico,
.signal-list--flow .ico-tile .ico {
  width: 28px;
  height: 28px;
}

.ico {
  filter: drop-shadow(0 0 6px rgba(34, 208, 234, 0.45));
}

@media (forced-colors: active) {
  .ico {
    filter: none;
  }
}

/* ==========================
   SPACING
   One gap between every pair of sections (--nx-gap), and one gap between a section
   heading and what follows it (--nx-head). Boxed sections keep their inner padding.
   ========================== */
:root {
  --nx-gap: clamp(2.5rem, 4.5vw, 4rem);
  --nx-head: 2rem;
}

main > .hero {
  padding-bottom: 0;
}

main > .section {
  margin-top: var(--nx-gap);
}

main > #stack,
main > #services,
main > #contact.section,
main > .section-clients {
  padding-top: 0;
  padding-bottom: 0;
}

.site-footer {
  padding-top: var(--nx-gap);
}

.journey-grid,
.engagement-grid,
.stack-scroller,
#services .services-carousel {
  margin-top: var(--nx-head);
}

/* Pinned stack: the stage keeps room for the fixed header above the panel. Pull the stage
   up into the heading's space by that room, so the panel sits --nx-head below the heading,
   and drop the stage's bottom padding so the next section is --nx-gap below the panel. */
.stack-scroller.is-interactive {
  margin-top: calc(var(--nx-head) - var(--header-h, 5.6rem) - 0.9rem);
  margin-bottom: -1.1rem;
}

/* Pinned services: motion.js measures the stage's empty room above the carousel
   (--svc-lead) and below the progress counter (--svc-tail) */
.services-scroller.is-pinned {
  margin-top: calc(var(--nx-head) - var(--svc-lead, 0px));
  margin-bottom: calc(0px - var(--svc-tail, 0px));
}

#services .services-scroller.is-pinned .services-carousel {
  margin-top: 0;
}

/* The stages' empty room now overlaps the neighbours: let clicks through it */
.is-interactive .stack-stage,
.services-scroller.is-pinned .services-stage {
  pointer-events: none;
}

.is-interactive .stack-stage > *,
.services-scroller.is-pinned .services-stage > * {
  pointer-events: auto;
}

/* ==========================
   "YOUR TEAM TAKES OVER" CHART
   Draw-in (plays forward on the way down, in reverse on the way back up): a scan line
   sweeps left to right and uncovers the bands, each step marker pops as the scan passes
   it, and its step label lights up. Then, while the chart is on screen, a light keeps
   travelling along the handover line and each marker ripples as it passes.
   motion.js adds the scan, the travelling light and the rings.
   ========================== */
.viz--handover .viz__comet,
.viz--handover .viz__scan,
.viz--handover .viz__ring {
  pointer-events: none;
}

/* Widths are in chart units (no non-scaling stroke: it breaks pathLength dashes in Chrome) */
.viz--handover .viz__comet {
  fill: none;
  stroke: #e6fbff;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-dasharray: 0.1 1.3;
  stroke-dashoffset: 0.1;
}

.viz--handover .viz__comet--glow {
  stroke: #22d0ea;
  stroke-width: 5.5;
  stroke-dasharray: 0.16 1.3;
  stroke-dashoffset: 0.16;
  opacity: 0.4;
}

.viz--handover .viz__scan {
  opacity: 0;
}

.viz--handover .viz__ring {
  fill: none;
  stroke: #22d0ea;
  stroke-width: 1.5;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}

@media (prefers-reduced-motion: no-preference) {
  .viz--handover .viz__wipe,
  .viz--handover .viz__wipe-inner {
    transition: transform 1.6s linear;
  }

  .viz--handover .viz__scan {
    opacity: 1;
    transition: opacity 0.35s ease 1.5s;
  }

  .viz--handover.is-drawn .viz__scan {
    opacity: 0;
  }

  .js .viz--handover:not(.is-drawn) .viz__scan {
    transition-delay: 0s;
  }

  /* Step markers pop when the scan reaches them (x / 320 of the 1.6s sweep) */
  .viz--handover .viz__node {
    transform-box: fill-box;
    transform-origin: center;
    transition:
      opacity 0.25s ease,
      transform 0.5s cubic-bezier(0.34, 1.8, 0.64, 1);
    transition-delay: calc(0.13s + var(--viz-delay, 0) * 0.267s);
  }

  .js .viz--handover:not(.is-drawn) .viz__node {
    transform: scale(0);
  }

  .viz--handover .viz__tick-num,
  .viz--handover .viz__tick-name {
    transition:
      opacity 0.4s ease,
      color 0.4s ease,
      transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
  }

  .viz--handover .viz__axis li:nth-child(1) > * { transition-delay: 0.13s; }
  .viz--handover .viz__axis li:nth-child(2) > * { transition-delay: 0.4s; }
  .viz--handover .viz__axis li:nth-child(3) > * { transition-delay: 0.67s; }
  .viz--handover .viz__axis li:nth-child(4) > * { transition-delay: 0.93s; }
  .viz--handover .viz__axis li:nth-child(5) > * { transition-delay: 1.2s; }
  .viz--handover .viz__axis li:nth-child(6) > * { transition-delay: 1.47s; }

  .js .viz--handover:not(.is-drawn) .viz__tick-num,
  .js .viz--handover:not(.is-drawn) .viz__tick-name {
    opacity: 0.3;
    transform: translateY(4px);
  }

  /* Legend and caption fade in first */
  .viz--handover .viz__legend,
  .viz--handover .viz__caption {
    transition: opacity 0.5s ease;
  }

  .js .viz--handover:not(.is-drawn) .viz__legend,
  .js .viz--handover:not(.is-drawn) .viz__caption {
    opacity: 0;
  }

  /* After the draw-in: the travelling light (5s loop, 2.75s along the line) */
  .viz--handover.is-drawn .viz__comet {
    animation: hv-comet 5s linear 1.7s infinite;
  }

  .viz--handover.is-drawn .viz__comet--glow {
    animation-name: hv-comet-glow;
  }

  /* Each marker ripples when the light passes it (its share of the line's length) */
  .viz--handover.is-drawn .viz__ring {
    animation: hv-ring 5s ease-out infinite;
  }

  .viz--handover.is-drawn .viz__node {
    animation: hv-node 5s ease-out infinite;
  }

  .viz--handover .viz__marks > :nth-child(1),
  .viz--handover .viz__marks > :nth-child(2) { animation-delay: calc(1.7s + 0.19s); }
  .viz--handover .viz__marks > :nth-child(3),
  .viz--handover .viz__marks > :nth-child(4) { animation-delay: calc(1.7s + 0.56s); }
  .viz--handover .viz__marks > :nth-child(5),
  .viz--handover .viz__marks > :nth-child(6) { animation-delay: calc(1.7s + 0.95s); }
  .viz--handover .viz__marks > :nth-child(7),
  .viz--handover .viz__marks > :nth-child(8) { animation-delay: calc(1.7s + 1.36s); }
  .viz--handover .viz__marks > :nth-child(9),
  .viz--handover .viz__marks > :nth-child(10) { animation-delay: calc(1.7s + 1.78s); }
  .viz--handover .viz__marks > :nth-child(11),
  .viz--handover .viz__marks > :nth-child(12) { animation-delay: calc(1.7s + 2.18s); }

  /* The step labels light up in step with the markers */
  .viz--handover.is-drawn .viz__tick-num {
    animation: hv-tick 5s ease-out infinite;
  }

  .viz--handover .viz__axis li:nth-child(1) .viz__tick-num { animation-delay: calc(1.7s + 0.19s); }
  .viz--handover .viz__axis li:nth-child(2) .viz__tick-num { animation-delay: calc(1.7s + 0.56s); }
  .viz--handover .viz__axis li:nth-child(3) .viz__tick-num { animation-delay: calc(1.7s + 0.95s); }
  .viz--handover .viz__axis li:nth-child(4) .viz__tick-num { animation-delay: calc(1.7s + 1.36s); }
  .viz--handover .viz__axis li:nth-child(5) .viz__tick-num { animation-delay: calc(1.7s + 1.78s); }
  .viz--handover .viz__axis li:nth-child(6) .viz__tick-num { animation-delay: calc(1.7s + 2.18s); }

  /* Your team's share breathes gently */
  .viz--handover.is-drawn .viz__area--team {
    animation: hv-team 5s ease-in-out 1.7s infinite;
  }
}

/* Both heads go from 0 to 1.16 of the line in the first 55% (2.75s) of the loop */
@keyframes hv-comet {
  0% { stroke-dashoffset: 0.1; }
  55%, 100% { stroke-dashoffset: -1.06; }
}

@keyframes hv-comet-glow {
  0% { stroke-dashoffset: 0.16; }
  55%, 100% { stroke-dashoffset: -1; }
}

@keyframes hv-ring {
  0% { opacity: 0.9; transform: scale(1); }
  22% { opacity: 0; transform: scale(3.6); }
  100% { opacity: 0; transform: scale(3.6); }
}

@keyframes hv-node {
  0% { transform: scale(1); }
  5% { transform: scale(1.5); fill: #e6fbff; }
  16%, 100% { transform: scale(1); }
}

@keyframes hv-tick {
  0% { color: var(--ink); text-shadow: none; }
  5% { color: #b5f7ff; text-shadow: 0 0 10px rgba(34, 208, 234, 0.9); }
  20%, 100% { color: var(--ink); text-shadow: none; }
}

@keyframes hv-team {
  0%, 100% { fill-opacity: 0.28; }
  50% { fill-opacity: 0.4; }
}

/* ==========================
   AMBIENT MOTION
   Small loops that keep every part of the page alive without scrolling. All of them
   stop for reduced motion, and none moves text.
   ========================== */
@media (prefers-reduced-motion: no-preference) {
  /* Icons drift up and down and their glow breathes, each on its own beat */
  .ico-tile > .ico,
  .journey-card > .ico,
  .contact-points .ico,
  .panel-metrics .ico {
    animation: nx-float 4.8s ease-in-out infinite;
  }

  :nth-child(2n) > .ico-tile > .ico,
  .journey-card:nth-child(2n) > .ico,
  .panel-metrics > :nth-child(2) .ico { animation-delay: -1.6s; }
  :nth-child(3n) > .ico-tile > .ico,
  .journey-card:nth-child(3n) > .ico,
  .panel-metrics > :nth-child(3) .ico { animation-delay: -3.2s; }

  /* Hero service flow: light runs down each connector, Advisory to Delivery to Operations */
  .signal-list--flow .viz--hero-link::before {
    background:
      linear-gradient(180deg, transparent 0 42%, rgba(230, 251, 255, 0.95) 50%, transparent 58% 100%),
      linear-gradient(180deg, rgba(34, 208, 234, 0), var(--accent) 55%);
    background-size: 100% 300%, 100% 100%;
    background-repeat: no-repeat;
    animation: nx-flow-y 2.6s linear infinite;
  }

  .signal-list--flow > article:nth-child(2) .viz--hero-link::before {
    animation-delay: 1.3s;
  }

  /* Hero cards: the hub's halo breathes, the roadmap bars brighten in order,
     and a light goes round the twelve monthly sessions */
  .viz--hero-hub.is-drawn .viz__hub-halo {
    transform-box: fill-box;
    transform-origin: center;
    animation: nx-halo 3.2s ease-in-out 1s infinite;
  }

  .viz--hero-gantt.is-drawn .viz__bar {
    animation: nx-bar 4.5s ease-in-out infinite;
  }

  .viz--hero-gantt.is-drawn .viz__bar--1 { animation-delay: 1.2s; }
  .viz--hero-gantt.is-drawn .viz__bar--2 { animation-delay: 1.7s; }
  .viz--hero-gantt.is-drawn .viz__bar--3 { animation-delay: 2.2s; }

  .viz--hero-cycle.is-drawn .viz__dot {
    animation: nx-month 6s ease-out infinite;
    animation-delay: calc(1.6s + var(--viz-delay, 0) * 0.5s);
  }

  /* Solution stack: the stack floats; the scroll cue nudges downwards */
  .stack-scene {
    animation: nx-bob 7s ease-in-out infinite;
  }

  .stack-hint svg {
    animation: nx-nudge 1.8s ease-in-out infinite;
  }

  /* How We Work: a ring travels through the six step numbers, 01 to 06 */
  .journey-card > span {
    position: relative;
  }

  .journey-card > span::after {
    content: "";
    position: absolute;
    inset: -1px;
    border-radius: 50%;
    border: 1.5px solid #b5f7ff;
    opacity: 0;
    pointer-events: none;
    animation: nx-step 6s ease-out infinite;
  }

  .journey-card:nth-child(2) > span::after { animation-delay: 1s; }
  .journey-card:nth-child(3) > span::after { animation-delay: 2s; }
  .journey-card:nth-child(4) > span::after { animation-delay: 3s; }
  .journey-card:nth-child(5) > span::after { animation-delay: 4s; }
  .journey-card:nth-child(6) > span::after { animation-delay: 5s; }

  /* Typical Projects: a soft light sweeps across each drawing, one card after another */
  .engagement-card > .viz {
    position: relative;
    overflow: hidden;
  }

  .engagement-card > .viz::after {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 40%;
    background: linear-gradient(100deg, transparent, rgba(181, 247, 255, 0.07) 45%, rgba(181, 247, 255, 0.12) 50%, rgba(181, 247, 255, 0.07) 55%, transparent);
    transform: translateX(-110%);
    pointer-events: none;
    animation: nx-glare 9s ease-in-out infinite;
  }

  .engagement-card:nth-child(2) > .viz::after { animation-delay: 1.5s; }
  .engagement-card:nth-child(3) > .viz::after { animation-delay: 3s; }
  .engagement-card:nth-child(4) > .viz::after { animation-delay: 4.5s; }
  .engagement-card:nth-child(5) > .viz::after { animation-delay: 6s; }
  .engagement-card:nth-child(6) > .viz::after { animation-delay: 7.5s; }

  /* ... and each drawing has its own small loop */
  .viz--residency.is-drawn .viz__led {
    animation: nx-blink 2.4s steps(1, end) infinite;
  }

  .viz--residency.is-drawn .viz__led:nth-of-type(2n) { animation-delay: -0.6s; }
  .viz--residency.is-drawn .viz__led:nth-of-type(3n) { animation-delay: -1.5s; }

  :is(.viz--upgrade-stability, .viz--capacity-forecast).is-drawn .viz__halo {
    transform-box: fill-box;
    transform-origin: center;
    animation: nx-halo 2.8s ease-in-out 1.4s infinite;
  }

  .viz--capacity-forecast.is-drawn .viz__forecast {
    animation: nx-march 1.6s linear infinite;
  }

  .viz--cluster.is-drawn .viz__block {
    animation: nx-twinkle 3.6s ease-in-out infinite;
  }

  .viz--cluster.is-drawn .viz__block:nth-of-type(3n + 1) { animation-delay: -1.2s; }
  .viz--cluster.is-drawn .viz__block:nth-of-type(3n + 2) { animation-delay: -2.4s; }

  .viz--risk-matrix.is-drawn .viz__badge {
    transform-box: fill-box;
    transform-origin: center;
    animation: nx-check 5s ease-out infinite;
  }

  .viz--risk-matrix.is-drawn .viz__badge:nth-of-type(2) { animation-delay: 0.4s; }
  .viz--risk-matrix.is-drawn .viz__badge:nth-of-type(3) { animation-delay: 0.8s; }
  .viz--risk-matrix.is-drawn .viz__badge:nth-of-type(4) { animation-delay: 1.2s; }
  .viz--risk-matrix.is-drawn .viz__badge:nth-of-type(5) { animation-delay: 1.6s; }

  /* Customers and contact: their corner glows drift */
  .customer-frame::before,
  .contact-panel::after {
    animation: nx-drift 11s ease-in-out infinite alternate;
  }

  /* Contact: a light runs down the arrow from "What to Bring" to "What You Get".
     It moves by translate, which the compositor animates without relaying out the page;
     the travel is the arrow's 1.1rem height (styles.css) plus 2px past each end. */
  .contact-flow::before {
    content: "";
    position: absolute;
    left: 50%;
    top: -4px;
    width: 7px;
    height: 7px;
    margin-left: -3.5px;
    border-radius: 50%;
    background: #e6fbff;
    box-shadow: 0 0 10px 2px rgba(34, 208, 234, 0.9);
    opacity: 0;
    animation: nx-drop 2.4s ease-in infinite;
  }

  /* Primary buttons: a light sweeps across every few seconds (hover still sweeps at once) */
  .button-primary::before,
  .nav-cta::before {
    animation: nx-sheen 6s ease-in-out 2.5s infinite;
  }

  .button-primary:is(:hover, :focus-visible)::before,
  .nav-cta:is(:hover, :focus-visible)::before {
    animation: none;
  }
}

/* Big boxes: natural edge lighting (motion.js adds one nx-rim per box).
   Light comes from above: the top edge catches it, the sides pick up less of it on the way
   down, and the bottom edge gets a faint cyan bounce. Inside, a soft light wraps in from the
   top and side edges and the bottom settles into shade, so the box reads as a lit surface
   instead of a flat panel with an outline. Sits under the content. */
.nx-box {
  border-color: transparent;
}

.nx-box > nx-rim {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: var(--nx-ri, 40px);
  background: linear-gradient(0deg, rgba(1, 6, 22, 0.32), rgba(1, 6, 22, 0) 30%);
  pointer-events: none;
}

.nx-box > nx-rim::before,
.nx-box > nx-rim::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}

/* The rim: one pixel, bright along the top, fading down the sides */
.nx-box > nx-rim::before {
  padding: 1px;
  background: linear-gradient(
    180deg,
    rgba(214, 238, 255, 0.6) 0,
    rgba(150, 205, 255, 0.3) 7rem,
    rgba(110, 170, 255, 0.12) 55%,
    rgba(34, 208, 234, 0.12) 100%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}

/* Inner light: a sheen under the top edge and a glow wrapping in from each side */
.nx-box > nx-rim::after {
  background:
    linear-gradient(180deg, rgba(220, 240, 255, 0.08), rgba(220, 240, 255, 0) 9rem),
    linear-gradient(90deg, rgba(150, 205, 255, 0.15), rgba(150, 205, 255, 0) 3.5rem),
    linear-gradient(270deg, rgba(150, 205, 255, 0.15), rgba(150, 205, 255, 0) 3.5rem);
  -webkit-mask-image: linear-gradient(180deg, #000, rgba(0, 0, 0, 0.45) 50%, rgba(0, 0, 0, 0) 92%);
  mask-image: linear-gradient(180deg, #000, rgba(0, 0, 0, 0.45) 50%, rgba(0, 0, 0, 0) 92%);
}

@media (forced-colors: active) {
  .nx-box {
    border-color: CanvasText;
  }

  .nx-box > nx-rim {
    display: none;
  }
}

/* Light running along the edge of the big boxes (motion.js adds one per box) */
nx-runner {
  display: none;
}

@supports (offset-path: inset(0 round 1px)) {
  @media (prefers-reduced-motion: no-preference) {
    .nx-box > nx-runner {
      display: block;
      position: absolute;
      z-index: 4;
      top: 0;
      left: 0;
      width: 9rem;
      height: 2px;
      border-radius: 2px;
      background: linear-gradient(90deg, rgba(34, 208, 234, 0), rgba(34, 208, 234, 0.55) 70%, #e6fbff);
      box-shadow: 0 0 10px rgba(34, 208, 234, 0.55);
      offset-path: inset(0.5px round var(--nx-r, 40px));
      offset-anchor: 100% 50%;
      offset-rotate: auto;
      pointer-events: none;
      animation: nx-run 16s linear var(--nx-start, 0s) infinite;
    }

    .nx-box.nx-off > nx-runner {
      animation-play-state: paused;
    }
  }
}

/* Ambient loops pause while their section is off screen (motion.js adds .nx-rest) and carry
   on from the same place when it comes back. Scroll-linked animations are not listed: they
   always follow the scroll position. */
.nx-rest :is(
  .accent-word, .ico, .stack-scene, .stack-hint svg,
  .viz__hub-halo, .viz__bar, .viz__dot, .viz__led, .viz__halo, .viz__forecast, .viz__block, .viz__badge,
  .viz__comet, .viz__ring, .viz__node, .viz__tick-num, .viz__area--team
),
.nx-rest :is(.viz--hero-link, .contact-flow, .button-primary)::before,
.nx-rest :is(.journey-card > span, .engagement-card > .viz, .contact-panel)::after,
.nx-rest .customer-frame::before {
  animation-play-state: paused !important;
}

@keyframes nx-float {
  0%, 100% { translate: 0 0; filter: drop-shadow(0 0 4px rgba(34, 208, 234, 0.35)); }
  50% { translate: 0 -3px; filter: drop-shadow(0 0 9px rgba(34, 208, 234, 0.85)); }
}

@keyframes nx-flow-y {
  from { background-position: 0 100%, 0 0; }
  to { background-position: 0 0, 0 0; }
}

@keyframes nx-halo {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.25); opacity: 0.45; }
}

@keyframes nx-bar {
  0%, 100% { filter: none; }
  12% { filter: brightness(1.7) drop-shadow(0 0 4px rgba(34, 208, 234, 0.9)); }
  30% { filter: none; }
}

@keyframes nx-month {
  0% { transform: scale(1); fill: var(--accent); }
  4% { transform: scale(1.45); fill: #e6fbff; }
  14%, 100% { transform: scale(1); fill: var(--accent); }
}

@keyframes nx-bob {
  0%, 100% { translate: 0 0; }
  50% { translate: 0 -8px; }
}

@keyframes nx-nudge {
  0%, 100% { translate: 0 0; }
  50% { translate: 0 4px; }
}

@keyframes nx-step {
  0% { opacity: 0.95; transform: scale(1); }
  14% { opacity: 0; transform: scale(1.7); }
  100% { opacity: 0; transform: scale(1.7); }
}

@keyframes nx-glare {
  0% { transform: translateX(-110%); }
  30%, 100% { transform: translateX(260%); }
}

@keyframes nx-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.25; }
}

@keyframes nx-march {
  to { stroke-dashoffset: -20; }
}

@keyframes nx-twinkle {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

@keyframes nx-check {
  0% { transform: scale(1); }
  6% { transform: scale(1.3); }
  18%, 100% { transform: scale(1); }
}

@keyframes nx-drift {
  from { translate: 0 0; }
  to { translate: -3rem 2rem; }
}

@keyframes nx-drop {
  0% { translate: 0 0; opacity: 0; }
  15% { opacity: 1; }
  70% { translate: 0 calc(1.1rem + 6px); opacity: 1; }
  85%, 100% { translate: 0 calc(1.1rem + 6px); opacity: 0; }
}

@keyframes nx-sheen {
  0% { transform: translateX(-120%); }
  22%, 100% { transform: translateX(120%); }
}

@keyframes nx-run {
  from { offset-distance: 0%; }
  to { offset-distance: 100%; }
}
