/* =====================================================================
   The process rail.

   Six stages are a progression, so the progression is what animates: a
   faint track showing the route, ONE rust line travelling down it, and a
   node committing at each stage as it arrives.

   NOTHING HERE HIDES ANYTHING BY ITSELF. Every rule that starts an
   element invisible is scoped to .js-rail, which include/rail.js adds
   only once it has decided it is really going to run. With JavaScript
   off, or with reduced motion on, the class never lands and the whole
   process reads as normal.
   ===================================================================== */

.method{
  --rail-ease: cubic-bezier(.2,.8,.25,1);
  --rail-track: var(--line-2);
}

/* The intro stays with the stages while they scroll past it. */
.method__manifesto{
  position: sticky;
  top: 120px;
}

/* Room for the rail down the left of the stage rows. */
.method__steps{
  position: relative;
  padding-left: 2.25rem;
}

/* ---- the rail --------------------------------------------------------
   Track and line share one left edge; the node squares are centred on
   it. Both are sized and placed by rail.js from measured positions, so
   the rail lines up with the copy however a heading wraps. */
.rail{
  position: absolute;
  left: 3px;
  top: 0;
  bottom: 0;
  width: 7px;
  pointer-events: none;
}
.rail__track{
  position: absolute;
  left: 3px;
  width: 1px;
  background: var(--rail-track);
}
/* Starts at the first node and only ever grows.

   Height is a TRANSITION, not an animation. That is the whole
   difference between one line travelling down the page and six
   segments chasing each other: a transition extends from wherever the
   line currently is to wherever the newest stage sits, so however fast
   the reader scrolls it stays one continuous travel. */
.rail__line{
  position: absolute;
  left: 3px;
  width: 1px;
  height: 0;
  background: var(--accent);
  transition: height 620ms var(--rail-ease);
}
/* So the line terminates instead of trailing off into nothing. */
.rail__cap{
  position: absolute;
  left: 0;
  width: 9px;
  height: 1px;
  background: var(--accent);
  opacity: 0;
  transition: opacity 420ms var(--rail-ease);
}
.rail__cap.is-on{ opacity: 1; }

.rail__node{
  position: absolute;
  left: 0;
  width: 7px;
  height: 7px;
  box-sizing: border-box;
  border: 1px solid var(--rail-track);
  background: transparent;
  transition: background 420ms var(--rail-ease),
              border-color 420ms var(--rail-ease),
              transform 420ms var(--rail-ease);
}
.rail__node.is-on{
  background: var(--accent);
  border-color: var(--accent);
  transform: scale(1.35);
}
/* ONE expanding ring, never a loop. A ring that keeps pulsing reads as
   "waiting", and by the time it fires the stage has already arrived. */
.rail__ring{
  position: absolute;
  left: 0;
  width: 7px;
  height: 7px;
  box-sizing: border-box;
  border: 1px solid var(--accent);
  opacity: 0;
  pointer-events: none;
}
.rail__node.is-on + .rail__ring{
  animation: rail-ring 620ms var(--rail-ease) forwards;
}
@keyframes rail-ring{
  from{ opacity: .8; transform: scale(1); }
  to  { opacity: 0;  transform: scale(4.2); }
}

/* ---- per-stage content ----------------------------------------------
   Node commits, then the ring, then the content rises: meta, title,
   paragraph, a beat apart. Arrive, commit, continue — the order is the
   metaphor, and running it backwards ruins it. */
.js-rail .step__n,
.js-rail .step h4,
.js-rail .step p{
  opacity: 0;
}
.js-rail .step.is-in .step__n{
  animation: rail-rise 480ms var(--rail-ease) 90ms forwards;
}
.js-rail .step.is-in h4{
  animation: rail-rise 480ms var(--rail-ease) 180ms forwards;
}
.js-rail .step.is-in p{
  animation: rail-rise 520ms var(--rail-ease) 270ms forwards;
}
@keyframes rail-rise{
  from{ opacity: 0; transform: translateY(6px); }
  to  { opacity: 1; transform: none; }
}

/* ---- stage 04: the heading performs the promotion it describes -------
   Dev, Test, UAT, Live arrive one environment at a time, each arrow a
   beat behind the name before it. Once, on this heading only: repeated
   anywhere else it would read as a tic. */
.pipe{ display: inline-block; }
.pipe__arrow{ color: var(--accent); }
.js-rail .pipe{ opacity: 0; }
.js-rail .step.is-in .pipe{
  animation: rail-rise 420ms var(--rail-ease) forwards;
  animation-delay: calc(180ms + var(--i) * 150ms);
}

/* ---- the intro's accent word ----------------------------------------
   It arrives as ink and warms to rust a beat later, so it reads as
   emphasis being applied rather than a colour that was always there. */
.js-rail .method__manifesto em{ color: var(--text); }
.js-rail .method.is-in .method__manifesto em{
  transition: color 560ms var(--rail-ease) 620ms;
  color: var(--accent);
}

@media (max-width: 900px){
  .method__manifesto{ position: static; }
  .method__steps{ padding-left: 1.75rem; }
}

/* Belt and braces: rail.js declines to add .js-rail under reduced
   motion, so these should never be needed. If the class ever lands
   another way, the rail is drawn in its final state and the content is
   present — nothing hidden. */
@media (prefers-reduced-motion: reduce){
  .js-rail .step__n,
  .js-rail .step h4,
  .js-rail .step p,
  .js-rail .pipe{ opacity: 1; animation: none; }
  .rail__node{ background: var(--accent); border-color: var(--accent); }
  .rail__ring{ display: none; }
  .rail__cap{ opacity: 1; }
  .js-rail .method__manifesto em{ color: var(--accent); }
}
