/* =====================================================================
   NORTHERN EDGE — utilities
   Loaded last. Single-purpose helpers only, and the reduced-motion
   override that must win over everything above it.
   ===================================================================== */

/* Screen-reader-only. Replaces the framework class of the same purpose:
   nothing is loaded here that would provide one. */
.ne-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.ne-mt-3 { margin-top: var(--ne-space-3); }
.ne-mt-4 { margin-top: var(--ne-space-4); }
.ne-mt-5 { margin-top: var(--ne-space-5); }
.ne-mt-6 { margin-top: var(--ne-space-6); }
.ne-mt-7 { margin-top: var(--ne-space-7); }
/* about.html asks for this step and there was no rule for it, so the margin
   was silently doing nothing. */
.ne-mt-8 { margin-top: var(--ne-space-8); }
.ne-mb-6 { margin-bottom: var(--ne-space-6); }
.ne-mb-7 { margin-bottom: var(--ne-space-7); }

/* ------------------------------------------------------ REDUCED MOTION
   The site must be completely usable and still handsome with motion off.
   Every animated state resolves to its finished value.

   The GSAP layer is switched off in animations.js under this preference —
   these rules cover the CSS-driven pieces and anything GSAP had already
   set before the preference was read.
   ==================================================================== */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    transition-duration: 0.01ms;
    scroll-behavior: auto;
  }
  html {
    scroll-behavior: auto;
  }

  /* The loader's dismissal keyframe must survive: it is the fallback that
     clears the overlay, not decoration. Shortened, not disabled. */
  .ne-load {
    animation-duration: 1ms;
    animation-delay: 1ms;
  }
  .ne-load__fill {
    animation: none;
  }
  .ne-load__mark {
    opacity: 1;
  }
  .ne-load__ridge {
    stroke-dasharray: none;
  }

  /* Continuous loops that exist only to move. */
  .ne-marquee__track,
  .ne-status.is-open .ne-status__dot::after,
  .ne-hero__scroll-line::after {
    animation: none;
  }
  .ne-hero__scroll {
    display: none;
  }

  /* Parallax layers sit still. */
  .ne-slide__media img,
  .ne-tile__media img,
  .ne-sband__media img,
  .ne-page-hero__media img {
    transform: none;
  }

  /* Scrubbed reveals resolve to their finished value. */
  .ne-steps__line-fill,
  .ne-pl__fill,
  .ne-tline__fill,
  .ne-hero__bar-fill {
    transform: scaleY(1);
  }
  .ne-pl__row .ne-pl__n {
    color: var(--ne-amber);
  }
  @media (min-width: 900px) {
    .ne-tline__fill {
      transform: scaleX(1);
    }
  }
  .ne-seam {
    transform: none;
  }
  .ne-step__n {
    color: var(--ne-amber);
  }

  /* The kinetic label swap is the one hover flourish that stays off: a
     translate of the full line height is exactly the kind of movement this
     preference is asking us not to make. */
  .ne-kin__a,
  .ne-kin__b {
    transition: none;
  }
  :is(a, button):hover .ne-kin__a {
    transform: none;
  }
  :is(a, button):hover .ne-kin__b {
    transform: translateY(100%);
  }

  /* The cursor-following preview never attaches under reduced motion, but
     make certain it cannot linger. The capability list keeps its inline
     thumbnail below 1200px, where the preview is not fixed to the cursor. */
  @media (min-width: 1200px) {
    .ne-cap__preview {
      display: none;
    }
  }
}
