/**
 * Kakkoi Online — the pixel skin.
 *
 * This file used to be layout only, with component styling coming from
 * vendor/basecoat.min.css. It now owns both, and basecoat is dropped from
 * index.html: a rounded, soft-shadowed component library and a 16px sprite
 * cannot share a screen without one of them looking like a mistake.
 *
 * Two rules hold the whole thing together.
 *
 * 1. THE UNIT. Every size here is in `rem`, and src/ui/scale.js sets the root
 *    font size to a whole number of pixels (9–15) worked out from the size of
 *    the window. A phone gets a small unit, a desktop a big one, and the layout
 *    itself never changes — so there is no breakpoint anywhere in this file
 *    except where the SHAPE of something has to change, which is twice.
 *
 *    Interactive rows carry `max(Nrem, Npx)`. The unit can shrink; a thumb
 *    cannot, so the px floor is what a finger actually gets.
 *
 *    Two things stay in whole pixels rather than scaling: the black edge below,
 *    and the tracking on the pixel font (`--track`). Both are drawn ON the pixel
 *    grid, and a fraction of one is what a browser has to round away — a lumpy
 *    border, or a word with uneven gaps between its letters.
 *
 * 2. THE EDGE. One border treatment, everywhere: 0.14rem of solid black and a
 *    hard offset shadow of the same black. No radii, no blur, no gradient. That
 *    is what makes a name plate, a badge, a bubble and a button all read as the
 *    same object at different sizes, and it is why nothing here needs a colour
 *    of its own to look deliberate.
 */

/* The two families, vendored rather than linked.
 *
 * The game is installable and opens with no network, so a webfont from a CDN is
 * a webfont that is missing on a plane — and a `<link>` to fonts.googleapis.com
 * is also a request to somebody else's server for every child who opens this
 * page. These are the latin subsets, 33KB the pair, sitting in vendor/ with
 * their licences beside the rest of the third-party files, and precached by
 * `sw.js` like everything else.
 *
 * `swap` on purpose: the game must be readable before the font has arrived, and
 * the fallbacks below are chosen so a missing font is ugly, not broken. */
@font-face {
  font-family: 'DotGothic16';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../../vendor/fonts/dotgothic16-latin.woff2') format('woff2');
}

/* One variable file covers all three weights the interface asks for. */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../../vendor/fonts/space-grotesk-latin.woff2') format('woff2');
}

:root {
  color-scheme: dark;

  /* The ink and the two families. Chrome is pixel type; anything longer than a
     few words is not, because a paragraph set in a pixel font is a paragraph
     nobody reads. */
  --ink: #e8e8ef;
  --ink-quiet: #8f8fae;
  --ink-faint: #6f6f8c;
  --gold: #ffd76a;          /* the action colour, and only ever that */
  --gold-ink: #22160a;
  --rose: #ff7ab8;          /* the opponent's side of a duel */
  --win: #7ce07c;
  --wait: #9fc7ff;
  --panel: #191927;
  --sunk: #14141f;
  --raised: #23233a;
  --edge: #33334f;
  --void: #06060a;

  --pixel: 'DotGothic16', ui-monospace, 'SF Mono', Menlo, monospace;
  --prose: 'Space Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Tracking for the pixel family, in WHOLE PIXELS, and it goes everywhere that
     family goes.
     Two reasons it cannot be an `em`. The first is that this font needs it at
     all: at interface sizes DotGothic16's latin letters sit close enough to
     touch — `WASD` and `walk.` in the help line ran together with none. The
     second is why it must be whole: `0.04em` of a 12.35px font is 0.494px, and
     a browser cannot put half a pixel between two letters, so it rounds each
     gap up or down as it goes and the word comes out visibly lumpy. The need
     comes from the pixel grid, not from the type size, so the answer is a flat
     pixel at every size rather than a fraction of each one. */
  --track: 1px;
  --track-wide: 2px;   /* the two small uppercase labels, where it is the effect */

  /* The edge, in one place. */
  --line: 0.14rem solid #000;
  --lift: 0 0.2rem 0 #000;
  --lift-lg: 0 0.25rem 0 #000;
  --inset: inset 0 0 0 0.14rem var(--edge);
}

/* The one thing a component library was quietly providing that this file has to
   provide for itself. Without it, `width: 100%` plus padding is wider than its
   parent, and the duel card hangs off both edges of a phone. */
