:root {
  font-family: Arial, Helvetica, sans-serif;
  color: #edf8ef;
  background: #052b32;
  font-synthesis: none;
}
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  overflow: hidden;
  /* Kills double-tap-to-zoom, which fires constantly when a player taps
     quickly. Pinch is handled separately in controls.js, and only while
     playing, so the menu can still be zoomed by anyone who needs to. */
  touch-action: manipulation;
  -webkit-text-size-adjust: 100%;
}
/* Mashing the screen should never pop the text-selection magnifier or select
   the HUD. The name field opts back in below. */
header,
#hud,
#touch,
.games,
.lobby,
.overlay {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
input,
#name {
  -webkit-user-select: text;
  user-select: text;
}
canvas {
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  display: block;
  outline: none;
  touch-action: none;
}
/* Only ever visible when startup has already failed, so it can afford to be
   loud and to sit above everything. */
#crash {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  padding: 10px 14px;
  background: #58150f;
  color: #ffd2a4;
  font-size: 12px;
  line-height: 1.5;
  font-family: ui-monospace, Menlo, monospace;
  word-break: break-word;
  /* The report puts what failed on one line and what to do about it on the
     next, so the newline it writes has to survive. */
  white-space: pre-line;
}
.vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(90deg, rgba(1, 25, 30, 0.7), transparent 75%),
    linear-gradient(0deg, rgba(1, 25, 30, 0.55), transparent 25%);
}
header {
  position: absolute;
  top: 34px;
  left: 42px;
  right: 42px;
  display: flex;
  justify-content: space-between;
  /* flex-start, not center. The header once held the roster, which grows with
     the player count, and centred that growth pushed the wordmark down the
     screen into the stats panel below - which sits at a fixed offset and knows
     nothing about it. The roster lives in the bottom corner now, but the
     wordmark and the control pill still want to start at the same line. */
  align-items: flex-start;
  pointer-events: none;
}
/* The top-left corner is one column, and --hud-col is its measure: the
   wordmark and the stats panel below it are both exactly this wide, so the
   corner reads as a single stack rather than two things that happen to share
   an edge. The values are each tier's natural stats width - the three columns
   at that tier's type size, plus its gaps and padding - so the panel looks
   unchanged and simply stops breathing in and out as the score gains a digit
   and the timer loses one. The wordmark's file is cropped tight to its ink by
   tools/make-brand.py, so its box is its ink and a width lines it up exactly.
   Legibility still sets the floor, it is just no longer what does the sizing:
   the word reads down to about 40 px tall and is a smudge by 28, and every
   tier here clears that - 146 px of width is 43 px of it, 256 px is 75. That
   floor is also why the corner mark is the whole word rather than the little
   fish: a mark this small has to be a shape, and the shape here is the word. */
/* Two measures, one per corner. --hud-col is the top left column's: the
   wordmark and the stats panel are both exactly this wide. --roster-col is the
   bottom right panel's, and is its own number because a list of names has
   nothing to line up with. */
:root {
  --hud-col: 256px;
  --roster-col: 264px;
}
@media (max-width: 650px) {
  :root {
    --hud-col: 200px;
    --roster-col: 210px;
  }
}
@media (max-height: 520px) and (orientation: landscape) {
  :root {
    --hud-col: 146px;
    --roster-col: 210px;
  }
}
.brand {
  display: block;
  line-height: 0;
  pointer-events: auto;
}
.brand img {
  display: block;
  width: var(--hud-col);
  height: auto;
}
.brand:hover img {
  filter: brightness(1.08);
}
/* The corner logo steps aside while the menu is up, because the title art is
   already saying the name three times the size directly below it, and comes
   back for the game where it is the only place the name appears. Hidden rather
   than removed, so the header's spacing does not shift when a round starts. */
body:has(#menu:not([hidden])) .brand {
  visibility: hidden;
}
/* In the water rather than on a panel, at the foot of the corner column. The
   shadow is what makes that safe: the vignette thins out to almost nothing
   over the sand while a round is on, so bare text has to carry its own
   contrast rather than trust the tank behind it. Left selectable, unlike the
   rest of the chrome - the whole purpose of the line is to be got out of the
   screen and into someone else's device. */
.connection {
  font-size: 10px;
  letter-spacing: 1.6px;
  text-align: right;
  color: #cfe6dc;
  text-shadow: 0 1px 7px #01191ed9;
  pointer-events: auto;
}
.panel {
  position: absolute;
  left: 8%;
  top: 50%;
  transform: translateY(-45%);
  width: 530px;
  max-width: 85vw;
}
.eyebrow {
  font-size: 10px;
  letter-spacing: 2.5px;
  color: #b5d9b0;
  font-weight: bold;
}
h1 {
  font-size: clamp(44px, 5.4vw, 76px);
  line-height: 0.95;
  letter-spacing: -3.6px;
  font-weight: 800;
  margin: 0 0 34px;
  text-shadow: 0 3px 22px #01191ea8;
}
/* The menu title is artwork. Its own shadow and highlights are painted in, so
   it takes none of the type styling above - only the space. */
