:root {
  color-scheme: dark;
}

html,
body {
  margin: 0;
  height: 100%;
  overflow: hidden;
}

body {
  /* Matches grid_pixel.png's own margin/border grey (132,126,135) so the
     page background blends with the board's margin instead of contrasting
     it. */
  background: #847e87;
  font-family: system-ui, sans-serif;
  color: #f0f0f0;
}

#app {
  position: fixed;
  top: 50%;
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

/* title.png is a deliberately tiny 90x20px source scaled 10x via
   image-rendering: pixelated, matching the other pixel-art sprites (same
   10x scale factor as the 90px-wide banners, just twice as tall). */
.title-banner {
  width: 900px;
  height: 200px;
  background-image: url('assets/title.png');
  background-size: 900px 200px;
  background-repeat: no-repeat;
  image-rendering: pixelated;
}

.board-wrap {
  position: relative;
  box-sizing: content-box;
}

.board {
  position: relative;
  box-sizing: border-box;
  /* grid_pixel.png bakes its margin and cell-square fill in as opaque
     pixels already (only the circular cell holes are transparent), so
     this only ever shows through those holes. Matching it to the page
     background (rather than a contrasting color) avoids a visible seam
     where the fit-to-screen fractional CSS scale anti-aliases the
     board's edge against the page behind it. */
  background: #847e87;
}

.piece-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* grid_pixel.png is a deliberately tiny 90x90px source meant to be scaled
   10x with nearest-neighbor (image-rendering: pixelated) to match the
   pixel-art style of the other sprite sheets, rather than blurring on
   upscale like a hi-res export would. */
.grid-overlay {
  position: absolute;
  inset: 0;
  background-image: url('assets/grid_pixel.png');
  background-size: 900px 900px;
  background-repeat: no-repeat;
  image-rendering: pixelated;
  pointer-events: none;
}

.piece {
  position: absolute;
  top: 0;
  left: 0;
  width: 100px;
  height: 100px;
  box-sizing: border-box;
  border-radius: 50%;
  padding: 6px;
  image-rendering: pixelated;
  /* Duration must match ANIMATION_DURATION_MS in render.ts. */
  transition: transform 280ms ease, opacity 280ms ease;
}

/* p1-piece.png is the yellow art, p2-piece.png is the red art (verified by
   pixel content, not filename — "p1"/"p2" don't correspond to turn order).
   Both are 10x10 sources scaled 10x, matching the other pixel-art sprites. */
.piece-red {
  background-image: url('assets/p2-piece.png');
  background-size: 100px 100px;
}

.piece-yellow {
  background-image: url('assets/p1-piece.png');
  background-size: 100px 100px;
}

