/* =====================================================================
   motion layer r12 (16 Oct, owner): three pieces that belong to this site and to nothing else.
     A  the Ø cursor: the contents rail's current mark is the game's own Ø, riding the rule like a metal fitting
     B  the stamp: a seal is pressed onto the page as its entry arrives, instead of settling by four pixels
     C  the blade: rules, the current-page underline and the parry / dodge signal bars are cut from left to right
   Transform and opacity only, CSS driven. Nothing here moves for a reader who prefers reduced motion; every element
   is complete at rest and without script; no size, colour or copy of an existing element changes. Loaded last.
   ===================================================================== */

/* ---------- A · Ø cursor (desktop rail only; app.js creates .oc once and moves it with the active link) ---------- */
/* the rail clips at its own box, so it borrows 12px on the left for the cursor and gives them back with a negative
   margin: the content stays where it was, the padding is inside the scroll clip */
@media (min-width: 1200px) { html.js .rail { padding-left: 12px; margin-left: -12px; } }
html.js .toc ol { position: relative; }
.toc .oc { position: absolute; left: -8px; top: 0; width: 16px; height: 22px; display: grid; place-items: center; font: 500 15px/1 var(--serif); color: var(--red-text); background: var(--bg); pointer-events: none; opacity: 0; transform: translateY(var(--oc-y, 0px)); }
.toc .oc.is-on { opacity: 1; }

/* ---------- everything that moves ---------- */
@media (prefers-reduced-motion: no-preference) {
  .toc .oc { transition: transform .45s var(--ease-out), opacity .3s ease; }

  /* B · the stamp (round 2, 16 Oct night): a seal is pressed, not faded. It waits until its entry is mostly visible,
     drops from a size and a tilt, squashes a hair and settles. --sr is the seal's rest rotation, --so its rest opacity,
     --sd its delay; the keyframes land exactly on the rest state (.sl--stamp / .sl--zh / the wall's seal-only portrait
     rest at -2deg, the wall's corner seal at -3deg, the seal-only portrait at .9 opacity). */
  .rv .sl--block { transform: none; transition: none; }
  .rv .sl img, .rv .seal, .rv .hw__st, .rv .hw__p--seal > img { opacity: 0; transform: scale(1.5) rotate(calc(var(--sr, 0deg) - 7deg)); }
  .rv .sl--stamp img, .rv .sl--zh img, .rv .hw__p--seal > img { --sr: -2deg; }
  .rv .hw__st { --sr: -3deg; }
  .rv .hw__p--seal > img { --so: .9; }
  /* the animation only fills backwards (its first frame during the delay); when it ends the element falls back to these rules,
     which are the original rest state, so nothing stays on an animated layer and the page at rest is the page as it was */
  .rv.is-in .sl img, .rv.is-in .seal, .rv.is-in .hw__st, .rv.is-in .hw__p--seal > img { opacity: 1; transform: none; animation: kfp-stamp .6s cubic-bezier(.3, .7, .2, 1) var(--sd, .3s) backwards; }
  .rv.is-in .sl--stamp img, .rv.is-in .sl--zh img { transform: rotate(-2deg); }
  .rv.is-in .hw__p--seal > img { opacity: .9; transform: rotate(-2deg); }
  .rv.is-in .hw__st { transform: rotate(-3deg); }
  /* the wall: portraits arrive in a course, one after another, each seal landing just after its portrait */
  .hw__list li.rv:nth-child(2) { transition-delay: .07s; --sd: .37s; } .hw__list li.rv:nth-child(3) { transition-delay: .14s; --sd: .44s; }
  .hw__list li.rv:nth-child(4) { transition-delay: .21s; --sd: .51s; } .hw__list li.rv:nth-child(5) { transition-delay: .28s; --sd: .58s; }
  .hw__list li.rv:nth-child(6) { transition-delay: .35s; --sd: .65s; }

  /* C1 · the current page's red underline is cut once on arrival */
  .nav a[aria-current="page"]::after { animation: kfp-blade .6s var(--ease-out) .2s both; }

  /* C2 · a rule cuts itself from left to right. The element's own border goes transparent and a 1px line of the same
     colour is drawn over its row by a pseudo-element; --bl-x is where the cut starts (after the red tick on a band). */
  html.js .rv.bl, html.js .rv.bl-b { position: relative; }
  html.js .rv.bl { border-top-color: transparent; }
  html.js .rv.bl-b { border-bottom-color: transparent; }
  html.js .rv.bl::after, html.js .rv.bl-b::after { content: ""; position: absolute; left: var(--bl-x, 0px); right: 0; height: 1px; background: var(--bl, var(--rule)); transform: scaleX(0); transform-origin: 0 50%; transition: transform .7s var(--ease-out) .1s; }
  html.js .rv.bl::after { top: -1px; }
  html.js .rv.bl-b::after { bottom: -1px; }
  html.js .rv.is-in.bl::after, html.js .rv.is-in.bl-b::after { transform: scaleX(1); }
  html.js .rv.era__band { --bl-x: 64px; }
  html.js .rv.hw__yr, html.js .rv.sig { opacity: 1; transform: none; }   /* these only draw their line; they do not fade */
  html.js .rv.hw__yr { --bl: var(--rule-soft); }

  /* C3 · combat page: the blue (parry) and red (dodge) bars are cut top to bottom, then the dot lights up once, swelling
     like the cue in the game (round 2: it used to dim, which read as "off"); red answers a beat after blue */
  html.js .sig__i.rv::before { transform: scaleY(0); transform-origin: 50% 0; transition: transform .4s var(--ease-out) .15s; }
  html.js .sig__i.rv.is-in::before { transform: none; }
  html.js .sig__i.rv.is-in .flash { animation: kfp-cue .55s cubic-bezier(.3, .7, .2, 1) .55s 1; }
  html.js .sig__i--red.rv.is-in .flash { animation-delay: .75s; }
}
/* r12 round 5: a jump (filter, in-page link) shows what lands in view at once; app.js sets .rv-now for two frames */
html.rv-now .rv, html.rv-now .rv *, html.rv-now .rv::after, html.rv-now .rv::before { transition: none !important; animation: none !important; }
@keyframes kfp-blade { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes kfp-stamp {
  0% { opacity: 0; transform: scale(1.5) rotate(calc(var(--sr, 0deg) - 7deg)); }
  38% { opacity: var(--so, 1); transform: scale(.94) rotate(var(--sr, 0deg)); }
  64% { transform: scale(1.025) rotate(var(--sr, 0deg)); }
  100% { opacity: var(--so, 1); transform: rotate(var(--sr, 0deg)); }
}
@keyframes kfp-cue { 0% { transform: scale(1); } 32% { transform: scale(1.9); } 100% { transform: scale(1); } }
