/* ==========================================================================
   ezoplay — cream-white game UI
   ==========================================================================
   Flat by design: no glows, no neon, no shiny gradients. Depth comes from a
   solid bottom edge on pressable things and a plain soft drop shadow, nothing
   else.

   Your brand colours are light tints, and a light tint on cream is unreadable as
   text (#70D6FF on #FBF8F1 is about 1.4:1). So each accent exists twice: the
   bright version is a FILL, and a darkened "ink" version is used for text and
   icons. Every text pairing here clears 4.5:1.
   ========================================================================== */

:root {
  --cyan: #70d6ff;
  --lavender: #b8c0ff;
  --mint: #7dffc4;

  --cyan-ink: #0c6b87;
  --lavender-ink: #4e56a6;
  --mint-ink: #0f7a52;

  --cream: #fbf8f1;
  --cream-2: #f3eee3;
  --card: #ffffff;
  --line: #e7e0d2;
  --line-2: #d5cdba;

  --ink: #1a1d28;
  --ink-2: #4a4f5e;
  --dim: #6e6a63;

  --warn-ink: #8a5a00;
  --warn: #ffd479;
  --danger-ink: #a32a45;
  --danger: #ff9eb5;

  --r-sm: 12px;
  --r: 18px;
  --r-lg: 24px;

  --shadow-sm: 0 1px 3px rgba(26, 29, 40, .08);
  --shadow: 0 4px 14px rgba(26, 29, 40, .09);

  --ui: "Segoe UI Variable Display", "Segoe UI", Inter, system-ui, -apple-system, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  /* Display type, for the one headline that is meant to be looked at rather than read.
     Falls back to the UI stack, so a font that fails to load costs a look, not a layout. */
  --display: Audex, var(--ui);

  --topbar-h: 58px;
  --appbar-h: 64px;
}

/* Audex by GGBotNet, under the SIL Open Font License 1.1 - see public/fonts/OFL.txt, which
   is served alongside the font because the licence requires it to travel with the files.
   Regular only: the hero is one line and never asks for italic or a second weight.
   `swap` so the headline is readable immediately in the fallback and upgrades when the font
   arrives - the alternative is a hero that is blank for the first moment of every visit. */