.title {
  margin: 0 0 26px;
  line-height: 0;
}
.title img {
  width: 100%;
  max-width: 470px;
  height: auto;
}
p {
  font-size: 15px;
  line-height: 1.7;
  color: #b9ceca;
}
label,
.group-label {
  display: block;
  font-size: 9px;
  letter-spacing: 2px;
  margin: 30px 0 10px;
}
.group-label {
  margin-top: 22px;
}
/* Labels the layout no longer shows but assistive tech still needs. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.species-strip {
  margin-top: 16px;
}
.species {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  max-width: 520px;
}
.species-nav {
  display: none;
}
.species-option {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  width: 66px;
  margin: 0;
  padding: 7px 4px 6px;
  border: 1px solid #5b827a;
  border-radius: 10px;
  background: #153e40a8;
  color: #b6d2c6;
  font-size: 7px;
  letter-spacing: 1px;
  text-transform: uppercase;
  text-align: center;
  cursor: pointer;
  transition:
    border-color 0.15s,
    background 0.15s,
    color 0.15s;
}
.species-option:hover {
  border-color: #8fb3a8;
}
.species-option:has(:checked) {
  border-color: #d1ed9e;
  background: #1f5a52c0;
  color: #edf8ef;
  box-shadow: 0 0 0 1px #d1ed9e66;
}
.species-option:has(:focus-visible) {
  outline: 2px solid #d1ed9e;
  outline-offset: 2px;
}
.species-option input {
  position: absolute;
  width: 0;
  height: 0;
  margin: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;
}
.species-option img {
  width: 58px;
  height: 48px;
  object-fit: contain;
  filter: drop-shadow(0 4px 6px #00000055);
}
input {
  width: 100%;
  max-width: 380px;
  padding: 15px 17px;
  border: 1px solid #5b827a;
  background: #153e40a8;
  color: white;
  font-size: 16px;
  border-radius: 8px;
  outline: none;
}
input:focus {
  border-color: #d1ed9e;
}
/* The name, and the die that rolls another one, side by side. The row owns the
   width the field used to own, so the field itself is unchanged and the die
   sits beside it rather than inside it - nothing overlaps the text, and a
   thumb aiming for the die cannot land in the field. `stretch` means the
   button is exactly as tall as whatever the field is on this screen, which is
   two different numbers on a phone and a desktop and neither of them written
   here twice. */
.name-row {
  display: flex;
  align-items: stretch;
  gap: 8px;
  max-width: 432px;
}
.name-row input {
  flex: 1 1 auto;
  min-width: 0;
  max-width: none;
}
.reroll {
  flex: 0 0 auto;
  width: 52px;
  padding: 0;
  border-radius: 12px;
  color: #cfe6dc;
}
.reroll:hover {
  color: #edf8ef;
}
/* Every other icon in the game is a stroke; a die's spots are the exception,
   because a ring of five outlines at 20px is a smudge. */
.reroll .pip {
  fill: currentColor;
  stroke: none;
}
.buttons {
  display: flex;
  gap: 10px;
  margin-top: 14px;
}
button {
  border: 0;
  border-radius: 8px;
  background: #d1ed9e;
  color: #183c33;
  padding: 16px 19px;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}
button:hover {
  filter: brightness(1.1);
}
button span {
  margin-left: 20px;
}
.secondary {
  background: #224c4bb3;
  border: 1px solid #60837a;
  color: #edf8ef;
}
.error {
  color: #ffd2a4;
  font-size: 13px;
}
.menu-instructions {
  position: absolute;
  bottom: 26px;
  left: 42px;
  right: 42px;
  display: flex;
  justify-content: space-between;
  color: #a8c5bd;
  font-size: 10px;
  letter-spacing: 0.8px;
  pointer-events: none;
}
/* Quiet enough to ignore, present enough to check after a deploy. */
.version {
  font-variant-numeric: tabular-nums;
  letter-spacing: 1.4px;
  color: #7f9c94;
  white-space: nowrap;
}
/* Running old code while a new build is deployed. Worth interrupting for:
   until it is reloaded, nothing you were told was fixed is fixed. */
.version.stale {
  color: #183c33;
  background: #d1ed9e;
  border-radius: 999px;
  padding: 3px 9px;
  cursor: pointer;
  /* And tappable, which needs saying twice over: the line this sits in is
     decoration and takes `pointer-events: none` so it can never eat a click
     meant for the tank, and on a phone the whole menu panel does too. This is
     the one thing in there that is really a button - it clears the caches and
     reloads - and it said TAP FOR THE NEW ONE while refusing the tap. */
  pointer-events: auto;
}
.key {
  display: inline-block;
  border: 1px solid #6e8a80;
  border-radius: 4px;
  padding: 4px 6px;
  margin-right: 4px;
  font-size: 9px;
}
.divider {
  margin: 0 14px;
}
.glass {
  background: #092f36d9;
  border: 1px solid #6eaaa43b;
  border-radius: 14px;
  backdrop-filter: blur(12px);
}
.stats {
  position: absolute;
  top: 126px;
  left: 42px;
  display: flex;
  /* The width is held, and the three readings spread themselves across it.
     Sized to content the panel breathed in and out all round - every digit
     the score gained pushed its right edge out - which is invisible until the
     wordmark above shares that edge. So `gap` here is only a floor for the
     case where the numbers grow enough to close the space up; what sets the
     spacing normally is space-between. The generous padding is the safety
     margin: even a capped 1800.0 mass beside a five-figure score reaches into
     it rather than through the glass. */
  width: var(--hud-col);
  justify-content: space-between;
  padding: 20px 26px;
  gap: 12px;
}
.stats small {
  display: block;
  font-size: 9px;
  letter-spacing: 2px;
  color: #9bbeb3;
}
.stats b {
  display: block;
  font-size: 27px;
  margin-top: 8px;
  font-weight: 500;
}
/* The bottom right corner, as one column: the roster, then the code under it.
   One set of coordinates for the pair, so the code cannot drift away from the
   panel above it when either changes height - the roster grows with the player
   count and the code wraps to two lines on a narrow screen. The top left
   corner is laid out the same way and for the same reason.
   Bottom left is the touch joystick's and stays empty. */
.tank-corner {
  position: absolute;
  right: 42px;
  bottom: 26px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  /* Transparent to the pointer, like the header it used to live in. A panel
     this size over the tank would otherwise eat the click that takes pointer
     lock, and the only thing in here worth clicking is the code, which asks
     for the pointer back itself. */
  pointer-events: none;
}
.roster {
  width: var(--roster-col);
  padding: 16px 18px;
}
.roster .eyebrow {
  font-size: 9px;
  letter-spacing: 1.8px;
  /* One line: the count belongs beside the word, not under it. */
  white-space: nowrap;
}
.roster ol {
  margin: 0;
  padding-left: 20px;
}
.roster li {
  font-size: 12px;
  padding: 8px 0;
  color: #b7d4cc;
}
.roster li span {
  float: right;
  color: #d1ed9e;
}
/* Both of these are set by main.js and are empty until a tank is joined; an
   empty one must not leave a row's worth of gap behind it. */
