/* ═══════════════════════════════════════════════════════════════════
   Nondescripts — Mark in Motion
   Six CSS animations for the ND mark. The seventh (Cradle) is
   JavaScript and lives in nd-cradle.js.

   Usage
     1. Put the mark's markup on the page (see nd-mark.html).
     2. Put a ground class or [data-ground] on an ancestor.
     3. Add one animation class to the .nd-mark element.

   One motion per surface. A page that cascades on load does not
   also breathe.
   ═══════════════════════════════════════════════════════════════════ */

:root{
  /* ── ND palette ───────────────────────────────────────────── */
  --nd-ink:    #0A0C00;
  --nd-bone:   #F6F5F3;
  --nd-yellow: #E3FF04;

  /* ── The three curves. Nothing else. ──────────────────────────
     No browser defaults (too weak at these durations) and no
     ease-in on any interface element — it holds back the exact
     moment the eye is watching. `linear` is allowed only for
     genuinely constant motion. */
  --nd-ease-out:    cubic-bezier(0.23, 1, 0.32, 1);      /* entrances, exits, rebounds */
  --nd-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);     /* on-screen movement */
  --nd-ease-back:   cubic-bezier(0.34, 1.56, 0.64, 1);   /* the bounce — hover only */

  /* Rehearsal speed. 1 = ship speed. Set to 4 to study the feel. */
  --nd-speed: 1;
}

/* ── Grounds ──────────────────────────────────────────────────────
   Every color the mark uses is a token, so switching the ground
   repaints the artwork and not just the backdrop. On yellow the
   lockup inverts: the tile goes to Ink and the bars knock out in
   the ground color, so the mark still reads as holes cut in a
   solid rather than yellow on yellow. */

[data-ground],
.nd-ground{
  --nd-mark:  var(--nd-bone);     /* the six bars, standalone */
  --nd-tile:  var(--nd-yellow);   /* the lockup's rounded square */
  --nd-knock: var(--nd-ink);      /* bars knocked out of that square */
  --nd-word:  var(--nd-bone);     /* the wordmark */
  --nd-pass:  var(--nd-yellow);   /* where the Accent Pass pulse travels to */
  background: var(--nd-ink);
}
[data-ground="bone"]{
  --nd-mark: var(--nd-ink);
  --nd-word: var(--nd-ink);
  background: var(--nd-bone);
}
[data-ground="yellow"]{
  --nd-mark:  var(--nd-ink);
  --nd-tile:  var(--nd-ink);
  --nd-knock: var(--nd-yellow);
  --nd-word:  var(--nd-ink);
  --nd-pass:  var(--nd-bone);
  background: var(--nd-yellow);
}

/* ── The mark ─────────────────────────────────────────────────────
   `overflow: visible` matters: Press lifts the bars past the top
   edge and Cradle swings them well outside the box. */

.nd-mark{ display: block; overflow: visible; }
.nd-mark .bar{ fill: var(--nd-mark, var(--nd-bone)); }

.nd-lockup{ display: block; overflow: visible; }
.nd-lockup .tile  { fill: var(--nd-tile); }
.nd-lockup .knock { fill: var(--nd-knock); }
.nd-lockup .letter{ fill: var(--nd-word); }

/* Every stagger on this page is driven by --i, the bar's index 0–5,
   set inline on each rect. transform-origin is given in viewBox
   units, so no transform-box is needed and the motion is identical
   at every rendered size. */


/* ═══════════════════════════════════════════════════════════════════
   I. CASCADE          Entrance · page load, hero, site loader
   460ms · 55ms stagger · 735ms total
   ═══════════════════════════════════════════════════════════════════ */

.nd-cascade .bar{
  transform-origin: 0 8.09px;        /* the mark's baseline, in viewBox units */
  transform: scaleY(0.04);
}
.nd-cascade.is-in .bar{
  animation: nd-cascade calc(460ms * var(--nd-speed)) var(--nd-ease-out) forwards;
  animation-delay: calc(var(--i) * 55ms * var(--nd-speed));
}
@keyframes nd-cascade{
  from{ transform: scaleY(0.04); }
  to  { transform: scaleY(1); }
}


/* ── Trigger ──────────────────────────────────────────────────────
   Press and Accent Pass go on the element that IS the logo — the
   link or button wrapping the mark — never on a full-bleed panel
   around it. Put them on a container and the whole panel becomes
   the hover target, which fires with the pointer nowhere near the
   mark. This helper is the smallest correct wrapper. */

.nd-trigger{
  display: inline-block;
  line-height: 0;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: inherit;
}

/* ═══════════════════════════════════════════════════════════════════
   II. PRESS           Interaction · nav logo, button
   200ms hover · 18ms stagger · 290ms total, inside the 300ms budget
   press 150ms → release 110ms
   ═══════════════════════════════════════════════════════════════════ */