*,
*::before,
*::after { box-sizing: border-box; }

body {
  margin: 0;
  height: 100svh;
  overflow: hidden;
  background: var(--void);
  color: var(--ink);
  font-family: var(--prose);
  overscroll-behavior: none;
  /* Chrome text is pixel type, and pixel type must not be hinted or smoothed
     into something softer than the art beside it. */
  -webkit-font-smoothing: none;
  font-smooth: never;
}

/* Prose is smoothed again: only the pixel font wants hard edges. */
p, li, label { -webkit-font-smoothing: antialiased; }

#stage {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--void);
}

/* The world fills the screen. It is no longer letterboxed inside it: the art
   keeps a whole-number zoom and a small screen simply sees less of the town.
   A 640×480 world shown whole on a phone wastes half the display and makes a
   16px monster four millimetres tall. */
/* No `line-height: 0` here, and none on #screen either. It is the usual trick
   for killing the line box under a canvas — and a canvas that is `display:
   block`, as the one below is, never had one. What it did instead was inherit
   into every panel positioned inside this box: the duel card's heading came out
   nought pixels tall and printed itself over the card's own top edge. */
#arena {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}

#screen {
  position: relative;
}

#world {
  display: block;
  image-rendering: pixelated;
  background: #0c0c12;
  touch-action: none;
  cursor: crosshair;
}

/* ------------------------------------------------------------- components */

/* What basecoat used to provide, in the shape this game actually is. Square,
   outlined, and pressed with a real offset rather than a shadow that pretends
   to be light.

   Chrome is pixel type — buttons, badges, the name field — and pixel type comes
   with its tracking, always the two together. A card is NOT on this list: it
   holds paragraphs as well as headings, and every heading in this file asks for
   the pixel family by name, so leaving a card's own text alone is what keeps a
   sentence in the family that was chosen for sentences. */
.btn,
.btn-outline,
.badge,
input[type="text"] {
  font-family: var(--pixel);
  letter-spacing: var(--track);
}

.btn,
.btn-outline,
.badge,
.card,
input[type="text"] { border-radius: 0; }

.btn,
.btn-outline {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 0 0.7rem;
  font-size: 0.95rem;
  line-height: 1;
  color: var(--ink);
  background: var(--raised);
  border: var(--line);
  box-shadow: var(--lift);
  cursor: pointer;
  touch-action: manipulation;
  /* A pressed pixel button moves down onto its own shadow. Nothing fades. */
  transition: none;
}

.btn { background: var(--gold); color: var(--gold-ink); box-shadow: var(--lift-lg); }
.btn-outline { background: var(--panel); }

.btn:hover { background: #ffe49a; }
.btn-outline:hover { background: #2e2e4a; }

.btn:active,
.btn-outline:active { transform: translateY(0.15rem); box-shadow: 0 0.05rem 0 #000; }

.btn:focus-visible,
.btn-outline:focus-visible {
  outline: 0.14rem solid var(--gold);
  outline-offset: 0.14rem;
}

.btn:disabled,
.btn-outline:disabled { opacity: 0.45; cursor: default; transform: none; }

.badge {
  display: inline-flex;
  align-items: center;
  min-height: max(1.9rem, 24px);
  padding: 0 0.55rem;
  font-family: var(--pixel);
  letter-spacing: var(--track);
  font-size: 0.95rem;
  line-height: 1;
  color: #cfcfe4;
  background: var(--panel);
  border: var(--line);
  box-shadow: 0 0.14rem 0 #000;
}

.card {
  background: var(--panel);
  border: 0.2rem solid #000;
  box-shadow: 0 0.4rem 0 rgb(0 0 0 / 0.9), var(--inset);
}

input[type="text"] {
  min-height: max(3rem, 44px);
  padding: 0 0.7rem;
  font-size: 1.2rem;
  color: var(--ink);
  background: #0f0f18;
  border: 0.16rem solid #000;
  box-shadow: var(--inset);
  outline: none;
}

input[type="text"]:focus { box-shadow: inset 0 0 0 0.14rem var(--gold); }

/* Headings and prose. A heading is pixel type; a sentence is not. */
h2 { margin: 0; font-family: var(--pixel); letter-spacing: var(--track); font-size: 1.5rem; font-weight: 400; color: var(--gold); }
p { text-wrap: pretty; }

/* ------------------------------------------------------------------ the HUD */

#hud {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  right: 0.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.3rem;
  padding-right: 3.2rem;    /* the ⚙ sits in that corner; leave it alone */
  pointer-events: none;
  z-index: 4;
}

/* Your own name is the gold one. Nothing else on the HUD is. */
#hud-name { background: var(--gold); color: var(--gold-ink); }
.hud-quiet { color: var(--ink-quiet); }
#hud .badge:empty { display: none; }

/* "3 others here" gets a lit square rather than a word, so a glance is enough. */
#hud-online::before {
  content: '';
  width: 0.5rem;
  height: 0.5rem;
  margin-right: 0.35rem;
  background: var(--win);
}

#hud-online:empty::before { display: none; }

/* The same square, in the colour of a thing that has stopped. */
#hud-online.is-offline { color: var(--rose); }
#hud-online.is-offline::before { background: var(--rose); }

/* ------------------------------------------------------ settings, behind a ⚙ */

#settingsBtn {
  position: absolute;
  top: 0;
  right: 0;
  pointer-events: auto;
  display: grid;
  place-items: center;
  /* game.css used to say 34px flat. It is now the unit with 34px as the floor,
     so the gear grows on a desktop and never shrinks below a finger. */
  width: max(2.6rem, 34px);
  min-height: max(2.6rem, 34px);
  padding: 0;
  background: var(--raised);
}

