/* Gauntlet is one landscape scene that fills the Activity frame and never
   scrolls as a page. The layout is a five-region grid (top bar, left rail,
   stage, right rail, command bar); the stage is a size container so the 16×16
   board is always the largest square that fits, whatever the window's aspect
   ratio. Lists that can grow scroll inside their own panel. */

/* SAFE AREAS COME FROM DISCORD FIRST AND `env()` SECOND, AND BOTH HALVES ARE
   LOAD-BEARING. Discord injects `--discord-safe-area-inset-*` into the frame on
   mobile, which is the only source that knows about ITS chrome, where `env()`
   knows only about the notch. `env()` is the fallback for iOS and for a plain
   browser during development, and 0px is the fallback for desktop. */
:root {
  --safe-top: var(--discord-safe-area-inset-top, env(safe-area-inset-top, 0px));
  --safe-right: var(--discord-safe-area-inset-right, env(safe-area-inset-right, 0px));
  --safe-bottom: var(--discord-safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));
  --safe-left: var(--discord-safe-area-inset-left, env(safe-area-inset-left, 0px));

  color-scheme: dark;

  --bg: #101918;
  --panel: #171923;
  --panel-raised: #283631;
  --card: #15211f;
  --line: #465247;
  --ink: #f0ecda;
  --ink-dim: #b4bbae;

  --gold: #d8a54a;
  --hp: #c34a4a;
  --mana: #4a7fc3;
  --good: #4fbf6a;
  --ally: #477c5c;
  --enemy: #8b4f47;

  /* Region sizes. The stage's height is derived from them so the board column
     can be exactly as wide as the board is tall. */
  --pad: 0.5rem;
  --gap: 0.5rem;
  --top-h: 2.75rem;
  --bar-h: 3.75rem;
  --stage-h: calc(100dvh - var(--safe-top) - var(--safe-bottom) - 2 * var(--pad) - var(--top-h) - var(--bar-h) - 2 * var(--gap));
}

@font-face {
  font-family: 'Sigil Pixel';
  src: url('/assets/fonts/m5x7/m5x7.ttf') format('truetype');
  font-display: swap;
  unicode-range: U+0020-007E, U+00B0, U+00B7, U+00D7;
}

* {
  box-sizing: border-box;
}

/* One type scale for every window: about 11px on a landscape phone, up to 17px
   on a large monitor. Everything below is in rem so the scene scales as one. */
html {
  font-size: clamp(11px, 0.55vw + 0.75vh + 3px, 17px);
}

html,
body {
  height: 100%;
  overflow: hidden;
  /* An overscroll on iOS detaches the content from Discord's own frame and reads
     as the Activity coming loose. */
  overscroll-behavior: none;
}

body {
  margin: 0;
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  background: radial-gradient(ellipse at top, #2d4036, var(--bg) 70%);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  /* A long-press on a phone must not select text or flash a grey box. */
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  -webkit-text-size-adjust: 100%;
}

#app {
  position: fixed;
  inset: 0;
  overflow: hidden;
  display: grid;
  padding: calc(var(--pad) + var(--safe-top)) calc(var(--pad) + var(--safe-right))
    calc(var(--pad) + var(--safe-bottom)) calc(var(--pad) + var(--safe-left));
}

.status {
  place-self: center;
  color: var(--ink-dim);
  text-align: center;
  font-size: 1rem;
}

.status.error {
  color: var(--hp);
}

h1,
h2,
h3 {
  font-family: 'Sigil Pixel', monospace;
  font-weight: normal;
  margin: 0;
}

p {
  margin: 0;
  line-height: 1.4;
}

.muted {
  color: var(--ink-dim);
}

.good {
  color: var(--good);
}

.bad {
  color: var(--hp);
}

/* ---------------------------------------------------------------- Scene */

.scene {
  position: relative;
  display: grid;
  /* On an ultrawide window the rails stop growing at 30rem and the scene
     centres, instead of stretching lists across the whole monitor. */
  width: 100%;
  max-width: calc(var(--stage-h) + 60rem + 2 * var(--gap));
  margin-inline: auto;
  min-width: 0;
  min-height: 0;
  gap: var(--gap);
  grid-template-rows: var(--top-h) minmax(0, 1fr) var(--bar-h);
  /* The stage track may grow to the board's height and no further; the rails
     share what is left. On a wide window that gives the rails room, on a narrow
     one the board shrinks before a rail drops below its minimum. */
  grid-template-columns: minmax(14rem, 1fr) minmax(0, var(--stage-h)) minmax(14rem, 1fr);
  grid-template-areas:
    'top top top'
    'left stage right'
    'bar bar bar';
}