.nd-press .nd-mark{
  transition: transform calc(110ms * var(--nd-speed)) var(--nd-ease-out);   /* release snaps */
}
.nd-press .bar{
  transform-origin: 0 4.045px;
  transition: transform calc(200ms * var(--nd-speed)) var(--nd-ease-back);
  transition-delay: calc(var(--i) * 18ms * var(--nd-speed));
}
@media (hover: hover) and (pointer: fine){
  .nd-press:hover .bar{ transform: translateY(-0.9px) scaleY(1.06); }
}
.nd-press:focus-visible .bar,
.nd-press.is-demo .bar{ transform: translateY(-0.9px) scaleY(1.06); }

/* Asymmetric: the press is the deliberate half, the release snaps. */
.nd-press:active .nd-mark,
.nd-press.is-demo-press .nd-mark{
  transform: scale(0.955);
  transition-duration: calc(150ms * var(--nd-speed));
}
.nd-press:active .bar,
.nd-press.is-demo-press .bar{ transition-delay: 0ms; }


/* ═══════════════════════════════════════════════════════════════════
   III. METER          State · loading, processing, busy
   1150ms · −95ms phase offset per bar · the only one legible at 16px
   ═══════════════════════════════════════════════════════════════════ */

.nd-meter .bar{
  transform-origin: 0 8.09px;
  animation: nd-meter calc(1150ms * var(--nd-speed)) var(--nd-ease-in-out) infinite alternate;
  animation-delay: calc(var(--i) * -95ms * var(--nd-speed));   /* negative = start mid-cycle */
}
@keyframes nd-meter{
  from{ transform: scaleY(0.32); }
  to  { transform: scaleY(1); }
}
/* Stop it the moment the work finishes — a spinner that outlives
   its task is worse than no spinner. */
.nd-meter.is-done .bar{ animation: none; transform: scaleY(1); }
.nd-meter.is-paused .bar{ animation-play-state: paused; }


/* ═══════════════════════════════════════════════════════════════════
   IV. STING           Sequence · outro, title card, deck cover
   tile 0–560 · bars 300–908 · wordmark 560–1280ms
   Three moves in one direction; nothing crosses back over itself.
   ═══════════════════════════════════════════════════════════════════ */

.nd-sting .tile-clip-rect{ transform: scaleX(0); transform-origin:  0px 0px; }
.nd-sting .word-clip-rect{ transform: scaleX(0); transform-origin: 29.9px 0px; }
.nd-sting .knock { transform: scaleY(0.04); transform-origin: 0 17.04px; }
.nd-sting .letter{ opacity: 0; }

.nd-sting.is-in .tile-clip-rect{
  animation: nd-wipe-x calc(560ms * var(--nd-speed)) var(--nd-ease-in-out) forwards;
}
.nd-sting.is-in .knock{
  animation: nd-sting-bar calc(420ms * var(--nd-speed)) var(--nd-ease-out) forwards;
  animation-delay: calc(300ms * var(--nd-speed) + var(--i) * 48ms * var(--nd-speed));
}
.nd-sting.is-in .word-clip-rect{
  animation: nd-wipe-x calc(720ms * var(--nd-speed)) var(--nd-ease-in-out) forwards;
  animation-delay: calc(560ms * var(--nd-speed));
}
.nd-sting.is-in .letter{
  animation: nd-fade calc(320ms * var(--nd-speed)) var(--nd-ease-out) forwards;
  animation-delay: calc(560ms * var(--nd-speed));
}
@keyframes nd-wipe-x   { from{ transform: scaleX(0);    } to{ transform: scaleX(1); } }
@keyframes nd-sting-bar{ from{ transform: scaleY(0.04); } to{ transform: scaleY(1); } }
@keyframes nd-fade     { from{ opacity: 0; } to{ opacity: 1; } }


/* ═══════════════════════════════════════════════════════════════════
   V. RHYTHM           Ambient · social loop, standing screen
   4600ms half-cycle · −260ms phase per bar · 9.2s round trip
   Nothing grows or fades; the gaps do the moving.
   --drift is set per bar: 0.62 0.38 0.14 −0.14 −0.38 −0.62
   ═══════════════════════════════════════════════════════════════════ */

.nd-rhythm .bar{
  animation: nd-rhythm calc(4600ms * var(--nd-speed)) var(--nd-ease-in-out) infinite alternate;
  animation-delay: calc(var(--i) * -260ms * var(--nd-speed));
}
@keyframes nd-rhythm{
  from{ transform: translateX(calc(var(--drift) * -1px)); }
  to  { transform: translateX(calc(var(--drift) *  1px)); }
}
.nd-rhythm.is-paused .bar{ animation-play-state: paused; }


/* ═══════════════════════════════════════════════════════════════════
   VI. ACCENT PASS     Interaction · link, CTA, hover state
   160ms · 28ms stagger · 300ms to pass through, exactly on budget
   The stagger IS the animation, so it takes the whole budget.
   ═══════════════════════════════════════════════════════════════════ */