.connection:empty {
  display: none;
}
/* Anything that means nothing outside a round. The HUD's hidden attribute is
   already exactly that truth - set for the menu, the join screen and the
   lobby, cleared when the round starts - so the CSS reads it rather than
   keeping a second copy of it. */
body:has(#hud[hidden]) .in-play {
  display: none;
}
.overlay {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 32px 40px;
  text-align: center;
  min-width: 300px;
}
.overlay h2 {
  font-size: 32px;
  letter-spacing: -1px;
}
.overlay p {
  white-space: pre-line;
}
/* The panel is centred text, so its actions centre too rather than sitting
   against the left edge the way a form's row does. Two of them, because a
   finished match has exactly two answers: go again, or get out. */
#overlay-actions {
  justify-content: center;
  margin-top: 18px;
}
#overlay-actions button {
  border-radius: 12px;
}
#toast {
  position: absolute;
  left: 50%;
  top: 25%;
  transform: translateX(-50%);
  color: #dbf5a7;
  font-weight: bold;
  font-size: 18px;
}
#touch {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}
/* HUD panels sit above the touch zones so their buttons stay tappable. */
#hud > * {
  z-index: 2;
}
/* The header sits above those, and above the menu and lobby panels. It was
   first set for the graphics dropdown that used to hang out of it - header and
   panels were both on 2, the header comes first in the document, so on a short
   landscape screen the open list painted behind the panel - and it stays for
   the plainer version of the same reason: on a phone the menu panel is the
   whole screen and the pill has to be pressable through it. It is chrome; it
   belongs on top. The turn-your-phone overlay at 10 and the pause panel in the
   top layer still win. */
header {
  z-index: 4;
}
.touch-zone {
  position: absolute;
  top: 0;
  bottom: 0;
  pointer-events: auto;
  touch-action: none;
}
.steer-zone {
  left: 0;
  width: 50%;
}
.depth-zone {
  right: 0;
  width: 50%;
}
.zone-hint {
  position: absolute;
  bottom: max(18px, env(safe-area-inset-bottom));
  left: 0;
  right: 0;
  text-align: center;
  font-size: 9px;
  letter-spacing: 2px;
  color: #d4e9dc99;
  pointer-events: none;
}
.floating {
  position: absolute;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.joystick {
  height: 112px;
  width: 112px;
  border-radius: 50%;
  background: #abc8bf1a;
  border: 1px solid #d1ed9e88;
}
.joystick .knob {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 42px;
  height: 42px;
  margin: -21px;
  border-radius: 50%;
  background: #d1ed9e99;
}
.games {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 430px;
  max-width: 92vw;
  max-height: 90vh;
  max-height: 90dvh;
  overflow-y: auto;
  padding: 26px 30px;
  z-index: 2;
}
.games-hint {
  font-size: 11px;
  line-height: 1.6;
  margin: 16px 0 0;
  color: #9bb5ae;
}
.lobby {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 380px;
  max-width: 92vw;
  max-height: 90vh;
  max-height: 90dvh;
  overflow-y: auto;
  padding: 26px 30px;
  z-index: 2;
}
/* The two halves the markup groups the lobby into. On anything but a landscape
   phone they are not there at all: `display: contents` leaves their children in
   the panel's own column, in document order, exactly as before the grouping
   existed. The phone turns them into two columns. */
.lobby-col {
  display: contents;
}
.lobby h2 {
  font-size: 28px;
  letter-spacing: -1px;
  margin: 10px 0 14px;
}
/* The roster's own label, holding the only count on this screen. It sits on
   the list because that is what it counts - it used to be half of an eyebrow
   naming the panel. */
.roster-head {
  margin-bottom: 5px;
}
.lobby ol {
  list-style: none;
  margin: 0;
  padding: 0;
}
.lobby li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid #6eaaa42b;
  font-size: 13px;
  color: #b7d4cc;
}
.lobby li b {
  color: #edf8ef;
  font-weight: 500;
}
.lobby li .state {
  color: #9bb5ae;
  font-size: 10px;
  letter-spacing: 1.5px;
}
.lobby li .state.ready {
  color: #d1ed9e;
}
.lobby-setting {
  display: block;
  margin: 18px 0 0;
  font-size: 9px;
  letter-spacing: 2px;
}
.lobby-setting b {
  color: #d1ed9e;
}
.lobby-setting input {
  display: block;
  width: 100%;
  max-width: none;
  margin: 10px 0 0;
  padding: 0;
  border: 0;
  background: transparent;
  accent-color: #d1ed9e;
}
.lobby-setting input:disabled {
  opacity: 0.55;
}
/* What the chosen tier means, in words. Sentence case and no tracking: it is
   a line to read, not a label to scan, and it sits under the slider it
   describes rather than in the panel's one hint line at the bottom. */
.lobby-setting small {
  display: block;
  margin: 6px 0 0;
  font-size: 11px;
  letter-spacing: 0;
  text-transform: none;
  color: #9bb5ae;
}
.lobby-hint {
  margin: 14px 0 0;
  font-size: 12px;
  color: #9bb5ae;
}
/* The code a host reads out. Big, monospaced and selectable, because it gets
   typed on another device by someone squinting at this screen. */
.lobby-invite {
  display: block;
  margin: 0 0 16px;
  padding: 12px 14px;
  border: 1px solid #6eaaa44a;
  border-radius: 10px;
  background: #0e2b2633;
  text-align: center;
}
.lobby-invite span {
  display: block;
  font-size: 9px;
  letter-spacing: 2px;
  color: #9bb5ae;
}
.lobby-invite b {
  display: block;
  margin: 4px 0 6px;
  font-family: ui-monospace, Menlo, monospace;
  font-size: 30px;
  font-weight: 500;
  letter-spacing: 5px;
  color: #d1ed9e;
  user-select: all;
}
.lobby-invite small {
  display: block;
  font-size: 11px;
  line-height: 1.45;
  color: #9bb5ae;
}
/* The code as pictures, above the same code as words. Both, always: one is for
   the player who cannot type it and one is for the parent pasting it into a
   chat. The pictures take their size from the `b` rule above, which both
   halves share, and it is the words below that are shrunk out of their way -
   this rule cannot say otherwise even where it looks like it does, because
   `.lobby-invite b` carries a type selector and outranks a bare class. It
   asked for 40px for a while and got 30. */