@font-face {
  font-family: Audex;
  src: url("/fonts/audex-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

* { box-sizing: border-box; }

html { height: 100%; overflow-x: hidden; }

body {
  margin: 0;
  min-height: 100%;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

/* Flat wash. No animated grid, no bloom. */
.bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(700px 460px at 10% -14%, rgba(112, 214, 255, .16), transparent 66%),
    radial-gradient(620px 420px at 92% 2%, rgba(184, 192, 255, .14), transparent 68%);
}

/* --- app bar --------------------------------------------------------------
   Logo and name pinned top-left, settings top-right, on every screen except the
   match itself (where the game topbar already carries both).
   -------------------------------------------------------------------------- */

.app-bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 6;
  height: var(--appbar-h);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 16px;
  background: rgba(251, 248, 241, .92);
  backdrop-filter: blur(10px);
  border-bottom: 1.5px solid var(--line);
}
.app-bar[hidden] { display: none; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  margin-right: auto;
  padding: 4px 8px 4px 4px;
  background: none;
  border: 0;
  border-radius: 12px;
  cursor: pointer;
  font-family: inherit;
  color: var(--ink);
  transition: background .14s;
}
.brand:hover { background: var(--cream-2); }
.brand:focus-visible { outline: 3px solid var(--cyan-ink); outline-offset: 2px; }

.brand-mark { height: 40px; width: auto; display: block; }

.brand-name {
  font-size: 17px;
  font-weight: 860;
  letter-spacing: -.02em;
  line-height: 1;
  white-space: nowrap;
}
.brand-name em { font-style: normal; color: var(--cyan-ink); }

/* Larger, deliberately prominent settings button. */
.icon-btn.gear-lg {
  width: 46px;
  height: 46px;
  font-size: 22px;
  border-radius: 13px;
}

/* --- view scaffolding -----------------------------------------------------
   Scrolling views (menus, lobby, result) grow as tall as their content and the
   page scrolls normally. The game view is the one exception: it is pinned to
   the viewport so the chat scrolls inside itself instead of the page moving.
   -------------------------------------------------------------------------- */

.view {
  position: relative;
  z-index: 1;
  min-height: 100svh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(var(--appbar-h) + 26px) 18px 40px;
}
.view[hidden] { display: none; }

/* The match view is pinned to the viewport and supplies its own header. */
.view-game { padding: 0; }

h1, h2, h3 { font-weight: 780; letter-spacing: -.01em; margin: 0; }
.center { text-align: center; }

/* --- buttons -------------------------------------------------------------- */

.btn {
  --fill: #ffffff;
  --edge: var(--line-2);
  --fg: var(--ink);
  font-family: inherit;
  font-size: 14px;
  font-weight: 750;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: 13px 22px;
  border: 0;
  border-radius: var(--r-sm);
  background: var(--fill);
  color: var(--fg);
  cursor: pointer;
  box-shadow: 0 4px 0 var(--edge);
  transition: transform .07s ease, box-shadow .07s ease, background .14s ease;
  user-select: none;
}
.btn:hover:not(:disabled) { background: color-mix(in srgb, var(--fill) 90%, #000 10%); }
.btn:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 0 0 var(--edge); }
.btn:focus-visible { outline: 3px solid var(--cyan-ink); outline-offset: 2px; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

.btn-primary { --fill: var(--cyan); --edge: #3e93b0; --fg: #06222e; }
.btn-lav { --fill: var(--lavender); --edge: #7b83c9; --fg: #191d38; }
.btn-mint { --fill: var(--mint); --edge: #3ab989; --fg: #05301f; }
.btn-warn { --fill: var(--warn); --edge: #c79b32; --fg: #3a2a00; }
.btn-danger { --fill: var(--danger); --edge: #c26480; --fg: #46101f; }

/* Ko-fi, in their own brand teal (#30c9c1) so it reads as an external action
   rather than part of the game's own button set. */
.btn-kofi {
  --fill: #30c9c1;
  --edge: #1f8f89;
  --fg: #06282a;
}

/* Anchors styled as buttons need these; <button> gets them for free. */
a.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  justify-content: center;
  text-decoration: none;
}
.kofi-cup { font-size: 14px; line-height: 1; }

.btn-ghost {
  background: transparent;
  color: var(--ink-2);
  box-shadow: inset 0 0 0 1.5px var(--line-2);
}
.btn-ghost:hover:not(:disabled) { background: var(--cream-2); color: var(--cyan-ink); box-shadow: inset 0 0 0 1.5px var(--cyan-ink); }
.btn-ghost:active:not(:disabled) { transform: translateY(2px); }

.btn-sm { padding: 9px 14px; font-size: 12px; }
.btn-lg { padding: 17px 30px; font-size: 15.5px; }
.btn-block { width: 100%; }
.row { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }

/* --- inputs --------------------------------------------------------------- */

input {
  font-family: inherit;
  font-size: 15px;
  padding: 13px 16px;
  border: 1.5px solid var(--line-2);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--ink);
  outline: none;
  min-width: 0;
  transition: border-color .14s;
}
input:focus { border-color: var(--cyan-ink); }
input::placeholder { color: #a09a90; }

/* --- panels --------------------------------------------------------------- */

.panel {
  background: var(--card);
  border: 1.5px solid var(--line);
  border-radius: var(--r);
  padding: 20px;
  box-shadow: var(--shadow);
}
.panel h2 {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .15em;
  color: var(--lavender-ink);
  margin-bottom: 14px;
}
.count { color: var(--dim); }
.hint { color: var(--dim); font-size: 12.5px; margin: 12px 0 0; }
.hint.center { margin: 0; }

/* --- avatars -------------------------------------------------------------- */

.avatar {
  display: block;
  border-radius: 50%;
  background: var(--cream-2);
  object-fit: cover;
  flex: none;
}
.avatar.ring { box-shadow: 0 0 0 2.5px currentColor; }

/* --- home ----------------------------------------------------------------- */

.home { width: 100%; max-width: 780px; text-align: center; }

.tagline {
  color: var(--ink-2);
  font-size: 15.5px;
  margin: 0 auto 22px;
  max-width: 30em;
}

/* --- the hero -------------------------------------------------------------
   Everything in here arrives on load, in order, roughly as fast as an eye moves down
   the page. Nothing loops: a landing page that keeps moving after you have read it is
   just noise. See @media (prefers-reduced-motion) at the end of this block. */
.hero { display: grid; justify-items: center; gap: 0; margin-bottom: 28px; }
.hero-copy { display: grid; justify-items: center; }

/* Deliberately not larger: the top of the shelf should still be visible on a laptop
   without scrolling, so the hero has a height budget. */
.hero-mark {
  width: 84px;
  height: 84px;
  margin-bottom: 11px;
  filter: drop-shadow(0 8px 18px rgba(24, 30, 52, .16));
  animation: hero-in .5s cubic-bezier(.2, .8, .25, 1) both;
}

.hero-title {
  font-family: var(--display);
  /* Audex runs wider than the UI face, so the same clamp would overflow a phone. */
  font-size: clamp(27px, 5.6vw, 46px);
  font-weight: 400; /* the display face has one weight; asking for 780 would fake-bold it */
  line-height: 1.08;
  letter-spacing: -.005em;
  margin: 0 0 12px;
  max-width: 17em;
  animation: hero-in .5s cubic-bezier(.2, .8, .25, 1) .06s both;
}
/* The second half of the line is the punchline, so it is said quieter. */
.hero-title em { font-style: normal; color: var(--cyan-ink); }

.hero .tagline {
  margin-bottom: 20px;
  animation: hero-in .5s cubic-bezier(.2, .8, .25, 1) .12s both;
}
.hero .tagline b { color: var(--ink); }

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  animation: hero-in .5s cubic-bezier(.2, .8, .25, 1) .18s both;
}

@keyframes hero-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

/* --- the round that plays itself ------------------------------------------
   Fixed height, always. The bubbles arrive one at a time, and a panel that grew with each
   one would shove the whole page down four times a loop. */
.hero-demo {
  width: 100%;
  max-width: 380px;
  text-align: left;
  background: var(--card);
  border: 1.5px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  padding: 12px 14px 14px;
  animation: hero-in .5s cubic-bezier(.2, .8, .25, 1) .28s both;
}

.demo-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
/* Says what it is, in the one place somebody might mistake it for a live room. */
.demo-tag {
  font-size: 9.5px; font-weight: 850; text-transform: uppercase; letter-spacing: .16em;
  color: var(--lavender-ink); background: #eeefff;
  border-radius: 999px; padding: 3px 8px;
}
.demo-what { font-size: 11px; font-weight: 700; color: var(--dim); }

.demo-log {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 8px;
  /* Sized so the longest round fits whole. A test computes every round's height from these
     numbers and this box, and fails if one of them does not fit. */
  height: 186px;

  /* THE AVATAR RINGS WERE BEING SHAVED HERE.
     `.avatar.ring` is a box-shadow, which paints OUTSIDE the element's box. A plain
     `overflow: hidden` clips at the content edge, so it took 2.5px off the left of every
     ring and off the bottom of the last row - the flat edge you can see on the circles.
     `clip` with a margin clips in the same place but lets that overhang paint, which is
     exactly what this property is for. `hidden` first so an engine that does not know
     `clip` still contains a long round instead of spilling it. */
  overflow: hidden;
  overflow: clip;
  overflow-clip-margin: 6px;

  transition: opacity .45s ease;
}
.demo-log.fading { opacity: 0; }

/* The game talking rather than a player: a dealt word, nightfall, a body in the morning.
   Centred and quiet so it is never mistaken for somebody's line. */
.demo-note {
  align-self: center;
  text-align: center;
  font-size: 11.5px;
  line-height: 1.35;
  font-weight: 750;
  color: var(--dim);
  background: var(--cream-2);
  border-radius: 999px;
  padding: 5px 12px;
  animation: demo-line-in .34s cubic-bezier(.2, .8, .25, 1) both;
}

.demo-line {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 28px;
  animation: demo-line-in .34s cubic-bezier(.2, .8, .25, 1) both;
}
.demo-line .avatar { width: 26px; height: 26px; flex: none; }
/* Its own size and leading rather than the inherited 15px/1.5, which built a line box
   taller than the text in it and pushed the two halves of the row out of step. */
.demo-body { min-width: 0; font-size: 13px; line-height: 1.4; }
.demo-who {
  font-size: 11px;
  font-weight: 850;
  margin-right: 6px;
  /* The avatar palette is pale by design - readable as a fill, not as 11px text on white. */
  filter: brightness(.7) saturate(1.35);
}
.demo-said { font-size: 13px; color: var(--ink-2); overflow-wrap: anywhere; }

@keyframes demo-line-in {
  from { opacity: 0; transform: translateY(7px); }
  to { opacity: 1; transform: none; }
}

/* Somebody is typing. Its own dots rather than the chat's, so restyling one never quietly
   restyles the other. The row around them is a normal .demo-line, avatar included, so it is
   exactly the height of the line it turns into. */
.demo-dots { display: inline-flex; align-items: center; gap: 4px; height: 19px; }
.demo-dots span {
  width: 5px; height: 5px; border-radius: 50%;
  background: currentColor; opacity: .45;
  animation: demo-blink 1.1s infinite;
}
.demo-dots span:nth-child(2) { animation-delay: .18s; }
.demo-dots span:nth-child(3) { animation-delay: .36s; }
@keyframes demo-blink {
  0%, 60%, 100% { opacity: .3; transform: none; }
  30% { opacity: 1; transform: translateY(-2px); }
}

.demo-verdict {
  margin-top: 2px;
  font-size: 12px;
  line-height: 1.35;
  font-weight: 800;
  color: var(--danger-ink);
  background: #ffe9ef;
  border-radius: 10px;
  padding: 8px 11px;
  opacity: 0;
  transform: scale(.94);
  transition: opacity .3s ease, transform .3s cubic-bezier(.2, .8, .25, 1);
}
.demo-verdict.in { opacity: 1; transform: none; }
/* The room got it right. Both outcomes are in the rotation on purpose - a demo where the
   liar always loses would be a worse advert than one where sometimes they walk. */
.demo-verdict.good { color: var(--mint-ink); background: #e3fff2; }

/* Side by side once there is room, which costs the page no extra height at all - the demo
   is shorter than the words next to it. Below this it stacks and everything centres. */
@media (min-width: 900px) {
  .home { max-width: 880px; }
  .hero {
    grid-template-columns: minmax(0, 1fr) 380px;
    align-items: center;
    gap: 30px;
    text-align: left;
  }
  .hero-copy { justify-items: start; text-align: left; }
  .hero-copy .tagline { margin-left: 0; margin-right: 0; }
  .hero-title { max-width: none; }
  .hero-actions { justify-content: flex-start; }
}

/* Panels below the shelf rise in as they are scrolled to, once each. Set by an
   IntersectionObserver in app.js; without one, nothing is ever hidden. */
.reveal { opacity: 0; transform: translateY(18px); transition: opacity .5s ease, transform .5s cubic-bezier(.2, .8, .25, 1); }
.reveal.shown { opacity: 1; transform: none; }

/* Profile chip. Lives in the app bar on the right, next to support and settings. */
.me-chip {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 5px 13px 5px 5px;
  background: var(--card);
  border: 1.5px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  font-family: inherit;
  font-size: 13.5px;
  color: var(--ink);
  transition: border-color .14s, transform .07s;
}
.me-chip:hover { border-color: var(--cyan-ink); }
.me-chip:active { transform: translateY(2px); }
.me-chip:focus-visible { outline: 3px solid var(--cyan-ink); outline-offset: 2px; }
.me-chip b { font-weight: 750; }
.me-chip .me-text { display: inline-flex; align-items: center; gap: 6px; }
.me-chip .edit { color: var(--cyan-ink); font-size: 10px; text-transform: uppercase; letter-spacing: .12em; font-weight: 800; }
.me-chip .pid { color: var(--dim); font-family: var(--mono); font-size: 11px; }

/* --- the shelf -----------------------------------------------------------
   Covers, sized by the grid rather than by the image, so art of the wrong
   dimensions is cropped instead of breaking the row.
   ------------------------------------------------------------------------ */

.shelf {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 18px;
  text-align: left;
}

.cover-card {
  padding: 0;
  border: 1.5px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--card);
  box-shadow: var(--shadow);
  overflow: hidden;
  cursor: pointer;
  font-family: inherit;
  color: var(--ink);
  text-align: left;
  display: flex;
  flex-direction: column;
  transition: transform .16s cubic-bezier(.2, .8, .25, 1), border-color .14s ease, box-shadow .18s ease;
  /* The stagger index, set by renderShelf() so the covers land one after another
     instead of all at once. */
  animation: cover-in .48s cubic-bezier(.2, .8, .25, 1) calc(var(--i, 0) * 55ms) both;
}
.cover-card:hover { border-color: var(--cyan-ink); transform: translateY(-5px); box-shadow: 0 14px 28px rgba(24, 30, 52, .13); }
.cover-card:active { transform: translateY(-1px); }
.cover-card:focus-visible { outline: 3px solid var(--cyan-ink); outline-offset: 3px; }
.cover-card.soon { cursor: default; }
.cover-card.soon:hover { transform: none; border-color: var(--line); box-shadow: var(--shadow); }

@keyframes cover-in {
  from { opacity: 0; transform: translateY(20px) scale(.97); }
  to { opacity: 1; transform: none; }
}

/* Square, held by aspect-ratio so the row stays even before the images decode.
   The art is an isolated object on transparency and several pieces are near-white, so
   the tile carries the game's accent behind it - on cream those would disappear. */
.cover-art {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--cream-2);
  overflow: hidden;
}
.cover-art img {
  width: 100%;
  height: 100%;
  /* contain, not cover: cropping an icon cuts the drawing, and on a square tile with
     square art the two are identical anyway. */
  object-fit: contain;
  padding: 7%;
  box-sizing: border-box;
  display: block;
  transition: transform .3s cubic-bezier(.2, .8, .25, 1);
}
/* The art leans in when you point at it. 3% is enough to feel and not enough to notice
   the padding shrinking. */
.cover-card:not(.soon):hover .cover-art img { transform: scale(1.045); }

/* A light band crosses the art on hover - one pass, left to right. It is a pseudo-element
   on the frame rather than a filter so it never touches the drawing itself. */
.cover-art::after {
  content: "";
  position: absolute;
  top: -20%;
  bottom: -20%;
  left: -60%;
  width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .5), transparent);
  transform: skewX(-14deg);
  pointer-events: none;
  opacity: 0;
}
.cover-card:not(.soon):hover .cover-art::after { animation: sheen .62s ease-out; }
@keyframes sheen {
  from { left: -60%; opacity: .9; }
  to { left: 130%; opacity: 0; }
}
.accent-cyan .cover-art, .accent-cyan .play-art { background: var(--cyan); }
.accent-lav .cover-art, .accent-lav .play-art { background: var(--lavender); }
.accent-mint .cover-art, .accent-mint .play-art { background: var(--mint); }

/* Shown when a cover is missing or fails to load, so a game with no art yet still
   looks like a deliberate card rather than a broken image. */
.cover-fallback {
  position: absolute;
  inset: 0;
  display: none;
  place-items: center;
  padding: 18px;
  text-align: center;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: .02em;
}
.cover-art.no-art .cover-fallback { display: grid; }
.cover-art.no-art img { display: none; }
.accent-cyan .cover-fallback { background: var(--cyan); color: #06222e; }
.accent-lav .cover-fallback { background: var(--lavender); color: #191d38; }

.cover-label { padding: 13px 15px 15px; border-top: 1.5px solid var(--line); }
.cover-label .who,
.play-info .who { font-size: 10.5px; text-transform: uppercase; letter-spacing: .14em; font-weight: 800; }
.accent-cyan .who { color: var(--cyan-ink); }
.accent-lav .who { color: var(--lavender-ink); }
.cover-label h3 { font-size: 17px; margin: 3px 0 4px; }
.cover-label p { margin: 0; font-size: 12.5px; color: var(--ink-2); }
.cover-badge {
  display: inline-block; margin-top: 8px;
  font-size: 10px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  padding: 4px 9px; border-radius: 999px;
  background: var(--cream-2); color: var(--dim);
}

/* --- play sheet ----------------------------------------------------------- */

.play-sheet { width: 100%; max-width: 780px; display: grid; gap: 16px; }

.play-card { display: grid; grid-template-columns: 240px 1fr; gap: 22px; align-items: start; }
.play-art {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--r);
  overflow: hidden;
  background: var(--cream-2);
  border: 1.5px solid var(--line);
}
.play-art img {
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 6%;
  box-sizing: border-box;
  display: block;
}
.play-art.no-art .cover-fallback { display: grid; }
.play-art.no-art img { display: none; }

.play-info h2 { font-size: 26px; margin: 4px 0 10px; text-transform: none; letter-spacing: -.01em; color: var(--ink); }
.play-info p { color: var(--ink-2); font-size: 14px; margin: 0 0 18px; }
.play-info .actions { display: grid; gap: 9px; }
.play-info .join-row { display: flex; gap: 8px; }
.play-info .join-row input { flex: 1; }

#input-code {
  text-align: center;
  letter-spacing: .3em;
  text-indent: .3em;
  text-transform: uppercase;
  font-family: var(--mono);
  font-size: 17px;
  font-weight: 700;
  color: var(--cyan-ink);
}

.rules { margin-top: 20px; text-align: left; }
.rules summary { cursor: pointer; color: var(--lavender-ink); font-size: 12px; letter-spacing: .12em; text-transform: uppercase; font-weight: 750; }
.rules summary:hover { color: var(--cyan-ink); }
.rules ol { color: var(--ink-2); font-size: 13.5px; padding-left: 20px; margin: 12px 0 4px; }
.rules li { margin-bottom: 8px; }
.rules[hidden] { display: none; }

/* Per-game rules on the play sheet: same block, no panel around it. */
.play-info .rules { margin-top: 16px; border-top: 1.5px solid var(--line); padding-top: 14px; }

/* How you actually win. The one line people scan for, so it gets its own box. */
.win-line {
  margin: 10px 0 0;
  padding: 10px 13px;
  border-radius: var(--r-sm);
  background: #e9fff6;
  border: 1.5px solid var(--mint-ink);
  color: #06392a;
  font-size: 13px;
  font-weight: 650;
}
.rules b { color: var(--ink); }

/* --- profile editor ------------------------------------------------------- */

.profile-edit { width: 100%; max-width: 540px; display: grid; gap: 15px; }

.avatar-preview { display: grid; place-items: center; gap: 12px; }
.avatar-preview .avatar { width: 112px; height: 112px; }
/* Sixteen wide characters is a legal name and wider than the column, so it wraps rather
   than pushing the panel out. */
.avatar-preview .nm { font-size: 20px; font-weight: 800; max-width: 100%; overflow-wrap: anywhere; text-align: center; }

/* --- the profile screen, in two columns -----------------------------------
   Choosing a skin or a colour is worthless if you cannot see what it did, and the pickers
   sit far enough down the page that the preview used to scroll away exactly when it
   mattered. So the preview gets its own column on the left and sticks to the top of it.
   It is sticky rather than fixed so it still scrolls out of the way at the end of the
   page, where there is nothing left to preview. */
.profile-split { max-width: 900px; }
.profile-main { display: grid; gap: 15px; min-width: 0; }
.profile-side { min-width: 0; }

@media (min-width: 800px) {
  .profile-split {
    grid-template-columns: 236px minmax(0, 1fr);
    gap: 18px;
    align-items: start;
  }
  .profile-side {
    position: sticky;
    /* Clears the fixed app bar, which would otherwise sit on top of it. */
    top: calc(var(--appbar-h) + 18px);
  }
  .avatar-preview .avatar { width: 132px; height: 132px; }
}

/* On a phone there is one column and no room for a 112px portrait pinned to the top, so
   the same panel turns into a slim bar: avatar beside name instead of above it. */
@media (max-width: 799px) {
  .profile-side {
    position: sticky;
    top: calc(var(--appbar-h) + 8px);
    z-index: 3;
  }
  .profile-side .avatar-preview {
    grid-template-columns: auto 1fr;
    place-items: center start;
    gap: 14px;
    padding: 12px 16px;
  }
  .avatar-preview .avatar { width: 60px; height: 60px; }
  .avatar-preview .nm { font-size: 17px; }
}

.mood-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }

.tile {
  background: var(--cream);
  border: 2px solid var(--line);
  border-radius: var(--r-sm);
  padding: 8px 6px 6px;
  cursor: pointer;
  display: grid;
  justify-items: center;
  gap: 5px;
  font-family: inherit;
  font-size: 10.5px;
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: .06em;
  font-weight: 700;
  transition: border-color .14s, transform .07s, background .14s;
}
.tile:hover { border-color: var(--cyan); }
.tile:active { transform: translateY(2px); }
.tile .avatar { width: 50px; height: 50px; }
.tile.on { border-color: var(--cyan-ink); background: #eaf8ff; color: var(--cyan-ink); }

/* 30+ swatches, so a dense grid rather than a row */
.swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(34px, 1fr)); gap: 8px; }
.swatch {
  aspect-ratio: 1;
  border-radius: 50%;
  border: 3px solid transparent;
  box-shadow: inset 0 0 0 1.5px rgba(26, 29, 40, .12);
  cursor: pointer;
  padding: 0;
  transition: transform .09s;
}
.swatch:hover { transform: scale(1.12); }
.swatch.on { border-color: var(--ink); }

/* --- settings ------------------------------------------------------------- */

.toggle {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  cursor: pointer;
  font-size: 14.5px;
  font-weight: 650;
  padding: 4px 0;
}
.toggle > span { flex: 1; display: grid; gap: 3px; }
.toggle em {
  font-style: normal;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--dim);
  line-height: 1.4;
}