.hotzones {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Sprite sheets are deliberately tiny 60x10px sources (6 frames of 10x10)
   meant to be scaled 10x with nearest-neighbor (image-rendering: pixelated)
   rather than a hi-res export, so they stay crisp instead of blurring on
   upscale. Per-player art (yellow for p1/red, red for p2 — matches the
   piece art color mapping) is picked via the [data-player] attribute set
   to the current mover, so the arrows always match whoever's turn it is. */
.hotzone {
  position: absolute;
  width: 100px;
  height: 100px;
  margin: 0;
  padding: 0;
  background-color: transparent;
  background-repeat: no-repeat;
  background-size: 600px 100px;
  background-position: 0 0;
  image-rendering: pixelated;
  border: none;
  cursor: pointer;
  pointer-events: auto;
  animation: hotzone-frames 900ms steps(6) infinite;
}

.hotzone[data-player='yellow'] {
  background-image: url('assets/insert-piece-button-sheet-p1.png');
}

.hotzone[data-player='red'] {
  background-image: url('assets/insert-piece-button-sheet-p2.png');
}

@keyframes hotzone-frames {
  from {
    background-position: 0 0;
  }
  to {
    background-position: -600px 0;
  }
}

/* insert-piece-button.png's default art points down; rotate per edge so it
   always points in the direction pieces will fall from that edge. */
.hotzones-top .hotzone {
  transform: rotate(0deg);
}

.hotzones-bottom .hotzone {
  transform: rotate(180deg);
}

.hotzones-right .hotzone {
  transform: rotate(90deg);
}

.hotzones-left .hotzone {
  transform: rotate(-90deg);
}

.hotzone-disabled {
  cursor: not-allowed;
  opacity: 0.25;
  pointer-events: none;
  animation-play-state: paused;
}

/* Sprite sheets are deliberately tiny 60x10px sources (6 frames of 10x10)
   meant to be scaled 10x with nearest-neighbor (image-rendering: pixelated)
   rather than a hi-res export, so they stay crisp instead of blurring.
   Per-player art (yellow for p1/red, red for p2 — matches the piece art
   color mapping) is picked via the [data-player] attribute set to the
   current mover, same scheme as the insert-piece arrows. */
.gravity-button {
  position: absolute;
  width: 100px;
  height: 100px;
  margin: 0;
  padding: 0;
  background-color: transparent;
  background-repeat: no-repeat;
  background-size: 600px 100px;
  background-position: 0 0;
  image-rendering: pixelated;
  border: none;
  cursor: pointer;
  animation: gravity-button-frames 900ms steps(6) infinite;
}

.gravity-button[data-player='yellow'] {
  background-image: url('assets/change-gravity-button-p1-sheet.png');
}

.gravity-button[data-player='red'] {
  background-image: url('assets/change-gravity-button-p2-sheet.png');
}

@keyframes gravity-button-frames {
  from {
    background-position: 0 0;
  }
  to {
    background-position: -600px 0;
  }
}

/* The direction gravity is already pointing isn't a valid choice, so instead
   of hiding that button it's shown as an unclickable indicator using the
   original (non-player-colored) 4-frame sheet. Selector specificity matches
   the [data-player] rules above so this wins by source order regardless of
   which player's turn it is. */
.gravity-button.gravity-button-current {
  background-image: url('assets/change-gravity-button-sheet.png');
  background-size: 400px 100px;
  cursor: default;
  animation: gravity-button-current-frames 600ms steps(4) infinite;
}

@keyframes gravity-button-current-frames {
  from {
    background-position: 0 0;
  }
  to {
    background-position: -400px 0;
  }
}

/* change-gravity-button-sheet.png's drawn orientation actually points
   opposite to its nominal "down" default (confirmed visually), so every
   rotation here is offset 180deg from the naive down=0/up=180/left=90/
   right=-90 mapping used elsewhere (e.g. insert-piece-button.png). */
.gravity-button[data-direction='down'] {
  transform: rotate(180deg);
}

.gravity-button[data-direction='up'] {
  transform: rotate(0deg);
}

.gravity-button[data-direction='left'] {
  transform: rotate(-90deg);
}

.gravity-button[data-direction='right'] {
  transform: rotate(90deg);
}

/* win-p1.png / win-p2.png are deliberately tiny 90x10px sources scaled 10x
   with nearest-neighbor (image-rendering: pixelated), matching the other
   pixel-art sprites. Sits in the same top margin band the top-edge
   insert-piece buttons occupy (see .hotzone's top:0/height:100px), hidden
   until a win. */
.win-banner {
  position: absolute;
  left: 0;
  top: 0;
  background-repeat: no-repeat;
  background-size: 900px 100px;
  image-rendering: pixelated;
  pointer-events: none;
  display: none;
}

.win-banner-visible {
  display: block;
}

.win-banner[data-player='yellow'] {
  background-image: url('assets/win-p1.png');
}

.win-banner[data-player='red'] {
  background-image: url('assets/win-p2.png');
}

/* new-game-button.png is a deliberately tiny 90x10px source scaled 10x via
   image-rendering: pixelated, matching the other pixel-art sprites. */
.new-game-button {
  width: 900px;
  height: 100px;
  margin: 0;
  padding: 0;
  background-color: transparent;
  background-image: url('assets/new-game-button.png');
  background-size: 900px 100px;
  background-repeat: no-repeat;
  image-rendering: pixelated;
  border: none;
  cursor: pointer;
}