.lobby-icons {
  line-height: 1.1;
}
.lobby-invite #lobby-code {
  font-size: 17px;
  letter-spacing: 3px;
}
.lobby-invite button {
  margin: 2px 0 10px;
  padding: 8px 14px;
  font-size: 12px;
}
/* The dialpad. Keys are deliberately large: this exists for a player who
   finds a keyboard hard, and a 64px target that responds the instant it is
   touched is the whole point. */
/* The code so far, drawn from whatever the field says. Dots for the slots yet
   to be filled, so three taps have a visible shape before the first one. */
.dialpad-slots {
  display: block;
  min-height: 46px;
  margin: 14px 0 10px;
  font-size: 34px;
  letter-spacing: 10px;
  line-height: 1.3;
  text-align: center;
}
.dialpad-keys {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 8px;
  margin: 0 0 12px;
}
.dialpad-keys button {
  min-height: 64px;
  padding: 6px 0;
  border: 1px solid #6eaaa44a;
  border-radius: 12px;
  background: #0e2b2655;
  font-size: 30px;
  line-height: 1;
  cursor: pointer;
  /* No tap-highlight lag and no double-tap zoom stealing the second key. */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition:
    transform 0.06s ease-out,
    background 0.12s ease-out;
}
.dialpad-keys button:active {
  transform: scale(0.92);
  background: #1d4c4488;
}
@media (max-width: 460px) {
  .dialpad-keys {
    grid-template-columns: repeat(4, 1fr);
  }
}
/* One panel, one column: the slots, the keys, the field and the row of
   buttons all line up on the same edges, and every button in that row is the
   same height. The old version had a Host button, a field and a Join button
   sharing a flex row with a second row of buttons underneath, which is why it
   looked like three screens stacked. */
.games .dialpad-slots,
.games .dialpad-keys,
.games input,
.games .buttons {
  width: 100%;
  max-width: none;
}
.games input {
  text-align: center;
  letter-spacing: 1px;
}
.games .buttons {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 8px;
  margin-top: 14px;
}
.games .buttons button {
  padding: 14px 16px;
  font-size: 13px;
}
.games .games-hint {
  min-height: 1.6em;
  margin: 10px 0 0;
  text-align: center;
}
.lobby .buttons {
  margin-top: 16px;
  flex-wrap: wrap;
}
.lobby .buttons button {
  padding: 12px 16px;
}
.lobby button[aria-pressed="true"] {
  background: #d1ed9e;
  color: #183c33;
}
.lobby button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
/* The lobby on a phone: two columns, and nothing to scroll to.
   Stacked in one column it came to something like 460px of content on a 390px
   screen, and the half you had to scroll for was the half you came here to use
   - the match length, the difficulty, and the button that starts the game. Who
   is in the tank and the code to get into it go on the left, the two dials and
   the way in or out on the right, and both halves are on screen at once.
   Each half is its own flex column rather than rows placed in one shared grid,
   because a shared row track is as tall as the taller of the two things in it:
   the invite block is 140px of code and the slider beside it would inherit
   that. Two independent stacks cannot open gaps like that. */
@media (max-height: 520px) and (orientation: landscape) {
  .lobby {
    padding: 14px 18px;
    width: min(660px, 94vw);
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: 18px;
    align-items: start;
  }
  .lobby-col {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }
  .lobby h2 {
    font-size: 16px;
    margin: 4px 0 6px;
  }
  .lobby li {
    padding: 3px 0;
    font-size: 10px;
  }
  /* Smaller than the names it labels, or the two read as one list. */
  .roster-head {
    margin-bottom: 3px;
    font-size: 8px;
    letter-spacing: 1.8px;
  }
  /* The roster scrolls instead of pushing the panel down the screen. Sixteen
     can be in a tank and each row is 21px, so the list was what decided how
     tall this panel got - and the things it was pushing off the bottom were
     the sliders and Start. Four and a half rows: the half is deliberate, since
     a clipped row is the only honest way a list says there is more of it, and
     the count in the eyebrow above says how many that is. */
  .lobby ol {
    max-height: 96px;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  /* The invite block, turned on its side. Stacked - eyebrow, pictures, words,
     button, note, five things down a column - it took 130px of the 350 a
     landscape phone has, to carry three symbols and a line of text. Across the
     panel it is 90: the pictures on the left, still the size they are read out
     from, the words and Copy beside them, the note under both.
     `#lobby-icons` rather than the class it also has, because `.lobby-invite b`
     would outrank the class - see the note on `.lobby-icons` above. */
  .lobby-invite {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: 10px;
    align-items: center;
    margin-bottom: 8px;
    padding: 7px 9px;
    text-align: left;
  }
  .lobby-invite span {
    grid-area: 1 / 1 / 2 / 3;
  }
  .lobby-invite #lobby-icons {
    grid-area: 2 / 1 / 4 / 2;
    margin: 0;
    font-size: 24px;
    letter-spacing: 2px;
  }
  .lobby-invite #lobby-code {
    grid-area: 2 / 2 / 3 / 3;
    margin: 0;
    font-size: 13px;
    letter-spacing: 2px;
  }
  .lobby-invite button {
    grid-area: 3 / 2 / 4 / 3;
    justify-self: start;
    margin: 0;
    padding: 5px 10px;
    font-size: 10px;
  }
  .lobby-invite small {
    grid-area: 4 / 1 / 5 / 3;
    margin-top: 4px;
    font-size: 10px;
  }
  .lobby-setting {
    margin-top: 10px;
  }
  .lobby .buttons {
    margin-top: 10px;
  }
  .lobby .buttons button {
    padding: 8px 12px;
    font-size: 11px;
  }
}
/* Portrait phones get a full-screen prompt instead of a squeezed game. */
.rotate {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 10;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 24px;
  background: #052b32;
  color: #edf8ef;
  text-align: center;
}
.rotate h2 {
  margin: 18px 0 6px;
  font-size: 28px;
  letter-spacing: -1px;
}
.rotate p {
  margin: 0;
  color: #b6d2c6;
  font-size: 14px;
}
/* The way out of the overlay for a phone that would rather stay portrait:
   fullscreen pins landscape, so this is the button that actually starts the
   game rather than asking again. */
