/* css/chassis.css — the page the game is held in, and nothing else.
 *
 * ONE COLUMN, LETTERBOXED. Blob Hop is a phone game and the shaft is 420 units
 * wide; on a phone that is the whole screen, and on a desktop it is a portrait
 * column in the middle with the page darkened around it. The alternative —
 * stretching to the window — makes the tower either a corridor or a field, and
 * the fling was tuned against neither.
 *
 * The canvas is the game. Everything the player READS — score, hearts, panels —
 * is DOM on top of it, because text drawn into a canvas is text that cannot be
 * selected, scaled by the OS, read by a screen reader or laid out by anything
 * that already knows how.
 */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--void);
  color: #fff;
  font-family: var(--face);
  font-weight: var(--weight-body);
  user-select: none;
  -webkit-user-select: none;
  /* A phone that scrolls the page while the thumb is aiming is a phone that
   * cannot play this game. */
  overscroll-behavior: none;
  touch-action: none;
}

#stage {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The column. Refined in JS against the real viewport, because 100vh on a phone
 * is a promise the browser does not keep once the address bar moves.
 *
 * BUT IT IS SIZED IN CSS FIRST, and that matters more than it looks. With no
 * dimensions until `resize()` ran, the column sized itself to its own content —
 * about 150px tall — so every absolutely-positioned overlay inside it was laid
 * out at 150px and then jumped to 900 the moment JavaScript arrived. Measured
 * at a 0.42 layout shift on a slow connection, from an element the player never
 * even saw.
 *
 * The aspect here is config.MIN_ASPECT (0.62). JS still has the last word — it
 * has to, for the address bar — but by then the box is already the right shape
 * and there is nothing to move. */
#column {
  position: relative;
  flex: 0 0 auto;
  overflow: hidden;
  background: var(--void);
  height: 100%;
  aspect-ratio: 62 / 100;
  max-width: 100%;
}

/* Only rounded and shadowed when it is genuinely a column in a page — on a
 * phone it IS the page, and a rounded corner there is a bezel the device
 * already has. */
@media (min-aspect-ratio: 3/4) {
  #column { border-radius: 18px; box-shadow: 0 18px 50px rgba(0, 0, 0, .6); }
}

canvas#shaft {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

/* WHERE THE CHROME LIVES. One layer over the canvas that does not take pointer
 * events — the whole surface is the aim control — with the individual pieces
 * turning them back on. */
#chrome {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
#chrome > * { pointer-events: auto; }

/* THE LOADER, and the reason it is here rather than in css/ui.css.
 *
 * tokens.css is first in the head and this is second; ui.css is third and
 * carries three hundred lines the loader does not need. This is the one piece
 * of chrome that has to be right on the very first paint, so it is styled by
 * the sheet that arrives soonest — out of the tokens, like everything else.
 *
 * It covers the column completely, so nothing behind it can be seen resizing
 * as the paintings land — which is the whole of the layout-shift fix: there is
 * no shift if there is nothing on screen to shift yet. */
#loading {
  position: absolute; inset: 0; z-index: 60;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 22px;
  background: var(--void);
  transition: opacity .25s ease-out;
}
#loading.done { opacity: 0; pointer-events: none; }

#loading .wordmark {
  font-size: 44px; font-weight: var(--weight-heavy);
  letter-spacing: -1px; line-height: .9; text-align: center;
  color: var(--rib-pink); text-shadow: 0 4px 0 var(--rib-pink-2), 0 7px 14px rgba(0, 0, 0, .5);
}
#loading .wordmark span { color: var(--coin); text-shadow: 0 4px 0 var(--btn-gold-3); }

/* THE TRACK HAS TO BE VISIBLE WHEN IT IS EMPTY. It was `rgba(8,20,36,.9)` with
 * a `--panel-edge` border, on a `--void` background — three shades of the same
 * navy, so on a slow connection the player looked at a wordmark and a barely
 * perceptible dark line and had no idea anything was happening. */
.loadbar {
  width: min(60%, 220px); height: 14px;
  background: rgba(255, 255, 255, .1);
  border: 3px solid rgba(255, 255, 255, .22);
  border-radius: var(--r-pill);
  overflow: hidden;
}
/* TWO PHASES, and the bar is honest about which it is in.
 *
 * Before the module has run there is nothing to measure — the browser is still
 * fetching the code, and a progress bar pinned at 0% for four seconds reads as
 * a game that has hung. So it sweeps: "something is happening", which is all
 * that is true yet.
 *
 * The moment src/main.js reports its first settled asset it adds `.measured`,
 * the sweep stops and the fill becomes a real proportion. */
.loadbar i {
  display: block; height: 100%; width: 40%;
  background: linear-gradient(var(--btn-green), var(--btn-green-2));
  border-radius: var(--r-pill);
  animation: sweep 1.4s ease-in-out infinite;
}
.loadbar i.measured {
  width: 0%;
  animation: none;
  transition: width .18s linear;
}
/* ALWAYS PARTLY IN VIEW. Sweeping from -110% to 260% of the fill's OWN width
 * takes it completely outside the track for a third of every cycle, so the bar
 * reads as empty half the time it is being looked at. These bounds keep a slice
 * of it on the track at every point of the loop. */
@keyframes sweep {
  0% { transform: translateX(-60%); }
  100% { transform: translateX(210%); }
}
/* A player who has asked their device for less motion gets a still bar rather
 * than a moving one. It is the first thing on screen; it should not be the
 * first thing that makes them look away. */
@media (prefers-reduced-motion: reduce) {
  .loadbar i { animation: none; width: 100%; opacity: .35; }
}

/* The self-test target. Rendered by src/main.js under ?selftest=1 and invisible
 * to a player, because it only exists in a staging build. */
#selftest { position: absolute; left: -9999px; top: 0; }

/* A build that refuses to run here says so, plainly, instead of drawing
 * nothing. See src/sitelock.js. */
#locked {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  text-align: center;
  font-size: 15px;
  line-height: 1.5;
  background: var(--void);
}