/* Checkbox drawn as a switch, flat like everything else. */
.toggle input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 46px;
  height: 27px;
  margin: 2px 0 0;
  padding: 0;
  border: 1.5px solid var(--line-2);
  border-radius: 999px;
  background: var(--cream-2);
  cursor: pointer;
  position: relative;
  transition: background .16s, border-color .16s;
}
.toggle input[type="checkbox"]::after {
  content: "";
  position: absolute;
  top: 2px; left: 2px;
  width: 19px; height: 19px;
  border-radius: 50%;
  background: #fff;
  border: 1.5px solid var(--line-2);
  transition: transform .16s;
}
.toggle input[type="checkbox"]:checked {
  background: var(--cyan);
  border-color: var(--cyan-ink);
}
.toggle input[type="checkbox"]:checked::after {
  transform: translateX(19px);
  border-color: var(--cyan-ink);
}
.toggle input[type="checkbox"]:focus-visible { outline: 3px solid var(--cyan-ink); outline-offset: 2px; }

.rule { height: 1.5px; background: var(--line); margin: 14px 0; }

/* --- searching ------------------------------------------------------------ */

.searching { text-align: center; display: grid; gap: 18px; justify-items: center; }
.searching h2 { font-size: 22px; text-transform: none; letter-spacing: -.01em; }
.pulse-ring {
  width: 84px; height: 84px;
  border-radius: 50%;
  border: 5px solid var(--line-2);
  border-top-color: var(--cyan-ink);
  animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* --- role reveal ---------------------------------------------------------- */

.role-card { width: 100%; max-width: 480px; text-align: center; padding: 30px; }
.role-kicker { font-size: 11px; text-transform: uppercase; letter-spacing: .18em; color: var(--dim); font-weight: 750; }
.role-name { font-size: clamp(28px, 8vw, 44px); font-weight: 870; letter-spacing: -.02em; margin: 8px 0 4px; }
.role-name.imposter { color: var(--danger-ink); }
.role-name.guesser { color: var(--cyan-ink); }
.role-brief { color: var(--ink-2); font-size: 15px; margin: 14px 0 6px; }
.role-brief b { color: var(--ink); }
.role-rules { text-align: left; display: grid; gap: 8px; margin: 18px 0 22px; }
.role-rules div {
  background: var(--cream); border: 1.5px solid var(--line);
  border-radius: var(--r-sm); padding: 10px 14px; font-size: 13.5px; color: var(--ink-2);
}
.role-rules b { color: var(--ink); }
.role-countdown { font-family: var(--mono); font-size: 13px; color: var(--dim); }

/* --- lobby ---------------------------------------------------------------- */

.lobby { width: 100%; max-width: 470px; display: grid; gap: 14px; }

.code-card { text-align: center; }
.code-label { color: var(--lavender-ink); font-size: 10.5px; text-transform: uppercase; letter-spacing: .2em; font-weight: 750; }
.code {
  font-family: var(--mono);
  font-size: 48px;
  font-weight: 800;
  letter-spacing: .16em;
  text-indent: .16em;
  color: var(--cyan-ink);
  margin: 6px 0 12px;
}

.player-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.player-list li {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 13px;
  background: var(--cream);
  border: 1.5px solid var(--line);
  border-radius: 13px;
  font-size: 14px;
}
.player-list .avatar { width: 36px; height: 36px; }
.player-list .nm { font-weight: 700; }
.player-list .pid { color: var(--dim); font-family: var(--mono); font-size: 11px; }
.player-list .tag { margin-left: auto; font-size: 10px; color: var(--dim); text-transform: uppercase; letter-spacing: .1em; font-weight: 750; }
.player-list li.me { border-color: var(--cyan-ink); background: #f2fbff; }
.player-list li.off { opacity: .5; }
/* Seats whose identity the server is withholding until the round starts. */
.player-list li.anon { color: var(--dim); border-style: dashed; background: #fff; }
.player-list li.anon .dot {
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--cream-2); color: var(--dim);
  display: grid; place-items: center; font-size: 15px; font-weight: 800; flex: none;
}
.player-list li.anon .nm { font-weight: 650; }

.slot {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 13px;
  border: 1.5px dashed var(--line-2);
  border-radius: 13px;
  color: var(--dim);
  font-size: 13px;
}
.slot .dot {
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--cream-2);
  display: grid; place-items: center;
  font-size: 16px;
}

.slider { display: block; font-size: 11.5px; color: var(--dim); margin-bottom: 16px; text-transform: uppercase; letter-spacing: .1em; font-weight: 700; }
.slider output { color: var(--cyan-ink); float: right; font-family: var(--mono); font-weight: 800; }
.slider input[type="range"] {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 7px; margin-top: 9px; padding: 0; border: 0;
  border-radius: 999px;
  background: var(--cream-2);
  box-shadow: inset 0 0 0 1.5px var(--line-2);
}
.slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--cyan); border: 3px solid #fff;
  box-shadow: 0 0 0 1.5px var(--line-2);
  cursor: pointer;
}
.slider input[type="range"]::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--cyan); border: 3px solid #fff; cursor: pointer;
}