.nd-accent .bar{
  transition: fill calc(160ms * var(--nd-speed)) ease;   /* `ease` is correct for color */
  transition-delay: calc(var(--i) * 28ms * var(--nd-speed));
}
/* Same delay order in both directions: the pulse passes through
   rather than draining back the way it came. */
@media (hover: hover) and (pointer: fine){
  .nd-accent:hover .bar{ fill: var(--nd-pass); }
}
.nd-accent:focus-visible .bar,
.nd-accent.is-demo .bar{ fill: var(--nd-pass); }


/* ═══════════════════════════════════════════════════════════════════
   VII. CRADLE — see nd-cradle.js. No CSS to declare; every keyframe
   is computed from the bar widths at runtime.
   ═══════════════════════════════════════════════════════════════════ */

.nd-cradle .bar{ transform: translateX(0); }


/* ═══════════════════════════════════════════════════════════════════
   VIII. WAVE          Interaction · nav logo
   200ms hover · 18ms stagger · 290ms end to end
   Press's geometry with Accent Pass's color, and the click
   inverted: Wave GROWS where Press shrinks, so the click reads as a
   state change rather than as a button being pushed down.
   ═══════════════════════════════════════════════════════════════════ */

.nd-wave .bar{
  transform-origin: 0 4.045px;
  transition:
    transform calc(200ms * var(--nd-speed)) var(--nd-ease-back),
    fill      calc(200ms * var(--nd-speed)) ease;   /* `ease` is right for color */
  transition-delay: calc(var(--i) * 18ms * var(--nd-speed));
}

/* --nd-pass rather than --nd-yellow. The pass token is ground-aware:
   it resolves to yellow on Ink and to Bone on Yellow, so the wave
   stays visible on every surface instead of going yellow-on-yellow
   the first time the mark sits on the accent ground. */
@media (hover: hover) and (pointer: fine){
  .nd-wave:hover .bar{
    transform: translateY(-0.9px) scaleY(1.06);
    fill: var(--nd-pass);
  }
}
.nd-wave:focus-visible .bar,
.nd-wave.is-demo .bar{
  transform: translateY(-0.9px) scaleY(1.06);
  fill: var(--nd-pass);
}

/* The stagger drops to zero on the click so all six answer together. */
.nd-wave .nd-mark{ transition: transform calc(110ms * var(--nd-speed)) var(--nd-ease-out); }
.nd-wave:active .nd-mark,
.nd-wave.is-demo-press .nd-mark{
  transform: scale(1.04);
  transition-duration: calc(150ms * var(--nd-speed));
}
.nd-wave:active .bar,
.nd-wave.is-demo-press .bar{ fill: var(--nd-pass); transition-delay: 0ms; }


/* ═══════════════════════════════════════════════════════════════════
   Reduced motion — fewer and gentler, not zero.
   The distinction is travel vs feedback. Entrances keep their
   opacity and lose their travel; the infinite loops stop at full
   height; every stagger goes to zero. Press keeps its scale but
   loses the hover ripple, and Accent Pass comes through untouched
   because color is not movement — removing feedback makes an
   interface feel broken rather than calm.
   Cradle is not built at all (nd-cradle.js checks this itself).
   ═══════════════════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce){
  .nd-cascade .bar,
  .nd-sting .knock,
  .nd-sting .tile-clip-rect,
  .nd-sting .word-clip-rect,
  .nd-rhythm .bar{ transform: none; }

  .nd-cascade .bar,
  .nd-sting .knock{ opacity: 0; }

  .nd-cascade.is-in .bar,
  .nd-sting.is-in .knock,
  .nd-sting.is-in .letter{
    animation: nd-fade calc(240ms * var(--nd-speed)) ease forwards;
    animation-delay: calc(var(--i, 0) * 24ms);
  }
  .nd-sting.is-in .tile-clip-rect,
  .nd-sting.is-in .word-clip-rect{ animation: none; transform: none; }

  .nd-meter .bar,
  .nd-rhythm .bar{ animation: none; }
  .nd-meter .bar{ transform: scaleY(1); }

  .nd-press .bar,
  .nd-press .nd-mark{ transition-duration: 120ms; transition-delay: 0ms; }
  .nd-press:hover .bar,
  .nd-press:focus-visible .bar,
  .nd-press.is-demo .bar{ transform: none; }
  .nd-press:active .nd-mark,
  .nd-press.is-demo-press .nd-mark{ transform: scale(0.97); transition-duration: 120ms; }

  .nd-accent .bar{ transition-delay: 0ms; }

  /* Wave: the lift is travel and goes. The color is feedback and
     stays, for the same reason Accent Pass comes through untouched. */
  .nd-wave .bar,
  .nd-wave .nd-mark{ transition-duration: 120ms; transition-delay: 0ms; }
  .nd-wave:hover .bar,
  .nd-wave:focus-visible .bar,
  .nd-wave.is-demo .bar{ transform: none; fill: var(--nd-pass); }
  .nd-wave:active .nd-mark,
  .nd-wave.is-demo-press .nd-mark{ transform: scale(1.02); transition-duration: 120ms; }
}