.rotate-go {
  margin: 18px 0 0;
  padding: 14px 22px;
  border: 0;
  border-radius: 999px;
  background: #d1ed9e;
  color: #183c33;
  font: inherit;
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
}
.rotate-go span {
  margin-left: 4px;
}
/* Beats `.rotate p` above, which would otherwise win on colour and size. */
.rotate .rotate-note {
  margin: 12px 0 0;
  max-width: 32ch;
  font-size: 12px;
  color: #ffd2a4;
}
.rotate-art {
  width: min(62vw, 300px);
  height: auto;
}
.rotate-phone {
  transform-origin: 120px 120px;
  animation: rotate-phone 2.6s ease-in-out infinite;
}
@keyframes rotate-phone {
  0%,
  30% {
    transform: rotate(0deg);
  }
  60%,
  100% {
    transform: rotate(90deg);
  }
}
.rotate-arrow {
  stroke-dasharray: 300;
  animation: rotate-arrow 2.6s ease-in-out infinite;
}
@keyframes rotate-arrow {
  0%,
  30% {
    stroke-dashoffset: 300;
    opacity: 0.25;
  }
  60%,
  100% {
    stroke-dashoffset: 0;
    opacity: 1;
  }
}
.header-tools {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}
/* The graphics picker: five rows in the game's own glass and lime, in the flow
   of the panel the gear opens. A `<select>` could not have been this - its
   popup is drawn by the operating system and is the one part of a form CSS
   never reaches, which is exactly what gave away that this was a web page -
   and it is not a dropdown of ours either any more. Nothing opens, nothing
   floats, so nothing here can be clipped: the trigger, the caret, the second
   layer of glass, the absolute position, the z-index and the list's own scroll
   were all in service of a popup, and went with it. */
.picker-list {
  display: grid;
  gap: 2px;
}
.picker-option {
  display: grid;
  gap: 1px;
  padding: 9px 11px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: #d7e7e0;
  text-align: left;
  font-weight: 600;
}
.picker-option b {
  font-size: 12px;
  letter-spacing: 0.3px;
}
.picker-option small {
  font-size: 10px;
  font-weight: 500;
  line-height: 1.35;
  color: #9bb5ae;
}
.picker-option:hover {
  background: #1f5a5299;
  filter: none;
}
/* The chosen row is lit the way a chosen fish is, and marked with the tick
   rather than by colour alone. */