/* --- game ----------------------------------------------------------------
   The only pinned view: exactly one viewport tall, so the chat scrolls inside
   itself and the page never moves.
   ------------------------------------------------------------------------ */

.view-game {
  align-items: stretch;
  flex-direction: column;
  padding: 0;
  height: 100svh;
  min-height: 0;
  overflow: hidden;
}

.topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1.5px solid var(--line);
  background: #fff;
  flex: none;
  min-height: var(--topbar-h);
}
.topbar .mark { height: 30px; width: auto; }

.phase-pill {
  font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .14em;
  padding: 6px 12px; border-radius: 999px;
  color: var(--cyan-ink); background: #e4f6ff;
}
.phase-pill.vote { color: var(--warn-ink); background: #fff3d9; }

.role-pill {
  font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .14em;
  padding: 6px 12px; border-radius: 999px;
}
.role-pill.imposter { color: var(--danger-ink); background: #ffe9ef; }
.role-pill.guesser { color: var(--cyan-ink); background: #e4f6ff; }

.timer { font-family: var(--mono); font-size: 21px; font-weight: 800; font-variant-numeric: tabular-nums; }
.timer.low { color: var(--danger-ink); }

.you-chip { margin-left: auto; display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--dim); }
.you-chip .avatar { width: 28px; height: 28px; }
.you-chip b { color: var(--ink); }

.icon-btn {
  flex: none; width: 36px; height: 36px;
  display: grid; place-items: center; padding: 0;
  font-size: 15px; border-radius: 10px;
  background: #fff; color: var(--ink-2);
  border: 1.5px solid var(--line-2);
  cursor: pointer;
  transition: color .14s, border-color .14s, background .14s;
}
.icon-btn:hover { color: var(--cyan-ink); border-color: var(--cyan-ink); }
.icon-btn.off { color: #a09a90; }
.icon-btn.leave:hover { color: var(--danger-ink); border-color: var(--danger-ink); }

.game-grid { flex: 1; display: grid; grid-template-columns: 214px 1fr; min-height: 0; }
.game-grid.solo { grid-template-columns: 1fr; }

.roster { border-right: 1.5px solid var(--line); padding: 14px 12px; overflow-y: auto; background: #fff; }
.roster h3 { font-size: 10.5px; text-transform: uppercase; letter-spacing: .15em; color: var(--lavender-ink); margin-bottom: 11px; }
.roster ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.roster li {
  font-size: 13px; padding: 7px 9px; border-radius: 11px;
  background: var(--cream); border: 1.5px solid var(--line);
  display: flex; align-items: center; gap: 9px;
}
.roster li.me { border-color: var(--cyan-ink); background: #f2fbff; }
.roster li .avatar { width: 30px; height: 30px; }
.roster li .nm { font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.roster li.voted::after { content: "✓"; margin-left: auto; color: var(--warn-ink); font-weight: 800; }

.chat-col { display: flex; flex-direction: column; min-height: 0; }

.chat {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 16px;
  display: flex; flex-direction: column; gap: 10px;
  scrollbar-width: thin;
}
.chat::-webkit-scrollbar { width: 10px; }
.chat::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 9px; border: 3px solid var(--cream); }

.msg { display: flex; gap: 10px; align-items: flex-end; }
.msg .avatar { width: 32px; height: 32px; }
.msg .stack { max-width: min(74%, 520px); }
.msg .who { font-size: 11.5px; font-weight: 750; margin-bottom: 3px; padding-left: 2px; }
.msg .bubble {
  background: #fff;
  border: 1.5px solid var(--line);
  border-radius: 16px 16px 16px 5px;
  padding: 9px 14px;
  word-break: break-word;
}
.msg.mine { flex-direction: row-reverse; }
.msg.mine .stack { text-align: right; }
.msg.mine .who { padding-right: 2px; }
.msg.mine .bubble {
  background: #dff4ff;
  border-color: #a5dcf2;
  border-radius: 16px 16px 5px 16px;
  text-align: left;
}

.msg.sys { justify-content: center; color: var(--lavender-ink); font-size: 12px; font-weight: 650; padding: 4px 0; }
.msg.sys span { background: #f0f1ff; border: 1.5px solid #dcdffb; border-radius: 999px; padding: 5px 15px; }

.typing-row { height: 22px; padding: 0 16px; color: var(--dim); font-size: 12px; flex: none; }
.typing-row .dots span { display: inline-block; animation: blink 1.3s infinite; }
.typing-row .dots span:nth-child(2) { animation-delay: .18s; }
.typing-row .dots span:nth-child(3) { animation-delay: .36s; }
@keyframes blink { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }

.composer {
  display: flex; gap: 10px; padding: 12px 16px;
  border-top: 1.5px solid var(--line);
  background: #fff;
  flex: none;
}
.composer input { flex: 1; }

.action-bar {
  border-top: 1.5px solid var(--warn);
  background: #fffaf0;
  padding: 13px 16px;
  flex: none;
  max-height: 34vh;
  overflow-y: auto;
}
.action-bar-title { font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .15em; color: var(--warn-ink); margin-bottom: 10px; }
.vote-options { display: flex; gap: 9px; flex-wrap: wrap; }
.vote-chip {
  display: flex; align-items: center; gap: 8px;
  background: #fff; border: 2px solid var(--line-2);
  border-radius: 999px; padding: 6px 15px 6px 6px;
  cursor: pointer; font-family: inherit; font-size: 13.5px; font-weight: 700;
  color: var(--ink);
  transition: border-color .14s, transform .07s, background .14s;
}
.vote-chip .avatar { width: 30px; height: 30px; }
.vote-chip:hover { border-color: var(--warn); }
.vote-chip:active { transform: translateY(2px); }
.vote-chip.picked { border-color: var(--warn-ink); background: #fff3d6; }

.verdict-row { display: flex; gap: 12px; }
.verdict-row .btn { flex: 1; }
.waiting-note { color: var(--warn-ink); font-size: 13px; font-weight: 700; }

/* --- result --------------------------------------------------------------- */

.result { max-width: 560px; width: 100%; text-align: center; padding: 28px; }

.verdict-big { font-size: clamp(24px, 6.4vw, 36px); font-weight: 870; letter-spacing: -.02em; }
.verdict-big.win { color: var(--mint-ink); }
.verdict-big.lose { color: var(--danger-ink); }

.reveal { color: var(--ink-2); font-size: 14.5px; margin: 12px 0 20px; }
.reveal b { color: var(--ink); }

.reveal-card {
  display: inline-flex; align-items: center; gap: 13px;
  background: var(--cream); border: 1.5px solid var(--line);
  border-radius: var(--r); padding: 12px 20px 12px 12px;
  margin: 0 0 20px;
}
.reveal-card .avatar { width: 54px; height: 54px; }
.reveal-card .nm { font-size: 18px; font-weight: 820; text-align: left; }
.reveal-card .role { font-size: 10.5px; text-transform: uppercase; letter-spacing: .14em; font-weight: 800; text-align: left; }
.reveal-card .role.ai { color: var(--danger-ink); }
.reveal-card .role.human { color: var(--mint-ink); }

.tally { display: grid; gap: 9px; margin-bottom: 22px; text-align: left; }
.tally-row { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.tally-row .name { width: 130px; flex: none; color: var(--ink-2); font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tally-row .bar { height: 11px; border-radius: 6px; min-width: 6px; background: var(--cyan); }
.tally-row.was-ai .name { color: var(--danger-ink); }
.tally-row.was-ai .bar { background: var(--danger); }

.result-actions { display: grid; gap: 10px; justify-items: center; }

.transcript {
  margin-top: 20px; text-align: left; max-height: 240px; overflow-y: auto;
  border-top: 1.5px solid var(--line); padding-top: 14px;
  font-size: 12.5px; display: grid; gap: 5px;
}
.transcript .who { font-weight: 750; }
.transcript .ai-line { background: #fff0f4; border-left: 3px solid var(--danger); border-radius: 5px; padding: 3px 8px; }

/* --- module games: roles, targets, panels ---------------------------------
   Shared by every game that runs on a game module, so a new game inherits the
   whole look without adding CSS.
   ------------------------------------------------------------------------- */

.phase-pill.night { color: #2e3566; background: #e7e9ff; }

.role-tag {
  font-size: 9.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .1em;
  padding: 3px 7px; border-radius: 999px;
  background: var(--cream-2); color: var(--dim);
  flex: none;
}
/* Pushed to the far end of a roster row - unless a score is there, which goes last. */
.roster li .role-tag { margin-left: auto; }
.roster li .role-tag:not(:last-child) { margin-left: auto; margin-right: 0; }

/* A player's running score, beside the player it belongs to. */
.score-pip {
  margin-left: auto;
  flex: none;
  min-width: 22px;
  text-align: center;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  padding: 2px 7px;
  border-radius: 999px;
  background: #e4f6ff;
  color: var(--cyan-ink);
}
.roster li.me .score-pip { background: var(--cyan); color: #06222e; }
.roster li.dead .score-pip, .roster li.off .score-pip { background: var(--cream-2); color: var(--dim); }
.role-mafia { color: var(--danger-ink); background: #ffe9ef; }
.role-doctor { color: var(--mint-ink); background: #e3fff2; }
.role-detective { color: var(--cyan-ink); background: #e4f6ff; }
.role-citizen { color: var(--dim); background: var(--cream-2); }

.role-pill.role-mafia { color: var(--danger-ink); background: #ffe9ef; }
.role-pill.role-doctor { color: var(--mint-ink); background: #e3fff2; }
.role-pill.role-detective { color: var(--cyan-ink); background: #e4f6ff; }
.role-pill.role-citizen { color: var(--dim); background: var(--cream-2); }

.role-name.role-mafia { color: var(--danger-ink); }
.role-name.role-doctor { color: var(--mint-ink); }
.role-name.role-detective { color: var(--cyan-ink); }
.role-name.role-citizen { color: var(--ink); }

.role-partners { margin-top: 16px; padding-top: 14px; border-top: 1.5px solid var(--line); }
.partners-title { font-size: 10.5px; text-transform: uppercase; letter-spacing: .14em; font-weight: 800; color: var(--dim); margin-bottom: 6px; }
.role-partners .partner { font-weight: 800; margin-right: 12px; }

.roster li.dead { opacity: .45; }
.roster li.dead .nm { text-decoration: line-through; }
/* Knocked out, and it should read that way at a glance: faded, struck through, and no
   longer wearing a colour. The colour is what makes a player look present. */
.roster li.dead .avatar { filter: grayscale(1); }

/* A place in the leaderboard. Rendered only where the order is a real ranking, so the
   number always means what it looks like it means. */
.rank {
  flex: none;
  width: 16px;
  text-align: center;
  font-size: 11px;
  font-weight: 800;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}
.roster li:first-child .rank { color: var(--warn-ink); }
.roster li.me .rank { color: var(--cyan-ink); }

.game-panel { display: grid; gap: 10px; }
.panel-title { font-size: 11px; text-transform: uppercase; letter-spacing: .15em; font-weight: 800; color: var(--lavender-ink); }
.panel-note { margin: 0; font-size: 12.5px; color: var(--dim); }

.target-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.target {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 13px 7px 7px;
  font-family: inherit; font-size: 13px; font-weight: 650;
  background: #fff; color: var(--ink);
  border: 1.5px solid var(--line-2);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color .14s, background .14s, transform .07s;
}
.target:hover:not(:disabled) { border-color: var(--cyan-ink); }
.target:active:not(:disabled) { transform: translateY(2px); }
.target:disabled { opacity: .4; cursor: not-allowed; }
.target.picked { border-color: var(--danger-ink); background: #ffe9ef; }
.target .role-tag { margin-left: 2px; }
.count-pip {
  min-width: 20px; text-align: center;
  font-family: var(--mono); font-size: 11.5px; font-weight: 800;
  padding: 2px 6px; border-radius: 999px;
  background: var(--warn); color: #3a2a00;
}

.findings { border-top: 1.5px solid var(--line); padding-top: 10px; display: grid; gap: 5px; }
.findings-title { font-size: 10px; text-transform: uppercase; letter-spacing: .14em; font-weight: 800; color: var(--dim); }
.finding { display: flex; gap: 10px; font-size: 12.5px; }
.finding .nm { font-weight: 750; }
.finding.hit { color: var(--danger-ink); }
.finding.clear { color: var(--mint-ink); }

/* A line from a private channel. Tinted so it can never be mistaken for public. */
.msg.private .bubble { background: #fff0f4; border-color: var(--danger); }
.composer.private input { border-color: var(--danger); background: #fff8fa; }
.composer.muted input { background: var(--cream-2); }

.reveal-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 9px; margin: 4px 0 16px; }
.reveal-seat {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 11px; border-radius: 14px;
  background: var(--cream); border: 1.5px solid var(--line);
  text-align: left;
}
.reveal-seat.dead { opacity: .55; }
.reveal-seat .nm { font-weight: 750; font-size: 13.5px; }
.reveal-seat .role { font-size: 10px; text-transform: uppercase; letter-spacing: .12em; font-weight: 800; }
.reveal-seat.role-mafia { border-color: var(--danger); background: #fff0f4; }
.reveal-seat.role-mafia .role { color: var(--danger-ink); }
.reveal-seat.role-doctor .role { color: var(--mint-ink); }
.reveal-seat.role-detective .role { color: var(--cyan-ink); }
.reveal-seat.role-citizen .role { color: var(--dim); }

.timeline summary { cursor: pointer; color: var(--lavender-ink); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; font-weight: 800; }
.timeline-list { margin-top: 10px; display: grid; gap: 4px; font-size: 12.5px; color: var(--ink-2); text-align: left; }
.transcript .private-line { background: #fff0f4; border-left: 3px solid var(--danger); border-radius: 5px; padding: 3px 8px; }

/* Fake Insider's last move: four words, one of them real. */
.choice-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 9px; }
.btn.choice { text-transform: none; letter-spacing: 0; font-size: 16px; }

.guess-line {
  margin: -6px 0 14px;
  font-size: 13.5px;
  font-weight: 750;
  text-align: center;
}
.guess-line.right { color: var(--mint-ink); }
.guess-line.wrong { color: var(--dim); }

/* --- answers, statements, scoreboards -------------------------------------
   The submission games (Fake Insider, Bluff Masters, Unique Mind) and Alibi all
   draw from these, so they look like one site rather than four.
   ------------------------------------------------------------------------- */

.answer-box { display: grid; gap: 8px; }
.answer-form { display: flex; gap: 8px; }
.answer-form input { flex: 1; }
.answer-locked {
  padding: 12px 16px; border-radius: var(--r-sm);
  background: #eaf8ff; border: 1.5px solid var(--cyan-ink);
  color: var(--cyan-ink); font-weight: 750;
}

/* The prompt word, or the word that was being hidden. Deliberately huge. */
.big-word {
  font-size: clamp(30px, 7vw, 54px);
  font-weight: 880;
  letter-spacing: -.02em;
  line-height: 1.05;
  text-align: center;
  margin: 6px 0 14px;
  color: var(--ink);
}

.turn-order { display: flex; flex-wrap: wrap; gap: 6px; }
.turn-chip {
  font-size: 12px; font-weight: 700;
  padding: 5px 10px; border-radius: 999px;
  background: var(--cream-2); color: var(--dim);
}
.turn-chip.now { background: var(--cyan); color: #06222e; }
.turn-chip.done { opacity: .5; text-decoration: line-through; }

.hint-list, .answer-list { display: grid; gap: 5px; }
.hint-row, .answer-row {
  display: flex; align-items: center; gap: 10px;
  font-size: 13.5px;
  padding: 6px 10px; border-radius: 10px;
  background: var(--cream); border: 1.5px solid var(--line);
}
.hint-row .nm, .answer-row .nm { font-weight: 750; min-width: 84px; }
.hint-row.insider { border-color: var(--danger); background: #fff0f4; }
.answer-word { font-weight: 750; flex: 1; }
.answer-row .role-tag { margin-left: auto; }
.answer-row.good { border-color: var(--mint); background: #e9fff6; }
.answer-row.dup { border-color: var(--warn); background: #fff8e8; }
.answer-row.bad { border-color: var(--danger); background: #fff0f4; }

.fact-card {
  padding: 14px 16px; border-radius: var(--r-sm);
  background: #e9fff6; border: 1.5px solid var(--mint-ink);
  color: #06392a; font-size: 14.5px; font-weight: 650;
}

.card-list { display: grid; gap: 8px; }
.statement {
  display: flex; align-items: center; gap: 10px; text-align: left;
  width: 100%; padding: 12px 14px;
  font-family: inherit; font-size: 14px; line-height: 1.4;
  background: #fff; color: var(--ink);
  border: 1.5px solid var(--line-2); border-radius: var(--r-sm);
  cursor: pointer;
  transition: border-color .14s, background .14s, transform .07s;
}
.statement:hover:not(:disabled) { border-color: var(--cyan-ink); }
.statement:active:not(:disabled) { transform: translateY(2px); }
.statement:disabled { cursor: default; }
.statement.flat { cursor: default; }
.statement.picked { border-color: var(--cyan-ink); background: #eaf8ff; }
.statement.mine { border-style: dashed; opacity: .8; }
.statement.real { border-color: var(--mint-ink); background: #e9fff6; }
.statement-text { flex: 1; }

.scoreboard { border-top: 1.5px solid var(--line); padding-top: 10px; display: grid; gap: 4px; }
.score-row { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.score-row .nm { font-weight: 700; }
.score-row .score { margin-left: auto; font-family: var(--mono); font-weight: 800; }
.score-row.me { font-weight: 800; }
.score-row.out { opacity: .45; text-decoration: line-through; }

.checklist { display: grid; gap: 4px; font-size: 13px; color: var(--ink-2); }
.checklist div::before { content: "· "; color: var(--cyan-ink); font-weight: 800; }

.story-box {
  display: grid; gap: 4px; text-align: left;
  padding: 12px 14px; margin: 4px 0 14px;
  font-size: 13px; color: var(--ink-2);
  background: var(--cream); border: 1.5px solid var(--line); border-radius: var(--r-sm);
}

/* Badges over the cover art. */
.cover-solo {
  position: absolute;
  left: 8px;
  font-size: 9.5px;
  font-weight: 850;
  text-transform: uppercase;
  letter-spacing: .1em;
  padding: 4px 8px;
  border-radius: 999px;
}
.cover-solo {
  top: 8px;
  background: rgba(255, 255, 255, .92);
  color: var(--ink);
}

/* --- the guest pitch and the idea box ------------------------------------ */

.pitch {
  display: flex;
  align-items: center;
  gap: 18px;
  text-align: left;
  margin-top: 18px;
}
.pitch h3 { font-size: 17px; margin-bottom: 5px; }
.pitch p { font-size: 13.5px; color: var(--ink-2); margin: 0 0 12px; }
.pitch-art { display: flex; flex: none; }
/* Overlapped, so four avatars read as a set rather than a row of buttons. */
.pitch-art .avatar { margin-right: -12px; color: var(--cyan); }
.pitch-art .avatar:last-child { margin-right: 0; }

.idea-box { margin-top: 16px; text-align: left; }
.idea-box h2 { color: var(--lavender-ink); }

textarea {
  font-family: inherit;
  font-size: 14px;
  line-height: 1.5;
  padding: 12px 14px;
  border: 1.5px solid var(--line-2);
  border-radius: var(--r-sm);
  background: #fff;
  color: var(--ink);
  outline: none;
  resize: vertical;
  width: 100%;
}
textarea:focus { border-color: var(--cyan-ink); }

/* The level, on the profile chip in the app bar. */
.level-pip {
  flex: none;
  min-width: 22px;
  text-align: center;
  font-family: var(--mono);
  font-size: 11.5px;
  font-weight: 850;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--cyan);
  color: #06222e;
}

/* --- friends -------------------------------------------------------------
   A note that slides out of the right edge, with its own tab. One column: the
   list, and the conversation on top of it. A drawer this narrow has no room for
   a sidebar - and it means nobody has to be told to "pick a friend on the left".
   ------------------------------------------------------------------------ */

/* The tab, attached to the left edge of the note and OUTSIDE it, so it travels with the
   paper - pull the tab, the sheet comes with it. It sits in the app bar's place because the
   app bar is hidden inside a match, which is exactly when you want to invite somebody. */
.friends-tab {
  position: absolute;
  top: 50%;
  left: -40px;
  transform: translateY(-50%);
  width: 40px;
  height: 54px;
  display: grid;
  place-items: center;
  padding: 0;
  font-size: 19px;
  cursor: pointer;
  color: var(--ink-2);
  /* Same paper as the sheet, and no right border, so the two read as one piece. */
  background: #fffdf8;
  border: 1.5px solid var(--line-2);
  border-right: 0;
  border-radius: 14px 0 0 14px;
  box-shadow: -4px 0 14px rgba(26, 29, 40, .12);
  transition: color .14s, border-color .14s;
}
.friends-tab:hover { color: var(--cyan-ink); border-color: var(--cyan-ink); }
.friends-tab[hidden] { display: none; }
.friends-tab .unread-pip { position: absolute; top: -5px; left: -7px; }

.drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 41;
  background: rgba(26, 29, 40, .22);
}
.drawer-scrim[hidden] { display: none; }

.friends-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 42;
  width: min(360px, 100vw);
  display: flex;
  flex-direction: column;
  /* Paper: a warm white, a ruled edge, and lines behind the list. Flat - no gloss. */
  background:
    linear-gradient(var(--line) 0 0) no-repeat 26px 0 / 1.5px 100%,
    repeating-linear-gradient(#fffdf8 0 27px, #f6f1e6 27px 28px);
  border-left: 1.5px solid var(--line-2);
  box-shadow: -10px 0 30px rgba(26, 29, 40, .16);
  transform: translateX(100%);
  transition: transform .26s cubic-bezier(.2, .8, .2, 1);
  /* NOT overflow:hidden - that would clip the tab hanging off the left edge. The panes
     handle their own scrolling instead. */
}
.friends-drawer.open { transform: translateX(0); }
.friends-drawer[hidden] { display: none; }

/* Closed, the sheet is off-screen but still in the layout, because the tab lives inside it.
   Hiding the panes keeps their inputs out of the tab order while it is shut. */
.friends-drawer:not(.open) .drawer-pane { visibility: hidden; }

.drawer-pane { display: flex; flex-direction: column; min-height: 0; height: 100%; padding: 16px 16px 14px 40px; gap: 11px; overflow: hidden; }
.drawer-pane[hidden] { display: none; }

.drawer-head { display: flex; align-items: center; gap: 9px; }
.drawer-head h2 {
  flex: 1;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .15em;
  color: var(--lavender-ink);
  margin: 0;
}
.drawer-head .icon-btn { flex: none; width: 30px; height: 30px; font-size: 14px; }
.drawer-scroll { flex: 1; overflow-y: auto; display: grid; gap: 11px; align-content: start; padding-right: 2px; }

.friends-drawer .join-row { display: flex; gap: 8px; }
.friends-drawer .join-row input { flex: 1; padding: 10px 13px; font-size: 14px; }

.people { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.person, .request {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, .82);
  border: 1.5px solid var(--line);
  text-align: left;
}
.person { cursor: pointer; transition: border-color .14s, background .14s; }
.person:hover { border-color: var(--cyan-ink); background: #fff; }
.person.on { border-color: var(--cyan-ink); background: #f2fbff; }
.person-text { min-width: 0; flex: 1; }
.person .nm, .request .nm { font-weight: 750; font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.request { flex-wrap: wrap; }
.request .btn { flex: none; }

/* Presence. The colour carries the same meaning as the words, for a glance. */
.status { font-size: 11px; font-weight: 700; }
.on-playing { color: var(--danger-ink); }
.on-waiting { color: var(--warn-ink); }
.on-online { color: var(--mint-ink); }
.on-offline { color: var(--dim); font-weight: 600; }

.level-pip.sm { font-size: 9.5px; padding: 1px 5px; min-width: 0; }

.unread-pip {
  flex: none;
  min-width: 18px;
  padding: 1px 5px;
  border-radius: 999px;
  background: var(--danger-ink);
  color: #fff;
  font-size: 10.5px;
  font-weight: 850;
  text-align: center;
}

.chat-head { display: flex; align-items: center; gap: 9px; flex: 1; min-width: 0; flex-wrap: wrap; }
.chat-head .person-text { flex: 1; min-width: 90px; }
.chat-head .btn { flex: none; }

.dm-log {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-right: 2px;
}
.dm {
  max-width: 84%;
  padding: 9px 13px;
  border-radius: 16px 16px 16px 5px;
  background: #fff;
  border: 1.5px solid var(--line);
  font-size: 14px;
  line-height: 1.45;
  align-self: flex-start;
  word-break: break-word;
}
.dm.mine {
  align-self: flex-end;
  border-radius: 16px 16px 5px 16px;
  background: var(--cyan);
  border-color: #3e93b0;
  color: #06222e;
}
/* An invite is a message with a button in it. */
.dm.invite { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.friends-drawer .composer { display: flex; gap: 8px; }
.friends-drawer .composer input { flex: 1; padding: 11px 14px; font-size: 14px; }

/* --- friend groups --------------------------------------------------------
   One thread, several friends, and one room code instead of five. Drawn in the same drawer
   as the friend list because it is the same question: who can I play with right now. */

.drawer-section { display: grid; gap: 6px; }
.section-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.section-head .btn { flex: none; }

/* Three faces, overlapped, instead of a list of names. Reversed row direction so the FIRST
   avatar ends up on top without any of them needing a z-index. */
.faces { display: flex; flex-direction: row-reverse; flex: none; }
.faces .avatar { margin-left: -9px; box-shadow: 0 0 0 2px var(--card); }
.faces .avatar:last-child { margin-left: 0; }
.person.group .faces { margin-right: 2px; }

/* Who is in it. A panel between the head and the log, shown only when asked for, so the
   thread is not permanently paying screen space for a list that rarely changes. */
.member-panel {
  display: grid;
  gap: 6px;
  padding: 10px;
  margin-bottom: 8px;
  max-height: 45%;
  overflow-y: auto;
  background: var(--cream-2);
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
}
.member-panel .request { background: #fff; }
.member-panel .role-tag { margin-left: 6px; }

/* Who said it. A group bubble without a name on it is unreadable. */
.dm-who {
  display: block;
  font-size: 11px;
  font-weight: 800;
  margin-bottom: 2px;
  filter: brightness(.72) saturate(1.3); /* the avatar colours are pale by design */
}

/* Picking who is in a new group. */
.pick-list {
  display: grid;
  gap: 6px;
  max-height: 210px;
  overflow-y: auto;
  padding: 4px;
  background: var(--cream-2);
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
}
.pick {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 9px;
  border-radius: 10px;
  background: #fff;
  border: 1.5px solid var(--line);
  cursor: pointer;
  font-size: 13.5px;
  font-weight: 700;
}
.pick:hover { border-color: var(--cyan-ink); }
.pick input { width: 17px; height: 17px; accent-color: var(--cyan-ink); flex: none; }
.pick .avatar { width: 24px; height: 24px; flex: none; }
.pick:has(input:checked) { border-color: var(--cyan-ink); background: #f2fbff; }

/* --- levels, badges, unlocks --------------------------------------------- */

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

.level-badge {
  flex: none;
  width: 52px; height: 52px;
  display: grid; place-items: center;
  border-radius: 16px;
  background: var(--cyan);
  color: #06222e;
  font-size: 21px; font-weight: 880;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 4px 0 #3e93b0;
}

.level-meter { flex: 1; min-width: 0; }
.level-bar {
  height: 12px;
  border-radius: 999px;
  background: var(--cream-2);
  border: 1.5px solid var(--line);
  overflow: hidden;
}
.level-bar span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--cyan);
  border-radius: 999px;
  transition: width .5s cubic-bezier(.2, .8, .2, 1);
}
.level-text { font-size: 12px; color: var(--dim); margin-top: 6px; font-variant-numeric: tabular-nums; }

.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(74px, 1fr)); gap: 8px; }
.stat {
  text-align: center;
  padding: 10px 6px;
  border-radius: var(--r-sm);
  background: var(--cream);
  border: 1.5px solid var(--line);
}
.stat b { display: block; font-size: 19px; font-weight: 850; font-variant-numeric: tabular-nums; }
.stat span { font-size: 10.5px; text-transform: uppercase; letter-spacing: .1em; color: var(--dim); }

.badge-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px; }
.badge {
  padding: 9px 11px;
  border-radius: var(--r-sm);
  background: var(--cream);
  border: 1.5px solid var(--line);
  text-align: left;
}
.badge b { display: block; font-size: 12.5px; font-weight: 800; }
.badge span { font-size: 11px; color: var(--dim); }
.badge.earned { background: #e9fff6; border-color: var(--mint-ink); }
.badge.earned b { color: #06392a; }

.unlock-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 9px; }
.tile.unlock { position: relative; font-size: 11.5px; }
.tile.unlock .avatar { width: 52px; height: 52px; }
/* A locked avatar is shown, not hidden: you cannot want something you cannot see. */
.tile.unlock.locked { opacity: .55; cursor: not-allowed; background: var(--cream-2); }
.tile.unlock.locked .avatar { filter: grayscale(1) contrast(.85); }
.lock-note {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .1em; font-weight: 800;
  color: var(--dim);
}

/* --- rewards ------------------------------------------------------------- */

.reward-sheet { text-align: center; }
.reward-sheet .who { color: var(--mint-ink); }
.reward-xp { font-family: var(--mono); font-size: 15px; font-weight: 800; color: var(--cyan-ink); margin: 0 0 12px; }
.reward-item {
  display: flex; align-items: center; gap: 12px; text-align: left;
  padding: 11px; margin-bottom: 8px;
  border-radius: var(--r-sm);
  background: #e9fff6; border: 1.5px solid var(--mint-ink);
}
.reward-item .nm { font-weight: 800; }
.reward-note { font-size: 12px; color: var(--ink-2); }
.reward-sheet .badge-grid { text-align: left; margin-top: 6px; }
.reward-sheet .row { justify-content: center; }

/* The fireworks canvas is only in the document while something is going off.
   It is a popover so it lands in the browser's top layer, ABOVE an open modal dialog -
   a plain z-index, however high, loses to showModal() every time. */
.fireworks {
  position: fixed;
  inset: 0;
  z-index: 200; /* the fallback, for browsers without popover */
  pointer-events: none;
}
/* A popover comes with UA styles that would shrink this to fit-content and give it a
   border and a background. All of that has to go. */
.fireworks:popover-open {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  border: 0;
  padding: 0;
  background: transparent;
  overflow: hidden;
}
/* Fireworks going off behind a 38% dark backdrop are fireworks nobody sees. The reward
   card gets a lighter one so the celebration reads through it. */
.reward-sheet::backdrop { background: rgba(26, 29, 40, .18); }

.warn-note { color: var(--warn-ink); background: #fff8e8; border: 1.5px solid var(--warn); border-radius: var(--r-sm); padding: 9px 12px; }
#invite-link { font-size: 12.5px; font-family: var(--mono); }

/* --- toast --------------------------------------------------------------- */

.toast {
  position: fixed; top: 16px; left: 50%;
  transform: translateX(-50%);
  z-index: 99;
  background: #fff;
  border: 1.5px solid var(--danger);
  color: var(--danger-ink);
  padding: 12px 20px;
  border-radius: 12px;
  font-size: 13px;
  font-weight: 650;
  box-shadow: var(--shadow);
}
.toast[hidden] { display: none; }
/* Confirmations are not warnings - the default red styling would read as an error. */
.toast.good { border-color: var(--mint); color: var(--mint-ink); }

/* --- account row (settings) ---------------------------------------------- */

.account-row { display: flex; align-items: center; gap: 12px; }
.account-row .nm { font-weight: 750; }
.account-row .pid { font-size: 11.5px; color: var(--dim); font-family: var(--mono); }
.account-row .btn { margin-left: auto; }

/* --- report dialog ------------------------------------------------------- */

.sheet {
  width: min(460px, calc(100vw - 32px));
  border: 1.5px solid var(--line);
  border-radius: var(--r);
  padding: 22px;
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--shadow);
  font-family: var(--ui);
}
.sheet::backdrop { background: rgba(26, 29, 40, .38); }
.sheet h2 {
  font-size: 11px; text-transform: uppercase; letter-spacing: .15em;
  color: var(--danger-ink); margin-bottom: 8px;
}
/* The rules dialog leads with the game's name, so its heading is a title rather than
   the small uppercase label the report dialog uses. */
.sheet .sheet-title {
  font-size: 22px; text-transform: none; letter-spacing: -.01em;
  color: var(--ink); margin: 2px 0 4px;
}
.sheet .who { font-size: 10.5px; text-transform: uppercase; letter-spacing: .14em; font-weight: 800; color: var(--cyan-ink); }
.rules-body { margin-top: 6px; }
.rules-body ol { margin-top: 0; }
.sheet .hint { margin: 0 0 16px; }
.sheet .row { justify-content: flex-start; margin-top: 20px; }

.field { display: grid; gap: 8px; margin-bottom: 16px; }
.field-label { font-size: 11px; text-transform: uppercase; letter-spacing: .13em; font-weight: 750; color: var(--dim); }
.field-label em { font-style: normal; text-transform: none; letter-spacing: 0; }

.pick-row { display: flex; flex-wrap: wrap; gap: 7px; }
.pick {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 12px;
  font-family: inherit; font-size: 13px; font-weight: 650;
  background: var(--cream);
  border: 1.5px solid var(--line-2);
  border-radius: 999px;
  color: var(--ink-2);
  cursor: pointer;
  transition: border-color .14s, background .14s, color .14s;
}
.pick:hover { border-color: var(--danger-ink); color: var(--danger-ink); }
.pick.on { border-color: var(--danger-ink); background: #ffe9ef; color: var(--danger-ink); }
.pick .avatar { width: 22px; height: 22px; }

/* --- motion / mobile ----------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .pulse-ring, .typing-row .dots span { animation: none; }
  /* The landing page arrives all at once instead. Dropping the animation leaves each of
     these at its natural state - visible, unmoved - because the keyframes only apply
     while one is running. */
  .hero-mark, .hero-title, .hero .tagline, .hero-actions, .cover-card,
  .hero-demo, .demo-line, .demo-note, .demo-dots span { animation: none; }
  .cover-card:not(.soon):hover .cover-art::after { animation: none; }
  .cover-card:not(.soon):hover .cover-art img { transform: none; }
  .reveal, .reveal.shown { opacity: 1; transform: none; transition: none; }
  /* demo.js draws the whole exchange at once in this mode, so the log is a static block
     and the verdict has to be visible without the transition that normally reveals it. */
  .demo-log { height: auto; justify-content: flex-start; }
  .demo-verdict { opacity: 1; transform: none; transition: none; }
}

/* Set by the in-game "reduce motion" switch, on top of the OS preference. */
.no-motion *,
.no-motion *::before,
.no-motion *::after {
  animation-duration: .001s !important;
  animation-iteration-count: 1 !important;
  transition-duration: .001s !important;
}

@media (max-width: 720px) {
  .mood-grid { grid-template-columns: repeat(3, 1fr); }
  /* Cover beside text stops working around here; stack it and cap the art height so
     the buttons stay above the fold. */
  .play-card { grid-template-columns: 1fr; }
  .play-art { max-width: 210px; margin: 0 auto; }
}

@media (max-width: 640px) {
  :root { --appbar-h: 58px; }
  /* Room for the tab to stay on screen when the sheet is out. */
  .friends-drawer { width: calc(100vw - 46px); }
  .view { padding: calc(var(--appbar-h) + 18px) 14px 32px; }
  .brand-mark { height: 34px; }
  .brand-name { font-size: 15px; }
  .icon-btn.gear-lg { width: 42px; height: 42px; font-size: 20px; }
  /* Space is tight next to the brand, so the support button becomes just the cup and
     the profile chip becomes just the avatar - both still tap to the same place. */
  .app-bar .btn-kofi { padding: 9px 11px; font-size: 0; gap: 0; }
  .app-bar .btn-kofi .kofi-cup { font-size: 17px; }
  .me-chip { padding: 4px; gap: 0; }
  .me-chip .me-text, .me-chip .edit { display: none; }
  .game-grid { grid-template-columns: 1fr; }
  .roster { border-right: 0; border-bottom: 1.5px solid var(--line); padding: 8px 10px; max-height: 64px; }
  .roster h3 { display: none; }
  .roster ul { grid-auto-flow: column; grid-auto-columns: max-content; overflow-x: auto; }
  .roster li .nm { max-width: 68px; }
  .timer { font-size: 18px; }
  .topbar { gap: 7px; }
  /* Four icon buttons plus a timer does not fit a 360px topbar. The logo goes, the
     buttons shrink, and the "you are <name>" chip goes too - it is the one thing on
     that bar the player already knows. */
  .topbar .mark, .you-chip { display: none; }
  .topbar .icon-btn { width: 32px; height: 32px; font-size: 14px; }
  /* The hidden chip was what pushed the buttons right; the rules button takes over. */
  .topbar #btn-rules { margin-left: auto; }
  .topbar .icon-btn.gear-lg { width: 36px; height: 36px; font-size: 17px; }
  .phase-pill, .role-pill { padding: 5px 9px; letter-spacing: .1em; }
  /* Five buttons, a timer and two pills do not fit a phone. The pills give way rather
     than the controls: a long role name ellipsises, and below 480px the phase pill goes
     entirely - the panel under the chat already names the phase in bigger type. */
  .phase-pill, .role-pill { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .shelf { grid-template-columns: 1fr 1fr; gap: 12px; }
  .cover-label { padding: 10px 11px 12px; }
  .cover-label h3 { font-size: 15px; }
  .cover-label p { font-size: 11.5px; }
  .code { font-size: 38px; }
  .result { padding: 20px; }
  .tally-row .name { width: 94px; }
  .msg .stack { max-width: 82%; }
}

/* Narrow phones. The match topbar is the tightest thing on the site, so the phase pill
   gives way here - the panel under the chat already names the phase in bigger type. */
@media (max-width: 480px) {
  .topbar { gap: 6px; padding: 10px; }
  .topbar .phase-pill { display: none; }
}
