/**
 * Breakout's app shell layout.
 *
 * Loaded INSTEAD of css/layout.css, which encodes Tetris's hold/field/next grid
 * and a 1:2 playfield. Everything else — components.css, backgrounds.css,
 * animations.css — is shared unchanged, so the three games look like one
 * product.
 *
 * Viewport height is declared three times, last-valid-wins:
 *   100vh   legacy fallback
 *   100svh  small viewport — never taller than the visible area, so the layout
 *           cannot overflow behind mobile browser chrome
 *   100dvh  dynamic viewport — tracks the address bar as it collapses
 *
 * A 7:8 PLAYFIELD, WITHOUT THE CIRCULARITY
 *
 * The virtual grid is 28 x 32 cells, so the board is 7 wide to 8 tall. Tetris
 * derives its board width from its height via aspect-ratio on an `auto` grid
 * column; that works for a board that is almost always height-bound, but this
 * one is width-bound as often as not, and an `auto` column asked to size a box
 * that sizes itself from the column is circular.
 *
 * So the field column gets a definite size and the slab is sized against the
 * container's own axes, exactly as css/snake.css does:
 *
 *   width  = min(100cqw, 87.5cqh)      87.5 = 100 x 7/8
 *   height = width x 8/7               via aspect-ratio
 *
 * Both terms are plain container units — no calc() inside min(), nothing to
 * take on faith. When the height is the binding constraint the slab is exactly
 * 100cqh tall; when the width is, it is exactly 100cqw wide. No measurement
 * feeds back into the thing being measured. `container-type: size` is what
 * makes those units resolve against the field rather than the viewport.
 */

.app {
  height: 100vh;
  height: 100svh;
  height: 100dvh;
  padding: var(--sp-5) var(--sp-4);
  gap: var(--sp-4);
  display: grid;
  align-items: center;
  justify-content: center;

  /* Wide default: board on the left, stats alongside. Both tracks are bounded
     so neither can be inflated by its own contents. */
  grid-template-columns: minmax(0, 1fr) var(--panel-w);
  grid-template-rows: minmax(0, 1fr);
  grid-template-areas: "field stats";
}

.panel--stats { grid-area: stats; align-self: start; }

.field {
  grid-area: field;
  height: 100%;
  min-height: 0;
  min-width: 0;
  display: grid;
  place-items: center;
  /* Establishes the query container the cq units below resolve against. Size
     containment is safe here precisely because the track is already definite. */
  container-type: size;
}

.field__slab {
  width: min(100cqw, 87.5cqh);
  aspect-ratio: 7 / 8;
  padding: var(--sp-2);
  background: var(--slab-bg);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3), var(--shadow-glow);
  backdrop-filter: blur(2px);
}

/* Both canvases occupy the same grid cell, z-ordered by DOM order. They are
   absolutely positioned by the renderer, which also pins their CSS box to the
   snapped fit — so nothing in here can stretch them off their whole pixels. */
.field__stack {
  position: relative;
  width: 100%;
  height: 100%;
  display: grid;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--well-bg);

  /* Load-bearing for the pointer control, not just for zoom suppression: the
     paddle follows pointermove across this element, and without it a drag
     scrolls the page instead. Scoped here so dialogs stay scrollable. */
  touch-action: none;
  -webkit-touch-callout: none;
  user-select: none;
  -webkit-user-select: none;
  cursor: none;             /* the paddle IS the cursor */
}

.field__stack > .layer {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
}

/* Hidden on desktop; the touch layout below reveals it. layout.css normally
   supplies this default, and Breakout does not load layout.css. */
.controls { display: none; }

/* --- Touch layout --------------------------------------------------------- */

/* Narrow OR portrait OR coarse-pointer, so a landscape tablet still gets
   on-screen controls rather than a keyboard-only desktop layout it cannot
   drive. Stats across the top, board in the middle, controls at the bottom. */
@media (max-width: 900px), (orientation: portrait), (pointer: coarse) {
  .app {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "stats"
      "field"
      "controls";
    padding: var(--sp-2);
    gap: var(--sp-2);
  }

  .controls { grid-area: controls; display: flex; justify-content: center; }

  .field__slab { padding: var(--sp-1); }

  /* A finger is the paddle here, and hiding the system cursor on a touch
     device does nothing useful while breaking a stylus. */
  .field__stack { cursor: auto; }
}

/* --- Control cluster ------------------------------------------------------ */

/* Arrows either side of Launch. Dragging on the board is the primary control
   and these are the fallback, so they are laid out for a two-thumb grip: the
   arrows at the outer edges where thumbs rest, Launch in the middle where it
   is hard to hit by accident mid-rally. */
.controls__cluster--paddle {
  grid-template-columns: repeat(3, minmax(3.5rem, 5.5rem));
  grid-template-areas: "left launch right";
  align-items: center;
}

.cbtn--left   { grid-area: left; }
.cbtn--right  { grid-area: right; }
.cbtn--launch { grid-area: launch; }

.cbtn--paddle {
  color: var(--text-primary);
  font-size: var(--fs-lg);
}

.cbtn--launch {
  font-size: var(--fs-sm);
  letter-spacing: 0.04em;
}

/* --- Settings notes ------------------------------------------------------- */

/* Speed changes what a score means, so it carries a line of explanation.
   components.css lays a settings row out as label | control with no room for a
   third child, so the row is allowed to wrap and the note takes the whole of
   the second line. */
.settings-row { flex-wrap: wrap; }

.settings-row__note {
  flex-basis: 100%;
  color: var(--text-muted);
  font-size: var(--fs-xs);
}

/* --- Lives ---------------------------------------------------------------- */

/* Lives read as pips rather than a number: three of something is faster to
   count than to parse, and it is the one stat a player checks mid-rally. */
.lives {
  display: flex;
  gap: var(--sp-1);
  align-items: center;
  min-height: 1rem;
}

.lives__pip {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background: var(--ball, #f8fafc);
  box-shadow: 0 0 6px color-mix(in oklab, var(--ball, #f8fafc) 60%, transparent);
}

.lives__pip--spent {
  background: transparent;
  border: 1px solid var(--border-subtle);
  box-shadow: none;
}

/* --- Very short viewports (landscape phones) ------------------------------ */

@media (max-height: 520px) {
  .app { padding: var(--sp-1); gap: var(--sp-1); }
  .panel__label { display: none; }

  .controls__cluster--paddle {
    grid-template-columns: repeat(3, minmax(3rem, 4.5rem));
  }
}
