:root {
  --key: min(24vw, 92px);
  --key-gap: 10px;
  --topbar-h: 104px;
  /* The page's bottom breathing room. Named once and used in both the
     container's padding and the play screen's height, because the play
     screen has to account for it and a second copy would drift. */
  --page-pad-bottom: 40px;
}

/* tac aesthetic: white page, hard black borders, monospace, chunky buttons */

* {
  box-sizing: border-box;
  /* iOS Safari: kill double-tap-to-zoom on every tappable thing (still
     allows scroll/pinch). Board overrides this with touch-action: none. */
  touch-action: manipulation;
}

body {
  margin: 0;
  background: #fff;
  color: #000;
  font-family: "Courier New", Courier, monospace;
  -webkit-tap-highlight-color: transparent;
}

.container {
  max-width: 560px;
  margin: 0 auto;
  padding: 12px 16px var(--page-pad-bottom);
}

h1, h2, h3 {
  font-weight: bold;
  margin: 0.5em 0 0.3em;
}

a {
  color: #000;
}

/* --- neubrutalist controls: flat at rest (borders only); the hard offset
   shadow appears on hover, focus, press, or selection. ----------------- */

button {
  font-family: inherit;
  border: 2px solid #000;
  border-radius: 0;
  background: #fff;
  padding: 10px 16px;
  font-size: 18px;
  cursor: pointer;
  color: #000;
  /* everything reads left-aligned, buttons included */
  text-align: left;
  transition: transform 60ms ease, box-shadow 60ms ease;
}

button:hover:not(:disabled),
button:focus-visible {
  outline: none;
  box-shadow: 3px 3px 0 #000;
}

button:active:not(:disabled) {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 #000;
}

button:disabled {
  opacity: 0.45;
  cursor: default;
  box-shadow: none;
}

button.big {
  width: 100%;
  height: 70px;
  font-size: 30px;
}

button.big:hover:not(:disabled),
button.big:focus-visible {
  box-shadow: 4px 4px 0 #000;
}

input, select {
  font-family: inherit;
  font-size: 18px;
  border: 2px solid #000;
  border-radius: 0;
  padding: 10px;
  background: #fff;
  width: 100%;
  transition: box-shadow 60ms ease;
}

input:focus, select:focus {
  outline: none;
  box-shadow: 4px 4px 0 #000;
}

.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 8px;
}

.topbar .brand {
  font-size: 22px;
  font-weight: bold;
  text-decoration: none;
}

.profile-chip {
  display: inline-flex;
  align-items: center;
  height: 40px;
  border: 2px solid #000;
  padding: 0 12px;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  user-select: none;
}

.profile-chip:hover {
  box-shadow: 2px 2px 0 #000;
}

.profile-chip:active {
  transform: translate(1px, 1px);
  box-shadow: 1px 1px 0 #000;
}

.row {
  display: flex;
  gap: 10px;
  align-items: center;
}

.row > * {
  flex: 1;
}

.banner {
  border: 2px solid #000;
  padding: 10px;
  margin: 10px 0;
  font-size: 15px;
}

.banner.error {
  border-color: #c00;
  color: #c00;
}

.banner.rainbow {
  border: 2px solid;
  border-image: linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet) 1;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 16px;
}

th, td {
  text-align: left;
  padding: 6px 8px;
  border-bottom: 1px solid #000;
}

td.right, th.right {
  text-align: right;
}

td.kick {
  cursor: pointer;
  width: 40px;
}

/* home games list: quiet two-line rows, no dividers */
table.games td {
  border-bottom: none;
  padding: 8px 8px 8px 0;
  vertical-align: middle;
  font-size: 15px;
}

table.games td.host-emoji {
  font-size: 36px;
  line-height: 1;
  width: 48px;
  padding-right: 6px;
}

/* Join hugs the right edge, exactly under Start new game, two lines tall */
table.games td.join-cell {
  padding-right: 0;
  width: 1%;
  white-space: nowrap;
}

table.games button.join {
  height: 58px;
}

/* players roster: one framed block, uniform 1px separators inside */
table.players {
  border: 2px solid #000;
}

table.players td {
  border-bottom: none;
  padding: 8px;
}

table.players tr + tr td {
  border-top: 1px solid #000;
}

/* the add-a-bot footer row inside the players table */
.add-bot-row td {
  background: #fff;
}

table.players .add-bot-row td {
  border-top: 2px solid #000;
}

button.mini {
  font-size: 14px;
  padding: 5px 10px;
  margin-right: 6px;
}