#settingsBtn svg { width: 1.4rem; height: 1.4rem; }
#settingsBtn[aria-expanded="true"] { color: var(--gold); }

#settings[hidden] { display: none; }

#settings {
  position: absolute;
  top: 3.4rem;
  right: 0.5rem;
  z-index: 6;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  width: max-content;
  min-width: 12rem;
  padding: 0.4rem;
  background: var(--panel);
  border: 0.16rem solid #000;
  box-shadow: 0 0.35rem 0 rgb(0 0 0 / 0.9), var(--inset);
  /* This menu is the only route to everything that is not the game, so a row
     that cannot be reached is a control that has ceased to exist. Bounded by
     the box it hangs in, and it scrolls inside that. */
  max-height: calc(100% - 4rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}

#settings button,
#settings a {
  flex: 0 0 auto;          /* a scrolling column must not squash its own rows */
  min-height: max(2.5rem, 38px);
  padding: 0 0.6rem;
  font-size: 0.95rem;
  justify-content: flex-start;
  text-align: left;
}

#settings a {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  white-space: nowrap;
  color: var(--ink-quiet);
  background: var(--sunk);
}

#settings a:hover { color: var(--gold); }

/* The build line. Not a control, so it is not shaped like one: no border, no
   height, and small enough that it reads as a footnote to the menu rather than
   as another thing to press. It wraps, because a cache name plus a relay count
   does not fit on one line of a phone and truncating the version number would
   defeat the entire point of printing it. */
.settings-note {
  flex: 0 0 auto;
  margin: 0.2rem 0 0;
  padding: 0 0.6rem 0.2rem;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--ink-faint);
  overflow-wrap: anywhere;
}
.settings-note:empty { display: none; }
#settings button[aria-pressed="true"] { color: var(--gold); box-shadow: inset 0 0 0 0.14rem var(--gold), var(--lift); }

/* ------------------------------------------------------------- the help line */

#controls {
  position: absolute;
  /* Under the badges, even when they wrap. Three rows of them — which is what a
     320px phone gets — is 0.5rem of top inset plus 3 × 1.9rem plus 2 × 0.3rem,
     or 6.8rem; but a row only exists once there is a badge on it, and the third
     row only appears where the unit is small, so 6rem clears the wrap at every
     size this was measured at. A line that starts inside the row above it is a
     line nobody finishes. */
  top: 6rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  max-width: min(88%, 30rem);
  margin: 0;
  padding: 0.45rem 0.7rem;
  background: rgb(8 8 14 / 0.82);
  border: var(--line);
  font-family: var(--pixel);
  letter-spacing: var(--track);
  font-size: 0.9rem;
  line-height: 1.4;
  text-align: center;
  color: #cfcfe4;
  pointer-events: none;
  transition: opacity 0.8s ease;
  /* Below the name plates and bubbles (z-index 3 on #tags): a bubble that is
     covered is a message that was never delivered. */
  z-index: 2;
}

