/* The character. The only drawn thing on screen, and the one thing that never
   leaves it, scenes expand around him, nothing takes over.

   He's a sprite sheet now: one <img> per frame, stacked, with the current one
   shown. Which frame is which lives in src/engine/sprite.js; --char-h and
   --char-foot are set from there so the sizes have one home.

   There is no walk-cycle animation here. Frames advance on distance travelled
   (see sprite.js), so the cadence follows the speed instead of guessing at it. */

/* Anchored to the top of the window rather than to the bottom of it, and that
   isn't arbitrary: `--ground` is a share of the height the room was measured
   at, and a phone's fixed elements and its `vh` don't agree about where the
   bottom is while a toolbar is coming and going. The top edge is the one place
   every browser means the same thing, so his feet are placed *down* from it and
   the argument never arises. */
#char {
  position: fixed;
  left: calc(50% + var(--char-x, 0px));
  top: calc(var(--ground) - var(--char-h, 192px));
  width: 1px;              /* an anchor: the frames centre themselves on it */
  height: var(--char-h, 192px);
  z-index: 20;             /* in front of the objects he's standing among */
}

/* Only the clip being played is in the render tree. Frames he has worn before
   keep their <img> so walking back is instant, but a hidden 320x440 frame is
   half a megabyte of decoded bitmap and there are over a hundred by the end of
   the walk, so the rest are taken out until they're wanted again. Within a clip
   the swap is still opacity alone, which is a composite and nothing more. */
#char .frame { display: none; }
#char .frame.live {
  display: block;
  position: absolute;
  left: 50%;
  /* The art has empty space below his feet, and each clip sits at a slightly
     different height in its box. --char-foot is the constant part, --clip-drop
     the per-clip correction from sprite.js. Together they put him on the floor. */
  bottom: calc((var(--char-foot, 8px) + var(--clip-drop, 0px)) * -1);
  height: var(--char-h, 192px);
  width: auto;
  transform: translateX(-50%);
  image-rendering: auto;
  opacity: 0;
  pointer-events: none;
}
#char .frame.on { opacity: 1; }

#char .shadow {
  position: absolute;
  left: 50%;
  bottom: -5px;
  width: 82px;
  height: 13px;
  border-radius: 50%;
  background: #000;
  opacity: .11;
  filter: blur(5px);
  transform: translateX(-50%);
  transition: transform .3s ease;
}
body.room-dark #char .shadow { background: #fff; opacity: .07; }

/* Idle ----------------------------------------------------------------

   Not decoration: standing still for long enough should read as waiting for
   you, which is what tells a first-time visitor the keys are theirs. There's
   no idle sprite yet, so this is a breath rather than a pose. Replace it the
   moment there are frames for it. */
#char.idle .frame.on { animation: breathe 4.2s ease-in-out infinite; }

@keyframes breathe {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50% { transform: translateX(-50%) translateY(-2px); }
}
