/* Bellside motion layer — ADDITIVE. styles.css and app.js are untouched.
 *
 * Motif is derived from the product, not from a motion catalogue: this
 * register re-reads every sleeve's paperwork on each load and throws the
 * verdict away. So the motion is the ACT OF READING A LEDGER — a hairline
 * rule is drawn across a heading, rows are scanned in sequence, integrity
 * numbers are counted up as they are re-derived. No floating orbs, no
 * gradient glow, no dark-void particles.
 *
 * Every effect starts from an offset and lands on the element's REAL
 * static position, so with JS off (nothing gets tagged) the page renders
 * exactly as it does today.
 */

/* ---------- read-depth rail ----------
   A gauge, not decoration: it reports how far through the register you are.
   Sits above the sticky HUD (z-index 40) so it is never covered. */
#fx-rail {
  position: fixed; top: 0; left: 0; height: 2px; width: 100%;
  background: var(--lime); transform: scaleX(0); transform-origin: 0 50%;
  z-index: 45; pointer-events: none;
}

/* ---------- reveal ----------
   Short travel + fast settle. A market-desk surface should feel snapped
   into place, not floated in. */
.fx-rev {
  opacity: 0; transform: translateY(14px);
  transition: opacity .46s cubic-bezier(.2,.7,.2,1),
              transform .46s cubic-bezier(.2,.7,.2,1);
}
.fx-rev.in { opacity: 1; transform: none; }

/* ---------- stagger (bento / grids / coverage) ---------- */
.fx-st > * {
  opacity: 0; transform: translateY(12px);
  transition: opacity .42s cubic-bezier(.2,.7,.2,1),
              transform .42s cubic-bezier(.2,.7,.2,1);
}
.fx-st.in > * { opacity: 1; transform: none; }
.fx-st.in > *:nth-child(1){transition-delay:0ms}
.fx-st.in > *:nth-child(2){transition-delay:55ms}
.fx-st.in > *:nth-child(3){transition-delay:110ms}
.fx-st.in > *:nth-child(4){transition-delay:165ms}
.fx-st.in > *:nth-child(5){transition-delay:220ms}
.fx-st.in > *:nth-child(6){transition-delay:275ms}
.fx-st.in > *:nth-child(7){transition-delay:330ms}
.fx-st.in > *:nth-child(8){transition-delay:385ms}
.fx-st.in > *:nth-child(n+9){transition-delay:440ms}

/* ---------- ruling the ledger ----------
   The hairline under a section heading is DRAWN rather than just present.
   .shead has no ::after of its own in styles.css. */
.fx-rule { position: relative; }
.fx-rule::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -10px; height: 1px;
  background: var(--pitch); transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .62s cubic-bezier(.65,0,.35,1) .06s;
}
.fx-rule.in::after { transform: scaleX(1); }

/* ---------- section number stamps into place ---------- */
.fx-stamp .snum {
  opacity: 0; transform: translateY(-6px) scale(.9);
  transition: opacity .3s linear, transform .3s cubic-bezier(.34,1.56,.64,1);
}
.fx-stamp.in .snum { opacity: 1; transform: none; }

/* ---------- the register is scanned, row by row ----------
   Reuses the existing .flash lime, so the re-read on scroll speaks the
   same language as the 60-second remark already in app.js. */
.regwrap { position: relative; }
.fx-scan.in::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 34%;
  background: linear-gradient(180deg, transparent, #5e7c2f24, transparent);
  pointer-events: none; z-index: 3;
  animation: fxScan .95s cubic-bezier(.4,0,.6,1) .12s 1 both;
}
@keyframes fxScan {
  from { transform: translateY(-40%); opacity: 0; }
  18%  { opacity: 1; }
  82%  { opacity: 1; }
  to   { transform: translateY(300%); opacity: 0; }
}

/* Rows arrive in sequence — reading down the register. */
tr.fx-row {
  opacity: 0; transform: translateX(-8px);
  transition: opacity .3s linear, transform .3s cubic-bezier(.2,.7,.2,1);
}
tr.fx-row.in { opacity: 1; transform: none; }

/* ---------- counted numbers hold their column ---------- */
.fx-num { font-variant-numeric: tabular-nums; }

/* ---------- degradation ----------
   styles.css already carries a reduced-motion block; this extends the
   same contract for the tags this layer adds. Everything must be visible
   and inert, and the rail — which only exists to express motion — goes. */
@media (prefers-reduced-motion: reduce) {
  .fx-rev, .fx-st > *, tr.fx-row {
    opacity: 1 !important; transform: none !important; transition: none !important;
  }
  .fx-rule::after { transform: scaleX(1) !important; transition: none !important; }
  .fx-stamp .snum { opacity: 1 !important; transform: none !important; }
  .fx-scan.in::before { animation: none !important; display: none; }
  #fx-rail { display: none; }
}