.picker-option[aria-checked="true"] {
  background: #1f5a52c0;
  color: #edf8ef;
  box-shadow: inset 0 0 0 1px #d1ed9e66;
}
.picker-option[aria-checked="true"] b::after {
  content: " ✓";
  color: #d1ed9e;
}
.picker-option:focus-visible {
  outline: 2px solid #d1ed9e;
  outline-offset: -2px;
}
.playing .vignette {
  background: linear-gradient(0deg, #01202744, transparent 25%);
}
.labels {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.label {
  position: absolute;
  left: 0;
  top: 0;
  padding: 2px 9px;
  border: 1px solid #3f6b6380;
  border-radius: 999px;
  background: #0f2b2c99;
  color: #edf8ef;
  font-size: 11px;
  letter-spacing: 1px;
  white-space: nowrap;
  text-shadow: 0 1px 3px #000c;
  will-change: transform;
}
.cover {
  position: absolute;
  left: 50%;
  top: 62%;
  transform: translateX(-50%);
  padding: 8px 14px;
  border: 1px solid #3f6b63;
  border-radius: 999px;
  background: #0f2b2cb0;
  color: #b5d9b0;
  font-size: 10px;
  letter-spacing: 2.5px;
  pointer-events: none;
}
[hidden] {
  display: none !important;
}
/* The same floor for the one element whose closed state is not an attribute of
   ours. A closed `<dialog>` is `dialog:not([open])` in the UA sheet, so the
   rule above never covered it, and any author `display` - the pause panel's
   phone layout gave it a grid - outranks the UA sheet and paints a panel
   nobody opened over the whole game. It shipped exactly once. */
dialog:not([open]) {
  display: none !important;
}
@media (max-width: 650px) {
  header {
    top: 22px;
    left: 22px;
    right: 22px;
  }
  .tank-corner {
    right: 22px;
    bottom: 20px;
  }
  .connection {
    font-size: 8px;
  }
  .panel {
    left: 7%;
    top: 48%;
    width: 86%;
  }
  h1 {
    letter-spacing: -2px;
  }
  .menu-instructions {
    left: 22px;
    right: 22px;
    font-size: 8px;
  }
  /* The stats panel takes the header's own margin here. It used to sit 7 px
     further out than it, which is invisible until the logo above shares an
     edge with the panel below and the pair is 7 px adrift. */
  .stats {
    top: 93px;
    left: 22px;
    padding: 13px 18px;
    gap: 10px;
  }
  .stats b {
    font-size: 21px;
  }
  .roster {
    padding: 12px 13px;
  }
  .roster .eyebrow {
    font-size: 7px;
    letter-spacing: 1px;
  }
  .roster li {
    font-size: 10px;
  }
  .buttons button {
    padding: 14px 12px;
    font-size: 12px;
  }
  .buttons button span {
    margin-left: 5px;
  }
}
@media (pointer: coarse) {
  .menu-instructions .key,
  .menu-instructions .divider {
    display: none;
  }
  .menu-instructions > div {
    font-size: 0;
  }
  .menu-instructions > div:after {
    content: "LEFT: STEER · RIGHT: DIVE OR RISE · YOU ALWAYS SWIM";
    font-size: 9px;
  }
}

@media (max-width: 460px) {
  /* Narrow enough that two 200 px columns would meet in the middle: the
     stats panel reaches 222 px in from the left and the roster the same from
     the right, which collide below about 444 px. One measure narrower and
     they clear each other, and 160 px of wordmark is still 47 px tall - above
     the floor where the word stops reading. */
  :root {
    --hud-col: 160px;
  }
  .overlay {
    max-width: 92vw;
    padding: 24px;
    min-width: 280px;
  }
}

@media (pointer: coarse) {
  .playing .menu-instructions {
    display: none;
  }
}
@media (pointer: coarse) and (orientation: portrait) {
  .rotate {
    display: flex;
  }
}
@media (max-height: 520px) and (orientation: landscape) {
  header {
    top: 12px;
    left: max(20px, env(safe-area-inset-left));
    right: max(20px, env(safe-area-inset-right));
  }
  .header-tools {
    gap: 6px;
  }
  /* The panel is the whole screen here, and the form stands in the middle of
     it: title art top left, the fish below that, name and picker and buttons
     centred on the right.
     It used to start 56px down and run from there, which put the name field
     immediately under the control pill in the same corner - a 44px target and
     a text field a few pixels apart - while 130px of screen went unused below
     the form. Centring the rows in the full height moves the whole form clear
     of the header without shrinking anything.
     The art comes out of that flow: it belongs at the top of the left column,
     above the fish preview, and as a row spanning the form it would drag the
     centred group down over the fish. Absolute inside the panel, which is the
     box it wants to be measured against anyway. */
  .panel {
    top: 0;
    transform: none;
    left: 6%;
    width: 88%;
    max-width: none;
    height: 100vh;
    height: 100dvh;
    max-height: none;
    /* No scroll container: the panel is transparent to the pointer (below), so
       a drag on it reaches the fish rather than scrolling anything, and a
       scrollbar nobody can move is worse than none. body is overflow:hidden. */
    overflow: visible;
    display: grid;
    /* minmax(0, …) keeps a wide carousel from stretching its column. */
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    column-gap: 20px;
    /* Centred in the room the form actually has, which is the screen without
       the header - the same 56px the title art below is pushed down by. The
       panel is the whole screen so that "centred" has something to mean, but
       the top of it belongs to the control pill, and centring the form on the
       glass instead of on the screen is what puts it in the middle of what is
       left rather than a thumb's width under the corner. */
    padding-top: 56px;
    align-content: center;
    /* And if a phone really is too short for the form, `safe` flushes it to
       the top instead of centring its first row out of reach. */
    align-content: safe center;
  }
  .panel h1 {
    position: absolute;
    top: 56px;
    left: 0;
    width: 38%;
    margin: 0;
    font-size: 40px;
    letter-spacing: -2px;
  }
  /* Narrow and landscape: the art is bounded by the column it sits over rather
     than by a type size. */
  .panel .title img {
    max-width: 100%;
  }
  /* The form's own width, so it sits in from the right edge rather than
     running under the corner the pill is in. The instruction line below it is
     left out on purpose: it is one long line of grey text and the column's
     full width is fewer wraps. */
  .panel > .name-row,
  .panel .species-strip,
  .panel .buttons,
  .panel .error {
    max-width: 340px;
    margin-inline: auto;
  }
  .panel > .name-row {
    grid-column: 2;
    grid-row: 1;
  }
  .panel > .name-row input {
    padding: 9px 12px;
    font-size: 14px;
  }
  /* One-row carousel: swipe or tap the arrows; the fish never stack up. */
  .panel .species-strip {
    grid-column: 2;
    grid-row: 2;
    display: flex;
    align-items: center;
    gap: 4px;
    margin-top: 6px;
  }
  .panel .species {
    flex: 1 1 0;
    min-width: 0;
    max-width: none;
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    padding: 2px 6px;
    mask-image: linear-gradient(
      90deg,
      transparent,
      #000 7%,
      #000 93%,
      transparent
    );
  }
  .panel .species::-webkit-scrollbar {
    display: none;
  }
  .panel .species-option {
    flex: 0 0 auto;
    scroll-snap-align: center;
  }
  .species-nav {
    display: block;
    flex: 0 0 auto;
    width: 26px;
    height: 44px;
    padding: 0;
    font-size: 22px;
    line-height: 1;
    border-radius: 8px;
    background: #224c4bb3;
    border: 1px solid #60837a;
    color: #edf8ef;
  }
  .panel .buttons {
    grid-column: 2;
    grid-row: 3;
    margin-top: 8px;
  }
  .panel .buttons button {
    padding: 10px 12px;
  }
  .panel .buttons button span {
    display: none;
  }
  .panel .error {
    grid-column: 2;
    grid-row: 4;
    /* `auto` on the sides, not 0: this row is centred with the rest of the
       form above it. */
    margin: 4px auto 0;
  }
  .reroll {
    width: 40px;
  }
}

.display-controls {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* One line of control advice per input scheme, and only the one this device is
   going to get: keys and a mouse where there is a pointer, thumbs where there
   is a touch screen. Advice for controls you do not have is worse than none -
   the menu was telling phones about W, A, S, D and a click - and this is the
   same test main.js uses to pick the scheme, so the two cannot disagree. */
.touch-only {
  display: none;
}
@media (pointer: coarse) {
  .touch-only {
    display: revert;
  }
  .keys-only {
    display: none;
  }
}
.fullscreen-toggle,
.sound-toggle,
.game-menu {
  pointer-events: auto;
  width: 44px;
  height: 44px;
  padding: 0;
  font-size: 26px;
  line-height: 1;
}
.sound-toggle {
  font-size: 20px;
}
.sound-toggle[aria-pressed="true"] {
  opacity: 0.6;
}
.fullscreen-toggle[aria-pressed="true"] {
  background: #d1ed9e;
  color: #183c33;
}
.fullscreen-toggle:disabled {
  opacity: 0.5;
  cursor: wait;
}
#fullscreen-status {
  position: absolute;
  top: 100%;
  right: 0;
  max-width: 230px;
  padding: 10px;
  background: #123b40;
  color: #ffd2a4;
  font-size: 12px;
}
@media (max-width: 650px), (max-height: 520px) {
  .display-controls {
    gap: 4px;
  }
  .header-tools {
    gap: 2px;
  }
}
@media (max-height: 520px) and (orientation: landscape) {
  .group-label {
    margin-top: 12px;
  }
  .species {
    gap: 5px;
  }
  .species-option {
    width: 50px;
    padding: 4px 2px 4px;
    font-size: 6.5px;
    letter-spacing: 0.6px;
  }
  .species-option img {
    width: 44px;
    height: 34px;
  }
}
@media (max-height: 520px) and (orientation: landscape) {
  /* Phone-sized HUD: tuck the stats under the wordmark and keep everything
     small. The stats panel takes the header's own left margin, so it stays
     under the chrome it belongs with. */
  .stats {
    top: 68px;
    left: max(20px, env(safe-area-inset-left));
    padding: 7px 12px;
    gap: 8px;
    border-radius: 10px;
  }
  .stats small {
    font-size: 7px;
    letter-spacing: 1.4px;
  }
  .stats b {
    font-size: 15px;
    margin-top: 3px;
  }
  .roster {
    padding: 8px 10px;
    border-radius: 10px;
  }
  /* Clear of the touch zones' own hints along the bottom edge, and inside the
     notch on a landscape phone. */
  .tank-corner {
    right: max(20px, env(safe-area-inset-right));
    bottom: max(24px, env(safe-area-inset-bottom));
    gap: 6px;
  }
  .roster .eyebrow {
    font-size: 7px;
    letter-spacing: 1.6px;
  }
  .roster ol {
    padding-left: 14px;
  }
  .roster li {
    font-size: 9px;
    padding: 2px 0;
  }
  /* Only the top three fit comfortably on a phone. */
  .roster li:nth-child(n + 4) {
    display: none;
  }
  #toast {
    top: 16%;
    font-size: 13px;
  }
  .overlay {
    padding: 14px 20px;
    min-width: 240px;
    max-width: 70vw;
  }
  .overlay h2 {
    font-size: 20px;
    margin: 6px 0;
  }
  .overlay p {
    font-size: 11px;
    margin: 6px 0;
  }
  #overlay-actions {
    margin-top: 8px;
  }
  #overlay-actions button {
    padding: 9px 12px;
  }
  .cover {
    top: 66%;
    padding: 5px 10px;
    font-size: 8px;
  }
  .label {
    font-size: 9px;
    padding: 1px 7px;
  }
  .zone-hint {
    bottom: max(8px, env(safe-area-inset-bottom));
    font-size: 8px;
  }
}

