/* AISU HERO · fx layer (WP2). SPEC 4.4 (wipe, curtain), 5 (mascot actor and bubbles).
   #fx is fixed, full viewport, click-through, z 4 (base.css). Inside it: #wipe and #curtain (z 1), the one mascot
   #bichito (z 5), the poof pool .poofs (z 5, after the actor) and #cursor (z 6, WP1).
   Rules: only transform / translate / scale / opacity animate; no filter, no backdrop-filter; images are local.

   ---------------------------------------------------------------- the actor (mascot.js drives it)
   #bichito sits at the FEET point: mascot.js writes its inline `translate: Xpx Ypx` (the anchor's bottom-centre) and
   `--size` (the anchor's data-size, rem of canvas height). Everything inside is placed around that point, so
   transform-origin 0 0 is always "the feet".
     #bichito[data-state=off|in|on|out][data-rim=sticker|none][data-face=left|right][data-strip=appear|walk][data-anchor]
       .sprite            one-shot Web Animations: hop, appear pop, vanish, peek, boing, shake
         .sprite__body    the facing flip (`scale: -1 1`, 180ms turn) and the ONE infinite loop (beat squash)
           img.sprite__pose x14 (7 sticker + 7 bare), one has .on
           i.sprite__strip > b.sprite__film   walk / appear strip (4 frames, film = 400% wide)
       p.say.bubble       the bubble, placed by mascot.js (inline translate, --tail-x / --tail-y, data-tail)
   The pose art faces LEFT; the walk strip faces RIGHT (so the strip is always mirrored against the body).
   Canvas geometry (img/mascot/mascot.json, sticker.json): sticker 320x276, feet (161.2, 262.8); bare 300x256, feet
   (151.2, 252.8); appear frame 263x257, feet (131.2, 239.9); walk frame 212x276 (sticker) / 192x256, feet
   (118.3, 262.8) / (108.3, 252.8). --u = one source pixel. */

#bichito {
  --size: 6rem;
  --u: calc(var(--size) / 276);
  position: absolute; left: 0; top: 0; width: 0; height: 0;
  display: none; opacity: 1;
}
#bichito[data-rim="none"] { --u: calc(var(--size) / 256); }
#bichito[data-state]:not([data-state="off"]) { display: block; }

#bichito .sprite,
#bichito .sprite__body { position: absolute; left: 0; top: 0; width: 0; height: 0; transform-origin: 0 0; }
#bichito .sprite__body {
  transition: scale 180ms var(--ease-inout);
  /* the beat squash: big on the downbeat, small on the off-beat, 2 beats per cycle. beat.js aligns its phase */
  animation: wp2-idle calc(var(--beat) * 2) linear var(--beat-delay) infinite;
}
#bichito[data-face="right"] .sprite__body { scale: -1 1; }

@keyframes wp2-idle {
  0%, 100% { transform: scale(1.06, .93); }
  11%      { transform: scale(.97, 1.04) translateY(-.04rem); }
  24%, 44% { transform: scale(1); }
  50%      { transform: scale(1.03, .97); }
  60%      { transform: scale(.99, 1.015); }
  72%, 94% { transform: scale(1); }
}

/* poses: both sets stacked, only the current rim is displayed, one image is .on (swap = one class, no decode) */
#bichito .sprite__pose { position: absolute; display: none; bottom: auto; opacity: 0; max-width: none; }
#bichito[data-rim="sticker"] .sprite__pose[data-rim="sticker"],
#bichito[data-rim="none"] .sprite__pose[data-rim="none"] { display: block; }
#bichito .sprite__pose[data-rim="sticker"] {
  left: calc(-161.2 * var(--u)); top: calc(-262.8 * var(--u)); width: calc(320 * var(--u)); height: calc(276 * var(--u));
}
#bichito .sprite__pose[data-rim="none"] {
  left: calc(-151.2 * var(--u)); top: calc(-252.8 * var(--u)); width: calc(300 * var(--u)); height: calc(256 * var(--u));
}
#bichito .sprite__pose.on { opacity: 1; }
#bichito[data-strip] .sprite__pose.on { opacity: 0; }