/* --- board ------------------------------------------------------------- */

/* Full-bleed and square: breaks out of the page padding so the board spans
   the entire viewport width on mobile. */
.board-wrap {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  cursor: pointer;
}

@media (min-width: 592px) {
  .board-wrap {
    width: 560px;
    margin-left: auto;
    margin-right: auto;
  }
}

/* --- d-pad ------------------------------------------------------------- */

.dpad {
  display: grid;
  /* Four separate sides rather than a WASD huddle: down gets its own row,
     so every key is reachable without the thumb hunting for a middle. */
  grid-template-columns: repeat(3, var(--key));
  grid-template-rows: repeat(3, var(--key));
  gap: var(--key-gap);
  justify-content: center;
  margin: 0 auto;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

.dpad button {
  width: 100%;
  height: 100%;
  padding: 0;
  background: #fff;
  border: 2px solid #000;
  touch-action: manipulation;
  display: flex;
  align-items: center;
  justify-content: center;
  /* No transition, no shadow, no shift. A control you steer with should
     answer on the frame you touch it; anything easing in or moving under
     the thumb reads as lag even when the move has already gone. */
  transition: none;
  box-shadow: none;
}

.dpad button:hover:not(:disabled),
.dpad button:focus-visible {
  box-shadow: none;
}

.dpad button:active {
  background: #eee;
  transform: none;
  box-shadow: none;
}

/* A real d-pad's arrows: solid triangles, cut from a square so they stay
   crisp at whatever size the key ends up. */
.dpad .tri {
  width: 40%;
  aspect-ratio: 1;
  background: #000;
  display: block;
}

.d-up .tri { clip-path: polygon(50% 8%, 100% 92%, 0 92%); }
.d-down .tri { clip-path: polygon(0 8%, 100% 8%, 50% 92%); }
.d-left .tri { clip-path: polygon(92% 0, 92% 100%, 8% 50%); }
.d-right .tri { clip-path: polygon(8% 0, 8% 100%, 92% 50%); }

.d-up { grid-column: 2; grid-row: 1; }
.d-left { grid-column: 1; grid-row: 2; }
.d-right { grid-column: 3; grid-row: 2; }
.d-down { grid-column: 2; grid-row: 3; }

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(255, 255, 255, 0.75);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  z-index: 100;
  font-size: 34px;
  pointer-events: none;
  white-space: pre-line;
}

.emoji-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 6px;
  font-size: 24px;
}

.emoji-grid .pick,
.food-hand .pick {
  border: 2px solid transparent;
  padding: 4px;
  cursor: pointer;
  text-align: center;
}

.emoji-grid .pick.selected,
.food-hand .pick.selected {
  border-color: #000;
  box-shadow: 2px 2px 0 #000;
  background: #fff;
}

.swatches {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 6px;
}

.swatches .pick {
  height: 32px;
  border: 2px solid transparent;
  cursor: pointer;
}

.swatches .pick.selected {
  border-color: #000;
  box-shadow: 2px 2px 0 #000;
}

.modal-back {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
  padding: 16px;
}

.modal {
  background: #fff;
  border: 3px solid #000;
  max-width: 480px;
  width: 100%;
  padding: 16px;
  max-height: 90vh;
  overflow-y: auto;
}

.scrubber {
  width: 100%;
  accent-color: #000;
}

/* replay: compact icon button + scrubber + turn counter */
.replay-controls {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-top: 14px;
}

.replay-controls button.icon {
  flex: 0 0 auto;
  width: 46px;
  height: 40px;
  padding: 0;
  font-size: 16px;
  line-height: 1;
  text-align: center;
}

.replay-controls .scrubber {
  flex: 1;
}

.replay-controls .turn-count {
  flex: 0 0 auto;
  min-width: 44px;
  text-align: right;
}

.speed-group {
  flex: 0 0 auto;
  display: flex;
}

.speed-group button.speed {
  width: 38px;
  height: 40px;
  padding: 0;
  font-size: 14px;
  border-left-width: 1px;
  border-right-width: 1px;
  text-align: center;
}

.speed-group button.speed:hover:not(:disabled) {
  box-shadow: none;
}

.speed-group button.speed:active {
  transform: none;
  box-shadow: none;
}

.speed-group button.speed:first-child {
  border-left-width: 2px;
}

.speed-group button.speed:last-child {
  border-right-width: 2px;
}

.speed-group button.speed.selected {
  background: #000;
  color: #fff;
}

/* compute grid stat tiles */
.stat-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 14px;
}

