/*
 * idle.css - the whole layout contract for a figure.
 *
 * Chromium 103 is the floor, because that is what RedM and FiveM ship in
 * their NUI layer. So: no :has(), no CSS nesting, no oklch(), no color-mix(),
 * no container queries. Custom properties and transforms are fine.
 */

.idle-host {
  position: relative;
  overflow: hidden;
}

/* The stage is always the figure's own pixel size. It is centred in the host
 * and scaled to fit by the player, so one figure serves a 400 px card and a
 * 1920 px loading screen from the same files. */
.idle-stage {
  position: absolute;
  top: 50%;
  left: 50%;
  transform-origin: 50% 50%;
  will-change: transform;
}

.idle-layer,
.idle-layer img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

/* The backdrop covers the whole host, independent of the figure canvas. */
.idle-bg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform-origin: 50% 50%;
  user-select: none;
  -webkit-user-select: none;
  pointer-events: none;
}

/* Every layer box is the full canvas, and the pivot alone decides how it
 * moves. The image inside fills the box too, unless the figure carries a
 * `crop` for it: then idle.js pins the smaller image to its rectangle with
 * inline left/top/width/height, which beat the 100% above. The box never
 * changes, so pivots and parents do not care whether a layer was cut. */
/* `filter` is deliberately not in this list. A hint is cheap, but an actual
 * filter is not: it forces the layer onto its own render surface, which is a
 * whole extra render pass every frame. Grim is 22 full-canvas layers, and for
 * a while every one of them carried a constant brightness(1) - 22 render
 * passes to draw a picture that needs two. Edge gave up somewhere in the
 * middle of that and left whole layers unpainted for a frame; the motes, drawn
 * last, were the only thing that survived, so the figure strobed underneath a
 * spray that did not. Brave, on the same Chromium, absorbed it.
 *
 * So filters are handed out per layer, in idle.js, to the two that light up. */
.idle-layer {
  transform-origin: 0 0;
  pointer-events: none;
}

/* No will-change here, and that is the point.

   A hint is a promise to the compositor that this layer will change, and it
   answers by giving the layer its own texture. Twenty-two layers on grim is
   twenty-two textures of about 900x900 on screen - roughly 70 MB - to draw a
   picture that fits in one. Ten of those hold the spray, and each of those ten
   is a full-canvas image carrying about fifteen specks; measured over 300 s, a
   group sits invisible for up to 7.8 s at a stretch. A texture that large, that
   empty and that idle is the first thing a compositor short of memory throws
   away, and the frame it comes back it has to be rasterised again - which is
   one frame of missing specks. Brave had the room. Edge did not, and the spray
   strobed.

   Without the hint the whole stage rasterises into one surface. That is more
   raster work per frame and far less memory, and for a stack of flat images
   drawn at screen size it is the cheaper trade. The two layers that genuinely
   need isolating - the ones that light up - are hinted individually in
   idle.js, where the player can see which ones those are. */

.idle-layer img {
  user-select: none;
  -webkit-user-select: none;
  display: block;
}