/* strips: the frame box clips a 4-frame film; mascot.js steps the film with translateX (steps(4)) */
#bichito .sprite__strip { position: absolute; display: none; overflow: hidden; }
#bichito .sprite__film {
  position: absolute; left: 0; top: 0; display: block; width: 400%; height: 100%;
  background: no-repeat 0 0 / 100% 100%;
}
#bichito[data-strip] .sprite__strip { display: block; }
#bichito[data-strip="appear"] .sprite__strip {
  left: calc(-131.2 * var(--u)); top: calc(-239.9 * var(--u)); width: calc(263 * var(--u)); height: calc(257 * var(--u));
}
#bichito[data-strip="appear"] .sprite__film { background-image: url("../img/mascot/appear-strip@256.webp"); }
#bichito[data-strip="walk"] .sprite__strip {
  left: calc(-118.3 * var(--u)); top: calc(-262.8 * var(--u)); width: calc(212 * var(--u)); height: calc(276 * var(--u));
  transform-origin: calc(118.3 * var(--u)) calc(262.8 * var(--u));
  scale: -1 1;
}
#bichito[data-strip="walk"] .sprite__film { background-image: url("../img/mascot/sticker/walk-strip@256-sticker.webp"); }
#bichito[data-rim="none"][data-strip="walk"] .sprite__strip {
  left: calc(-108.3 * var(--u)); top: calc(-252.8 * var(--u)); width: calc(192 * var(--u)); height: calc(256 * var(--u));
  transform-origin: calc(108.3 * var(--u)) calc(252.8 * var(--u));
}
#bichito[data-rim="none"][data-strip="walk"] .sprite__film { background-image: url("../img/mascot/walk-strip@256.webp"); }

/* ---------------------------------------------------------------- the bubble (look: .bubble, WP1; position: here)
   mascot.js measures it, picks a side that clears Aisu + halo, every control and the viewport, then writes the inline
   translate (px from the feet), data-tail (down | left | right) and --tail-x / --tail-y (px inside the bubble).
   At most 2 lines: 17.5rem in menus, 8.5rem and .875rem text in the HUD. */
#bichito .say {
  position: absolute; left: 0; top: 0; margin: 0;
  width: max-content; max-width: 17.5rem;
  text-align: center; text-wrap: balance; white-space: normal;
  opacity: 0;
  animation: none;            /* mascot.js pops it from the tail (Web Animation), not the generic .bubble entrance */
}
#bichito .say.on { opacity: 1; }
#bichito:is([data-anchor="hud"], [data-anchor="hud-dock"], [data-anchor="pause"]) .say { max-width: 8.5rem; font-size: var(--fs-small); padding: .35rem .6rem; }
#bichito .say::after {
  content: ""; position: absolute; width: .85rem; height: .85rem;
  left: var(--tail-x, 50%); top: var(--tail-y, 100%); translate: -50% -50%;
  background: var(--paper, #FFFAF0); border: 0 solid var(--ink, #2C0F15); border-width: 0 var(--ow, 3px) var(--ow, 3px) 0;
  border-radius: 0 0 .2rem 0;
}
#bichito .say[data-tail="down"]::after  { top: calc(100% + var(--ow, 3px) * .5); rotate: 45deg; }
#bichito .say[data-tail="left"]::after  { left: calc(var(--ow, 3px) * -.5); rotate: 135deg; }
#bichito .say[data-tail="right"]::after { left: calc(100% + var(--ow, 3px) * .5); rotate: -45deg; }

/* ---------------------------------------------------------------- poofs (pool built by mascot.js; .poof is removed
   under reduced motion and body.lowfx by base.css) */
#fx > .poofs { position: absolute; inset: 0; z-index: 5; pointer-events: none; }
.poofs .poof { position: absolute; left: 0; top: 0; display: block; opacity: 0; max-width: none; }

/* ---------------------------------------------------------------- speed-streak wipe (4.4): 6 thin translucent bars,
   red / cobalt, skewed -20deg, crossing in 480ms with a 30ms stagger (transitions.js animates them; data-dir=r
   mirrors each bar, which reverses the travel and the trailing fade together). Removed under reduced motion and lowfx. */
