/* Northing Labs — motion layer (owned by the motion agent).
   Hidden initial states exist ONLY under html.js:not(.reduce-motion). motion.js adds .is-in
   when a reveal finishes and removes the .js class outright if GSAP never loads. */

/* ---- Scroll reveals ---- */
html.js:not(.reduce-motion) [data-reveal]:not(.is-in),
html.js:not(.reduce-motion) [data-reveal-stagger] > *:not(.is-in) {
  opacity: 0;
  transform: translateY(28px);
}

/* ---- Scrubbed statement words ---- */
.nl-word {
  display: inline-block;
  will-change: transform, opacity, filter;
}

/* ---- Coordinate readout ---- */
#coords [data-n],
#coords [data-w],
#coords [data-sheet] {
  font-variant-numeric: tabular-nums;
}

/* ---- Magnetic buttons ---- */
@media (hover: hover) and (pointer: fine) {
  [data-magnetic] {
    will-change: transform;
  }
}

/* ---- FAQ height animation ---- */
.faq details.is-animating {
  overflow: hidden;
}

/* ---- Ticker (track holds the item set twice, so -50% loops seamlessly) ---- */
.ticker {
  overflow: hidden;
}
.ticker__track {
  width: max-content;
  white-space: nowrap;
  animation: nl-ticker 36s linear infinite;
  will-change: transform;
}
.ticker:hover .ticker__track {
  animation-play-state: paused;
}
@keyframes nl-ticker {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
html.reduce-motion .ticker__track {
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  .ticker__track { animation: none; }
}