.stat {
  border: 2px solid #000;
  padding: 12px;
}

.stat-value {
  font-size: 28px;
  font-weight: bold;
  line-height: 1.1;
}

tr.clickable {
  cursor: pointer;
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
}

.player-chip {
  border: 2px solid #000;
  padding: 5px 10px;
  font-size: 15px;
}

.footnote {
  font-size: 13px;
  color: #333;
}

/* --- winner emoji rain -------------------------------------------------- */

.emoji-rain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 60;
}

.emoji-rain span {
  position: absolute;
  top: -8%;
  animation-name: emoji-fall;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

@keyframes emoji-fall {
  to {
    transform: translateY(120vh) rotate(var(--spin, 300deg));
  }
}

@keyframes shake {
  0% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  50% { transform: translateX(4px); }
  75% { transform: translateX(-4px); }
  100% { transform: translateX(0); }
}

.shake {
  animation: shake 0.5s ease infinite;
}

/* The lobby's hand of foods is the app's emoji grid, eight across. It must
   never scroll sideways — a choice you have to swipe to see is a choice you
   won't make. */
.food-hand {
  display: grid;
  /* minmax(0, 1fr) rather than 1fr: a plain 1fr column will not shrink below
     its content, so eight emoji at full size push the row wider than the
     screen and it scrolls. */
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 6px;
  font-size: 22px;
  margin-top: 4px;
}

.food-hand .pick {
  padding: 3px 0;
  overflow: hidden;
}

/* Under the d-pad, where the thumb already is. */
/* Bottom left, out of the thumb's way and away from the d-pad. */
.pause-row {
  position: absolute;
  left: 0;
  bottom: 0;
}

/* --- the play screen ----------------------------------------------------
   Everything must be on screen at once, on any phone, without scrolling.
   Rather than budget the space with arithmetic that goes stale the moment
   a font or a button changes, the screen is a column of a known height:
   the controls keep their footprint and the board is given whatever is
   left. The svg carries a viewBox, so it fits itself into that however
   tall or short it turns out to be. */
.play {
  /* Full bleed itself, rather than holding a board that has to fight its
     way out. The board escaped the page padding with a negative margin,
     and the overflow: hidden below then cut those 16px straight back off
     each side — a clip that no layout measurement can see, because the
     board's box really was the full width. The padding goes to the parts
     that want it instead. */
  width: 100vw;
  margin-left: calc(50% - 50vw);
  /* A little air under the header. */
  padding-top: 8px;
  /* dvh, not vh: on a phone the address bar comes and goes, and vh is the
     tall version, which would put the d-pad under it. */
  /* The page's generous bottom padding is for scrolling pages; this one
     doesn't scroll, and on a small phone those pixels are the difference
     between a thumbable d-pad and a fiddly one. Take them back. */
  margin-bottom: calc(-1 * var(--page-pad-bottom));
  height: calc(100dvh - var(--topbar-h) - 16px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Older browsers that don't know dvh get the static viewport. */
@supports not (height: 100dvh) {
  .play { height: calc(100vh - var(--topbar-h) - 16px); }
}

/* Edge to edge, and it stays that way. The board takes the full width of
   the phone and its own height from that, rather than being handed
   whatever is left over — which is what made it shrink during play and
   then jump back to full width when the game ended. The controls take the
   remainder. max-height is the safety net for a screen too short to hold a
   square board at full width; there, and only there, it letterboxes. */
.play .board-wrap {
  flex: 0 0 auto;
  width: 100%;
  aspect-ratio: var(--board-aspect, 1);
  max-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

@media (min-width: 592px) {
  .play {
    width: 100%;
    margin-left: 0;
  }
}

.play-note {
  margin: 0 0 4px;
  padding: 0 16px;
  flex: 0 0 auto;
}

.controls {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  padding: 10px 16px 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Results start under the board; the d-pad centres itself in whatever
     room is left (margin: auto below) rather than both being centred, which
     left the scoreboard floating in the middle of the screen. */
  justify-content: flex-start;
  overflow-y: auto;
}

/* The d-pad is a square as tall as the room it is given, so the keys grow
   on a big screen and give way to the board on a small one, without any
   size being written down twice. */
.play .dpad {
  margin: auto;
  height: 100%;
  width: auto;
  aspect-ratio: 1;
  max-height: 320px;
  max-width: 100%;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
}

.results {
  width: 100%;
}

.results .row {
  gap: 8px;
  margin-top: 8px;
}

.results .row button {
  flex: 1 1 0;
  text-align: center;
}