/* The header's pill: the same three buttons on every screen, in their own
   glass. It briefly lived inside the roster panel, which put the way out and
   the scoreboard in one box; the roster is in the bottom corner now and this
   is a pill again. */
.display-controls {
  gap: 6px;
  padding: 5px;
  border: 1px solid #badbcd26;
  border-radius: 14px;
  background: #072c32d9;
  box-shadow: 0 6px 24px #001c2426;
}
/* A comfortable target for a finger, in a panel that has the room for it. */
.picker-option {
  padding: 11px 12px;
}
.sound-toggle,
.fullscreen-toggle,
.game-menu {
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 8px;
  background: transparent;
}
button svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: middle;
  pointer-events: none;
}
.sound-slash,
.sound-toggle[aria-pressed="true"] .sound-waves {
  display: none;
}
.sound-toggle[aria-pressed="true"] .sound-slash {
  display: initial;
}
.sound-toggle[aria-pressed="true"] {
  opacity: 1;
  color: #a3bcb7;
}
button:focus-visible,
a:focus-visible {
  outline: 2px solid #d1ed9e;
  outline-offset: 4px;
}
.panel input {
  background: #092f36d9;
  border-color: #83afa35c;
  border-radius: 12px;
}
.species-option {
  border-color: #83afa33d;
  background: #092f36c9;
  border-radius: 12px;
}
.species-option[aria-checked="true"] {
  border-color: #d1ed9e;
  box-shadow: inset 0 0 0 1px #d1ed9e;
}
.panel .buttons button,
.lobby .buttons button {
  border-radius: 12px;
}
.secondary {
  border-color: #83afa35c;
}
/* Mute and fullscreen flip a switch where they stand; this one opens a panel
   over the tank, which is a different kind of thing to press. A hairline and a
   little air say so without making it a separate pill floating on its own. */
.game-menu {
  margin-left: 3px;
  border-left: 1px solid #badbcd2e;
  border-radius: 0 8px 8px 0;
}
/* Every button in the pill sits on the pill's own glass with no background of
   its own, so the global brightness hover has almost nothing to brighten. An
   inset wash reads on a transparent button and on the lime of a pressed
   fullscreen alike, which a background colour would have overwritten. */
.sound-toggle:hover,
.fullscreen-toggle:hover,
.game-menu:hover {
  box-shadow: inset 0 0 0 999px #eafcf11f;
}
/* The pause panel is the same frosted glass as every other panel, and the
   tank stays visible through it and through its backdrop. It used to be an
   opaque box over a heavy blur, which is a modal dialog belonging to a web
   page; this belongs to an aquarium, and seeing the water through it is what
   says so. Whether that water is still moving now depends on who is in it -
   alone, the panel really does stop the tank, and its own eyebrow says
   PAUSED. With company it cannot, and does not claim to. */
#leave-dialog {
  width: min(440px, calc(100vw - 32px));
  margin: auto;
  padding: 28px 30px;
  border: 1px solid #6eaaa44a;
  border-radius: 18px;
  background: #092f36e8;
  color: #edf8ef;
  box-shadow: 0 24px 100px #0009;
  /* No backdrop-filter on the panel itself, and no max-height. Both were
     wrong here for the same reason: `backdrop-filter` makes an element a
     containing block that clips its absolutely positioned children, and with
     a max-height on top of it the graphics dropdown was cut off at the panel's
     own rounded edge and had to be scrolled inside it. The blur lives on the
     ::backdrop below, which softens the tank without boxing anything in, so
     the list is free to hang past the panel like a dropdown should. */
  overflow: visible;
}
#leave-dialog::backdrop {
  /* Enough to lift the panel off the water, not enough to hide it: the point
     of pausing here is that the aquarium keeps going. */
  background: #00191d73;
  backdrop-filter: blur(6px);
}
/* The label above the graphics row: the dialog is tight, so it does not get
   the generous spacing the menu's own group labels have. */
