/* critters.css (owner: the critters agent): the hopping Poring and friends.
 *
 * critters.js appends <div class="fx-layer critters-layer" aria-hidden="true"> to an art section (base.css,
 * Layers: over the art, under the panels, text and the arched edge, whose curve also masks the layer) and
 * puts one critter at a time in it:
 *
 *   .critter                  placed with transform (and leaning with a sloping rim, about its feet);
 *                             its sideways travel is the transition below
 *                             .is-risen: it stands on an edge's rim, coming up from behind it
 *     .critter__shadow        on the ground: shrinks and fades while the critter is in the air
 *     .critter__air           the height of a hop
 *       .critter__body        squash and stretch, from the feet
 *         picture > img       the sprite (/assets/img/critters/), mirrored with .is-left
 *       .critter__hit         the tap target of the easter egg
 *
 * The steps are classes on .critter (.is-hop, .is-bounce, .is-jump, .is-idle, .is-turn), each one CSS
 * animation; the script starts the next step when one ends. Everything that moves is transform or
 * opacity, which the browser animates off the main thread. With prefers-reduced-motion, in print and
 * with forced colours there are no layers at all (base.css), and the script does not start. */

.critter {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--critter-w, 60px);
  aspect-ratio: 128 / 117;
  transform-origin: 50% 97%;   /* its feet: on a slope it leans about them */
  /* A hop's sideways travel: only while in the air, after the crouch. */
  transition: transform var(--critter-air, 370ms) linear var(--critter-crouch, 100ms);
}

.critter__shadow,
.critter__air,
.critter__body,
.critter__hit {
  position: absolute;
}

.critter__air,
.critter__body {
  inset: 0;
}

/* The parts that move keep their own compositor layers for the whole visit (a critter exists only while
   it is on stage), so no step has to paint them afresh. */
.critter,
.critter__shadow,
.critter__air,
.critter__body {
  will-change: transform;
}

/* The art's jelly ends 3 % above the bottom of the sprite: that is where the feet are. */
.critter__body {
  transform-origin: 50% 97%;
}

.critter__body picture,
.critter__body img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
}

.critter.is-left .critter__body img {
  transform: scaleX(-1);
}

.critter__shadow {
  left: 13%;
  width: 74%;
  bottom: -6%;
  height: 18%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(58 38 12 / 0.34), rgb(58 38 12 / 0.16) 60%, rgb(58 38 12 / 0));
}

/* On an edge's gold rim the critter needs no shadow: half of one would smudge the art above the rim. */
.critter.is-risen .critter__shadow {
  display: none;
}

/* The easter egg: a tap or click on the critter. base.css lets no child of a layer catch one, so this
   asks for it, on this box only; it moves with the critter, which never comes near text or controls,
   and the content above the layer still gets every click that lands on it. */
.fx-layer.critters-layer .critter__hit {
  left: -12%;
  right: -12%;
  top: -14%;
  bottom: -6%;
  min-width: 44px;
  min-height: 44px;
  pointer-events: auto !important;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* The footer's crown (components.css: an arched edge standing on the footer's top edge, in the margin
   above it). Its layer stands there in the same way, as tall as the script asks, so the crown's mask
   (components.css) cuts it along the rim; the hops on it are low. */
.site-footer > .critters-layer {
  inset: auto 0 calc(100% - 1px);
  height: var(--critters-h, 0px);
}

.critter.is-low.is-hop .critter__air {
  animation-name: critter-air-low;
}

/* ---- Steps ------------------------------------------------------------------------------------- */
.critter.is-hop .critter__air {
  animation: critter-air var(--critter-hop, 640ms) linear both;
}

.critter.is-bounce .critter__air {
  animation: critter-air-low var(--critter-hop, 640ms) linear both;
}

.critter.is-jump .critter__air {
  animation: critter-air-high var(--critter-jump, 980ms) linear both;
}

.critter.is-hop .critter__body,
.critter.is-bounce .critter__body {
  animation: critter-squash var(--critter-hop, 640ms) ease-in-out both;
}

.critter.is-jump .critter__body {
  animation: critter-squash var(--critter-jump, 980ms) ease-in-out both;
}

.critter.is-hop .critter__shadow,
.critter.is-bounce .critter__shadow {
  animation: critter-shadow var(--critter-hop, 640ms) linear both;
}

.critter.is-jump .critter__shadow {
  animation: critter-shadow-high var(--critter-jump, 980ms) linear both;
}

.critter.is-idle .critter__body {
  animation: critter-idle 1.3s ease-in-out var(--critter-idle-n, 2) both;
}

.critter.is-turn .critter__body {
  animation: critter-turn 340ms ease-in-out both;
}

/* A hop in three parts: the crouch (0-16 %), the flight (16-74 %, a true parabola: the rise eases
   out and the fall eases in quadratically) and the landing (74-100 %). */
@keyframes critter-air {
  0%, 16% { transform: translateY(0); animation-timing-function: cubic-bezier(0.333, 0.667, 0.667, 1); }
  45% { transform: translateY(-62%); animation-timing-function: cubic-bezier(0.333, 0, 0.667, 0.333); }
  74%, 100% { transform: translateY(0); }
}

@keyframes critter-air-low {
  0%, 16% { transform: translateY(0); animation-timing-function: cubic-bezier(0.333, 0.667, 0.667, 1); }
  45% { transform: translateY(-26%); animation-timing-function: cubic-bezier(0.333, 0, 0.667, 0.333); }
  74%, 100% { transform: translateY(0); }
}