.scene.busy {
  cursor: progress;
}

.top-bar {
  grid-area: top;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
  padding: 0 0.25rem;
  border-bottom: 1px solid var(--line);
}

.top-bar h1 {
  flex: none;
  font-size: 2rem;
  letter-spacing: 0.06em;
  color: var(--gold);
}

.top-context {
  display: flex;
  flex: none;
  align-items: center;
  gap: 0.4rem;
}

.chip {
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  color: var(--ink-dim);
  font-size: 0.8rem;
  white-space: nowrap;
}

.route-track {
  display: flex;
  gap: 0.2rem;
}

.route-track i {
  width: 0.55rem;
  height: 0.55rem;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: #0d1413;
}

.route-track i.visited {
  background: var(--ink-dim);
}

.route-track i.current {
  border-color: var(--good);
  background: var(--good);
}

/* The notice has a fixed slot so a message never pushes the scene around. */
.notice {
  flex: 1;
  min-width: 0;
  padding: 0.3rem 0.6rem;
  border-left: 3px solid var(--good);
  background: var(--card);
  font-size: 0.85rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  -webkit-user-select: text;
  user-select: text;
}

.notice.bad {
  border-left-color: var(--hp);
  color: var(--ink);
}

.notice.empty {
  visibility: hidden;
}

.top-user {
  flex: none;
  max-width: 10rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink-dim);
  font-size: 0.85rem;
}

.rail {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}

.rail-left {
  grid-area: left;
}

.rail-right {
  grid-area: right;
}

.stage {
  grid-area: stage;
  position: relative;
  display: grid;
  place-items: center;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  container-type: size;
}

.command-bar {
  grid-area: bar;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--line);
  background: #0f1716e6;
  /* The last resort on a very narrow window: the bar scrolls sideways inside
     itself rather than growing a row and pushing the board. */
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
}

.bar-group {
  display: flex;
  flex: none;
  align-items: center;
  gap: 0.4rem;
}

.bar-group.end {
  margin-left: auto;
}

.bar-group.actor {
  flex-shrink: 1;
  min-width: 6rem;
  gap: 0.5rem;
  padding-right: 0.75rem;
  border-right: 1px solid var(--line);
}

/* Abilities are the one group that can outgrow the bar; they scroll inside
   themselves so Confirm and End turn always stay on screen. */
.bar-group.modes {
  flex: 0 1 auto;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  /* It scrolls (a mouse wheel works too, see main.js) without a scrollbar
     that would appear and take height the moment a button is pressed. */
  scrollbar-width: none;
}

.bar-group.modes::-webkit-scrollbar {
  display: none;
}

.cmd[aria-disabled='true'] {
  opacity: 0.45;
  cursor: default;
}

.bar-group.modes .cmd {
  padding: 0.4rem 0.6rem;
}

.hint {
  color: var(--ink-dim);
  font-size: 0.8rem;
  white-space: nowrap;
}

/* ---------------------------------------------------------------- Panels */

.panel {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-height: 0;
  padding: 0.6rem;
  border: 10px solid transparent;
  border-image: url('/assets/ui/gauntlet/panel-frame.png') 16 fill / 10px / 0 stretch;
  background: var(--panel);
  background-clip: padding-box;
  image-rendering: pixelated;
  overflow: hidden auto;
  scrollbar-width: thin;
}

.panel .cmd {
  white-space: normal;
}

.panel.grow {
  flex: 1;
}

.panel h3 {
  flex: none;
  font-size: 1.35rem;
  color: var(--ink);
}

.label {
  color: var(--good);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.eyebrow {
  color: var(--gold);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.12em;
}

.card-list {
  display: grid;
  gap: 0.4rem;
}

.unit-card {
  display: grid;
  grid-template-columns: 2.6rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.35rem 0.45rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--card);
  color: var(--ink);
  font: inherit;
  text-align: left;
}

.unit-card.selected {
  border-color: var(--gold);
  background: #2b2a1d;
}

.unit-card.active {
  border-color: var(--good);
  box-shadow: inset 3px 0 var(--good);
}

.unit-card.dead {
  opacity: 0.5;
  border-color: var(--hp);
}

.roster-entry {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.3rem;
}

.cmd.inspect {
  width: 2rem;
  padding: 0;
  font-family: Georgia, serif;
  font-size: 1.1rem;
  font-style: italic;
  font-weight: 700;
}

.unit-card .check {
  color: var(--gold);
  font-size: 1.1rem;
}