#leave-dialog .group-label {
  margin: 0 0 8px;
  color: #9bb5ae;
}
#leave-dialog .quality-slot {
  margin-bottom: 22px;
}
/* Compact enough that all five rows fit a landscape phone without the panel
   needing to scroll at all, which is the whole point of the exercise. */
#leave-dialog .picker-option {
  padding: 8px 10px;
  border-radius: 9px;
}
.dialog-actions {
  display: grid;
  gap: 10px;
}
/* Only ever seen when the browser refuses to close its own tab, which it does
   for any tab a script did not open. Amber, like the other line in this game
   that has to admit something did not work. */
.pause-note {
  margin: 14px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: #ffd2a4;
}
.dialog-actions button {
  border-radius: 10px;
}
@media (max-width: 650px), (max-height: 520px) {
  .display-controls {
    padding: 3px;
    gap: 2px;
  }
  .game-menu {
    margin-left: 2px;
  }
}
/* The pause panel on a phone: the whole screen, in two columns, and no scroll
   at all.
   A short landscape screen is wide and shallow, and this panel was stacking
   five graphics rows on top of two buttons down the middle of it - a column
   taller than the phone, which is why it scrolled however the picker was laid
   out. Turned on its side it fits with room to spare: the rows on the left,
   the two actions on the right, everything centred vertically. Full bleed
   because there is nothing to gain from a 440 px card floating in a 780 px
   window, and the glass is translucent, so this is still the tank with a panel
   over it rather than a page.
   Explicit grid placement rather than source order: the eyebrow spans the top,
   the label and the rows take the left column, and the actions take the right
   for both of its rows. */
@media (max-height: 520px) and (orientation: landscape) {
  /* `[open]` is load-bearing. This is the only place in the file that gives
     the dialog a `display` of its own, and any author `display` outranks the
     UA sheet's `dialog:not([open]) { display: none }` - so unscoped, the
     full-bleed panel painted over the tank from the moment the page loaded,
     on every phone, landscape being the only orientation this game plays in.
     Continue could not dismiss it either: close() on a dialog that was never
     open does nothing. Anything here that sets `display` must say `[open]`. */
  #leave-dialog[open] {
    inset: 0;
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    margin: 0;
    border: 0;
    border-radius: 0;
    overflow: visible;
    padding: 14px max(24px, env(safe-area-inset-left)) 14px
      max(24px, env(safe-area-inset-right));
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto auto 1fr;
    align-content: center;
    column-gap: 26px;
  }
  #pause-tag {
    grid-area: 1 / 1 / 2 / 3;
    margin-bottom: 10px;
  }
  #leave-dialog .group-label {
    grid-area: 2 / 1 / 3 / 2;
  }
  #leave-dialog .quality-slot {
    grid-area: 3 / 1 / 4 / 2;
    margin-bottom: 0;
  }
  #leave-dialog .dialog-actions {
    grid-area: 2 / 2 / 4 / 3;
    align-content: center;
  }
  #pause-note {
    grid-area: 4 / 1 / 5 / 3;
  }
  /* The rows have the room for their notes here, where the stacked layout
     did not. */
  #leave-dialog .picker-option {
    padding: 6px 10px;
  }
}

#fish-preview {
  position: absolute;
  right: 3%;
  top: 23%;
  width: 43%;
  height: 58%;
  cursor: grab;
  touch-action: none;
  user-select: none;
}
#fish-preview.grabbing {
  cursor: grabbing;
}
#fish-preview span {
  position: absolute;
  bottom: 0;
  width: 100%;
  text-align: center;
  font-size: 10px;
  letter-spacing: 1px;
  color: #b9ceca;
  pointer-events: none;
}
@media (max-height: 520px) and (orientation: landscape) {
  #fish-preview {
    left: 6%;
    right: auto;
    top: 155px;
    width: 35%;
    height: calc(100vh - 180px);
    height: calc(100dvh - 180px);
  }
  /* The panel covers the screen here, and the fish preview is underneath it:
     everything but the controls themselves is transparent to the pointer, so a
     drag on the water turns the fish. The name row is one of them - it carries
     the die now, which is a button and has to be pressable. */
  .panel {
    pointer-events: none;
  }
  .panel .name-row,
  .panel .species-strip,
  .panel .buttons {
    pointer-events: auto;
  }
}

/* Instructions follow the play actions instead of floating over the preview. */
.panel .menu-instructions {
  position: static;
  margin-top: 14px;
  color: #b9ceca;
  font-size: 10px;
  line-height: 1.9;
  letter-spacing: 0.2px;
}
.menu-instructions .divider {
  margin: 0 6px;
}
@media (max-height: 520px) and (orientation: landscape) {
  /* Down at the foot of the screen here, the way it sits on a desktop, rather
     than as the last row of the form. It is a line of keyboard help and a
     build number - neither is what a phone came to this screen for - and as a
     row it was 30px of the group being centred, which lifted the name field
     and the fish that far off the middle.
     `grid-area: auto` is load-bearing: an absolutely positioned grid child
     that still names a grid area is positioned inside that area, not against
     the panel, so it would have stayed exactly where it was. */
  .panel .menu-instructions {
    position: absolute;
    grid-area: auto;
    left: 0;
    right: 0;
    bottom: max(6px, env(safe-area-inset-bottom));
    margin: 0;
  }
}

/* Mobile puts the preview on the left, where the desktop text scrim was
   darkening it. Keep a lighter scrim here so the fish's colors read clearly. */
@media (pointer: coarse) {
  body:not(.playing) .vignette {
    background:
      linear-gradient(90deg, rgba(1, 25, 30, 0.18), transparent 75%),
      linear-gradient(0deg, rgba(1, 25, 30, 0.3), transparent 25%);
  }
}