@keyframes critter-air-high {
  0%, 16% { transform: translateY(0); animation-timing-function: cubic-bezier(0.333, 0.667, 0.667, 1); }
  45% { transform: translateY(-150%); animation-timing-function: cubic-bezier(0.333, 0, 0.667, 0.333); }
  74%, 100% { transform: translateY(0); }
}

/* Squash before the leap, stretch on the way up and down, squash on landing, wobble back. */
@keyframes critter-squash {
  0% { transform: scale(1, 1); }
  11% { transform: scale(1.12, 0.86); }
  16% { transform: scale(1.16, 0.82); }
  24% { transform: scale(0.88, 1.14); }
  38% { transform: scale(0.97, 1.03); }
  45% { transform: scale(1.02, 0.98); }
  62% { transform: scale(0.94, 1.07); }
  74% { transform: scale(0.97, 1.03); }
  80% { transform: scale(1.2, 0.8); }
  88% { transform: scale(0.95, 1.05); }
  95% { transform: scale(1.02, 0.98); }
  100% { transform: scale(1, 1); }
}

@keyframes critter-shadow {
  0%, 16% { transform: scale(1); opacity: 1; animation-timing-function: cubic-bezier(0.333, 0.667, 0.667, 1); }
  45% { transform: scale(0.62); opacity: 0.45; animation-timing-function: cubic-bezier(0.333, 0, 0.667, 0.333); }
  74% { transform: scale(1); opacity: 1; }
  80% { transform: scale(1.14, 1); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

@keyframes critter-shadow-high {
  0%, 16% { transform: scale(1); opacity: 1; animation-timing-function: cubic-bezier(0.333, 0.667, 0.667, 1); }
  45% { transform: scale(0.4); opacity: 0.25; animation-timing-function: cubic-bezier(0.333, 0, 0.667, 0.333); }
  74% { transform: scale(1); opacity: 1; }
  80% { transform: scale(1.18, 1); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

/* Resting: the jelly breathes. */
@keyframes critter-idle {
  0%, 100% { transform: scale(1, 1); }
  30% { transform: scale(1.045, 0.955); }
  60% { transform: scale(0.975, 1.03); }
  80% { transform: scale(1.01, 0.99); }
}

/* Turning round: a quick squish hides the mirror flip. */
@keyframes critter-turn {
  0% { transform: scale(1, 1); }
  40% { transform: scale(1.1, 0.9); }
  70% { transform: scale(0.96, 1.04); }
  100% { transform: scale(1, 1); }
}

/* ---- The Angeling ------------------------------------------------------------------------------
 * Glides across the hero's sky, between the header and the panel, bobbing; its travel is one long
 * transition of .critter. A golden halo over its cap and a soft glow behind it. */
.critter--angel {
  transition: transform var(--critter-glide, 16s) linear;
}

.critter--angel .critter__air {
  animation: critter-float 2.4s ease-in-out infinite alternate;
}

.critter--angel .critter__body {
  animation: critter-flutter 0.8s ease-in-out infinite alternate;
}

.critter__aura {
  position: absolute;
  inset: -22% -16%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(255 250 230 / 0.55), rgb(255 250 230 / 0.2) 60%, rgb(255 250 230 / 0));
}

.critter__halo {
  position: absolute;
  left: 33%;
  width: 36%;
  top: -11%;
  height: 12%;
  border: 2px solid #f3c65a;
  border-radius: 50%;
  box-shadow: 0 0 5px 1px rgb(255 236 160 / 0.9), inset 0 0 3px 1px rgb(255 247 214 / 0.85);
  transform: rotate(-7deg);
}

@keyframes critter-float {
  from { transform: translateY(-8%) rotate(-3deg); }
  to { transform: translateY(8%) rotate(3deg); }
}

@keyframes critter-flutter {
  from { transform: scale(1.025, 0.975); }
  to { transform: scale(0.98, 1.02); }
}

/* ---- The sparkle the easter egg drops ------------------------------------------------------------
 * A soft glow, a gold star and a small one, that pop out at the top of the jump and fall away. */
.critter-sparkle {
  position: absolute;
  left: 0;
  top: 0;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
}

.critter-sparkle > span {
  position: absolute;
  inset: 0;
  animation: critter-sparkle 1.2s cubic-bezier(0.2, 0.7, 0.3, 1) var(--critter-sparkle-delay, 0ms) both;
}

.critter-sparkle > span:first-child {
  inset: -45%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(255 250 225 / 0.9), rgb(255 232 160 / 0.35) 50%, rgb(255 232 160 / 0));
}

.critter-sparkle > span + span {
  background: radial-gradient(circle, #fffef6 0 16%, #fff0b8 30%, #f3c65a 50%, #cf9536 72%);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}

.critter-sparkle > span + span + span {
  inset: 62% -30% -30% 62%;
  animation-duration: 1s;
  animation-delay: calc(var(--critter-sparkle-delay, 0ms) + 120ms);
}

@keyframes critter-sparkle {
  0% { transform: translateY(0) scale(0.2) rotate(0deg); opacity: 0; }
  15% { transform: translateY(-4px) scale(1.15) rotate(20deg); opacity: 1; }
  100% { transform: translateY(30px) scale(0.5) rotate(110deg); opacity: 0; }
}

/* Paused while the tab is hidden or the section is off screen (for browsers without getAnimations). */
.critters-layer.is-paused * {
  animation-play-state: paused !important;
}