#controls.is-gone { opacity: 0; }
.help-line[hidden] { display: none; }

/* Before `ui/help.js` has put its two lines in — which is to say for the whole
   of loading — this is an empty box with a border and a background, and it drew
   as a little rectangle floating over the loading screen. Same rule the HUD
   badges already use for the same reason. */
#controls:empty { display: none; }

@media (prefers-reduced-motion: reduce) { #controls { transition: none; } }

/* -------------------------------------------------- name plates and bubbles */

/* Text over the world lives in the DOM, not on the canvas: text drawn into a
   canvas can be read by an eye and by nothing else. This layer is positioned
   from the numbers drawActor() returns — see src/ui/bubbles.js. */
#tags {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
}

.tag-plate {
  position: absolute;
  transform: translateX(-50%);
  z-index: 2;              /* a bubble must never hide a name */
  padding: 0.2rem 0.35rem;
  white-space: nowrap;
  font-family: var(--pixel);
  letter-spacing: var(--track);
  font-size: 0.85rem;
  line-height: 1;
  color: var(--ink);
  background: rgb(10 10 18 / 0.85);
  border: var(--line);
}

.tag-plate.is-self { color: var(--gold-ink); background: var(--gold); }

/* One bubble shape, six contents: the three moves, won, lost, thinking, plus a
   phrase. The doubled shadow is the stepped tail — two hard offsets, no curve,
   so it belongs to the same drawing as the sprite under it. */
.tag-bubble {
  position: absolute;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 2.1rem;
  min-height: 1.8rem;
  padding: 0.15rem 0.35rem;
  white-space: nowrap;
  font-family: var(--pixel);
  letter-spacing: var(--track);
  font-size: 0.9rem;
  color: var(--gold-ink);
  background: #f3f3fb;
  border: var(--line);
  box-shadow: 0 0.16rem 0 #000, 0 0.3rem 0 -0.1rem #000;
}

.tag-bubble svg { width: 1.3rem; height: 1.3rem; }
.tag-bubble.is-win { background: #1b3a1b; }
.tag-bubble.is-lose { background: #16243a; }
.tag-bubble.is-think { background: var(--raised); }
.tag-bubble.is-think svg { animation: blink 1.2s steps(1, end) infinite; }

@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .tag-bubble.is-think svg { animation: none; } }

/* ---------------------------------------------------------------- chat bar */

#chatbar[hidden] { display: none; }

#chatbar {
  display: flex;
  flex-wrap: nowrap;
  gap: 0.25rem;
  justify-content: center;
  max-width: 100%;
  pointer-events: auto;
  overflow-x: auto;
  scrollbar-width: none;
}

#chatbar::-webkit-scrollbar { display: none; }

/* All six across a phone, one row, no scroll. The clamp is against the viewport
   because the bar is as wide as the screen; the 40px floor is the thumb. */
.phrase-btn {
  flex: 0 0 auto;
  min-height: max(2.7rem, 40px);
  /* 1vw rather than 1.3: six buttons have twelve edges, and the pixel font's
     tracking now spends about a pixel per letter. At 320px — the narrowest
     phone anybody is still holding — 1.3vw put the row 3px over its own width,
     which a centred row pays for by clipping a pixel off each end. */
  padding: 0 clamp(2px, 1vw, 0.7rem);
  font-size: clamp(9px, 2.5vw, 0.95rem);
  white-space: nowrap;
  /* Not gold: six of these in a row is not six calls to action, and `Challenge`
     is the only gold thing at the bottom of the screen. The colour has to come
     with the background, though — `.btn` sets dark ink to sit on gold, and
     leaving that behind on a dark panel is six unreadable buttons. */
  color: var(--ink);
  background: var(--panel);
}

/* …and the same again for hover, or the pointer turns one phrase gold and its
   pale text disappears into it. */
.phrase-btn:hover { background: #2e2e4a; }

/* --------------------------------------------- the bottom of the world */

/* The challenge prompt and the phrase row are ONE stack, pinned to the bottom of
   the canvas's own box. The prompt is above the phrases because it comes before
   them: take the phrases away and it takes the bottom of the canvas, with no
   hand-tuned offset to go stale. */
#bottom {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.45rem;
  max-width: 100%;
  max-height: 100%;
  padding: 0 0.4rem 0.5rem;
  pointer-events: none;
  z-index: 5;
}