.unit-text {
  display: grid;
  gap: 0.15rem;
  min-width: 0;
}

.unit-text strong,
.unit-text small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.unit-text small {
  color: var(--ink-dim);
  font-size: 0.75rem;
}

.unit-text .numbers {
  font-variant-numeric: tabular-nums;
}

.portrait {
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  border: 1px solid var(--line);
  background: #0d1413;
  overflow: hidden;
}

.portrait.large {
  width: 4.5rem;
  height: 4.5rem;
}

.sprite {
  display: block;
  height: 100%;
  max-width: 100%;
  aspect-ratio: 1;
  /* A four-frame 64x16 idle sheet: at 400% wide, frame k sits at k/3 of the
     way across, so stepping to 133.33% shows each frame once per loop. */
  background: no-repeat 0 0 / 400% 100%;
  image-rendering: pixelated;
  pointer-events: none;
  animation: sprite-idle 0.9s steps(4) infinite;
}

@keyframes sprite-idle {
  to {
    background-position: 133.333% 0;
  }
}

.meter {
  display: block;
  height: 0.35rem;
  border-radius: 1rem;
  background: #0d1413;
  overflow: hidden;
}

.meter span {
  display: block;
  height: 100%;
  background: var(--hp);
  transition: width 180ms ease-out;
}

.meter.mana span {
  background: var(--mana);
}

