/*
 * The mascot's canvas.
 *
 * It has two homes. During the age gate it is fixed over the whole page, above
 * the gate itself -- the gate is opaque, so a canvas behind it is a canvas
 * nobody sees. On the blast it is moved into .hero-art and becomes a sibling of
 * the explosion canvas it replaces, which is what lets it inherit every one of
 * the hero's scroll transforms and breakpoint rules for free instead of
 * restating them in JavaScript.
 */

.mascot-canvas {
  /*
   * Never interactive, in either home. During the gate this canvas covers the
   * "Yes" button and the way out, and both have to stay clickable -- that is
   * the whole reason this is a stylesheet rule and not something set in JS
   * where a later line could undo it.
   */
  pointer-events: none;
  display: block;
}

/*
 * UNDER the gate, not over it -- he is its backdrop.
 *
 * The obvious layering is the wrong one. Put the canvas above #ageGate and the
 * whole dialog is behind him, and no z-index on `.age-card` can rescue it:
 * #ageGate carries `z-index: 100` and so opens a stacking context, which means
 * everything inside it paints at 100 no matter what the children ask for. On a
 * phone, where the brand column is gone and he stands centre, that put a mascot
 * across the legally required question. It looked fine on a desktop, which is
 * exactly why it was worth checking on a phone.
 *
 * So he goes below, and the gate's opaque base colour comes off (below) and is
 * painted by the renderer instead. He becomes the dark the card sits on, the
 * card and the question paint over him for free, and the visitor still reads
 * the dialog first.
 */
.mascot-canvas[data-home="gate"] {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 99;
}

/*
 * The gate keeps its glow and loses its floor.
 *
 * Only the flat #08070a underneath goes -- the canvas clears to exactly that
 * colour while he is standing on it. The radial highlight stays here, over him,
 * where it always was.
 *
 * Scoped to the class the script only adds once he is actually mounted, so a
 * visitor with no WebGL, no model or no room never gets a gate with no
 * background.
 */
body.mascot-gate .age-gate {
  background: radial-gradient(circle at 50% 34%,
    rgba(135, 126, 188, .27), transparent 34%);
}

/*
 * The wall, and going through it.
 *
 * Answering Yes used to hand the page over immediately: the canvas went
 * transparent on the first line of lightFuse(), so the homepage -- a spinning
 * wheel, orbits, a smoke burst -- appeared behind him the instant the button was
 * pressed, while the gate dissolved on its own unrelated timer. Three things
 * moving at once, none of them caused by any of the others.
 *
 * Now one thing causes the next. He drops onto the gate and blows it out of the
 * way, and these two classes are what he blows.
 *
 * `.is-wall` is the moment the card stops being a card. It takes the opaque
 * floor back off the canvas -- which is why it has to outrank the rule above,
 * hence the longer selector -- and grows from its own bottom edge, so its top
 * rushes up to meet him as he falls. By the time he lands it covers the window,
 * and that is what lets the canvas go transparent underneath at the same moment
 * without anything showing through early.
 *
 * `.is-blown` is the hit. It carries on past the camera rather than fading where
 * it stands, so it reads as being driven out of the way rather than switched
 * off, and the hero is simply what was behind it.
 */
body.mascot-gate .age-gate.is-wall {
  background: #08070a;
  transform-origin: 50% 100%;
  transform: scale(1.9);
  /*
   * The background is deliberately NOT in this list, and that is load-bearing.
   *
   * It was, at 220ms, and the whole homepage showed through for those 220ms:
   * the canvas gives up being the opaque floor in the same frame this class
   * lands, so any interval where the gate is not yet opaque is an interval where
   * there is no backdrop at all. The swap has to be instant on both sides. Only
   * the transform is worth animating -- that is the wall rushing up.
   *
   * And it rushes from the first frame. This was an ease-IN, on the reasoning
   * that a wall being fallen towards should accelerate -- which is true of the
   * physics and wrong on the screen: an ease-in is almost stationary for its
   * first third, so the wall sat still through his whole crouch and only moved
   * once he had already committed, which read as the jump happening and the gate
   * reacting late. Front-loaded instead: it leaves immediately, which is what
   * makes the crouch look like the cause of it.
   */
  /*
   * The duration tracks GATE_DROP in mascot-stage.mjs. They have to end
   * together: finish sooner and the wall parks itself and waits to be hit,
   * which turns a collision into an arrival.
   */
  transition: transform 380ms cubic-bezier(.22, .61, .36, 1);
}
body.mascot-gate .age-gate.is-blown {
  opacity: 0;
  transform: scale(4.2);
  filter: blur(22px);
  pointer-events: none;
  transition: transform 620ms cubic-bezier(.2, .7, .3, 1),
              opacity 520ms ease-in,
              filter 620ms ease-out;
}

/*
 * ... and for that one beat he is in front of it.
 *
 * The whole time he is standing at the gate he paints underneath it, because the
 * question has to stay readable over him. Hitting the wall is the exception: the
 * shards have to fly in FRONT of the thing they are breaking, or he detonates
 * behind a panel and the wall appears to leave for no reason.
 *
 * Set only for the drop and the blast, and dropped again by toHero().
 */
body.mascot-gate.is-busting .mascot-canvas[data-home="gate"] { z-index: 101; }

.mascot-canvas[data-home="hero"] {
  /* Exactly where .hero-explosion-canvas sits, so the layering either side of
     it -- the lockup at 4, the copy at 7 -- is unchanged. */
  position: absolute;
  inset: 0;
  z-index: 5;
  width: 100%;
  height: 100%;
}

/*
 * visibility, not opacity, and not display.
 *
 * hero-scroll.js writes opacity to this element inline while it owns the
 * detonation, and an inline declaration beats a stylesheet one -- so an
 * opacity rule here would simply lose. The same trap is documented twice
 * already in hero-scroll.css.
 *
 * The layer keeps its box either way: it is still the thing the 3D mascot is
 * measured against on every resize.
 */
.hero.mascot-3d .hero-logo-bomb-layer {
  visibility: hidden;
}

/*
 * The same handover for the wordmark, and the same reasoning: hero-scroll.js
 * writes opacity to this img inline on every scroll frame, so an opacity rule
 * here would lose to it.
 *
 * A separate class from `mascot-3d`, not the same one. The mascot and the
 * wordmark are two models that fail independently -- the sign can be missing
 * while he is not -- and a shared class would hide an img that nothing had
 * replaced.
 *
 * The layer keeps its box: hero-scroll.css still runs the landing keyframes on
 * it, and `animationend` on those is what mascot-stage.mjs's stamp() waits for
 * to take `is-landing` off again.
 */
.hero.wordmark-3d .hero-logo-wordmark-layer {
  visibility: hidden;
}