#nearby[hidden] { display: none; }

#nearby {
  max-width: 90%;
  min-height: max(2.9rem, 44px);
  padding: 0 1.1rem;
  pointer-events: auto;
  font-size: 1.05rem;
  animation: nudge 1.6s ease-in-out infinite;
}

@keyframes nudge {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-0.2rem); }
}

@media (prefers-reduced-motion: reduce) { #nearby { animation: none; } }

/* -------------------------------------------------------------- duel screen */

#duel[hidden] { display: none; }

#duel {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 0.7rem;
  background: rgb(6 6 10 / 0.86);
  overflow-y: auto;
  z-index: 9;
}

/* The card never grows past the window, and what it spends its space on is in
   priority order: the three moves and the button that leaves are how you play,
   so they never shrink and never end up below the fold. */
.duel-card {
  width: min(30rem, 100%);
  max-height: 100%;
  padding: 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  text-align: center;
  overflow: hidden;
}

/* Everything above the buttons. This is the part that gives way: it shrinks
   first, and only when there is nothing left to give does it scroll inside
   itself — with the moves still on screen under it. */
.duel-top {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.4rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.duel-moves,
.duel-actions { flex: 0 0 auto; }

.duel-title { margin: 0; font-family: var(--pixel); letter-spacing: var(--track); font-size: 1.5rem; font-weight: 400; color: var(--gold); }
.duel-sub { margin: 0; font-size: 0.95rem; line-height: 1.5; color: var(--ink-quiet); }

/* The score always carries both names. A bare "1 — 0" is the numbers without
   the one thing you wanted them for. Yours is gold, theirs is rose — the same
   two colours the face-off uses, so you never have to work out which is which. */
.duel-score {
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0;
  font-family: var(--pixel);
  letter-spacing: var(--track);
  font-variant-numeric: tabular-nums;
}

.duel-num { font-size: 2.2rem; line-height: 1; color: var(--gold); }
.duel-num.is-them { color: var(--rose); }
.duel-dash { font-size: 1.2rem; color: #55556f; }

.duel-side {
  font-size: 0.95rem;
  color: var(--ink-quiet);
  max-width: 7rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ------------------------------------------------------------ the face-off */

.duel-face {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: stretch;
  gap: 0.4rem;
}

.duel-face[hidden] { display: none; }

.duel-side-box {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 0.15rem;
  min-height: max(7rem, 90px);
  padding: 0.5rem 0.3rem;
  background: var(--sunk);
  border: var(--line);
  box-shadow: inset 0 0 0 0.14rem #2a2a44;
}

.duel-side-box.is-known { background: #1d1d30; box-shadow: inset 0 0 0 0.14rem #3f3f66; }

.duel-face-name { font-family: var(--pixel); letter-spacing: var(--track); font-size: 0.85rem; color: var(--ink-faint); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.duel-face-icon { display: grid; place-items: center; width: 2.8rem; height: 2.8rem; }
.duel-face-icon svg { width: 100%; height: 100%; }
.duel-face-move { font-family: var(--pixel); letter-spacing: var(--track); font-size: 0.95rem; }

.duel-face-win {
  margin-top: 0.1rem;
  padding: 0.05rem 0.4rem;
  font-family: var(--pixel);
  font-size: 0.8rem;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  background: var(--gold);
  color: var(--gold-ink);
  visibility: hidden;
}

.duel-side-box.is-win {
  background: #2a2312;
  box-shadow: inset 0 0 0 0.14rem var(--gold);
}

.duel-side-box.is-win .duel-face-name { color: #a08e5a; }
.duel-side-box.is-win .duel-face-win { visibility: visible; }

/* Not chosen yet: three dim dots that breathe, so "we are waiting on this one"
   is visible from across a room and not only readable up close. */
.duel-side-box:not(.is-known) .duel-face-icon { opacity: 0.35; animation: think 1.4s ease-in-out infinite; }
.duel-side-box:not(.is-known) .duel-face-move { color: var(--ink-quiet); }

.duel-vs { align-self: center; font-family: var(--pixel); letter-spacing: var(--track); font-size: 0.9rem; color: #55556f; }

@keyframes think {
  0%, 100% { opacity: 0.3; }
  50%      { opacity: 0.75; }
}

@media (prefers-reduced-motion: reduce) {
  .duel-side-box:not(.is-known) .duel-face-icon { animation: none; }
}

/* ---- the three states, told apart by eye and not only by reading ---- */

.duel-banner { margin: 0; font-family: var(--pixel); letter-spacing: var(--track); font-size: 1.3rem; text-wrap: balance; }
.duel-why { margin: 0; font-size: 0.9rem; line-height: 1.45; color: var(--ink-quiet); }

.duel-card[data-phase="choosing"] .duel-banner { color: var(--gold); }
.duel-card[data-phase="waiting"] .duel-banner { color: var(--wait); }
.duel-card[data-phase="waiting"] .duel-moves { opacity: 0.5; }

.duel-card[data-phase="resolved"] .duel-banner { color: var(--win); font-size: 1.45rem; }
.duel-card[data-phase="over"] .duel-banner { color: var(--win); font-size: 1.6rem; }

/* Green is for the rounds you won. A round that went the other way takes the
   opponent's rose, and a draw takes neither — the score row above uses the same
   two colours, so the banner never has to be read to know which way it went. */
.duel-card[data-result="them"] .duel-banner { color: var(--rose); }
.duel-card[data-result="nobody"] .duel-banner { color: var(--ink-quiet); }
.duel-card[data-phase="resolved"] .duel-moves,
.duel-card[data-phase="over"] .duel-moves { opacity: 0.45; }
.duel-card[data-phase="resolved"] .duel-face { animation: reveal 0.28s ease-out; }
.duel-card[data-phase="over"] .duel-num { font-size: 3rem; }

/* The result sentence and the score row say the same thing twice. The row above
   already carries the numbers, so the sentence carries only the verdict. */
.duel-card[data-phase="over"] .duel-why { display: none; }

.duel-over-label { margin: 0; font-family: var(--pixel); font-size: 1.05rem; letter-spacing: var(--track-wide); color: var(--ink-faint); }

@keyframes reveal {
  from { transform: scale(0.9); }
  to   { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .duel-card[data-phase="resolved"] .duel-face { animation: none; }
}

/* Three targets a thumb cannot miss. This is the game, on a phone. */
.duel-moves {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
}

.duel-moves[hidden] { display: none; }

.duel-move {
  display: grid;
  justify-items: center;
  gap: 0.1rem;
  min-height: max(4.6rem, 54px);
  padding: 0.4rem 0.2rem;
  box-shadow: var(--lift-lg), inset 0 0.14rem 0 rgb(255 255 255 / 0.06);
}

.duel-move:hover { background: #32325a; }
.duel-move-icon { display: grid; place-items: center; width: 2.1rem; height: 2.1rem; }
.duel-move-icon svg { width: 100%; height: 100%; }
.duel-move-name { font-size: 0.95rem; }
.duel-move:disabled { opacity: 0.45; }
.duel-move.is-picked { outline: 0.16rem solid var(--gold); outline-offset: 0.14rem; }

.duel-score[hidden] { display: none; }

.duel-actions {
  display: flex;
  gap: 0.4rem;
  justify-content: center;
  margin-top: 0.3rem;
}

.duel-actions button {
  min-height: max(2.8rem, 44px);
  padding: 0 1.1rem;
  font-size: 1rem;
}

/* Giving up is not an action you should hit by accident. */
.duel-give-up { background: var(--sunk); color: var(--ink-quiet); }
.duel-give-up:hover { background: var(--sunk); color: var(--ink); }
.duel-actions button[hidden] { display: none; }

/* The only two shape changes in this file. Everything else is the unit.

   Short: the face-off goes ONLY while you are choosing, where both sides are
   dots and it is saying nothing the buttons below it do not. The moment there
   is a real move on it — yours while you wait, or both at the reveal — it comes
   back, because that is the whole panel. */
@media (max-height: 420px) {
  .duel-card[data-phase="choosing"] .duel-face { display: none; }
  .duel-sub,
  .duel-why { display: none; }
  .duel-side-box { min-height: max(3.6rem, 52px); }
  .duel-face-icon { width: 1.8rem; height: 1.8rem; }

  /* The sentence that says who took the round has to fit in the room the
     face-off leaves it. At full size it wraps to a second line that falls below
     the scrolling area, and a player will not scroll a duel to find out whether
     they won — measured at 1440x300 and at 740x360, where the second line was
     22 and 14 pixels past the bottom. */
  .duel-banner,
  .duel-card[data-phase="resolved"] .duel-banner,
  .duel-card[data-phase="over"] .duel-banner { font-size: 1.05rem; line-height: 1.15; }

  /* And the heading goes, once a fight is actually on: "You vs Flint" is what
     the score row underneath it already says, in bigger type, with the numbers.
     Only while fighting — before that the heading is the whole message ("Flint
     challenges you!") and there is no score row to carry it. */
  .duel-card[data-phase="choosing"] .duel-title,
  .duel-card[data-phase="waiting"] .duel-title,
  .duel-card[data-phase="resolved"] .duel-title,
  .duel-card[data-phase="over"] .duel-title { display: none; }
}

/* ----------------------------------------------------------- the raid panel */

/* The same card as the duel, because it is the same three moves and nobody
   should have to learn a second interface. What it adds is a clock and other
   people. */
#boss[hidden] { display: none; }

#boss {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 0.7rem;
  background: rgb(10 4 4 / 0.88);
  overflow-y: auto;
  z-index: 9;
}

.boss-card { width: min(32rem, 100%); }
.boss-card .duel-title { color: var(--rose); }

.boss-lives {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--pixel);
  letter-spacing: var(--track);
  font-size: 0.9rem;
  color: var(--ink-quiet);
}

.boss-lives-name { min-width: 3.6rem; text-align: right; }
.boss-lives-count { font-variant-numeric: tabular-nums; }
.boss-pips { display: flex; flex: 1 1 auto; gap: 0.15rem; }

.boss-pip {
  flex: 1 1 auto;
  height: 0.7rem;
  max-width: 1.4rem;
  background: var(--rose);
  border: var(--line);
}

.boss-pip.is-gone { background: var(--sunk); }
.boss-yours .boss-pip { background: var(--gold); }
.boss-yours .boss-pip.is-gone { background: var(--sunk); }

/* A round ends when this runs out, whatever anybody did. It is a clock, not a
   turn — which is the whole reason a fight with five people in it works. */
.boss-timer {
  height: 0.5rem;
  margin: 0.2rem 0;
  background: var(--sunk);
  border: var(--line);
}

.boss-timer-fill {
  height: 100%;
  background: var(--gold);
  transition: width 0.12s linear;
}

.boss-timer.is-closed .boss-timer-fill { background: #55556f; }

@media (prefers-reduced-motion: reduce) { .boss-timer-fill { transition: none; } }

/* Everybody else in this round, as their moves arrive. */
.boss-crowd {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  justify-content: center;
  min-height: 2rem;
}

.boss-other {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.15rem 0.4rem;
  background: var(--sunk);
  border: var(--line);
  font-family: var(--pixel);
  letter-spacing: var(--track);
  font-size: 0.8rem;
  color: var(--ink-quiet);
}

.boss-other-icon { display: grid; place-items: center; width: 1.1rem; height: 1.1rem; }
.boss-other-icon svg { width: 100%; height: 100%; }

/* The badge that says when he is due. Rose, because he is. */
#hud-boss:empty { display: none; }
#hud-boss.is-here { color: var(--rose); }

/* ---------------------------------------------------------------- overlay */

#overlay[hidden] { display: none; }

#overlay {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 0.8rem;
  background: rgb(6 6 10 / 0.86);
  overflow-y: auto;
  z-index: 10;
}

.onboarding-card { width: min(28rem, 100%); padding: 1.2rem; display: grid; gap: 0.3rem; }
.onboarding-card .onboarding-title { font-family: var(--pixel); letter-spacing: var(--track); font-size: 1.8rem; font-weight: 400; color: var(--gold); margin: 0; }
.onboarding-card p { margin: 0.3rem 0 0; font-size: 0.95rem; color: #a0a0bb; }
.onboarding-form { display: grid; gap: 0.4rem; margin-top: 1rem; }
.onboarding-form label { font-family: var(--pixel); letter-spacing: var(--track); font-size: 0.9rem; color: var(--ink-quiet); }
.onboarding-hint { min-height: 1.2rem; margin: 0.6rem 0 0; font-size: 0.85rem; color: #ff9a9a; }
.onboarding-confirm { width: 100%; min-height: max(3rem, 44px); margin-top: 0.3rem; font-size: 1.1rem; }

.reset-actions { display: flex; gap: 0.4rem; justify-content: center; margin-top: 1rem; }
.reset-actions button { min-height: max(3rem, 44px); padding: 0 1.1rem; }

/* The card about other people. Calm, not alarming: no red, no warning icon. */
.safety-card { width: min(34rem, 100%); }

.safety-list {
  margin: 0.9rem 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0.5rem;
}

.safety-list li {
  padding: 0.7rem 0.8rem;
  background: var(--sunk);
  border: var(--line);
  box-shadow: inset 0 0 0 0.14rem #2a2a44;
}

.safety-list strong { display: block; font-family: var(--pixel); letter-spacing: var(--track); font-size: 1.05rem; font-weight: 400; color: var(--ink); }
.safety-list p { margin: 0.2rem 0 0; font-size: 0.9rem; line-height: 1.5; color: #a0a0bb; }

.monster-grid {
  margin-top: 0.9rem;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
}

.monster-card {
  display: grid;
  justify-items: center;
  gap: 0.25rem;
  padding: 0.5rem 0.3rem;
  color: inherit;
  background: var(--sunk);
  border: var(--line);
  box-shadow: inset 0 0 0 0.14rem #2a2a44;
  font: inherit;
  cursor: pointer;
}

.monster-card:hover { background: #1d1d30; }
.monster-card[aria-pressed="true"] { background: #2a2312; box-shadow: inset 0 0 0 0.14rem var(--gold); }
.monster-swatch { image-rendering: pixelated; width: 3.4rem; height: 3.4rem; }
.monster-name { font-family: var(--pixel); letter-spacing: var(--track); font-size: 0.9rem; }

/* Nothing sits under an animal but its name: the animal you pick has no effect
   on a duel, so any badge under it would say otherwise. */

/* ------------------------------------------------- chests and the wardrobe */

/* The same grid the monster picker uses, and deliberately so: choosing what you
   are wearing is the same kind of choice as choosing who you are. */
.look-grid { grid-template-columns: repeat(3, 1fr); }

/* A look nobody has won yet is shown, not hidden — greyed, with the price under
   it. Three empty spaces are what the next hundred duels are for. */
.monster-card.is-locked {
  opacity: 0.42;
  cursor: default;
  background: var(--void);
}

.monster-card.is-locked .monster-swatch { filter: grayscale(1); }
.monster-card.is-locked:hover { background: var(--void); }

/* The chest itself: three beats, and this is the first one. Square, gold and
   shut, in the same black edge as everything else. */
.chest-stage {
  display: grid;
  place-items: center;
  min-height: 7rem;
  margin: 0.9rem 0 0.2rem;
}

.chest-box {
  width: 4.5rem;
  height: 3.4rem;
  background: var(--gold);
  border: var(--line);
  box-shadow: var(--lift-lg), inset 0 0.5rem 0 #e0b74a;
  animation: chest-shake 1.4s ease-in-out infinite;
}

@keyframes chest-shake {
  0%, 82%, 100% { transform: translate(0, 0) rotate(0deg); }
  86%           { transform: translate(-0.12rem, 0) rotate(-2deg); }
  92%           { transform: translate(0.12rem, 0) rotate(2deg); }
}

@media (prefers-reduced-motion: reduce) { .chest-box { animation: none; } }

/* The reward is drawn at four times life size, from the same sheet and through
   the same looks.js the world uses — a preview painted another way could lie. */
.chest-stage .monster-swatch { width: 6rem; height: 6rem; }
.chest-later { min-height: max(3rem, 44px); padding: 0 1.1rem; }

/* -------------------------------------------------- the paused window card */

#paused[hidden] { display: none; }

#paused {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 0.8rem;
  background: rgb(6 6 10 / 0.94);
  z-index: 20;
}

.paused-card { width: min(26rem, 100%); padding: 1.3rem; display: grid; gap: 0.5rem; text-align: center; }
.paused-title { margin: 0; font-family: var(--pixel); letter-spacing: var(--track); font-size: 1.4rem; font-weight: 400; color: var(--ink); }
.paused-body { margin: 0; font-size: 0.95rem; line-height: 1.55; color: #a0a0bb; }
.paused-resume { justify-self: center; min-height: max(3rem, 44px); margin-top: 0.4rem; padding: 0 1.2rem; font-size: 1.05rem; }