.rows {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

.rows li {
  display: grid;
  grid-template-columns: 3.2rem minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.35rem 0;
  border-top: 1px solid #2b292e;
  font-size: 0.85rem;
}

.rows span,
.rows small {
  color: var(--ink-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.turn-order {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.turn-order li {
  padding: 0.15rem 0.45rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--enemy);
  font-size: 0.78rem;
}

.turn-order li.ally {
  border-left-color: var(--ally);
}

.turn-order li.active {
  border-color: var(--gold);
  color: #ffe1a1;
}

.turn-order li.used,
.turn-order li.dead {
  opacity: 0.45;
}

.log {
  display: grid;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--ink-dim);
  font-size: 0.8rem;
}

.log li:first-child {
  color: var(--ink);
}

.inv-group {
  display: grid;
  gap: 0.3rem;
}

.inv-group p:not(.label) {
  font-size: 0.85rem;
}

.button-row {
  display: flex;
  gap: 0.35rem;
}

.button-row.wrap {
  flex-wrap: wrap;
}

.button-column {
  display: grid;
  gap: 0.35rem;
}

.assign-row {
  display: grid;
  gap: 0.25rem;
}

/* ---------------------------------------------------------------- Buttons */

button,
input,
select {
  font: inherit;
  touch-action: manipulation;
}

button {
  cursor: pointer;
}

button:disabled {
  cursor: default;
}

button:focus-visible,
input:focus-visible,
select:focus-visible {
  outline: 3px solid #f0c978;
  outline-offset: 2px;
}

.cmd {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: 2.75rem;
  padding: 0.4rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--panel-raised);
  color: var(--ink);
  font-size: 0.9rem;
  font-weight: 600;
  white-space: nowrap;
  transition: background 120ms ease, border-color 120ms ease;
}

.cmd.small {
  min-height: 2.2rem;
  padding: 0.25rem 0.6rem;
  font-size: 0.8rem;
}

.cmd.primary {
  border-color: var(--gold);
  background: #4a3b1c;
  color: #ffe1a1;
}

.cmd.danger {
  border-color: #7a3a36;
  background: #2a1716;
}

.cmd.ghost {
  background: transparent;
}

.cmd.mode[aria-pressed='true'] {
  border-color: var(--gold);
  background: #364132;
  color: #ffe1a1;
  box-shadow: inset 0 -3px var(--gold);
}

.cmd:disabled {
  opacity: 0.45;
}

.icon-button {
  min-width: 2.75rem;
  font-size: 1.4rem;
}

/* HOVER ONLY WHERE THERE IS A POINTER: on a touch screen `:hover` sticks after
   a tap and the last button pressed would stay lit. */
@media (hover: hover) and (pointer: fine) {
  .cmd:hover:not(:disabled),
  .unit-card:hover {
    border-color: var(--gold);
  }
}

.cmd:active:not(:disabled, [aria-disabled='true']) {
  filter: brightness(1.2);
}

.gauntlet-icon {
  --s: 1.4rem;
  display: inline-block;
  flex: none;
  width: var(--s);
  height: var(--s);
  background-image: url('/assets/ui/gauntlet/mv-icons-sheet.png');
  background-repeat: no-repeat;
  /* The MV sheet is 32px icons, 16 to a row; scaling it to 16 × --s makes one
     icon exactly --s square, and each position below is (column, row). */
  background-size: calc(var(--s) * 16) auto;
  image-rendering: pixelated;
}

.gauntlet-icon-sigil { background-position: calc(var(--s) * -4) calc(var(--s) * -3); }
.gauntlet-icon-warrior { background-position: calc(var(--s) * -4) calc(var(--s) * -1); }
.gauntlet-icon-rogue { background-position: calc(var(--s) * -2) calc(var(--s) * -1); }
.gauntlet-icon-mage { background-position: 0 calc(var(--s) * -3); }
.gauntlet-icon-sword { background-position: calc(var(--s) * -3) calc(var(--s) * -1); }
.gauntlet-icon-move { background-position: calc(var(--s) * -2) calc(var(--s) * -3); }
.gauntlet-icon-enemy { background-position: calc(var(--s) * -3) calc(var(--s) * -2); }

/* ---------------------------------------------------------------- Stage */

.stage-panel {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  width: 100%;
  height: 100%;
  padding: 1rem;
  border: 10px solid transparent;
  border-image: url('/assets/ui/gauntlet/panel-frame.png') 16 fill / 10px / 0 stretch;
  background: var(--panel);
  background-clip: padding-box;
  image-rendering: pixelated;
  overflow: auto;
  scrollbar-width: thin;
}

.stage-panel h2 {
  font-size: 2.2rem;
  line-height: 1;
}

.stage-panel h3 {
  margin-top: 0.4rem;
  font-size: 1.4rem;
}

.lineup {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.5rem;
  width: min(100%, 36rem);
  margin: auto auto 0;
}

.lineup:last-child {
  margin-bottom: auto;
}

.slot {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 0.25rem;
  grid-template-rows: minmax(0, 1fr) auto auto;
  aspect-ratio: 3 / 4;
  min-height: 0;
  overflow: hidden;
  padding: 0.4rem;
  border: 1px dashed var(--line);
  background: #0d1413;
  text-align: center;
  font-size: 0.8rem;
}

.slot.filled {
  border: 1px solid var(--gold);
  color: var(--ink);
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
}

.slot.filled:hover,
.slot.filled:focus-visible {
  background: #2b2a1d;
}

/* Home: the menu floats over a demo battle playing on the board behind it. */
.scene.mode-home {
  grid-template-areas:
    'top top top'
    'stage stage stage'
    'bar bar bar';
}

.scene.mode-home .rail {
  display: none;
}

.home-stage {
  position: relative;
  display: grid;
  align-items: center;
  justify-items: start;
  padding-left: 2cqw;
  width: 100%;
  height: 100%;
  overflow: hidden;
  container-type: size;
}

.home-demo {
  position: absolute;
  inset: 0 0 0 30%;
  display: grid;
  place-items: center;
  opacity: 0.9;
  filter: saturate(0.85);
  pointer-events: none;
}

.home-demo .board-frame {
  --t: min(70cqw / 17, 100cqh / 17.5);
}

/* Battle effects, shared by the demo and real battles (client/battle-fx.js):
   figures slide between tiles, strike, flash when hit, and numbers float up. */
.demo-wrap {
  position: relative;
}

.home-demo .figure,
.figure.fx-slide {
  transition: left 0.35s ease-in-out, top 0.35s ease-in-out, opacity 0.4s;
}

.home-demo .figure.demo-hidden {
  opacity: 0;
}

.figure.fx-appear {
  animation: fx-appear 0.4s ease-out;
}

.figure.fx-strike .sprite {
  animation: fx-strike 0.35s ease-out;
}

.figure.enemy.fx-strike .sprite {
  animation-name: fx-strike-left;
}

.figure.fx-hit .sprite {
  animation: fx-hit 0.45s steps(2) 2;
}

@keyframes fx-appear {
  from { opacity: 0; }
}

@keyframes fx-strike {
  40% { translate: 18% 0; }
}

@keyframes fx-strike-left {
  40% { translate: -18% 0; }
}

@keyframes fx-hit {
  50% { filter: brightness(2.2) sepia(1) hue-rotate(-40deg) saturate(4); }
}

.fx-float {
  position: absolute;
  left: calc((var(--x) + 0.5) * var(--t));
  top: calc(var(--y) * var(--t));
  z-index: 40;
  translate: -50% 0;
  font-family: 'Sigil Pixel', monospace;
  font-size: calc(var(--t) * 0.55);
  text-shadow: 0 2px 0 #000;
  animation: fx-float 1.2s ease-out forwards;
  pointer-events: none;
}

.fx-float.damage { color: #ff6b5e; }
.fx-float.poison { color: #9be06a; }
.fx-float.heal { color: #7fe0a0; }
.fx-float.status { color: #f2d06b; font-size: calc(var(--t) * 0.4); }

@keyframes fx-float {
  from { opacity: 1; transform: translateY(0); }
  to { opacity: 0; transform: translateY(calc(var(--t) * -1.1)); }
}

@media (prefers-reduced-motion: reduce) {
  .figure.fx-slide { transition: none; }
  .figure.fx-appear, .figure.fx-strike .sprite, .figure.fx-hit .sprite { animation: none; }
}

.demo-caption {
  position: absolute;
  left: 50%;
  bottom: 0.6rem;
  translate: -50% 0;
  max-width: 90%;
  padding: 0.3rem 0.7rem;
  background: rgb(0 0 0 / 0.6);
  color: var(--ink);
  font-size: 0.9rem;
  text-align: center;
}

.demo-caption:empty {
  display: none;
}

.home-menu {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 0.6rem;
  width: min(40%, 24rem);
  min-width: 15rem;
  max-height: 100%;
  overflow: auto;
  padding: 1.25rem;
  border: 10px solid transparent;
  border-image: url('/assets/ui/gauntlet/panel-frame.png') 16 fill / 10px / 0 stretch;
  background: rgb(23 25 35 / 0.86);
  background-clip: padding-box;
  text-align: center;
}

.home-title {
  color: var(--gold);
  font-family: 'Sigil Pixel', monospace;
  font-size: clamp(2rem, 9cqh, 4.5rem);
  line-height: 1;
  letter-spacing: 0.08em;
  text-shadow: 0 0.1em 0 #6b3f12;
}

.home-options {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.6rem;
  width: 100%;
  margin-top: 0.4rem;
}

.home-option {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto;
  align-items: center;
  column-gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  text-align: left;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.home-option:hover,
.home-option:focus-visible {
  border-color: var(--gold);
  background: #2b2a1d;
}

.home-option strong {
  font-family: 'Sigil Pixel', monospace;
  font-size: 1.5rem;
  font-weight: normal;
  color: var(--gold);
}

.home-option .portrait {
  grid-row: 1 / 3;
  width: 3.2rem;
  height: 3.2rem;
}

.home-option small {
  color: var(--ink-dim);
}

.home-foot {
  font-size: 0.8rem;
}

/* A hosted lobby, shown at the entrance until the party sets out. */
.lobby-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 1rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--gold);
  background: #1f1d15;
}

.join-code {
  color: var(--gold);
  font-family: 'Sigil Pixel', monospace;
  font-size: 2.2rem;
  letter-spacing: 0.15em;
}

.code-form {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.code-form input {
  width: 7rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink);
  font-family: 'Sigil Pixel', monospace;
  font-size: 1.2rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.cmd.choice.chosen {
  border-color: var(--gold);
  background: #2b2a1d;
}

/* Which town provisions go into the run's pack. */
.pack-picker {
  display: grid;
  gap: 0.35rem;
  width: min(100%, 36rem);
  margin: 0.75rem auto 0;
}

.pack-picker:last-child {
  margin-bottom: auto;
}

.pack-rows {
  display: grid;
  gap: 0.3rem;
}

.pack-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 2rem 2rem 2rem minmax(0, 1fr);
  align-items: center;
  gap: 0.4rem;
}

.pack-row strong {
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.pack-row .cmd {
  padding: 0.2rem 0;
}

.pack-row small {
  text-align: right;
}

/* The party is chosen: the descent buttons below are the next step. */
.ready {
  margin: 0.75rem 0 auto;
  color: var(--gold);
  font-family: 'Sigil Pixel', monospace;
  font-size: clamp(2rem, 7cqh, 4rem);
  line-height: 1;
  text-align: center;
  text-shadow: 0 0.12em 0 #6b3f12, 0 0 0.6em rgb(216 165 74 / 0.35);
}

.slot .sprite {
  width: 70%;
  height: auto;
  min-height: 0;
}

.slot > .muted {
  grid-row: 1 / -1;
  align-self: center;
}

.slot strong {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.slot small {
  color: var(--ink-dim);
}

.banner {
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--good);
  background: #1c3324;
  font-family: 'Sigil Pixel', monospace;
  font-size: 1.4rem;
  text-align: center;
}

.banner.bad {
  border-color: var(--hp);
  background: #3a1d1c;
  color: var(--ink);
}

.choices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.5rem;
}

.cmd.choice {
  display: grid;
  justify-content: stretch;
  justify-items: start;
  align-content: start;
  gap: 0.3rem;
  min-height: 6rem;
  padding: 0.6rem;
  white-space: normal;
  text-align: left;
  background: var(--card);
}

.cmd.choice small {
  color: var(--ink-dim);
  font-weight: 400;
}

/* THE BOARD IS A 3/4 TOP-DOWN VIEW. The floor is seen from above, walls show
   a stone top and a brick front, and characters stand upright, one tile wide
   and one tall, on a separate layer. One tile is `--t`: the frame is 17 tiles wide (the
   board plus half a tile of wall each side) and 17.5 tall (plus a wall front
   along the top), and `--t` is the largest size at which that fits the stage. */
.board-frame {
  --t: min(100cqw / 17, 100cqh / 17.5);
  position: relative;
  width: calc(var(--t) * 17);
  height: calc(var(--t) * 17.5);
  padding: calc(var(--t) * 1.25) calc(var(--t) * 0.5) calc(var(--t) * 0.25);
  background: url('/assets/ui/gauntlet/dungeon/tiles/wall-top.png') 0 0 / calc(var(--t) * 0.5) calc(var(--t) * 0.5);
  box-shadow: inset 0 0 0 2px #1d2622, 0 0.5rem 1.5rem #0008;
  image-rendering: pixelated;
}

.board-wall {
  position: absolute;
  top: calc(var(--t) * 0.25);
  left: calc(var(--t) * 0.5);
  right: calc(var(--t) * 0.5);
  height: var(--t);
  background: url('/assets/ui/gauntlet/dungeon/tiles/wall-face.png') 0 0 / var(--t) var(--t) repeat-x;
}

/* In battle the back wall sits in the same dark as the unseen floor. */
.mode-battle .board-wall {
  filter: brightness(0.3) saturate(0.4);
}

.board {
  display: grid;
  grid-template-columns: repeat(16, var(--t));
  grid-template-rows: repeat(16, var(--t));
  width: calc(var(--t) * 16);
  height: calc(var(--t) * 16);
  background: #0d1413;
}

.tile {
  position: relative;
  min-width: 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  outline-offset: -3px;
  background: #27352f center / 100% 100% no-repeat;
  image-rendering: pixelated;
  color: var(--ink);
}

.tile.floor-1 { background-image: url('/assets/ui/gauntlet/dungeon/tiles/floor-1.png'); }
.tile.floor-2 { background-image: url('/assets/ui/gauntlet/dungeon/tiles/floor-2.png'); }
.tile.floor-3 { background-image: url('/assets/ui/gauntlet/dungeon/tiles/floor-3.png'); }
.tile.floor-4 { background-image: url('/assets/ui/gauntlet/dungeon/tiles/floor-4.png'); }
.tile.rough-1 { background-image: url('/assets/ui/gauntlet/dungeon/tiles/rough-1.png'); }
.tile.rough-2 { background-image: url('/assets/ui/gauntlet/dungeon/tiles/rough-2.png'); }

.tile.wall {
  background-image: url('/assets/ui/gauntlet/dungeon/tiles/wall-top.png');
}

/* A wall with open floor in front of it shows its brick front on the lower
   part of the tile, which is what makes the board read as seen at an angle. */
.tile.wall-front {
  background-image: url('/assets/ui/gauntlet/dungeon/tiles/wall-top.png'), url('/assets/ui/gauntlet/dungeon/tiles/wall-face.png');
  background-size: 100% 42%, 100% 58%;
  background-position: top, bottom;
}

/* Floor just in front of a wall sits in its shadow. */
.tile.shaded {
  box-shadow: inset 0 calc(var(--t) * 0.22) calc(var(--t) * 0.12) calc(var(--t) * -0.12) #000a;
}

.tile.elevated {
  box-shadow: inset 0 3px 0 #e0c07a, inset 0 -3px 0 #0006;
  filter: brightness(1.12);
}

/* Out of sight, not unknown: the layout stays readable, the occupants do not
   (the server has already removed them). */
.tile.shadowed {
  filter: brightness(0.14) saturate(0.2);
}

/* Rock stays faintly recognisable as rock in the dark, not a hole in the map. */
.tile.wall.shadowed {
  filter: brightness(0.26) saturate(0.3);
}

/* The last ring of sight, where the light gives out. */
.tile.dim {
  filter: brightness(0.62) saturate(0.7);
}

.tile.objective::after {
  content: '';
  position: absolute;
  inset: 18%;
  border: 2px solid #64ebb8;
  transform: rotate(45deg);
  pointer-events: none;
}

.tile.deploy-zone {
  box-shadow: inset 0 0 0 1px #d8a54a88;
}

.tile.target::before,
.tile.obstructed::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.tile.target-move::before {
  background: #d8a54a55;
  box-shadow: inset 0 0 0 1px #f0c978;
}

.tile.target-act::before {
  background: #c34a4a66;
  box-shadow: inset 0 0 0 2px #ff8a78;
}

.tile.obstructed::before {
  background: repeating-linear-gradient(45deg, #0000 0 3px, #d8a54a88 3px 5px);
}

.tile.chosen {
  z-index: 1;
  box-shadow: inset 0 0 0 3px #ffe1a1, 0 0 0 2px #ffe1a1;
}

/* Characters: one tile wide and one tall, feet at the bottom of their tile,
   drawn in row order so a nearer one's badges overlap one further back. */
.figures {
  position: absolute;
  top: calc(var(--t) * 1.25);
  left: calc(var(--t) * 0.5);
  width: calc(var(--t) * 16);
  height: calc(var(--t) * 16);
  pointer-events: none;
}

.figure {
  position: absolute;
  left: calc((var(--x) + 0.5) * var(--t));
  top: calc((var(--y) + 0.94) * var(--t));
  z-index: calc(var(--y) + 1);
  display: grid;
  justify-items: center;
  align-items: end;
  width: var(--t);
  height: calc(var(--t) * 0.9);
  transform: translate(-50%, -100%);
}

/* The ground ring: whose side, and whose turn. */
.figure::before {
  content: '';
  position: absolute;
  bottom: calc(var(--t) * -0.08);
  width: calc(var(--t) * 0.8);
  height: calc(var(--t) * 0.28);
  border: 2px solid #6fd39a;
  border-radius: 50%;
  background: #0006;
}

.figure.enemy::before {
  border-color: #ff7b6b;
}

.figure.active::before {
  border-color: #ffe1a1;
  box-shadow: 0 0 calc(var(--t) * 0.25) #ffd36b;
}

.figure .sprite {
  position: relative;
  height: 100%;
}

.figure .gauntlet-icon {
  --s: calc(var(--t) * 0.8);
  position: relative;
}

.figure.downed .sprite {
  filter: grayscale(1) brightness(0.7);
  /* Lying down, still inside the tile. */
  transform: rotate(-90deg);
  transform-origin: center;
}

/* Every sheet is drawn facing right: the party faces right, across the board
   at the enemy, and enemies are mirrored to face left, back at the party. */
.figure.enemy .sprite {
  transform: scaleX(-1);
}

.figure.enemy.downed .sprite {
  transform: scaleX(-1) rotate(-90deg);
}

.figure-hp {
  position: absolute;
  bottom: calc(var(--t) * -0.02);
  width: calc(var(--t) * 0.8);
  height: max(2px, calc(var(--t) * 0.08));
  border: 1px solid #000;
  background: linear-gradient(90deg, var(--hp) calc(var(--hp-frac) * 100%), #000a 0);
}

/* Mini health and mana bars over a character while the mouse is on it. */
.figure-meters {
  position: absolute;
  top: calc(var(--t) * -0.52);
  display: none;
  gap: 1px;
  width: calc(var(--t) * 0.9);
  padding: 1px;
  border: 1px solid #000;
  background: #000c;
}

.figure.hovered {
  z-index: 20;
}

.figure.hovered .figure-meters {
  display: grid;
}

.figure-meter {
  height: max(3px, calc(var(--t) * 0.1));
  background: linear-gradient(90deg, var(--hp) calc(var(--frac) * 100%), #333 0);
}

.figure-meter.mana {
  background: linear-gradient(90deg, var(--mana) calc(var(--frac) * 100%), #333 0);
}

.figure-status {
  position: absolute;
  top: calc(var(--t) * -0.3);
  display: flex;
  gap: 2px;
}

.badge {
  padding: 0 3px;
  border: 1px solid #000;
  font: normal 700 max(7px, calc(var(--t) * 0.22)) / 1.1 system-ui, sans-serif;
  font-style: normal;
  color: #fff;
}

.badge.stun {
  background: #b58b12;
}

.badge.poison {
  background: #7a3fb0;
}

/* ---------------------------------------------------------------- Tooltip */

.tooltip {
  position: fixed;
  z-index: 50;
  max-width: min(22rem, calc(100vw - 16px));
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--gold);
  background: #10161ef2;
  box-shadow: 0 0.5rem 1.5rem #000c;
  pointer-events: none;
  font-size: 0.85rem;
}

.tip-body {
  display: grid;
  gap: 0.35rem;
}

.tip-body strong {
  color: #ffe1a1;
  font-size: 1rem;
}

.tip-body p {
  color: var(--ink-dim);
}

.tip-body dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.2rem 0.75rem;
  margin: 0;
}

.tip-body dt {
  color: var(--good);
  font-weight: 700;
}

.tip-body dd {
  margin: 0;
}

.tip-body .why {
  color: #ff9d8f;
}

.action-panel {
  flex: 0 1 auto;
  max-height: 45%;
  font-size: 0.85rem;
}

/* In a rail the label sits above its value; side by side wraps badly there. */
.action-panel .tip-body dl {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
}

.action-panel .tip-body dd {
  margin-bottom: 0.3rem;
}

/* ---------------------------------------------------------------- Overlay */

.overlay {
  grid-row: 2;
  grid-column: 1 / -1;
  z-index: 10;
  display: grid;
  place-items: center;
  min-height: 0;
  background: #05080899;
}

.overlay-panel {
  width: min(100%, 48rem);
  max-height: 100%;
  box-shadow: 0 1rem 2rem #000a;
}

.overlay-head {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.recruits {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.5rem;
}

.recruit-card {
  display: grid;
  justify-items: center;
  gap: 0.35rem;
  padding: 0.6rem;
  border: 1px solid var(--line);
  background: var(--card);
  text-align: center;
}

.recruit-card select {
  width: 100%;
  min-height: 2.2rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--bg);
  color: var(--ink);
}

.recruit-card .cmd {
  width: 100%;
}

.columns {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
}

.columns > div {
  display: grid;
  align-content: start;
  gap: 0.5rem;
}

.character-head {
  display: flex;
  gap: 0.75rem;
  align-items: center;
}

.stat-strip {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0.3rem;
  margin-top: 0.4rem;
}

.stat-strip span {
  display: grid;
  justify-items: center;
  padding: 0.3rem 0.2rem;
  border: 1px solid var(--line);
  background: var(--card);
}

.stat-strip small {
  color: var(--ink-dim);
  font-size: 0.65rem;
}

.sheet {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.2rem 0.75rem;
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.sheet dt {
  color: var(--ink-dim);
}

.sheet dd {
  margin: 0;
  text-align: right;
}

.sheet-list {
  display: grid;
  gap: 0.3rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.sheet-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
}

.ability-chip {
  cursor: help;
}

.talent-row {
  display: grid;
  gap: 0.25rem;
  margin-top: 0.4rem;
}

.talent-row small {
  color: var(--ink-dim);
}

.rotate-hint {
  display: none;
}

/* ---------------------------------------------------------------- Sizes */

/* Short but wide (a landscape phone): the title shrinks and the route dots go,
   since the chip already says where you are. */
@media (max-height: 520px) {
  .top-bar h1 {
    font-size: 1.6rem;
  }

  .route-track,
  .top-user {
    display: none;
  }
}

/* The how-to hint is the first thing to give up its room in the bar. */
@media (max-width: 1200px) {
  .bar-group.end .hint {
    display: none;
  }
}

/* Too narrow for rails beside the board: keep the board and the commands. This
   covers Discord's picture-in-picture tile and a squeezed desktop window. */
@media (max-width: 620px) and (orientation: landscape) {
  .scene {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'top' 'stage' 'bar';
  }

  .rail,
  .top-context,
  .notice {
    display: none;
  }
}

/* Picture-in-picture is a glance, not a place to play: show only the stage. */
@media (max-height: 300px) {
  .scene {
    grid-template-rows: minmax(0, 1fr);
    grid-template-areas: 'stage';
  }

  .top-bar,
  .command-bar {
    display: none;
  }
}

/* The scene is built for landscape. On a portrait phone the SDK's orientation
   lock should already have rotated the frame; if it did not, say so instead of
   drawing a board the width of a thumb. */
@media (orientation: portrait) and (max-width: 900px) {
  .rotate-hint {
    position: fixed;
    inset: 0;
    z-index: 20;
    display: grid;
    place-content: center;
    gap: 0.5rem;
    padding: 2rem;
    background: var(--bg);
    text-align: center;
  }

  .rotate-hint strong {
    font-family: 'Sigil Pixel', monospace;
    font-size: 2rem;
    color: var(--gold);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
}