#wipe.run { display: block; overflow: hidden; }
#wipe i {
  position: absolute; left: 0; display: block; border-radius: var(--r-pill, 999px);
  opacity: .35; transform: translateX(110vw) skewX(-20deg);
}
#wipe i:nth-child(odd)  { background: linear-gradient(90deg, var(--red) 0 45%, rgb(232 32 47 / 0)); }
#wipe i:nth-child(even) { background: linear-gradient(90deg, var(--cobalt) 0 45%, rgb(35 64 255 / 0)); }
#wipe i:nth-child(1) { top: 14%; height: .6rem;  width: 58vw; }
#wipe i:nth-child(2) { top: 27%; height: 1.1rem; width: 72vw; }
#wipe i:nth-child(3) { top: 39%; height: .5rem;  width: 46vw; }
#wipe i:nth-child(4) { top: 55%; height: 1.2rem; width: 75vw; }
#wipe i:nth-child(5) { top: 68%; height: .7rem;  width: 52vw; }
#wipe i:nth-child(6) { top: 83%; height: .9rem;  width: 64vw; }
#wipe[data-dir="r"] i { scale: -1 1; }

/* ---------------------------------------------------------------- curtain (3.1 exit, 4.4 title intro)
   Same recipe as the #loading backdrop: red + red seigaiha at 2rem, ivory 1.2rem hems at the outer edges, a night
   valance with scalloped lobes along the top (3rem). The halves overlap by 1% at the seam; the right half shifts its
   tile by -49.5vw so the pattern runs on. ff-curtain-l / -r: 700ms ease-inout, 80ms after the screen change. */
#curtain.on { display: block; overflow: hidden; }
#curtain .curtain__half {
  position: absolute; top: 0; bottom: 0; display: block; width: 50.5%;
  background: var(--red) url("../img/svg/pat-seigaiha-red.svg") 0 0 / 2rem 1rem repeat;
}
#curtain .curtain__half--l { left: 0; box-shadow: inset -.5rem 0 0 rgb(13 11 22 / .16); }
#curtain .curtain__half--r { right: 0; background-position: -49.5vw 0; box-shadow: inset .5rem 0 0 rgb(13 11 22 / .16); }
#curtain .curtain__half::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3rem; background: var(--night);
  mask: linear-gradient(#000 0 0) 0 0 / 100% 2rem no-repeat, url("../img/svg/edge-scallop.svg") 0 2rem / 2rem 1rem repeat-x;
}
#curtain .curtain__half--r::before { mask-position: 0 0, -49.5vw 2rem; }
#curtain .curtain__half::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 1.2rem; background: var(--ivory);
}
#curtain .curtain__half--l::after { left: 0; box-shadow: inset calc(-1 * var(--ow-s, 2px)) 0 0 var(--ink); }
#curtain .curtain__half--r::after { right: 0; box-shadow: inset var(--ow-s, 2px) 0 0 var(--ink); }
#curtain.on .curtain__half--l { animation: ff-curtain-l 700ms var(--ease-inout) 80ms both; }
#curtain.on .curtain__half--r { animation: ff-curtain-r 700ms var(--ease-inout) 80ms both; }

/* ---------------------------------------------------------------- reduced motion (4.8)
   No idle squash, no hop / walk / poof (mascot.js skips them); poses cross-fade in 120ms; the curtain fades out in
   120ms instead of splitting. The wipe and the poofs are display:none (base.css). */
@media (prefers-reduced-motion: reduce) {
  #bichito .sprite__body { animation: none; }
  #bichito .sprite__pose { transition: opacity 120ms linear; }
  #curtain.on .curtain__half--l,
  #curtain.on .curtain__half--r { animation: none; }
  #curtain.on { animation: ff-fade-out 120ms linear both; }
}

/* Ancla de respaldo de la pausa (la agrega mascot.js si #pause no trae una): parado sobre el borde de arriba de la
   hoja, del lado derecho, lejos del video. Invisible e inerte (base.css: visibility hidden, pointer-events none). */
#pause .pause-sheet > .mascot-anchor[data-mascot-anchor="pause"] { position: absolute; right: 1rem; top: -5.5rem; width: 5rem; height: 5.5rem; margin: 0; }
