/* lounge -- "lit windows".

   The page's single job is answering "where is everybody", so presence is the
   visual grammar rather than an indicator bolted onto it: a room with someone
   in it is lit, a room with nobody in it is genuinely dim. That contrast is
   deliberately aggressive. Most apps render every card at equal brightness and
   put a small badge in the corner; here the brightness IS the badge, and an
   empty building should look like one.

   Warm dark rather than the usual blue-black, because this is a lounge and not
   a dashboard. The accent is lamplight. */

:root {
  --ink:       #16120f;   /* the page: near-black with brown in it */
  --panel:     #1f1a16;   /* a room with the light off */
  --panel-lit: #2a2018;   /* a room with someone in it */
  --line:      #33291f;
  --paper:     #efe6da;   /* warm off-white */
  --muted:     #9a8b7b;
  --dim:       #6b5f53;
  --glow:      #e8a33d;   /* lamplight */
  --glow-soft: rgba(232, 163, 61, 0.14);
  --bad:       #d97757;

  --shell: 60rem;
  --radius: 14px;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

/* `hidden` is an attribute, but any `display` rule beats it. Anything in here
   that toggles with el.hidden needs this or it never actually hides. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--ink);
  /* One faint pool of light behind everything, top-left, like a lamp that is
     already on when you walk in. */
  background-image: radial-gradient(80rem 40rem at 15% -10%, #241c15 0%, var(--ink) 60%);
  background-attachment: fixed;
  color: var(--paper);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.55;
}

a { color: var(--glow); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: #f5bd68; }

:focus-visible {
  outline: 2px solid var(--glow);
  outline-offset: 3px;
  border-radius: 4px;
}

.shell {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 2rem 1.25rem 5rem;
}

/* ---------------------------------------------------------------- masthead */

.top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-bottom: 1.25rem;
  margin-bottom: 2.25rem;
  border-bottom: 1px solid var(--line);
}

.wordmark {
  font-family: Fraunces, Georgia, serif;
  font-optical-sizing: auto;
  font-variation-settings: "SOFT" 40, "WONK" 1;
  font-weight: 600;
  font-size: 1.35rem;
  letter-spacing: -0.01em;
  color: var(--paper);
  text-decoration: none;
}
.wordmark:hover { color: var(--glow); }

/* lounge › Book club. Together on the left, so the name reads as where you
   are rather than floating in the middle of the bar. */
.where { display: flex; align-items: baseline; gap: 0.55rem; min-width: 0; }
.crumb { display: flex; align-items: baseline; gap: 0.55rem; min-width: 0; }
.crumb:empty { display: none; }
.crumb .sep { color: var(--dim); font-size: 1.1rem; }
.crumb a {
  font-family: Fraunces, Georgia, serif;
  font-variation-settings: "SOFT" 40;
  font-weight: 600; font-size: 1.2rem; letter-spacing: -0.01em;
  color: var(--paper); text-decoration: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 50vw;
}
.crumb a:hover { color: var(--glow); }
.crumb-icon { margin-right: 0.4rem; }

.top nav {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  font-size: 0.86rem;
}
.top nav a { color: var(--muted); text-decoration: none; }
.top nav a:hover { color: var(--paper); }

.whoami { display: flex; align-items: center; gap: 0.55rem; color: var(--muted); }

.linkish {
  background: none; border: 0; padding: 0;
  font: inherit; color: var(--muted); cursor: pointer;
}
.linkish:hover { color: var(--paper); }

/* -------------------------------------------------------------------- type */

h1 {
  font-family: Fraunces, Georgia, serif;
  font-variation-settings: "SOFT" 40, "WONK" 1;
  font-weight: 600;
  font-size: clamp(1.9rem, 5vw, 2.7rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0 0 0.4rem;
}

h2 {
  font-family: Fraunces, Georgia, serif;
  font-variation-settings: "SOFT" 40;
  font-weight: 600;
  font-size: 1.15rem;
  margin: 2.5rem 0 0.9rem;
}

.lede { color: var(--muted); margin: 0 0 2.25rem; max-width: 42ch; }

.eyebrow {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
}

/* ------------------------------------------------------------------- rooms */

.rooms { display: flex; flex-direction: column; gap: 0.85rem; }
/* What a table can deal, when that is a dozen things. A card table's shelf has
   grown past the point where one column fits on a screen, and a list you have
   to scroll to see the end of is a list whose last few games never get picked.
   Two columns where there is room for two, one where there isn't -- and the
   cards stay the same size either way, because they are the same cards. */
@media (min-width: 46rem) {
  .rooms-pair { display: grid; grid-template-columns: 1fr 1fr; column-gap: 0.85rem; }
  /* A grid track is as wide as its widest child unless it is told otherwise,
     and one long blurb would otherwise push the second column off the edge. */
  .rooms-pair > .room { min-width: 0; }
  /* Half the width is not enough for "blinds go up on a clock, last one
     standing" on one line, and a blurb cut off mid-sentence is worse than a
     taller card: what the game IS is the whole reason this list has blurbs. */
  .rooms-pair .room-blurb { white-space: normal; line-height: 1.35; }
}

/* The signature. `.lit` is set server-side from the room's own presence, so
   the building looks right in the very first paint rather than after a socket
   connects. */
.room {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  padding: 1.15rem 1.35rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  color: inherit;
  transition: background 320ms ease, border-color 320ms ease, box-shadow 320ms ease;
}

.room:hover { background: var(--panel-lit); border-color: #4a3a2a; }

.room.lit {
  background: var(--panel-lit);
  border-left-color: var(--glow);
  box-shadow: inset 0 0 3.5rem -1rem var(--glow-soft);
}

.room-body { flex: 1; min-width: 0; }

.room-name {
  font-family: Fraunces, Georgia, serif;
  font-variation-settings: "SOFT" 40;
  font-weight: 600;
  font-size: 1.2rem;
  color: var(--dim);
  transition: color 320ms ease;
}
.room.lit .room-name, .room:hover .room-name { color: var(--paper); }

.room-blurb {
  font-size: 0.88rem;
  color: var(--dim);
  margin-top: 0.15rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.room-count {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--dim);
  white-space: nowrap;
}
.room.lit .room-count { color: var(--glow); }

/* ------------------------------------------------------------------ people */

.faces { display: flex; align-items: center; }
.faces .face:not(:first-child) { margin-left: -0.6rem; }

.face {
  width: 2.1rem; height: 2.1rem;
  border-radius: 50%;
  border: 2px solid var(--panel-lit);
  background: #3a2d22;
  color: var(--paper);
  font-size: 0.8rem;
  font-weight: 700;
  display: grid; place-items: center;
  object-fit: cover;
  flex: none;
  overflow: hidden;
}
.room.lit .face { border-color: var(--glow); }

/* How present they are, by their own tab's account. Idle is dimmed, away is
   greyed: a face at half strength is somebody whose tab is open and who is
   not behind it, which is the honest picture. */
.face[data-state="idle"] { opacity: 0.6; }
.face[data-state="away"] { opacity: 0.35; filter: grayscale(1); }
.room.lit .face[data-state="away"] { border-color: var(--panel-lit); }
.tile[data-state] .tile-name { color: var(--muted); }
.tile-state { font-family: var(--mono); font-size: 0.68rem; color: var(--dim); font-weight: 500; }

/* The roster on a room page: bigger, named, one per line. */
.roster { display: flex; flex-direction: column; gap: 0.7rem; margin: 0; padding: 0; list-style: none; }

.person {
  display: flex; align-items: center; gap: 0.75rem;
  animation: arrive 340ms ease both;
}
.person .face { width: 2.4rem; height: 2.4rem; border-color: var(--glow); }
.person .name { font-weight: 600; }
.person .you { font-family: var(--mono); font-size: 0.7rem; color: var(--dim); }

@keyframes arrive {
  from { opacity: 0; transform: translateY(4px) scale(0.94); }
  to   { opacity: 1; transform: none; }
}

.empty {
  color: var(--dim);
  font-style: italic;
  padding: 0.4rem 0;
}

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

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.35rem 1.5rem;
}

.door {
  max-width: 27rem;
  margin: 14vh auto 0;
  text-align: center;
}
.door h1 { margin-bottom: 0.6rem; }
.door p { color: var(--muted); }

/* One button, one size.

   Size is not a way of saying how important a button is. Weight is -- filled
   for the answer, quiet for everything else -- and weight can say it without
   changing the shape of the row. This had drifted: primary actions were the
   full size and everything beside them was `small`, so a row of four controls
   came out as one big pill and three footnotes at two type sizes.

   The border is here rather than only on `.quiet` because a button with an
   outline and a button without one have to come out the same height, and with
   `border-box` and no fixed height they only do if both have the border. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 0.6rem;
  margin-top: 0;
  padding: 0.55rem 1.15rem;
  background: var(--glow);
  color: #1b1208;
  border: 1px solid transparent; border-radius: 999px;
  font: inherit; font-size: 0.92rem; font-weight: 700; line-height: 1.35;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: transform 140ms ease, background 140ms ease;
}
/* The one place a button is the whole page: the door. */
.door .btn { margin-top: 1.4rem; padding: 0.7rem 1.4rem; font-size: 1rem; }
.btn:hover { background: #f5bd68; color: #1b1208; transform: translateY(-1px); }
/* A button that cannot be pressed has to look like one. This was missing
   until a game turned up with a button that is off more often than on. */
.btn:disabled, .btn:disabled:hover {
  opacity: 0.35;
  cursor: not-allowed;
  transform: none;
  background: transparent;
  color: var(--paper);
  border-color: var(--line);
}
.btn.quiet {
  background: transparent; color: var(--paper);
  border-color: var(--line); font-weight: 600;
}
.btn.quiet:hover { background: var(--panel-lit); color: var(--paper); border-color: #4a3a2a; }

/* ------------------------------------------------------------------- forms */

form.row { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center; }

input[type=text], input[type=email], input[type=number], select {
  background: #140f0c;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--paper);
  font: inherit;
  padding: 0.55rem 0.7rem;
  min-width: 0;
}
input::placeholder { color: var(--dim); }

table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
th {
  text-align: left; font-family: var(--mono); font-size: 0.68rem;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--dim);
  font-weight: 400; padding: 0.5rem 0.6rem 0.5rem 0; border-bottom: 1px solid var(--line);
}
td { padding: 0.6rem 0.6rem 0.6rem 0; border-bottom: 1px solid var(--line); vertical-align: middle; }
td.mono, .mono { font-family: var(--mono); font-size: 0.82rem; }

.status {
  display: flex; align-items: center; gap: 0.5rem;
  font-family: var(--mono); font-size: 0.72rem; color: var(--dim);
  margin-top: 2rem;
}
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dim); flex: none; }
.dot.on { background: var(--glow); box-shadow: 0 0 0 3px var(--glow-soft); }
.dot.off { background: var(--bad); }

.notice {
  background: var(--glow-soft);
  border: 1px solid #4a3a2a;
  border-radius: var(--radius);
  padding: 0.9rem 1.1rem;
  margin-bottom: 1.5rem;
  font-size: 0.92rem;
}
.notice .mono { word-break: break-all; }

/* Narrow: the name gets the full width and the faces drop to their own line,
   rather than the name wrapping to make room for a count. A room is found by
   its name first. */
@media (max-width: 34rem) {
  .room { flex-wrap: wrap; gap: 0.5rem 0.8rem; }
  .room-body { flex: 1 0 100%; }
  .room-blurb { white-space: normal; }
  .faces { order: 2; }
  .room-count { order: 3; margin-left: auto; }
}



/* ---------------------------------------------------------------- the room */

/* A room is two columns: the room itself, and the talking about it. The stage
   is mostly empty today on purpose -- it is where the faces go in phase 4, and
   laying the room out around that now is cheaper than moving a working chat
   out of the middle of the page later. */
body.roomy .shell {
  max-width: 78rem;
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  padding-bottom: 1.5rem;
}
body.roomy #stage { flex: 1; min-height: 0; display: flex; }

.room-layout {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 21rem;
  gap: 2rem;
  transition: grid-template-columns 260ms ease;
}
/* Collapsed leaves the head visible as a rail, so the way back is where the
   way out was. */
.room-layout.chat-shut { grid-template-columns: minmax(0, 1fr) 3rem; }

/* min-height: 0 so its children can be SMALLER than their content. Without
   it a flex item's floor is its content, the stage grows past the viewport
   instead of dividing it, and anything inside asking for `flex: 1` -- the
   bulletin board, say -- gets its natural size and no more. */
.stage { display: flex; flex-direction: column; min-width: 0; min-height: 0; }

/* While somebody is sharing, faces shrink to thumbnails. You are looking at
   the slide; the faces are there to say who is in the room with you. This
   only applies while the tiles are the grid at all -- with nothing to watch
   they are already a strip, and the strip is smaller than this. */
.stage.sharing .tiles:not(.strip) { grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr)); }
.stage.sharing .tiles:not(.strip) .tile { aspect-ratio: 1; }
.stage.sharing .tiles:not(.strip) .tile .face { width: 2rem; height: 2rem; font-size: 0.8rem; }
.stage.sharing .tiles:not(.strip) .tile-name { font-size: 0.72rem; }

/* One tile per person. Sized and shaped for the video that replaces the face
   in phase 4, so that change is a swap rather than a re-layout. */
.tiles {
  flex: 1;
  min-height: 5rem;
  align-content: start;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: 0.9rem;
  list-style: none;
  margin: 1.25rem 0 0;
  padding: 0;
  min-height: 0;
  overflow-y: auto;
}
.tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.6rem;
  aspect-ratio: 4 / 3;
  padding: 0.75rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  animation: arrive 300ms ease both;
}
.tile .face { width: 3rem; height: 3rem; font-size: 1.1rem; border-color: var(--glow); }
.tile-name { font-size: 0.85rem; font-weight: 600; text-align: center; }

.stage-empty { flex: 1; display: grid; place-items: center; margin: 0; }

.stage-foot {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding-top: 1.1rem; margin-top: 1.1rem;
  border-top: 1px solid var(--line);
}
/* The status takes the slack so the mic stays left and Leave stays right. */
.stage-foot .status { margin-top: 0; flex: 1; }
.status.warn { color: var(--bad); }

/* ----------------------------------------------------------------- mic */

/* The one control in the room that has to be unmistakable at a glance, from
   across a kitchen, mid-sentence. Off is quiet and outlined; on is filled. */
.mic {
  display: inline-flex; align-items: center; gap: 0.55rem;
  padding: 0.6rem 1.1rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--paper);
  font: inherit; font-weight: 600;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease, box-shadow 200ms ease;
}
.mic:hover:not(:disabled) { border-color: #4a3a2a; background: var(--panel-lit); }
.mic:disabled { opacity: 0.6; cursor: default; }
.mic.live {
  background: var(--glow); border-color: var(--glow); color: #1b1208;
}
.mic.live:hover:not(:disabled) { background: #f5bd68; border-color: #f5bd68; }
/* A ring that breathes while you are actually making noise, so you can tell a
   live mic from a hot mic without looking at anyone else's tile. */
.mic.speaking { box-shadow: 0 0 0 4px var(--glow-soft); }

.mic-glyph { width: 1.05rem; height: 1.05rem; flex: none; }

/* --------------------------------------------------------------- tiles */

.tile { position: relative; transition: border-color 180ms ease, box-shadow 180ms ease; }
/* Who is talking. Without this a silent room and a broken room look the same,
   which is the single most confusing thing a voice UI can do. */
.tile.speaking {
  border-color: var(--glow);
  box-shadow: 0 0 0 1px var(--glow), inset 0 0 3rem -1.5rem var(--glow-soft);
}
.tile.speaking .face { box-shadow: 0 0 0 3px var(--glow-soft); }

/* The camera sits over the face rather than replacing it, so a camera going
   off is a fade back to a portrait rather than a hole in the grid. */
.tile-video {
  position: absolute; inset: 0;
  border-radius: inherit;
  overflow: hidden;
  opacity: 0;
  transition: opacity 220ms ease;
  pointer-events: none;
}
.tile.has-video .tile-video { opacity: 1; }
.tile-video video, .tile-video canvas { width: 100%; height: 100%; object-fit: cover; display: block; }
/* An unmirrored self-view is subtly upsetting -- you reach the wrong way. */
.tile-video video.mirrored { transform: scaleX(-1); }
/* A camera that is on says who it is better than a label does, and the
   label was sitting on somebody's chin. The name comes back the moment the
   camera goes off. */
.tile.has-video .tile-name { display: none; }
.tile.has-video .face { display: none; }
.tile.has-video { justify-content: flex-end; }

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

/* Contained, not cropped: a tile cropping a face is fine, a tile cropping a
   slide loses the half with the point on it. */
.screen {
  margin: 1.25rem 0 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: #0d0a08;
  cursor: zoom-in;
}
/* Capped, not filled. A 16:9 stage at full column width eats the whole room
   and pushes every face off the bottom -- and the faces are the reason it is a
   lounge rather than a projector. This is enough to see THAT someone is
   sharing and roughly what; fullscreen is where you read it. */
.screen-video {
  aspect-ratio: 16 / 9;
  max-height: 38vh;
  margin: 0 auto;
  background: #0d0a08;
}
.screen-video video { width: 100%; height: 100%; object-fit: contain; display: block; }
.screen figcaption {
  padding: 0.5rem 0.75rem;
  font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.06em;
  color: var(--dim);
  border-top: 1px solid var(--line);
}
.screen:hover figcaption { color: var(--muted); }

/* ------------------------------------------------------------ controls */

.controls { display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* Dimmed rather than disabled. A disabled button explains nothing; a dim one
   still takes the click that produces the explanation. */
.mic.dim { opacity: 0.45; }
.mic.dim:hover:not(:disabled) { opacity: 0.7; }

.tile-mic {
  position: absolute; top: 0.5rem; right: 0.5rem;
  color: var(--dim);
  display: grid; place-items: center;
}
.tile-mic .mic-glyph { width: 0.95rem; height: 0.95rem; }

/* ---------------------------------------------------------------- the chat */

.chat {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

/* Beside the room, the chat is never taller than the window, and stays put
   while the room scrolls. It used to stretch to the height of everything on
   the left -- tiles, board, furniture -- which put the box you type in a
   long scroll below the bottom of the screen.

   Pinned where it first sits, under the masthead (and the rail, when there
   is one), so it fits the window at the top of the page and still fits once
   the room has scrolled. */
@media (min-width: 52.01rem) {
  .room-layout > .chat {
    --chat-top: calc(8.4rem + var(--rail-h, 0px));
    position: sticky;
    top: var(--chat-top);
    align-self: start;
    height: calc(100dvh - var(--chat-top) - 1.5rem);
  }
}

.chat-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.5rem;
  padding: 0.75rem 0.5rem 0.75rem 1rem;
  border-bottom: 1px solid var(--line);
  flex: none;
}
.chat-head h2 {
  margin: 0; font-size: 0.8rem; font-family: var(--mono); font-weight: 400;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--dim);
}

.chat-toggle {
  position: relative;
  width: 2rem; height: 2rem; flex: none;
  display: grid; place-items: center;
  background: none; border: 1px solid var(--line); border-radius: 8px;
  color: var(--muted); font-size: 1.1rem; line-height: 1;
  cursor: pointer;
}
.chat-toggle:hover { color: var(--paper); border-color: #4a3a2a; }

/* The unread count on the toggle, for the case the bar cannot cover: you are
   looking right at the room, but the panel is shut. */
.pip {
  position: absolute; top: -0.4rem; right: -0.4rem;
  min-width: 1.15rem; height: 1.15rem; padding: 0 0.28rem;
  border-radius: 999px;
  background: var(--glow); color: #1b1208;
  font-family: var(--mono); font-size: 0.62rem; font-weight: 700;
  display: grid; place-items: center;
}

/* Collapsed: everything but the head folds away, and the head turns on its
   side so "CHAT" still reads down the rail. */
.chat-shut .chat-head {
  /* Reversed so the toggle sits above the label without moving it in the DOM,
     where it belongs after the heading. Which means flex-end is the top. */
  flex-direction: column-reverse;
  justify-content: flex-end;
  gap: 0.9rem;
  height: 100%;
  padding: 0.5rem 0.5rem 1rem;
  border-bottom: 0;
}
.chat-shut .chat-head h2 { writing-mode: vertical-rl; letter-spacing: 0.2em; }
.chat-shut .log,
.chat-shut .composer,
.chat-shut .chat-shut-out { display: none; }

.log {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.line { display: flex; gap: 0.65rem; align-items: flex-start; }
/* A run of lines from one person keeps the gutter but drops the repetition. */
.line-face { width: 1.9rem; flex: none; }
.line-face .face { width: 1.9rem; height: 1.9rem; font-size: 0.72rem; border-color: var(--panel); }
.line-body { min-width: 0; flex: 1; }

.line-head { display: flex; align-items: baseline; gap: 0.5rem; margin-bottom: 0.05rem; }
.line-name { font-weight: 700; font-size: 0.88rem; }
.line-time { font-family: var(--mono); font-size: 0.64rem; color: var(--dim); }

.line-text {
  /* Whatever they typed, including the newlines, but still wrapping and still
     breaking a pasted URL rather than widening the column for everyone. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-size: 0.93rem;
  color: var(--paper);
}

.composer { display: flex; gap: 0.5rem; padding: 0.75rem; border-top: 1px solid var(--line); flex: none; }
.composer input { flex: 1; padding: 0.6rem 0.8rem; border-radius: 999px; min-width: 0; }
.composer .btn { padding: 0.55rem 0.9rem; font-size: 0.85rem; }

.chat-shut-out {
  margin: 0; padding: 0.9rem 1rem;
  border-top: 1px solid var(--line);
  font-size: 0.85rem; color: var(--dim); font-style: italic;
  flex: none;
}

/* Narrow: one column, chat below the room rather than beside it. A drawer
   would hide one behind the other, and on a phone the roster is small enough
   that both fit. */
@media (max-width: 52rem) {
  body.roomy .shell { min-height: 0; }
  body.roomy #stage { display: block; }
  .room-layout, .room-layout.chat-shut { grid-template-columns: minmax(0, 1fr); gap: 1.25rem; }
  .chat { min-height: 22rem; }
  .chat-shut .chat { min-height: 0; }
  .chat-shut .chat-head { flex-direction: row; height: auto; padding: 0.75rem 0.5rem 0.75rem 1rem; }
  .chat-shut .chat-head h2 { writing-mode: horizontal-tb; letter-spacing: 0.14em; }
  .tiles { overflow: visible; }
}

/* ---------------------------------------------------------- gin rummy */

/* Cards look like cards. A grid of labelled rectangles is technically the
   same information and is unreadable at a glance across a table -- the whole
   point of a playing card is that you can take in ten of them at once. */
.card {
  position: relative;
  /* Sized off a variable so a place with less room than it would like -- the
     middle of the poker oval, which has eight chairs round it -- can ask for
     smaller cards without a second class and a second set of rules. */
  width: var(--card-w, 2.6rem);
  height: calc(var(--card-w, 2.6rem) * 1.385);
  flex: none;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 0.05rem;
  background: #f3ece2;
  color: #17110a;
  border: 1px solid rgba(0,0,0,0.25);
  border-radius: 6px;
  box-shadow: 0 2px 6px -2px rgba(0,0,0,0.6);
  font-family: var(--mono);
  line-height: 1;
  padding: 0;
}
.card.red { color: #a3322a; }
.card-r { font-size: 0.92rem; font-weight: 700; }
.card-s { font-size: 0.86rem; }

.card.small { width: 1.5rem; height: 2.1rem; border-radius: 4px; }
.card.small .card-r { font-size: 0.6rem; }
.card.small .card-s { font-size: 0.55rem; }

/* A back that reads as a back at a glance and at a thumbnail. */
.card.back {
  background: repeating-linear-gradient(45deg, #4a3a2a, #4a3a2a 3px, #3a2d22 3px, #3a2d22 6px);
  border-color: #5c4834;
}
.card.empty { background: none; border-style: dashed; border-color: var(--line); box-shadow: none; }

button.card { cursor: pointer; transition: transform 120ms ease, box-shadow 120ms ease; }
button.card:hover { transform: translateY(-4px); }
/* Held, not played. Lifted out of the hand the way you would hold one. */
button.card.on {
  transform: translateY(-10px);
  box-shadow: 0 8px 16px -4px rgba(0,0,0,0.7), 0 0 0 2px var(--glow);
}

.table {
  margin: 1rem 0 0;
  padding: 1rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.table-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding-bottom: 0.75rem; margin-bottom: 0.9rem;
  border-bottom: 1px solid var(--line);
  font-size: 0.85rem; color: var(--muted);
}
.table-head b { color: var(--paper); font-size: 1.05rem; margin-left: 0.3rem; }
.table-head .round { font-family: var(--mono); font-size: 0.7rem; color: var(--dim); }

.opp { display: flex; align-items: center; gap: 0.8rem; margin-bottom: 0.9rem; flex-wrap: wrap; }
.opp-name { font-size: 0.85rem; color: var(--muted); min-width: 7rem; }
/* Overlapped, because a hand is held not laid out. */
.fan { display: flex; }
.fan .card:not(:first-child) { margin-left: -0.75rem; }

.piles { display: flex; gap: 1rem; justify-content: center; margin-bottom: 1rem; }
.pile {
  display: flex; flex-direction: column; align-items: center; gap: 0.35rem;
  background: none; border: 0; padding: 0.4rem; border-radius: 10px;
  color: var(--dim); font: inherit;
}
.pile-n { font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.06em; }
.pile.live { cursor: pointer; color: var(--glow); }
.pile.live:hover { background: var(--panel-lit); }
.pile:disabled { opacity: 0.75; }

/* Your hand. The order in it is yours -- see the gin block near the end. */
.mine { display: flex; gap: 0.9rem; flex-wrap: wrap; padding: 0.6rem 0 0.3rem; }

.table-foot {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  padding-top: 0.9rem; margin-top: 0.6rem;
  border-top: 1px solid var(--line);
}
.dead { font-size: 0.8rem; color: var(--muted); }
.dead b { color: var(--paper); font-family: var(--mono); }
.turn { flex: 1; font-size: 0.85rem; color: var(--dim); }

.result {
  padding: 0.8rem 1rem; margin-bottom: 0.9rem;
  border-radius: var(--radius);
  background: var(--panel-lit);
  border: 1px solid var(--line);
  animation: dropIn 260ms ease both;
}
.result.won { border-color: var(--glow); }
.result p { margin: 0; font-size: 0.95rem; }
.result .detail { margin-top: 0.25rem; font-size: 0.82rem; color: var(--dim); }

/* Smaller by default on a phone -- but still through the variable, so that a
   place which has worked out its own size (the middle of the poker oval) is
   not silently overruled here. This rule quietly beat that one for a while,
   which is how five community cards came to hang off the baize. */
@media (max-width: 34rem) {
  .card {
    width: var(--card-w, 2.15rem);
    height: calc(var(--card-w, 2.15rem) * 1.395);
  }
}

/* -------------------------------------------------------------- arcade */

/* Wider than a reading column, so that on a tall window it is the HEIGHT that
   decides how big the game gets. The cabinet is portrait; a text-width column
   would cap it well below what the screen could show. */
body.arcading .shell { max-width: 52rem; }
/* Geometroids is landscape, so for that one the width IS the constraint and
   the column has to give it room. */
body.cab-geometroids .shell { max-width: 66rem; }

/* Tight, because the cabinet's scale is a whole number and every pixel spent
   above it can cost a whole step. */
.arcade-top { display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap; }
.arcade-top .eyebrow { margin: 0; }
h1.arcade-title { font-size: 1.5rem; margin: 0; }

.cabinet {
  display: flex; justify-content: center;
  padding: 0.5rem;
  margin: 0.75rem 0 0.6rem;
  background: #0a0806;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* The glow of a screen in a dark room, which is where these lived. */
  box-shadow: inset 0 0 6rem -3rem #8fd3ff;
}

.cab {
  display: block;
  /* Nearest-neighbour: a pixel game smoothed is a pixel game ruined. */
  image-rendering: pixelated;
  border-radius: 3px;
  outline: none;
  touch-action: none;
}
.cab:focus-visible { outline: 2px solid var(--glow); outline-offset: 4px; }

/* ...except where the game is drawn in lines rather than pixels. Geometroids
   scales by a fraction so that it can fill whatever column it is given, and
   nearest-neighbour on a fractionally scaled canvas is the worst of both. */
body.cab-geometroids .cab { image-rendering: auto; }

.cab-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  font-size: 0.72rem; color: var(--dim);
}
.cab-buttons { display: flex; gap: 0.5rem; }

/* On a phone the reading column's margins are the difference between a game
   and a postage stamp, so the cabinet takes the whole width and gives up the
   chrome that made it look like a cabinet. */
@media (max-width: 34rem) {
  .cabinet {
    margin-left: -1.25rem;
    margin-right: -1.25rem;
    padding: 0.25rem;
    border-left: 0;
    border-right: 0;
    border-radius: 0;
  }
}

/* Full screen: the cabinet becomes the screen, and the canvas sits centred on
   black with its own whole-number size unchanged. The chrome that makes it a
   cabinet on the page would just be a glowing box around a game here. */
.cabinet:fullscreen {
  width: 100vw; height: 100vh;
  padding: 0; margin: 0; border: 0; border-radius: 0;
  background: #000;
  box-shadow: none;
  align-items: center;
}
.cabinet:-webkit-full-screen {
  width: 100vw; height: 100vh;
  padding: 0; margin: 0; border: 0; border-radius: 0;
  background: #000; align-items: center;
}

.try {
  margin: 0.4rem 0 0;
  font-size: 0.78rem;
  color: var(--dim);
}
.try .linkish { color: var(--glow); font-size: 0.78rem; }
.try .linkish:hover { color: #f5bd68; text-decoration: underline; }
.warn-note { color: #ff8a3d; margin-left: 0.5rem; }
.dim-note { font-size: 0.68rem; margin-left: 0.5rem; opacity: 0.7; }

.arcade-card { border-left-color: #8fd3ff; }
.arcade-card .room-name { color: var(--paper); }

/* --------------------------------------------------------- the puzzle */

/* Four levels, easiest first. Warm, and distinguishable by lightness as well
   as hue, so the groups stay separable for anyone who does not see the hues
   the way I do. */
:root {
  --level-0: #d8a13f;   /* lightest */
  --level-1: #b0894a;
  --level-2: #7d6a8f;
  --level-3: #5a4a7a;   /* darkest */
}

body.puzzling .shell { max-width: 44rem; }

.solved { display: flex; flex-direction: column; gap: 0.5rem; margin: 1.5rem 0 0.75rem; }
.solved:empty { margin: 0; }

.group {
  padding: 0.75rem 1rem;
  border-radius: var(--radius);
  text-align: center;
  color: #17110a;
  animation: dropIn 260ms ease both;
}
.group strong { display: block; font-weight: 700; font-size: 0.95rem; }
.group span { font-size: 0.85rem; opacity: 0.75; }
.group.level-0 { background: var(--level-0); }
.group.level-1 { background: var(--level-1); }
.group.level-2 { background: var(--level-2); color: var(--paper); }
.group.level-3 { background: var(--level-3); color: var(--paper); }
/* The ones it got past you: shown, but not as a trophy. */
.group.missed { opacity: 0.45; }

.grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
  margin: 0.75rem 0 1rem;
}
.cell {
  aspect-ratio: 5 / 3;
  display: grid; place-items: center;
  padding: 0.3rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--paper);
  font: inherit; font-weight: 600;
  font-size: clamp(0.62rem, 2.1vw, 0.86rem);
  letter-spacing: 0.01em;
  text-align: center;
  overflow-wrap: anywhere;
  cursor: pointer;
  transition: background 120ms ease, transform 120ms ease;
}
.cell:hover { background: var(--panel-lit); }
.cell.on { background: var(--glow); border-color: var(--glow); color: #1b1208; transform: translateY(-2px); }

/* A wrong guess should be felt, not read. */
.grid.shake { animation: shake 380ms ease; }
@keyframes shake {
  10%, 90% { transform: translateX(-2px); }
  30%, 70% { transform: translateX(4px); }
  50% { transform: translateX(-4px); }
}

.puzzle-foot { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.lives {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-family: var(--mono); font-size: 0.68rem; color: var(--dim);
}
.life {
  width: 0.5rem; height: 0.5rem; border-radius: 50%;
  background: var(--glow);
  transition: background 200ms ease;
}
.life.gone { background: var(--line); }
.said { flex: 1; font-size: 0.85rem; color: var(--muted); font-style: italic; }

.outcome { margin: 1.25rem 0 0; }
.outcome p { margin: 0 0 0.6rem; font-weight: 600; }
.outcome .won { color: var(--glow); }
.outcome .lost { color: var(--muted); }
.share {
  margin: 0 0 0.6rem;
  font-family: var(--mono);
  font-size: 0.95rem;
  line-height: 1.35;
  white-space: pre-wrap;
}

.tabs { display: flex; gap: 0.4rem; margin-bottom: 0.9rem; }
.tab {
  padding: 0.35rem 0.8rem; border-radius: 999px;
  background: none; border: 1px solid var(--line);
  color: var(--muted); font: inherit; font-size: 0.82rem; cursor: pointer;
}
.tab:hover { color: var(--paper); }
.tab.on { background: var(--panel-lit); border-color: var(--glow); color: var(--paper); }

.board td.rank { color: var(--dim); width: 2rem; }
.board td.who { display: flex; align-items: center; gap: 0.5rem; }
.board .face { width: 1.6rem; height: 1.6rem; font-size: 0.65rem; }


/* -------------------------------------------------------------- ringing */

/* Top centre, over everything. The bar sits bottom-left because it is ambient;
   a ring is not ambient -- somebody is waiting for you to answer, and it has
   about forty-five seconds of your attention to earn. */
.rings {
  position: fixed;
  top: 1rem; left: 50%; transform: translateX(-50%);
  z-index: 40;
  display: flex; flex-direction: column; gap: 0.6rem;
  width: min(30rem, calc(100vw - 2rem));
}

.ring {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.7rem 0.8rem 0.7rem 0.9rem;
  background: var(--panel-lit);
  border: 1px solid var(--glow);
  border-radius: var(--radius);
  box-shadow: 0 14px 40px -12px rgba(0,0,0,0.8), inset 0 0 4rem -2rem var(--glow-soft);
  animation: dropIn 240ms ease both;
}
.ring.outgoing { border-color: var(--line); }
.ring .face { flex: none; }
.ring-what { flex: 1; min-width: 0; font-size: 0.9rem; color: var(--muted); }
.ring-what strong { color: var(--paper); font-weight: 600; }

.ring-clock {
  font-family: var(--mono); font-size: 0.68rem; color: var(--dim);
  margin-left: 0.4rem;
}

/* A ring should be visibly alive, so an unanswered one reads as still trying
   rather than as a stuck notification. */
.ring-pulse {
  width: 0.6rem; height: 0.6rem; border-radius: 50%;
  background: var(--glow); flex: none;
  animation: pulse 1.4s ease-in-out infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 var(--glow-soft); }
  50% { opacity: 0.5; box-shadow: 0 0 0 0.5rem transparent; }
}
@keyframes dropIn {
  from { opacity: 0; transform: translateY(-0.5rem); }
  to { opacity: 1; transform: none; }
}

/* --------------------------------------------------------- who to ring */

.picker {
  margin-top: 0.75rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  max-height: 14rem; overflow-y: auto;
}
.person-row {
  display: flex; align-items: center; gap: 0.7rem; width: 100%;
  padding: 0.55rem 0.8rem;
  background: none; border: 0; border-bottom: 1px solid var(--line);
  color: var(--paper); font: inherit; text-align: left; cursor: pointer;
}
.person-row:last-child { border-bottom: 0; }
.person-row:hover { background: var(--panel-lit); }
.person-row .face { width: 1.8rem; height: 1.8rem; font-size: 0.7rem; }
.person-name { flex: 1; font-size: 0.9rem; }
.person-where {
  font-family: var(--mono); font-size: 0.65rem; letter-spacing: 0.06em;
  color: var(--glow);
}
/* Still ringable -- they might have it open somewhere you cannot see -- but
   said plainly, so it is a decision rather than a surprise. */
.person-row[data-away] { opacity: 0.55; }
.person-row[data-away] .person-where { color: var(--dim); }

@media (max-width: 34rem) {
  .ring { flex-wrap: wrap; }
  .ring-what { flex: 1 0 100%; }
}

/* ---------------------------------------------------------------- the bar */

/* Lower left, like the Shire's call bar, and for the same reason: it is the
   one piece of the page that must not move or redraw when you navigate. It is
   the visible proof that you are still in a room while looking at another. */
.bar {
  position: fixed;
  left: 1.25rem;
  bottom: 1.25rem;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.75rem 0.6rem 1rem;
  background: var(--panel-lit);
  border: 1px solid #4a3a2a;
  border-radius: 999px;
  box-shadow: 0 8px 24px -8px rgba(0,0,0,0.6), inset 0 0 2rem -1rem var(--glow-soft);
  animation: arrive 260ms ease both;
}

/* The bar floats over the page, so the page has to leave it somewhere to
   float. Without this it lands on top of the composer -- the one control most
   likely to be at the bottom of a room. */
body.has-bar .shell { padding-bottom: 7.5rem; }

.bar-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--glow);
  box-shadow: 0 0 0 3px var(--glow-soft);
  flex: none;
}
.bar-text { font-size: 0.9rem; color: var(--muted); white-space: nowrap; }
.bar-text strong { color: var(--paper); font-weight: 600; }
.bar-faces { display: flex; }
.bar-faces .face { width: 1.6rem; height: 1.6rem; font-size: 0.65rem; border-color: var(--panel-lit); }
.bar-faces .face:not(:first-child) { margin-left: -0.45rem; }

/* Small is for a button sitting inside a line of something else -- in a strip
   of tiles, beside a filename, on a card's own corner. */
.btn.small { padding: 0.35rem 0.8rem; font-size: 0.82rem; }

/* ...and not for a row of controls, where it is the thing that made the row
   look wonky. These rows set their own size, so a `small` that drifted into
   one is quietly brought back into line rather than chased through the
   markup. */
.table-acts .btn.small,
.betbar .btn.small,
.bidbar .btn.small,
.stage-foot .btn.small,
.table-foot .btn.small,
.puzzle-foot .btn.small,
.asking .btn.small,
.ring .btn.small {
  padding: 0.55rem 1.15rem;
  font-size: 0.92rem;
}

/* The room you are standing in reads differently from one that merely has
   people in it -- "3 here" is information, "you're in" is orientation. */
.room-count { font-variant-numeric: tabular-nums; }

@media (max-width: 34rem) {
  .bar { left: 0.75rem; right: 0.75rem; bottom: 0.75rem; justify-content: space-between; }
  .bar-text { white-space: normal; }
}

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

.room-picks {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem;
  margin: 0.6rem 0 0;
  font-size: 0.8rem; color: var(--dim);
}
.room-picks label { display: inline-flex; align-items: center; gap: 0.3rem; }

/* --------------------------------------------------------- the furniture */

/* What is in a room, along the bottom of the stage. Along the bottom rather
   than the top because everything above it costs the thing you came to look
   at some height, and the furniture is how you leave, not what you came for. */
.furniture {
  display: flex; flex-wrap: wrap; gap: 0.4rem;
  margin: 0.8rem 0 0;
  padding-top: 0.8rem;
  border-top: 1px solid var(--line);
}
.furniture.empty { border-top: 1px solid var(--line); color: var(--dim); }
.furniture .thing {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.82rem;
  color: var(--paper);
  text-decoration: none;
  transition: border-color 0.15s, background 0.15s;
}
.furniture .thing:hover { border-color: var(--glow); }
.furniture .thing.on { border-color: var(--glow); background: rgba(255,255,255,0.05); }
.furniture .glyph { font-size: 1rem; line-height: 1; }
.furniture .pip {
  background: var(--glow); color: #1a1410;
  border-radius: 999px; padding: 0 0.35rem;
  font-size: 0.68rem; font-weight: 600;
}

/* A fixture standing open on the stage. It is what you came to look at, so it
   takes the room the faces were using and they become a filmstrip under it --
   the same trade the stage already makes when somebody shares a screen. */
.panel { flex: 1; min-width: 0; min-height: 0; overflow-y: auto; }
.panel > h2:first-child { margin-top: 0; }
.panel .arcade-top { display: none; }
body.fixtured .stage .tiles {
  flex: 0 0 auto;
  max-height: 5.5rem;
  margin-top: 0.9rem;
  overflow-x: auto;
}

/* ---------------------------------------------------------- the bulletin board */

/* The board fills whatever the room can spare. A bulletin board whose whole point
   is that you can see everything at once should not be a fixed rectangle with
   empty stage underneath it -- so the panel is a column, the board is the part
   that stretches, and the pinner sits above it at its own size. */
.panel.corky {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* The board is its own card -- it has a border, a radius and a surface. The
     panel around it was a second one, and its padding was costing forty pixels
     of cork to draw a frame around a frame. */
  padding: 0;
  background: none;
  border: 0;
  /* The floor is written TWICE on purpose, as the flex basis and as the
     minimum, and they have to be the same number.

     A flex item whose size is clamped upwards by its own min-height is frozen
     by the algorithm before any growing happens -- so `flex: 1` (basis zero)
     plus `min-height: 26rem` does not mean "at least that, then fill". It
     means exactly that, for ever, which is what this was doing. Start the
     basis AT the floor and nothing is clamped, nothing is frozen, and the
     free space gets shared out the way you meant. */
  /* A large appetite and a low floor, the way the jigsaw table has: take
     whatever the room can spare and still fit on a phone. The board used to
     write its basis and its floor as the same number, which gave it a HARD
     size -- right for a wall, where you were meant to see the whole of it at
     once, and wrong for a column you scroll: a scrolling thing wants to be as
     tall as the room allows and the reader does the rest. */
  flex: 1 1 40rem;
  min-height: 20rem;
}
.panel.corky > h2, .panel.corky > .status { flex: 0 0 auto; }
/* Basis AT the floor, so nothing is frozen -- see the note above. */
.panel.corky .cork { flex: 1 1 18rem; min-height: 18rem; }
.panel.corky .pinner { flex: 0 0 auto; }

/* The board is a column you scroll, newest at the top, with the days marked
   down it -- not a wall you pin things anywhere on. What survives from the
   wall is the paper: the tints, the tilt and the pinhead, because those are
   what make it a board rather than a list, and they cost nothing to keep. */
.cork {
  position: relative;
  min-height: 18rem;
  overflow-y: auto;
  padding: 1.2rem 1rem 1.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* Cork, roughly: a warm ground with some grain thrown over it. */
  background:
    radial-gradient(circle at 20% 30%, rgba(0,0,0,0.16) 0 2px, transparent 3px),
    radial-gradient(circle at 70% 60%, rgba(0,0,0,0.13) 0 2px, transparent 3px),
    radial-gradient(circle at 45% 85%, rgba(255,255,255,0.05) 0 2px, transparent 3px),
    #6b4f36;
  background-size: 37px 41px, 53px 47px, 61px 59px, auto;
  display: flex; flex-direction: column; align-items: center; gap: 1.2rem;
}
.cork > .empty { color: #e8dccb; opacity: 0.75; margin: 4rem 0; }

/* A day, ruled across the board. Sticky, because the whole point of scrolling
   down a board is to arrive somewhere in the past and want to know where. */
.cork .day {
  position: sticky; top: -0.4rem; z-index: 3;
  align-self: stretch;
  display: flex; align-items: center; gap: 0.7rem;
  margin: 0.2rem 0 -0.3rem;
  font-family: var(--mono); font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: #f0e4d2;
}
.cork .day::before, .cork .day::after {
  content: ""; flex: 1; height: 1px;
  background: rgba(0, 0, 0, 0.28);
}
/* The label is backed, because it is sticky: a card scrolls under it and the
   day has to stay readable while that happens. */
.cork .day span {
  padding: 0.2rem 0.7rem;
  border-radius: 999px;
  background: rgba(38, 26, 17, 0.82);
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.35);
}
.cork .day:first-child { margin-top: 0; }

.pin {
  position: relative;
  transform: rotate(var(--tilt, -1.5deg));
  width: min(20rem, 100%);
  padding: 0.6rem 0.6rem 0.35rem;
  border-radius: 2px;
  color: #2a2119;
  box-shadow: 0 6px 14px rgba(0,0,0,0.35);
}
/* The pinhead. */
.pin::before {
  content: ""; position: absolute; top: -5px; left: 50%;
  width: 10px; height: 10px; margin-left: -5px;
  border-radius: 50%;
  background: #c9403a;
  box-shadow: 0 1px 2px rgba(0,0,0,0.5);
}
.pin-body { font-size: 0.85rem; line-height: 1.35; white-space: pre-wrap; word-break: break-word; }
.pin .ink { display: block; width: 100%; height: auto; aspect-ratio: 1; }
.pin .ink path { fill: none; stroke: #3a2f26; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.pin footer {
  display: flex; align-items: center; gap: 0.3rem;
  margin-top: 0.4rem; padding-top: 0.3rem;
  border-top: 1px solid rgba(0,0,0,0.12);
  font-size: 0.68rem; opacity: 0.7;
}
.pin footer .face { width: 1rem; height: 1rem; font-size: 0.55rem; }
.pin footer .linkish { margin-left: auto; color: #2a2119; }

/* Six papers, and the tilt comes off the tint so a wall of notes is not a
   grid of perfectly square ones. */
.tint0 { background: #f3e6a8; --tilt: -1.6deg; }
.tint1 { background: #cfe8b8; --tilt:  1.4deg; }
.tint2 { background: #f6c9bc; --tilt: -0.7deg; }
.tint3 { background: #bfd9f0; --tilt:  2.1deg; }
.tint4 { background: #e6cdf0; --tilt: -2.3deg; }
.tint5 { background: #f5f2e6; --tilt:  0.8deg; }

/* Above the board, not under it: the invitation comes before the thing it is
   an invitation to. */
.pinner { margin: 0 0 0.7rem; display: flex; flex-direction: column; gap: 0.5rem; }
/* Shut, it is one button and the board keeps the rest. */
.pinner.shut { margin: 0 0 0.7rem; }
/* And that one button says what it is for rather than sitting there quietly. */
.pinner.shut .btn { width: 100%; justify-content: center; }
.pin-head { align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.pin-head .tabs { margin: 0; }
/* The actions go to the far end, so the row reads left to right as: what kind
   of thing, what colour, and then do it. */
.pin-acts { margin-left: auto; display: inline-flex; align-items: center; gap: 0.6rem; }
.pinner textarea {
  width: 100%; resize: vertical;
  font: inherit; font-size: 0.9rem;
}
.pinner .row { align-items: center; }
.padslot { display: flex; justify-content: center; }
.pad {
  width: 100%; max-width: 24rem; height: auto; aspect-ratio: 8 / 5;
  background: #f5f2e6;
  border-radius: 3px;
  touch-action: none;
  cursor: crosshair;
}
.tints { display: inline-flex; gap: 0.3rem; }
.tintbtn {
  width: 1.35rem; height: 1.35rem;
  border: 2px solid transparent; border-radius: 50%;
  padding: 0; cursor: pointer;
}
.tintbtn.on { border-color: var(--paper); }

/* A page heading inside a fixture is not a page heading. */
.panel h1 { font-size: 1.55rem; margin-top: 0; }
.panel .lede { font-size: 0.92rem; }

/* And neither is the room's own heading, once you are standing at something.
   You know which room you are in -- you walked here -- and every line above
   the fixture is height taken off the thing you came to look at. Worth about
   five rems of bulletin board. */
body.fixtured .stage > .lede { display: none; }
body.fixtured .stage > .panel { margin-top: 0.7rem; }

/* On a narrow screen the stage is not a fixed height, so "fills the room"
   means nothing and the board needs a size of its own -- given, as above, to
   the panel rather than to the board inside it. */
@media (max-width: 52rem) {
  .panel.corky { flex-basis: max(26rem, 76vh); min-height: 20rem; }
}

/* ------------------------------------------------------------- polaroids */

/* A white frame with a deep chin, the picture square whatever shape the
   photograph was, and the caption written on the chin. The proportions are
   the ones an SX-70 print actually has: the image is very nearly square and
   the bottom border is about twice the others. */
.pin.polaroid {
  padding: 0.55rem 0.55rem 0.3rem;
  background: #fbfaf6;
  box-shadow: 0 7px 16px rgba(0,0,0,0.42);
  --tilt: 1.2deg;
}
.pin.polaroid:nth-of-type(even) { --tilt: -2deg; }
.pin.polaroid:nth-of-type(3n) { --tilt: 0.6deg; }

.pin .shot {
  aspect-ratio: 1;
  background: #16120e;
  overflow: hidden;
}
.pin .shot img {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  cursor: zoom-in;
  -webkit-user-drag: none;
}

/* The chin. Tall whether or not anybody wrote on it -- an empty one is still
   what makes the shape read as a polaroid rather than a photo with a border. */
.polaroid .hand {
  min-height: 2.4rem;
  display: flex; align-items: center; justify-content: center;
  padding: 0.35rem 0.2rem 0.1rem;
  text-align: center;
  font-family: "Segoe Script", "Bradley Hand", "Snell Roundhand", "Apple Chancery", cursive;
  font-size: 0.9rem;
  line-height: 1.15;
  color: #2b2b3a;
  word-break: break-word;
}
.polaroid footer { border-top: 0; margin-top: 0; padding-top: 0; opacity: 0.55; }

/* Choosing one. */
.pickfile {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0.9rem 1.2rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--dim);
}
.pickfile:hover { border-color: var(--glow); color: var(--paper); }
.shotpick { display: flex; flex-direction: column; gap: 0.5rem; align-items: center; }
.shotpick img {
  max-height: 11rem; width: auto; max-width: 100%;
  border-radius: 3px;
  background: #16120e;
}
.shotpick input { width: 100%; max-width: 22rem; text-align: center; }

/* Looking at one properly. */
.lightbox {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center;
  padding: 2rem;
  background: rgba(8,6,4,0.88);
  cursor: zoom-out;
  animation: arrive 160ms ease both;
}
.lightbox img {
  max-width: min(92vw, 70rem); max-height: 88vh;
  width: auto; height: auto;
  border: 0.6rem solid #fbfaf6;
  border-bottom-width: 2.2rem;
  box-shadow: 0 18px 50px rgba(0,0,0,0.6);
  background: #16120e;
}
.lightbox .close {
  position: absolute; top: 1rem; right: 1.4rem;
  font-size: 2rem; line-height: 1; color: var(--paper);
}

/* The things pinned to a board shrink with it. A note at eleven rems is half
   the width of a phone: four of them are a pile rather than a board. Last in
   the file because the polaroid's own width is set further up, and these have
   the same specificity. */
@media (max-width: 34rem) {
  .pin { width: 8.5rem; padding: 0.5rem 0.5rem 0.3rem; }
  .pin-body { font-size: 0.78rem; }
  .pin.polaroid { width: 8.75rem; padding: 0.4rem 0.4rem 0.2rem; }
  .polaroid .hand { min-height: 1.9rem; font-size: 0.78rem; }
  .pin footer { font-size: 0.62rem; }
  .pin footer .face { width: 0.85rem; height: 0.85rem; }
}

/* The puzzle's clock, which sits at the end of the instructions rather than
   anywhere it could be stared at. It is there to be read afterwards, not to
   be raced against -- these are meant to be thought about. */
.clockbox {
  margin-left: 0.5rem;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.78rem;
  color: var(--dim);
}
.clockbox strong { color: var(--paper); font-weight: 600; }

/* ------------------------------------------------------------- euchre */

/* Four chairs on a narrow stage. Your partner across the top, the opposition
   either side of the cards on the table, your own hand along the bottom --
   which is where a hand is, and the one bit of table geometry worth keeping
   when there is no room for the rest of it. */
.table.euchre { display: flex; flex-direction: column; gap: 0.7rem; }

.tricks-taken {
  display: flex; align-items: center; justify-content: center; gap: 0.6rem;
  font-size: 0.78rem; color: var(--dim);
}

.chair {
  display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
  padding: 0.4rem 0.5rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  min-width: 0;
}
.chair.turn { border-color: var(--glow); background: rgba(255,255,255,0.03); }
.chair.out { opacity: 0.45; }
.chair-name { font-size: 0.8rem; font-weight: 600; white-space: nowrap; }
.chair-note { font-size: 0.68rem; color: var(--dim); }
.pipx {
  display: inline-block;
  border: 1px solid var(--line); border-radius: 3px;
  padding: 0 0.2rem;
  font-size: 0.6rem; color: var(--dim);
}
.pipx.maker { border-color: var(--glow); color: var(--glow); }

/* The middle of the table: the opposition on the flanks, the cards between. */
.crosswise {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);
  align-items: center;
  gap: 0.5rem;
}
.laid-out {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 0.5rem;
  min-height: 4.2rem;
  padding: 0.5rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}
.laid { display: flex; flex-direction: column; align-items: center; gap: 0.2rem; }
.laid-who { font-size: 0.62rem; color: var(--dim); }
.laid.won .card { outline: 2px solid var(--glow); outline-offset: 2px; }

.upcard {
  display: flex; align-items: center; justify-content: center; gap: 0.5rem;
  font-size: 0.75rem;
}

.yours { display: flex; justify-content: center; }
.fan.big { display: flex; gap: 0.35rem; flex-wrap: wrap; justify-content: center; }
.card.dim { opacity: 0.32; }

/* The bidding bar, which is the only place in this app where a checkbox and
   four buttons need to sit on one line and still make a sentence. */
.bidbar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.6rem;
  justify-content: center;
}
.bidbar .lede { margin: 0; font-size: 0.9rem; }
.alone {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-size: 0.78rem; color: var(--dim);
}
.suitbtn { font-size: 1.1rem; line-height: 1; padding: 0.3rem 0.7rem; }
.suitbtn.red { color: #e2564d; }
.trump.red { color: #e2564d; }

/* Without `align-items` the short buttons in here stretched to the tall one's
   height, which is how a row of controls ends up looking like a fence. */
.table-acts {
  display: flex; align-items: center; justify-content: center;
  gap: 0.6rem; flex-wrap: wrap;
}
.table-foot { text-align: center; }
.table-foot .status { justify-content: center; }

/* A game to choose, drawn like a room because it is the same decision. */
button.room.deal-card {
  border-left-color: var(--glow);
  text-align: left;
  font: inherit;
  cursor: pointer;
  width: 100%;
}

/* Bidding numbers, and the one the dealer may not say. */
.bids { display: inline-flex; flex-wrap: wrap; gap: 0.25rem; }
.bidnum { min-width: 2rem; padding: 0.25rem 0.4rem; }
.bidnum.barred { opacity: 0.3; text-decoration: line-through; cursor: not-allowed; }
.bagpip {
  display: inline-block;
  border: 1px solid var(--line); border-radius: 999px;
  padding: 0 0.3rem; margin-left: 0.2rem;
  font-size: 0.6rem; color: var(--dim);
}

/* Three, five or six people are a row rather than a diamond. */
.chairline {
  display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: center;
}
.chairline .chair { flex: 0 1 auto; }

/* Your own bid and tricks. Everybody else's sit under their name; without
   this yours were the only ones on the table you could not see. */
.mine-note { font-size: 0.72rem; color: var(--dim); }
.yours { flex-direction: column; align-items: center; gap: 0.3rem; }


/* ------------------------------------------------------------- hold'em */

/* Green baize, because a poker table is the one table in here that everybody
   pictures the same way. */
.table.felt {
  display: flex; flex-direction: column; gap: 0.7rem;
  padding: 0.9rem;
  border-radius: var(--radius);
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(255,255,255,0.05), transparent 60%),
    #14402f;
  border: 1px solid #0c2a1f;
}
.felt .table-head, .felt .table-foot { color: #cfe3d8; }
.felt .round b, .felt .score b { color: #fff; }

/* The table, as a table.

   Seats are placed on an ellipse by the markup (which knows how many there
   are); everything here is what a chair looks like once it is somewhere. */
.ring {
  position: relative;
  width: 100%;
  /* Everything inside is measured against THIS, not the window. The ring
     shares a row with the chat panel on a wide screen and has it to itself on
     a phone, so the viewport is not what decides whether five cards fit
     between eight chairs -- the ring is. */
  container-type: inline-size;
  /* Wider than tall, like a real one -- but not so wide that eight chairs
     have nowhere to be. */
  /* Taller than wide on a phone, because the thing that runs out there is
     vertical room for eight chairs rather than horizontal room for the
     baize. It becomes a proper oval as soon as there is width for one. */
  aspect-ratio: 0.95;
  margin: 0.2rem 0;
}
@media (min-width: 30rem) { .ring { aspect-ratio: 1.25; } }
@media (min-width: 40rem) { .ring { aspect-ratio: 1.55; } }

/* The baize inside the ring of chairs. Inset far enough that a chair sits on
   its rim rather than on top of it. */
.ring .cloth {
  position: absolute;
  inset: 22% 24%;
  /* The board measures itself against THIS rather than against the ring, so
     that "five cards fit on the cloth" is arithmetic rather than a number
     tuned against one screen: a card is a sixth of the baize, five of them
     plus the gaps is most of it, and that holds at any size. */
  container-type: inline-size;
  border-radius: 50%;
  background: radial-gradient(120% 120% at 50% 20%, #1b5741, #123a2b 70%);
  border: 1px solid rgba(0, 0, 0, 0.35);
  box-shadow: inset 0 0 22px rgba(0, 0, 0, 0.35);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 0.3rem;
  padding: 0.4rem;
}
.ring .pot {
  font-size: 0.72rem; color: #bcd8cb; min-height: 1rem;
  font-variant-numeric: tabular-nums;
}
.ring .pot b { color: #fff; }
/* The community cards can run out of oval on a narrow screen, and a board you
   cannot see all of is worse than one that overflows its cloth. */
.ring .board {
  background: none; padding: 0; min-height: 0;
  gap: 0.2rem; flex-wrap: nowrap;
  /* Five cards have to fit between eight chairs. On anything roomy they are
     the ordinary size; on a phone they give up what they have to and no
     more, which is still a great deal larger than a thumbnail. */
  --card-w: min(2.6rem, 17cqw);
  max-width: 100%;
}
.ring .board .card-r { font-size: min(0.92rem, 6cqw); }
.ring .board .card-s { font-size: min(0.86rem, 5.6cqw); }

/* A chair, centred on its point on the ellipse. */
.ring .pew {
  position: absolute;
  transform: translate(-50%, -50%);
  z-index: 1;
}
.ring .pew .felt-seat { min-width: 0; width: min(5.1rem, 23cqw); }
.ring .pew .chair-name { font-size: min(0.72rem, 3.4cqw); }
.ring .pew .stack { font-size: min(0.78rem, 3.4cqw); }
.ring .pew .pipx { font-size: 0.5rem; padding: 0 0.12rem; }
/* Every chair the same height, whatever is happening in it: the face-down
   pair and the chip are reserved rather than added, so the ring does not
   breathe every time somebody bets or folds. */
.ring .pew .wager.none { background: none; }
/* Yours reads as yours from across the room -- it is the one chair you never
   have to look for, so it is the one that says where "the bottom" is. */
.ring .pew .felt-seat.mine { color: #fff; }
.ring .pew .felt-seat.mine .chair-name { color: var(--glow); }

.felt-seat {
  display: flex; flex-direction: column; align-items: center; gap: 0.2rem;
  padding: 0.3rem 0.4rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  min-width: 5.5rem;
  color: #dfeee6;
  background: rgba(0, 0, 0, 0.25);
}
.felt-seat .chair-name {
  max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Where somebody is relative to the money. Not the same shape as each other
   on purpose: the button is a disc because it is a disc, and a blind is
   written because which one it is is the whole question. */
.marks { display: flex; gap: 0.2rem; align-items: center; min-height: 1rem; }
.mark {
  font-size: 0.55rem; font-weight: 700; line-height: 1;
  letter-spacing: 0.02em;
}
.mark.dealer {
  width: 1.05rem; height: 1.05rem; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: #f4efe4; color: #1c1a16;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.mark.blind {
  padding: 0.12rem 0.28rem; border-radius: 999px;
  background: rgba(0, 0, 0, 0.4); color: #bcd8cb;
  border: 1px solid rgba(255, 255, 255, 0.18);
}
.mark.blind.big { color: var(--glow); border-color: rgba(232, 163, 61, 0.55); }
.felt-seat.turn { border-color: var(--glow); background: rgba(255,255,255,0.06); }
.felt-seat.folded { opacity: 0.4; }
.felt-seat.busted { opacity: 0.28; }
.felt-seat .stack { font-size: 0.78rem; font-variant-numeric: tabular-nums; }
.felt-seat .wager {
  font-size: 0.7rem;
  /* A fixed box whether or not there is a chip in it, so a chair is the same
     size before and after somebody bets. */
  height: 1.05rem; line-height: 1.05rem;
  background: rgba(0,0,0,0.35);
  border-radius: 999px;
  padding: 0 0.45rem;
}

/* The community cards, which is what everybody is looking at. */
.board {
  display: flex; gap: 0.4rem; justify-content: center; align-items: center;
  min-height: 4.6rem;
  padding: 0.5rem;
  border-radius: var(--radius);
  background: rgba(0,0,0,0.18);
}

.betbar {
  display: flex; flex-wrap: wrap; gap: 0.5rem;
  align-items: center; justify-content: center;
}
.raiser { display: inline-flex; align-items: center; gap: 0.4rem; }
.raiser input {
  width: 6.5rem;
  font: inherit;
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------- the jukebox */

.records { list-style: none; margin: 0.6rem 0 0; padding: 0; }
.record {
  display: grid;
  grid-template-columns: 2rem minmax(0, 1fr) auto auto auto;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0.5rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
}
.record + .record { border-top: 1px solid var(--line); border-radius: 0; }
.record.on { border-color: var(--line); background: rgba(255,255,255,0.03); }
.record.going { border-color: var(--glow); background: rgba(255,255,255,0.05); }
.record .play { font-size: 0.9rem; }
.record-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.record .who { font-size: 0.68rem; }

/* A record that is going gets the one thing a jukebox has that a list does
   not: something moving. */
.record.going .record-name::after {
  content: " ♪";
  color: var(--glow);
  animation: bob 1.4s ease-in-out infinite;
  display: inline-block;
}
@keyframes bob { 50% { transform: translateY(-0.18rem); } }

.deck {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem;
  margin-top: 0.8rem;
}
.pickfile.small { padding: 0.45rem 0.8rem; font-size: 0.85rem; }

@media (max-width: 34rem) {
  .record { grid-template-columns: 1.8rem minmax(0, 1fr) auto; }
  .record .who { display: none; }
}

/* The deck: the picture with its chips over it, then the transport. */
.jb { margin: 0.6rem 0 0.4rem; }
.jb-stage { position: relative; aspect-ratio: 16 / 9; background: #000; border-radius: var(--radius); overflow: hidden; border: 1px solid var(--line); }
.jb-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.jb-nogl { position: absolute; inset: 0; display: grid; place-items: center; color: var(--muted); font-size: 0.85rem; padding: 1rem; text-align: center; }
.jb-top { position: absolute; left: 0; right: 0; top: 0; display: flex; justify-content: space-between; align-items: flex-start; gap: 0.5rem; padding: 0.5rem; opacity: 0.55; transition: opacity 160ms; }
.jb-stage:hover .jb-top, .jb-stage:focus-within .jb-top, .jb.big .jb-top { opacity: 1; }
.jb-scenes, .jb-tools { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.jb-chip {
  font: 600 0.7rem var(--mono); letter-spacing: 0.02em; text-transform: uppercase;
  padding: 0.3rem 0.55rem; border-radius: 999px; border: 1px solid rgba(255,255,255,0.18);
  background: rgba(0,0,0,0.45); color: var(--paper); cursor: pointer; backdrop-filter: blur(4px);
}
.jb-chip.on { background: var(--glow); color: #2b1d08; border-color: var(--glow); }
.jb-chip.lit { border-color: var(--glow); color: var(--glow); }
.jb-fx { position: absolute; right: 0.5rem; top: 2.6rem; width: min(16rem, 80%); background: rgba(20,14,10,0.9); border: 1px solid var(--line); border-radius: 10px; padding: 0.5rem 0.6rem; display: grid; gap: 0.3rem; backdrop-filter: blur(6px); }
.jb-fxrow { display: grid; grid-template-columns: 4.6rem 1fr; align-items: center; gap: 0.5rem; font: 600 0.7rem var(--mono); text-transform: uppercase; color: var(--muted); }
.jb-fxrow input { width: 100%; accent-color: var(--glow); }
.jb-fxclear { justify-self: end; }
.jb-now { position: absolute; left: 0; right: 0; bottom: 0; padding: 0.5rem 0.7rem; background: linear-gradient(to top, rgba(0,0,0,0.7), rgba(0,0,0,0)); display: flex; justify-content: space-between; gap: 0.6rem; pointer-events: none; }
.jb-title { font-weight: 600; text-shadow: 0 1px 2px #000; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.jb-by { font: 500 0.7rem var(--mono); color: var(--muted); white-space: nowrap; }
.jb-transport { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; flex-wrap: wrap; }
.jb-btn { font-size: 1rem; min-width: 2.4rem; padding: 0.4rem 0.6rem; border-radius: 10px; border: 1px solid var(--line); background: var(--panel); color: var(--paper); cursor: pointer; }
.jb-btn:disabled { opacity: 0.4; cursor: default; }
.jb-play { background: var(--glow); color: #2b1d08; border-color: var(--glow); min-width: 3rem; }
.jb-play:disabled { background: var(--panel); color: var(--paper); border-color: var(--line); }
.jb-time { font: 500 0.72rem var(--mono); color: var(--muted); min-width: 2.4rem; text-align: center; font-variant-numeric: tabular-nums; }
.jb-scrub { display: flex; align-items: center; gap: 0.4rem; flex: 1 1 12rem; min-width: 0; }
.jb-seek { flex: 1 1 6rem; min-width: 4rem; accent-color: var(--glow); }
.jb-vol { display: flex; align-items: center; gap: 0.3rem; font-size: 0.8rem; }
.jb-volume { width: 5rem; accent-color: var(--glow); }
.jb.big { position: fixed; inset: 0; z-index: 60; margin: 0; background: #000; display: flex; flex-direction: column; }
.jb.big .jb-stage { flex: 1; aspect-ratio: auto; border-radius: 0; border: none; }
.jb.big .jb-transport { padding: 0.5rem 0.8rem calc(env(safe-area-inset-bottom, 0px) + 0.5rem); margin: 0; background: #0a0806; }
html.jb-big { overflow: hidden; }
@media (max-width: 34rem) { .jb-vol { display: none; } .jb-scrub { flex-basis: 100%; } }

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

/* A chessboard is the one thing on this stage that has to be square, and the
   one thing that is worth as much room as the room can spare. So it is sized
   off the smaller of the two -- the panel's width or the height left over --
   which is what keeps it whole on a phone held upright and on a laptop held
   sideways, without either of them needing its own rule. */
.boarding {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.7rem;
}

.board-frame {
  width: min(100%, 30rem, 62vh);
  display: grid;
  grid-template-columns: 1.1rem minmax(0, 1fr);
  grid-template-rows: auto 1.1rem;
  gap: 0.25rem;
}

.board-frame .grid {
  grid-column: 2; grid-row: 1;
  aspect-ratio: 1;
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-template-rows: repeat(8, 1fr);
  border: 1px solid var(--line);
  border-radius: 4px;
  overflow: hidden;
  /* Everything inside a square is sized off the board rather than off the
     page, because the board is the thing that shrinks. `cqw` asks the board
     how wide it ended up, which is the only unit here that survives a phone,
     a laptop and a panel with a video call squeezing it from the side. */
  container-type: inline-size;
}

/* The coordinates. Small, dim and outside the board, because they are for
   the two minutes it takes to stop needing them. */
.board-frame .ranks, .board-frame .files {
  display: grid; font-family: var(--mono);
  font-size: 0.62rem; color: var(--dim);
}
.board-frame .ranks {
  grid-column: 1; grid-row: 1;
  grid-template-rows: repeat(8, 1fr);
  align-items: center; justify-items: center;
}
.board-frame .files {
  grid-column: 2; grid-row: 2;
  grid-template-columns: repeat(8, 1fr);
  align-items: center; justify-items: center;
}

.sq {
  display: flex; align-items: center; justify-content: center;
  padding: 0; margin: 0; border: 0;
  position: relative;
  font: inherit;
  aspect-ratio: 1;
}
.sq.light { background: #cbb99a; }
.sq.dark  { background: #7d6244; }
.sq.live { cursor: pointer; }
.sq.still { cursor: default; }

/* Where the last move came from and went. Faint, and underneath everything
   else, because it answers "what just happened" for whoever looked away and
   should never be the loudest thing on the board. */
.sq.last::before {
  content: ""; position: absolute; inset: 0;
  background: var(--glow); opacity: 0.16;
}

/* Somewhere you may go: a dot on an empty square, a ring around a piece you
   may take. Both drawn inside the square rather than as a border, so nothing
   moves by a pixel when it lights up. */
.sq.go::after, .sq.take::after {
  content: ""; position: absolute; pointer-events: none;
}
.sq.go::after {
  width: 26%; height: 26%; border-radius: 50%;
  background: rgba(20, 16, 12, 0.42);
}
.sq.take::after {
  inset: 4%; border-radius: 50%;
  border: 0.35rem solid rgba(217, 119, 87, 0.62);
}
.sq.picked { box-shadow: inset 0 0 0 3px var(--glow); }
.sq.live:hover { filter: brightness(1.08); }

/* The pieces. Sized in `cqw`-free terms -- the square is a grid cell of a box
   that is already a known size, so a percentage of the board is enough. */
.man {
  line-height: 1; pointer-events: none;
  display: flex; align-items: center; justify-content: center;
}
.man.chess {
  font-size: 9cqw;               /* about three quarters of a square */
  /* One glyph set for both colours. The outline pieces disappear on a light
     square in half the fonts anybody has installed, so white is the solid
     glyph painted white with a dark edge, and black is the same glyph
     painted dark. */
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
}
.man.chess.w { color: #f6efe2; }
.man.chess.b { color: #1a1512; }

.man.disc {
  width: 72%; height: 72%; border-radius: 50%;
  box-shadow: inset 0 -0.12rem 0.2rem rgba(0, 0, 0, 0.35),
              0 0.08rem 0.2rem rgba(0, 0, 0, 0.4);
}
.man.disc.w { background: linear-gradient(#f6efe2, #cdbfa8); }
.man.disc.b { background: linear-gradient(#3a302a, #16120f); }
/* A crown, which is what the two stacked men actually are. */
.man.disc.crowned::after {
  content: "♛"; font-size: 5cqw; line-height: 1;
  opacity: 0.8;
}
.man.disc.w.crowned::after { color: #6b5f53; }
.man.disc.b.crowned::after { color: #cbb99a; }
.man.disc.crowned {
  display: flex; align-items: center; justify-content: center;
}

/* Who is which colour, and the running score. */
.board-sides {
  display: flex; gap: 1.2rem; flex-wrap: wrap; justify-content: center;
}
.board-sides .side {
  display: flex; align-items: center; gap: 0.45rem;
  font-size: 0.9rem; color: var(--muted);
}
.board-sides .side.to-play { color: var(--paper); }
.board-sides .chip {
  width: 0.7rem; height: 0.7rem; border-radius: 50%;
  border: 1px solid var(--line); flex: none;
}
.board-sides .chip.w { background: #f6efe2; }
.board-sides .chip.b { background: #1a1512; }
.board-sides .score { font-family: var(--mono); color: var(--dim); }

.board-say { margin: 0; color: var(--muted); text-align: center; }
.board-say.done { color: var(--glow); }

.promote {
  display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;
  justify-content: center; color: var(--muted); font-size: 0.9rem;
}
.promote .btn { font-size: 1.2rem; line-height: 1; padding: 0.3rem 0.6rem; }

/* A button that has said what it costs. Laid out in a row with the reason
   first, because the reason is the whole point of the second click. */
.asking {
  display: inline-flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
  justify-content: center;
}
.asking .why { color: var(--muted); font-size: 0.85rem; }
.asking .why:empty { display: none; }
.btn.warn {
  background: transparent;
  border-color: var(--bad);
  color: var(--bad);
  font-weight: 600;
}
.btn.warn:hover { background: rgba(217, 119, 87, 0.12); border-color: var(--bad); }

/* What else this table, board or console could be running. Quiet: it is a
   way out of a decision, not the decision -- so it reads as a row of small
   labels under the thing it changes, with the current one marked and inert. */
.switcher {
  display: flex; flex-wrap: wrap; gap: 0.35rem;
  align-items: center; justify-content: center;
  margin-top: 0.9rem;
}

/* A pick is a pick wherever it turns up: one of a set, with the current one
   marked. This was written scoped to `.switcher`, which meant the four other
   places that reached for it -- the tile sorts, the jigsaw's zooms and piece
   counts, The Last Word's target list -- came out as bare browser buttons
   sitting in a row of pills. */
.pick {
  font-family: var(--mono); font-size: 0.72rem;
  letter-spacing: 0.04em;
  padding: 0.3rem 0.65rem; border-radius: 999px;
  border: 1px solid var(--line);
  background: transparent; color: var(--muted);
  text-decoration: none; cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
}
a.pick:hover, button.pick:not([disabled]):hover {
  color: var(--paper); border-color: #4a3a2a; background: var(--panel-lit);
}
.pick.on {
  color: var(--glow); border-color: var(--glow);
  background: var(--glow-soft); cursor: default;
}
.pick[disabled] { opacity: 0.4; cursor: not-allowed; }

/* ----------------------------------------------- the room, furniture first */

/* A room used to open on a grid of faces with the furniture as a strip along
   the bottom, which had it backwards: most of the time a room is empty or
   nearly, and a wall of initials is not what anybody walked in to look at.
   So the furniture is the page, and the faces are the size of the fact they
   are reporting. The moment a camera or a screen comes on that stops being
   true and the tiles take the room back -- which is why this is a variant of
   the same markup rather than a second set of it: a stream is placed into a
   slot that has to survive the switch. */
.tiles.strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem;
  flex: none;
  overflow: visible;
  min-height: 0;
  margin-top: 1.1rem;
}
.tiles.strip .tile {
  aspect-ratio: auto;
  flex-direction: row;
  gap: 0.5rem;
  padding: 0.3rem 0.75rem 0.3rem 0.35rem;
  border-radius: 999px;
  position: relative;
}
.tiles.strip .tile .face { width: 1.75rem; height: 1.75rem; font-size: 0.72rem; }
.tiles.strip .tile-name { font-size: 0.8rem; }
.tiles.strip .tile-mic { position: static; }
.whos-here { margin: 1.4rem 0 0; font-size: 0.85rem; }

/* The furniture as cards rather than as a strip of tabs. Two across where
   there is room, because six things one under another is a list and this is
   meant to read as a room with things in it. */
.things {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: 0.7rem;
  margin-top: 1.3rem;
}
/* What a thing is doing is the reason the card is there, so it is allowed a
   second line rather than being cut off with dots. */
.thing-card .room-blurb { white-space: normal; overflow: visible; }
.thing-card {
  gap: 0.9rem;
  padding: 0.9rem 1.1rem;
  align-items: center;
}
.thing-card .room-name { font-size: 1rem; }
.thing-card .room-blurb { margin-top: 0.15rem; }
.thing-glyph {
  font-size: 1.35rem;
  line-height: 1;
  width: 2.4rem; height: 2.4rem;
  flex: none;
  display: grid; place-items: center;
  border-radius: 10px;
  background: var(--panel-lit);
  border: 1px solid var(--line);
}
.thing-card:hover .thing-glyph { border-color: #4a3a2a; }

/* Nothing on the stage wants the leftover height when the furniture is the
   point, so the foot sits under the content rather than at the bottom of the
   window. A page that ends where it ends. */
.stage.furnished .tiles { flex: none; }

/* A room card wraps on a narrow screen so the room's NAME gets the full
   width -- a room is found by its name. A furniture card is found by its
   icon, which is next to the name and has to stay there, or seven of them
   become a very long page. */
@media (max-width: 34rem) {
  .thing-card { flex-wrap: nowrap; padding: 0.75rem 0.9rem; }
  .thing-card .room-body { flex: 1 1 auto; min-width: 0; }
  .thing-glyph { width: 2.1rem; height: 2.1rem; font-size: 1.15rem; }
}

/* ------------------------------------------------------- reading the table */

/* Where everybody stands. Sorted, because the question is "where am I" and a
   row in seat order makes you sort it yourself. */
.standings {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 0.3rem 0.5rem;
  list-style: none; margin: 0.5rem 0 0; padding: 0;
}
.standings li {
  display: flex; align-items: baseline; gap: 0.35rem;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--line); border-radius: 999px;
  font-size: 0.8rem; color: var(--muted);
}
.standings .place { font-family: var(--mono); font-size: 0.68rem; color: var(--dim); }
.standings .score { font-family: var(--mono); color: var(--paper); }
.standings li.mine { border-color: #4a3a2a; color: var(--paper); }
.standings li.ahead .score { color: var(--glow); }

/* The hand so far, street by street. Quiet and dense: it is there to be
   glanced at between decisions, not read. */
.digest {
  margin: 0.7rem 0 0;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.16);
  font-size: 0.78rem;
  max-height: 8.5rem;
  overflow-y: auto;
}
.digest .street {
  display: flex; gap: 0.6rem; align-items: baseline;
  padding: 0.16rem 0;
}
.digest .street + .street { border-top: 1px solid rgba(255, 255, 255, 0.05); }
/* The street being played, which is at the top and is why anybody looks. */
.digest .street.now .street-did { color: var(--paper); }
.digest .street.now .street-name { color: var(--muted); }
.street-name {
  flex: 0 0 6.2rem;
  display: flex; align-items: center; gap: 0.15rem;
  font-family: var(--mono); font-size: 0.68rem;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--dim);
}
.street-did { display: flex; flex-wrap: wrap; gap: 0.2rem 0.5rem; color: var(--muted); }
/* Somebody's whole turn, kept together -- "Ada raises to 60" should never
   wrap between the name and the number. */
.digest .did { white-space: nowrap; }
.digest .did + .did::before { content: "·"; color: var(--dim); margin-right: 0.5rem; }
.digest .did.out { color: var(--dim); }
.digest .did b { color: var(--glow); font-weight: 600; }
.digest .card.small { width: 1.15rem; height: 1.6rem; font-size: 0.6rem; }

@media (max-width: 34rem) {
  .digest .street { flex-direction: column; gap: 0.1rem; }
  .street-name { flex: none; }
}

/* ------------------------------------------------------ the writing game */

/* Scattergories. A letter you cannot miss, twelve lines to fill in, and a
   clock. Everything else on the page is in the way. */
.scatting { display: flex; flex-direction: column; gap: 0.8rem; }

.scat-head { display: flex; align-items: center; gap: 1rem; }
.scat-head h2 { margin: 0; }
.scat-head .lede { margin: 0.1rem 0 0; font-size: 0.9rem; }
.scat-letter {
  flex: none;
  width: 3.6rem; height: 3.6rem;
  display: grid; place-items: center;
  font: 700 2rem/1 var(--mono);
  color: var(--glow);
  border: 2px solid var(--glow);
  border-radius: 12px;
  background: var(--glow-soft);
}
.scat-clock { font-family: var(--mono); color: var(--paper); }
.scat-clock.low { color: var(--bad); }

.scat-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.scat-list > li {
  display: grid;
  grid-template-columns: minmax(9rem, 14rem) minmax(0, 1fr);
  gap: 0.7rem;
  align-items: center;
  counter-increment: scat;
}
.scat-cat { font-size: 0.88rem; color: var(--muted); }
.scat-cat::before {
  content: counter(scat) ". ";
  font-family: var(--mono); font-size: 0.72rem; color: var(--dim);
}
.scat-list { counter-reset: scat; }
.scat-in {
  width: 100%;
  padding: 0.45rem 0.7rem;
  border-radius: 8px;
}
.scat-in.blank { display: block; height: 1px; background: var(--line); }

/* The arguing. Everybody's answer for a category, side by side, so the row
   IS the comparison -- which is what you are all looking at anyway. */
.scat-list.judging > li { align-items: baseline; }
.scat-answers { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.scat-a {
  display: inline-flex; align-items: baseline; gap: 0.35rem;
  padding: 0.22rem 0.6rem;
  border: 1px solid var(--line); border-radius: 999px;
  background: transparent; color: var(--paper);
  font: inherit; font-size: 0.85rem;
  text-align: left;
}
button.scat-a { cursor: pointer; }
button.scat-a:hover { border-color: var(--bad); }
.scat-who { font-family: var(--mono); font-size: 0.66rem; color: var(--dim); }
.scat-a.mine { border-color: #4a3a2a; background: var(--panel-lit); }
/* Out, for whatever reason -- the reason is on the hover, and the line
   through it is the part you need at a glance. */
.scat-a.out { color: var(--dim); text-decoration: line-through; }
/* Your own objection, shown back to you: a vote you cannot see is a vote you
   press twice. */
.scat-a.voted { border-color: var(--bad); color: var(--bad); }

.scat-got {
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; justify-content: center;
  color: var(--muted);
}
.scat-got .side b { font-family: var(--mono); color: var(--paper); font-size: 1.1rem; }
.scat-got .side.won b { color: var(--glow); }

@media (max-width: 34rem) {
  .scat-list > li { grid-template-columns: minmax(0, 1fr); gap: 0.15rem; }
  .scat-letter { width: 2.8rem; height: 2.8rem; font-size: 1.5rem; }
}

/* ------------------------------------------------------------- punchline */

/* Everybody writes at once and then the room reads them one at a time, so
   there is nothing on this panel about whose turn it is -- only what is being
   read and how long is left. */
.punching { display: flex; flex-direction: column; gap: 0.8rem; }
.pl-head { text-align: center; }
.pl-head h2 { margin-bottom: 0.2rem; }
.pl-head .lede { margin: 0; font-size: 0.9rem; }

.pl-asked { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.9rem; }
.pl-asked > li { display: grid; gap: 0.3rem; }
.pl-prompt { font-size: 1rem; color: var(--paper); }
.pl-prompt.big {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.4rem; text-align: center; margin: 0.4rem 0 0.2rem;
}
.pl-in { width: 100%; padding: 0.55rem 0.8rem; border-radius: 8px; font-size: 1rem; }

/* The two answers, side by side and the same size, because which one is on
   the left must not be worth anything. */
.pl-two {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 0.6rem;
}
.pl-answer {
  padding: 1rem 0.9rem;
  min-height: 4.5rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel);
  color: var(--paper);
  font: inherit; font-size: 1.05rem;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease, transform 160ms ease;
}
.pl-answer:not([disabled]):hover {
  background: var(--panel-lit); border-color: var(--glow); transform: translateY(-2px);
}
.pl-answer.picked { border-color: var(--glow); background: var(--glow-soft); }
.pl-answer.mine { border-style: dashed; color: var(--muted); }
.pl-answer[disabled] { cursor: default; }
.pl-say { margin: 0; text-align: center; color: var(--muted); font-size: 0.9rem; }

/* How the round went, read back. */
.pl-results { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7rem; }
.pl-results > li {
  display: grid; gap: 0.3rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--line);
}
.pl-said { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.pl-got {
  display: grid; gap: 0.1rem;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--line); border-radius: 8px;
  font-size: 0.9rem;
}
.pl-got.sweep { border-color: var(--glow); background: var(--glow-soft); }
.pl-got .dim-note { font-size: 0.7rem; }

/* ----------------------------------------------------------- badly drawn */

/* The drawing is the whole panel. Everything else is a caption on it. */
.drawing { display: flex; flex-direction: column; gap: 0.7rem; align-items: stretch; }
.bd-head { text-align: center; }
.bd-head h2 { margin-bottom: 0.2rem; }
.bd-head .lede { margin: 0; font-size: 0.9rem; }

.bd-prompt {
  margin: 0;
  text-align: center;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.45rem;
  color: var(--glow);
}

/* Where the pad goes, and where a finished drawing is shown. Both the same
   shape, so the picture does not move when you put the pencil down. */
.bd-pad, .bd-shown {
  align-self: center;
  width: 100%; max-width: 30rem;
  aspect-ratio: 8 / 5;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #efe6da;
  overflow: hidden;
}
/* The bulletin board caps the pad at 24rem, which is right on a bulletin board and
   wrong here -- this game IS the drawing. Overridden rather than the shared
   rule loosened, because the bulletin board's note is a note. */
.bd-pad .pad {
  width: 100%; height: 100%; max-width: none;
  display: block; border-radius: 0;
  touch-action: none; cursor: crosshair;
}
.bd-art { width: 100%; height: 100%; display: block; }
.bd-art path { fill: none; stroke: #3a2f26; stroke-width: 0.7; stroke-linecap: round; stroke-linejoin: round; }
.bd-tools { display: flex; gap: 0.4rem; justify-content: center; }
.bd-say { margin: 0; text-align: center; color: var(--muted); font-size: 0.9rem; }

.bd-write { display: flex; justify-content: center; }
.bd-in { width: 100%; max-width: 26rem; padding: 0.6rem 0.8rem; border-radius: 8px; font-size: 1rem; }

/* The titles. All the same size, because which one is the truth must not be
   guessable from the shape of it. */
.bd-choices {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.5rem;
}
.bd-choice {
  padding: 0.8rem;
  min-height: 3.4rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); color: var(--paper);
  font: inherit; font-size: 1rem;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease, transform 160ms ease;
}
.bd-choice:not([disabled]):hover {
  background: var(--panel-lit); border-color: var(--glow); transform: translateY(-2px);
}
.bd-choice.picked { border-color: var(--glow); background: var(--glow-soft); }
.bd-choice.mine { border-style: dashed; color: var(--muted); }
.bd-choice[disabled] { cursor: default; }

/* Everything, at the end. The prompts and the drawings together is the only
   time anybody sees what any of it was supposed to be. */
.bd-gallery {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.8rem;
}
.bd-gallery figure { margin: 0; display: grid; gap: 0.35rem; }
.bd-gallery .bd-art {
  background: #efe6da;
  border: 1px solid var(--line); border-radius: 10px;
  aspect-ratio: 8 / 5;
}
.bd-gallery figcaption { display: grid; gap: 0.15rem; font-size: 0.85rem; }
.bd-fell { color: var(--muted); font-size: 0.78rem; }
.bd-art.blank text {
  fill: #a99a86; font: 5px var(--mono); text-anchor: middle;
  letter-spacing: 0.3px;
}



/* ------------------------------------------- keeping score with cards ---

   How euchre is scored at a kitchen table: two low cards a side, arranged so
   the pips you can see are the score. Game is ten, so the pair is a four and
   a six -- one to four is pips of the four with the six lying face down
   across it, five to ten is the four in full plus that many of the six. Ten
   is both cards face up, which is the point: the arrangement itself says how
   close the game is. */
.keeping { display: flex; flex-direction: column; align-items: center; gap: 0.3rem; }
.keep-who { white-space: nowrap; }
.pipscore { display: inline-flex; gap: 0.25rem; }

.card.pip {
  width: 2rem; height: 2.8rem;
  border-radius: 4px;
  padding: 0.18rem 0.14rem;
}
/* Three columns and three rows, pips in the outer two -- the skeleton every
   real card is laid out on. */
.card.pip .pips {
  flex: 1; width: 100%;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  place-items: center;
  padding-bottom: 0.12rem;
}
.card.pip .pips i {
  width: 0.36rem; height: 0.36rem;
  border-radius: 50%;
  background: currentColor;
}
.card.back.pip { width: 2rem; height: 2.8rem; border-radius: 4px; }
/* Lying in a stack, the way two cards do. */
.pipscore .card + .card { margin-left: -0.35rem; transform: rotate(3deg); }

/* The commentary at a trick table. Same shape as the tile table's. */
.trick-log li { gap: 0.25rem; }
.trick-log .card.small { width: 1.15rem; height: 1.6rem; }
.trick-log .card.small .card-r { font-size: 0.52rem; }
.trick-log .card.small .card-s { font-size: 0.48rem; }
/* The card that took the trick, which is the only thing anybody asks. */
.trick-log .card.on { outline: 2px solid var(--glow); outline-offset: 1px; }
.trick-log li.sep { border-top: 1px solid var(--line); }
.trick-log .log-deal { font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.04em; }

/* The middle of the table holds full-size cards now, so it needs the room. */
.laid-out { min-height: 5.6rem; }

/* A hand at the end of a spades or oh hell log line: whose, and what it came
   to. Boxed so four of them in a row read as four things rather than a
   sentence. */
.trick-log .log-side {
  display: inline-flex; align-items: baseline; gap: 0.25rem;
  padding: 0.05rem 0.4rem;
  border: 1px solid var(--line); border-radius: 999px;
  white-space: nowrap;
}
/* A bid that did not come off, which in oh hell is worth exactly nothing. */
.trick-log .log-side.missed { opacity: 0.5; }

/* Two cards face up in front of somebody at a showdown want the room a card
   wants; the seat was already this wide for them. */
.felt-seat .fan { gap: 0.25rem; }

/* ------------------------------------------------ gin: your hand, your order

   One flat row rather than a box per meld, because a card has to be able to
   go anywhere in it. The melds are a bar UNDER the cards instead: the same
   shape while they sit together, and still true when you pull one out. */
.mine[data-gin-hand] {
  gap: 0;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 0.9rem;
}
.held {
  position: relative;
  display: inline-flex;
  padding-bottom: 0.35rem;
  /* The browser must not take the gesture for a scroll: this row is dragged
     sideways on a phone, which is exactly the gesture it would steal. */
  touch-action: none;
  cursor: grab;
}
.held:not(:first-child) { margin-left: -0.35rem; }
.held .card { pointer-events: auto; }
.held.melded::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 0.22rem;
  background: var(--glow);
  opacity: 0.55;
}
/* The ends of a run of them, so three together read as one bar. */
.held.melded.m-start::after { left: 0.2rem; border-radius: 999px 0 0 999px; }
.held.melded.m-end::after { right: 0.2rem; border-radius: 0 999px 999px 0; }
.held.melded.m-start.m-end::after { border-radius: 999px; }
.held.loose .card { opacity: 0.82; }

/* In the hand, mid-drag. Above its neighbours and following the finger. */
.held.lifting {
  z-index: 5;
  cursor: grabbing;
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.6));
}
.held.lifting .card { pointer-events: none; }

@media (max-width: 34rem) {
  .held:not(:first-child) { margin-left: -0.55rem; }
}

/* Period on one side, scope on the other. Two questions about the same board,
   so they share a row and are told apart by where they are rather than by a
   label neither of them needs. */
.tabrow {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.6rem; flex-wrap: wrap;
  margin-bottom: 0.9rem;
}
.tabrow .tabs { margin-bottom: 0; }

/* ------------------------------------------------- the bulletin board ---

   A pile of photographs, fanned the way a pile of photographs fans when you
   put it down. The fan is the only honest way to say "there are more of
   these" without a number pretending to be a picture -- the number is there
   too, small, for anybody who wants it. */
.pile {
  position: relative;
  aspect-ratio: 1;
  cursor: zoom-in;
}
/* Inset, so the ones underneath have somewhere to fan OUT to without
   leaving the frame. A pile that overflows its own polaroid reads as a
   mistake rather than as a pile. */
.pile .fanned {
  position: absolute; inset: 7%;
  width: 86%; height: 86%;
  object-fit: cover;
  background: #16120e;
  border: 3px solid #fbfaf6;
  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.4);
  -webkit-user-drag: none;
  transform-origin: 50% 85%;
  transition: transform 160ms ease;
}
/* Deepest first in the DOM, so the top one is last and lies flat. */
.pile .f3 { transform: rotate(-8deg) translate(-7%, 2%); }
.pile .f2 { transform: rotate(5.5deg) translate(7%, 1.5%); }
.pile .f1 { transform: rotate(-3deg) translate(-2.5%, 1%); }
.pile .f0 { transform: rotate(0.8deg); }
/* Spread a little under the pointer, the way a thumb spreads a pile. */
.pile:hover .f3 { transform: rotate(-13deg) translate(-11%, 3%); }
.pile:hover .f2 { transform: rotate(9deg) translate(11%, 2%); }
.pile:hover .f1 { transform: rotate(-5deg) translate(-4%, 1.5%); }
.pile-n {
  position: absolute; right: 0.35rem; bottom: 0.35rem; z-index: 2;
  min-width: 1.35rem; padding: 0.05rem 0.35rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.62);
  color: #f3ece2;
  font-family: var(--mono); font-size: 0.68rem; text-align: center;
}

/* What was said underneath it. Open, never behind a "3 comments" -- a comment
   on a photograph of somebody's garden in 1998 is usually the reason the
   photograph is up there, and folding it away hides the point of the board. */
.pin-said {
  margin-top: 0.4rem; padding-top: 0.35rem;
  border-top: 1px solid rgba(0, 0, 0, 0.12);
  display: flex; flex-direction: column; gap: 0.3rem;
}
.said {
  display: flex; align-items: baseline; gap: 0.35rem;
  font-size: 0.78rem; line-height: 1.35;
}
.said .face {
  width: 1rem; height: 1rem; font-size: 0.55rem;
  flex: none; align-self: center;
}
.said-who { font-weight: 700; white-space: nowrap; }
.said-body { word-break: break-word; }
.said .linkish { margin-left: auto; color: #2a2119; opacity: 0.5; }
.said .linkish:hover { opacity: 1; }

.say-form { display: flex; gap: 0.35rem; align-items: center; }
.say-form input {
  flex: 1; min-width: 0;
  padding: 0.3rem 0.5rem;
  border: 1px solid rgba(0, 0, 0, 0.18); border-radius: 999px;
  background: rgba(255, 255, 255, 0.55);
  color: #2a2119;
  font: inherit; font-size: 0.78rem;
}
.say-form input::placeholder { color: rgba(42, 33, 25, 0.45); }
.say-form input:focus { outline: 2px solid rgba(0, 0, 0, 0.25); outline-offset: 1px; }
.say-form .btn { padding: 0.25rem 0.7rem; font-size: 0.75rem; }

/* When it went up, beside who put it there. */
.pin footer time { opacity: 0.8; }

/* Choosing a pile. One line to write on each, which is the caption you read
   in the gallery; the post's own line is underneath and names the lot. */
.shotpicks { list-style: none; margin: 0.6rem 0; padding: 0; display: grid; gap: 0.4rem; }
.shotpicks li { display: flex; align-items: center; gap: 0.6rem; }
.shotpicks img {
  width: 3.2rem; height: 3.2rem; flex: none;
  object-fit: cover; border-radius: 4px;
  background: var(--panel-lit);
}
.shotpicks input { flex: 1; min-width: 0; }

/* Looking through a pile. */
.lightbox .big-cap {
  max-width: min(70rem, 92vw);
  margin: 0.7rem auto 0;
  text-align: center;
  color: var(--paper);
  font-size: 0.95rem;
}
.lightbox .big-of {
  margin: 0.3rem 0 0;
  font-family: var(--mono); font-size: 0.72rem; color: var(--dim);
}
.lightbox .big-back, .lightbox .big-on {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 2.6rem; height: 4rem;
  font-size: 2rem; line-height: 1;
  color: var(--paper);
  background: rgba(0, 0, 0, 0.35);
  border-radius: 8px;
}
.lightbox .big-back { left: 1rem; }
.lightbox .big-on { right: 1rem; }
.lightbox .big-back:hover, .lightbox .big-on:hover { background: rgba(0, 0, 0, 0.6); }

/* ------------------------------------------------- two at one cabinet ---

   Under the machine, where the instructions are, because it is the same kind
   of fact: something about this cabinet rather than something about the game
   going on inside it. */
.coop {
  display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
  margin: 0.5rem 0 0;
  font-size: 0.82rem; color: var(--muted);
}
.coop b { color: var(--paper); }
.coop .btn { margin: 0; }
.coop span { display: inline-flex; align-items: center; gap: 0.5rem; }
/* Somebody is at the other side right now. */
.coop-dot {
  width: 0.5rem; height: 0.5rem; border-radius: 50%;
  background: var(--glow);
  box-shadow: 0 0 0 4px var(--glow-soft);
}
.coop.asking { color: var(--paper); }

/* The full-screen games' settings sheets end above table mode's dock -- its
   mic, camera and way back sit bottom right, over the bottom of the sheet,
   which on a phone is where Done is. */
.golf-sheet, .sb-sheet, .db-sheet, .bocce-sheet, .cornhole-sheet, .pool-sheet, .pk-sheet, .tt-sheet {
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 66px) !important;
}

/* --------------------------------------------------------------- hold'em ---

   GK's second hold'em, full-screen: the felt lives in /pokertable.js. What
   is here is the panel it leaves in the room, and the table's DOM parts --
   the commentary, the action bar and its settings sheet. `--pk-lift` is how
   far they stand up off the bottom to clear table mode's dock. */
.pk-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.pk-line { margin: 0; color: var(--muted); }
.pk-stacks { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 1rem; }
.pk-who { color: var(--muted); }
.pk-who b { color: var(--paper); font-variant-numeric: tabular-nums; }
.pk-who.up { color: var(--glow); }
.pk-who.out { opacity: 0.5; }
.pk-go { display: flex; justify-content: center; }

html.pk-open, html.pk-open body { overflow: hidden; overscroll-behavior: none; }
.pk {
  --pk-bg: #0B1120; --pk-bg2: #111827; --pk-ink: #E5E7EB; --pk-muted: #8B93A7; --pk-accent: #00E0B8; --pk-lift: 0px;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--pk-bg); color: var(--pk-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
.pk[hidden] { display: none !important; }
.pk-felt { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.pk button { font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--pk-ink); }
.pk button:hover { border-color: var(--pk-accent); }
.pk button[hidden] { display: none; }

.pk-actions {
  position: absolute; left: 0; right: 0; bottom: var(--pk-lift); z-index: 5;
  padding: 10px 12px calc(env(safe-area-inset-bottom, 0px) + 12px);
  background: linear-gradient(to top, rgba(11,17,32,.96), rgba(11,17,32,.7) 80%, rgba(11,17,32,0));
}
.pk-actions[hidden] { display: none; }
.pk-btns { display: flex; gap: 8px; max-width: 520px; margin: 0 auto; }
.pk-btns button { flex: 1; padding: 14px 8px; font-size: 15px; border-radius: 12px; }
.pk-btns .fold { border-color: rgba(240,82,90,.6); color: #F0525A; }
.pk-btns .check { border-color: rgba(255,255,255,.2); }
.pk-btns .raise { background: var(--pk-accent); color: #062b25; border-color: var(--pk-accent); }
.pk-raise { max-width: 520px; margin: 0 auto 8px; display: grid; grid-template-columns: 1fr auto; gap: 6px 10px; align-items: center; }
.pk-raise[hidden] { display: none; }
.pk-presets { grid-column: 1 / -1; display: flex; gap: 6px; }
.pk-presets button { flex: 1; padding: 7px 4px; font-size: 12px; border-radius: 8px; color: var(--pk-muted); }
.pk-amt { width: 100%; margin: 0; accent-color: var(--pk-accent); }
.pk-val { font: 600 13px var(--mono); color: var(--pk-accent); min-width: 56px; text-align: right; }

.pk-log {
  position: absolute; left: 10px; right: 10px; bottom: calc(env(safe-area-inset-bottom, 0px) + 150px + var(--pk-lift)); height: 62px; z-index: 4;
  overflow-y: auto; padding: 5px 10px; border-radius: 10px; cursor: pointer;
  background: rgba(17,24,39,.78); border: 1px solid rgba(255,255,255,.08); backdrop-filter: blur(6px);
  font: 500 11px var(--mono); color: var(--pk-muted); line-height: 1.45; transition: height .2s;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 10px);
}
.pk-log.open { height: 42vh; }
.pk-log .line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pk-log .line.hand { color: var(--pk-ink); margin-top: 4px; }
.pk-log .line.street { color: var(--pk-ink); }
.pk-log .line.win { color: #F5C542; }
.pk-log .line.hl { color: var(--pk-accent); }
.pk-log b { font-weight: 700; }
.pk-log b.red { color: #ff7b84; }
.pk-log b.blk { color: #F3F4F6; }
@media (min-aspect-ratio: 1/1) {
  .pk-log { left: auto; right: 10px; top: 70px; bottom: calc(150px + var(--pk-lift)); width: 250px; height: auto; }
  .pk-log.open { height: auto; }
}

.pk-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.pk-sheet[hidden] { display: none; }
.pk-card {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--pk-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.pk-card h2 { margin: 0 0 14px; font-size: 20px; font-weight: 800; color: var(--pk-ink); }
.pk-card label { display: block; font-size: 13px; font-weight: 700; margin: 8px 0; }
.pk-card label.check { display: flex; align-items: center; gap: 10px; margin: 12px 0 4px; }
.pk-card label.check input { width: 18px; height: 18px; accent-color: var(--pk-accent); }
.pk-card small { display: block; font-weight: 400; font-size: 12px; color: var(--pk-muted); margin-top: 2px; line-height: 1.4; }
.pk-card select {
  display: block; width: 100%; margin-top: 6px; background: var(--pk-bg); color: var(--pk-ink);
  border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 9px 10px; font: 600 14px var(--mono);
}
.pk-card .row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.pk-card .rules { color: var(--pk-muted); font-size: 12.5px; line-height: 1.5; margin-top: 14px; }
.pk-card .acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.pk-card button.primary { background: var(--pk-accent); color: #062b25; border-color: var(--pk-accent); }
.pk-card :disabled { opacity: 0.5; }

/* The trick games on the full-screen table. On a phone the commentary is a
   short strip just above the bar -- where hold'em has it -- with your hand
   above that. */
.tt-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.tt-line { margin: 0; color: var(--muted); }
.tt-scores { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 1.2rem; color: var(--muted); }
.tt-scores b { color: var(--paper); font-size: 1.15rem; font-variant-numeric: tabular-nums; }
.tt-go { display: flex; justify-content: center; }

html.tt-open, html.tt-open body { overflow: hidden; overscroll-behavior: none; }
.tt {
  --tt-bg: #0B1120; --tt-bg2: #111827; --tt-ink: #E5E7EB; --tt-muted: #8B93A7; --tt-accent: #00E0B8; --tt-lift: 0px;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--tt-bg); color: var(--tt-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
.tt[hidden] { display: none !important; }
.tt-felt { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.tt button { font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--tt-ink); }
.tt button:hover { border-color: var(--tt-accent); }
.tt button[hidden] { display: none; }

.tt-actions {
  position: absolute; left: 0; right: 0; bottom: var(--tt-lift); z-index: 5;
  padding: 10px 12px calc(env(safe-area-inset-bottom, 0px) + 12px);
  background: linear-gradient(to top, rgba(11,17,32,.96), rgba(11,17,32,.7) 80%, rgba(11,17,32,0));
}
.tt-actions[hidden] { display: none; }
.tt-btns { display: flex; gap: 8px; max-width: 520px; margin: 0 auto; }
.tt-btns button { flex: 1; padding: 14px 8px; font-size: 15px; border-radius: 12px; }
.tt-btns .go { background: var(--tt-accent); color: #062b25; border-color: var(--tt-accent); }
.tt-bid { max-width: 520px; margin: 0 auto 8px; display: grid; grid-template-columns: 1fr auto; gap: 6px 10px; align-items: center; }
.tt-bid[hidden] { display: none; }
.tt-presets { grid-column: 1 / -1; display: flex; gap: 6px; }
.tt-presets button { flex: 1; padding: 7px 4px; font-size: 12px; border-radius: 8px; color: var(--tt-muted); }
.tt-amt { width: 100%; margin: 0; accent-color: var(--tt-accent); }
.tt-val { font: 600 13px var(--mono); color: var(--tt-accent); min-width: 56px; text-align: right; }

.tt-log {
  position: absolute; left: 10px; right: 10px; bottom: calc(env(safe-area-inset-bottom, 0px) + 150px + var(--tt-lift)); height: 62px; z-index: 4;
  overflow-y: auto; padding: 5px 10px; border-radius: 10px; cursor: pointer;
  background: rgba(17,24,39,.78); border: 1px solid rgba(255,255,255,.08); backdrop-filter: blur(6px);
  font: 500 11px var(--mono); color: var(--tt-muted); line-height: 1.45; transition: height .2s;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 10px);
}
.tt-log.open { height: 42vh; }
.tt-log .line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tt-log .line.hand { color: var(--tt-ink); margin-top: 4px; }
.tt-log .line.street { color: var(--tt-ink); }
.tt-log .line.win { color: #F5C542; }
.tt-log .line.hl { color: var(--tt-accent); }
.tt-log b { font-weight: 700; }
.tt-log b.red { color: #ff7b84; }
.tt-log b.blk { color: #F3F4F6; }
@media (min-aspect-ratio: 1/1) {
  .tt-log { left: auto; right: 10px; top: 70px; bottom: calc(150px + var(--tt-lift)); width: 250px; height: auto; }
  .tt-log.open { height: auto; }
}

.tt-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.tt-sheet[hidden] { display: none; }
.tt-card {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--tt-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.tt-card h2 { margin: 0 0 14px; font-size: 20px; font-weight: 800; color: var(--tt-ink); }
.tt-card label { display: block; font-size: 13px; font-weight: 700; margin: 8px 0; }
.tt-card label.check { display: flex; align-items: center; gap: 10px; margin: 12px 0 4px; }
.tt-card label.check input { width: 18px; height: 18px; accent-color: var(--tt-accent); }
.tt-card small { display: block; font-weight: 400; font-size: 12px; color: var(--tt-muted); margin-top: 2px; line-height: 1.4; }
.tt-card select {
  display: block; width: 100%; margin-top: 6px; background: var(--tt-bg); color: var(--tt-ink);
  border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 9px 10px; font: 600 14px var(--mono);
}
.tt-card .row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.tt-card .rules { color: var(--tt-muted); font-size: 12.5px; line-height: 1.5; margin-top: 14px; white-space: pre-line; }
.tt-card .acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.tt-card button.primary { background: var(--tt-accent); color: #062b25; border-color: var(--tt-accent); }
.tt-card :disabled { opacity: 0.5; }
.tt-btns .quiet { color: var(--tt-muted); }
.tt-btns .suit { flex: 0.7; font-size: 22px; padding: 10px 4px; }
.tt-btns .suit .red { color: #ff7b84; }
.tt-btns button:disabled { opacity: 0.45; cursor: default; }
.tt-presets button:disabled { opacity: 0.3; text-decoration: line-through; cursor: default; }
.tt-alone { display: flex; justify-content: center; align-items: center; gap: 8px; max-width: 520px; margin: 0 auto 8px;
  font: 600 13px var(--mono); color: var(--tt-muted); }
.tt-alone[hidden] { display: none; }
.tt-alone input { width: 18px; height: 18px; accent-color: var(--tt-accent); }
@media (max-aspect-ratio: 1/1) {
  .tt-log { top: auto; bottom: calc(env(safe-area-inset-bottom, 0px) + 150px + var(--tt-lift)); height: 46px; }
  .tt-log.open { height: 42vh; }
}

/* ------------------------------------------------------------- mini golf ---

   GK's prototype, full-screen: the course and the swing live in
   /golfcourse.js. What is here is the panel it leaves in the room -- who is
   on which hole, and the card -- and the course's own settings sheet. */
.golf-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.golf-line { margin: 0; color: var(--muted); }
.golf-go { display: flex; justify-content: center; }

html.golf-open, html.golf-open body { overflow: hidden; overscroll-behavior: none; }
.golf {
  --gf-bg: #0B1120; --gf-bg2: #111827; --gf-ink: #E5E7EB; --gf-muted: #8B93A7; --gf-accent: #00E0B8;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--gf-bg); color: var(--gf-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  touch-action: none;
}
.golf[hidden] { display: none !important; }
.golf-cloth { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.golf-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.golf-sheet[hidden] { display: none; }
.golf-box {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--gf-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.golf-box h2 { margin: 0 0 14px; font-size: 20px; font-weight: 800; color: var(--gf-ink); }
.golf-box label { display: block; font-size: 13px; font-weight: 700; margin: 8px 0; }
.golf-box label.slider { margin-top: 14px; }
.golf-box label .val { float: right; font: 600 12px var(--mono); color: var(--gf-accent); }
.golf-box small { display: block; font-weight: 400; font-size: 12px; color: var(--gf-muted); margin-top: 2px; line-height: 1.4; }
.golf-box input[type=range] { width: 100%; margin: 8px 0 2px; accent-color: var(--gf-accent); }
.golf-box select {
  display: block; width: 100%; margin-top: 6px; background: var(--gf-bg); color: var(--gf-ink);
  border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 9px 10px; font: 600 14px var(--mono);
}
.golf-box .rules { color: var(--gf-muted); font-size: 12.5px; line-height: 1.5; margin-top: 14px; }
.golf-box .acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.golf-box button {
  font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--gf-ink);
}
.golf-box button:hover { border-color: var(--gf-accent); }
.golf-box button.primary { background: var(--gf-accent); color: #062b25; border-color: var(--gf-accent); }
.golf-box .row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.golf-box label[hidden], .golf-box small[hidden] { display: none; }
.golf-box .rules b { color: var(--gf-ink); }
.golf-box .swatches { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 10px; }
.golf-box .swatch {
  width: 28px; height: 28px; padding: 0; border-radius: 50%; cursor: pointer;
  border: 2px solid rgba(255,255,255,.15);
}
.golf-box .swatch.on { border-color: var(--gf-ink); box-shadow: 0 0 0 2px var(--gf-accent); }
.golf-box .swatch.taken { opacity: 0.25; cursor: not-allowed; }
.golf-box .aids { display: flex; gap: 16px; }
.golf-box .aids label { display: flex; align-items: center; gap: 6px; font-weight: 600; }
.golf-box .aids input { accent-color: var(--gf-accent); }

/* The card. Holes across, everybody down -- the thing a family argues about
   afterwards, so it is legible before it is pretty. Eighteen holes is wider
   than a phone, so it scrolls on its own. */
.golf-cardwrap { max-width: 100%; overflow-x: auto; }
.golf-card {
  border-collapse: collapse;
  font-family: var(--mono); font-size: 0.74rem;
  color: var(--muted);
}
.golf-card th, .golf-card td {
  padding: 0.12rem 0.34rem;
  text-align: center;
  border: 1px solid rgba(255, 255, 255, 0.06);
}
.golf-card th { color: var(--dim); font-weight: 600; }
.golf-card .who { text-align: left; color: var(--paper); }
.golf-dot { display: inline-block; width: 0.55rem; height: 0.55rem; border-radius: 50%; margin-right: 0.35rem; vertical-align: 0.02rem; }
.golf-card tr.golf-me .who { color: var(--glow); }
.golf-card .tot { color: var(--paper); font-weight: 700; }
.golf-card .now { background: rgba(232, 163, 61, 0.14); color: var(--paper); }
/* Under par is worth seeing from across the room. */
.golf-card .under { color: #5fe08a; font-weight: 700; }
.golf-card .over { color: var(--dim); }
.golf-card .pars td { color: var(--dim); border-top: 1px solid var(--line); }

/* ------------------------------------------------------------- tiến lên ---

   Thirteen cards, and the hand is the thing you are working with -- so the
   hand gets the room and everything else is small. */
.table.vc {
  display: flex; flex-direction: column; gap: 0.7rem;
  padding: 0.9rem;
  border-radius: var(--radius);
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(255,255,255,0.05), transparent 60%),
    #17342c;
  border: 1px solid #0d211c;
}
.vc .table-head, .vc .table-foot { color: #cfe3d8; }
.vc .round b, .vc .score b { color: #fff; }

.vc-seat {
  display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
  padding: 0.35rem 0.6rem;
  border: 1px solid transparent; border-radius: var(--radius);
  background: rgba(0, 0, 0, 0.25);
  color: #dfeee6;
  min-width: 4.6rem;
}
.vc-seat.turn { border-color: var(--glow); background: rgba(255, 255, 255, 0.06); }
.vc-seat.passed { opacity: 0.45; }
.vc-seat.done { opacity: 0.35; }
/* How many they are still holding. Somebody down to one card is the entire
   tension of the endgame, so it is the biggest thing on the chair. */
.vc-count { font-size: 1.25rem; font-weight: 700; line-height: 1.1; }
.vc-count.last { color: var(--glow); }

/* What is on the table, which is what you have to beat. */
.vc-middle {
  display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
  min-height: 5.4rem; justify-content: center;
  padding: 0.5rem; border-radius: var(--radius);
  background: rgba(0, 0, 0, 0.2);
}
.vc-what { font-size: 0.75rem; color: #bcd8cb; }

/* Your hand. Thirteen cards have to be reachable with a thumb, so they wrap
   and overlap rather than shrinking -- a card you cannot read is worse than a
   row that takes two lines. */
.vc-hand {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 0.3rem 0.15rem;
  padding: 0.2rem 0;
}
.vc-hand .card { transition: transform 120ms ease; }
/* Picked up: lifted out of the row, which is what picking a card up looks
   like and needs no second colour. */
.vc-hand .card.on {
  transform: translateY(-0.55rem);
  border-color: var(--glow);
  box-shadow: 0 6px 12px -4px rgba(0, 0, 0, 0.7);
}

/* Decorating a room, in the admin. */
.decor {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.8rem 1.2rem;
  margin-top: 0.7rem; padding-top: 0.7rem;
  border-top: 1px dashed var(--line);
}
.decor input[type="text"] { width: 3.2rem; font-size: 1.1rem; }
.banner-thumb {
  height: 2.2rem; width: 6rem; object-fit: cover;
  border-radius: 6px; border: 1px solid var(--line);
}

/* ---------------------------------------------------------------- decor ---

   A room can look like itself. An icon and a banner, and no colour: presence
   is this building's visual grammar -- a lit room has somebody in it and an
   empty one is genuinely dim -- and two rooms in different colours are two
   rooms you can no longer compare at a glance. */
.room-icon {
  font-size: 1.5rem; line-height: 1; flex: none;
  filter: saturate(0.75) opacity(0.75);
  transition: filter 160ms ease;
}
.room.lit .room-icon { filter: none; }

/* The banner on a room's card in the hall: a wash behind the words rather
   than a picture beside them, so a row is still a row and the lit/unlit
   difference survives having a photograph in it. */
.room.bannered { position: relative; overflow: hidden; isolation: isolate; }
.room.bannered::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background-image: var(--banner);
  background-size: cover; background-position: center;
  /* Faint, and fading in from the left, so the name and the blurb sit on
     clean ground and the picture is decoration rather than a background.
     Without the fade a light patch in a photograph lands under the title and
     the row becomes hard to read -- which is a decoration making the building
     worse at the one thing it is for. */
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 60%);
  mask-image: linear-gradient(to right, transparent 0%, #000 60%);
  opacity: 0.1;
  transition: opacity 160ms ease;
}
/* And a shade brighter when somebody is in it -- never brighter than an EMPTY
   room with no banner is dim. A decorated room that looks occupied would take
   the badge off the brightness, which is the whole visual grammar here. */
.room.bannered.lit::before { opacity: 0.22; }

/* And across the top of the room itself, where it is the picture rather than
   a wash. Short, because it is a banner and not a hero: the furniture is what
   somebody came in for. */
.room-banner {
  margin: -0.2rem 0 1rem;
  border-radius: var(--radius);
  overflow: hidden;
  line-height: 0;
  border: 1px solid var(--line);
}
.room-banner img {
  width: 100%; height: clamp(5rem, 18vh, 9rem);
  object-fit: cover; display: block;
}
.title-icon { margin-right: 0.5rem; }

/* Picking a room's icon. Each is a submit button carrying its own value, so
   choosing one is a single click and this page stays a page of forms. */
.icons { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem; }
.pick-icon {
  font-size: 1.15rem; line-height: 1;
  width: 2rem; height: 2rem; padding: 0;
  display: grid; place-items: center;
  border: 1px solid transparent; border-radius: 8px;
  background: rgba(255, 255, 255, 0.04);
  cursor: pointer;
}
.pick-icon:hover { background: rgba(255, 255, 255, 0.1); }
.pick-icon.on { border-color: var(--glow); background: var(--glow-soft); }
.pick-icon.none { font-size: 0.8rem; color: var(--dim); }

/* ----------------------------------------------------------- shuffleboard ---

   GK's prototype's own look, carried across with it: full-screen, a dark
   ground, the board lit down the middle. Everything is scoped under .sb --
   the prototype styled bare `button`, `.panel` and `.overlay`, and the lounge
   has all three. */
.sb-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.sb-line { margin: 0; font-size: 1rem; }
.sb-line b { font-size: 1.3rem; font-variant-numeric: tabular-nums; }
.sb-r { color: #F0525A; } .sb-b { color: #4F8BFF; }
.sb-go { display: flex; justify-content: center; }

html.sb-open, html.sb-open body { overflow: hidden; overscroll-behavior: none; }

.sb {
  --sb-bg: #0B1120; --sb-bg2: #111827; --sb-ink: #E5E7EB; --sb-muted: #8B93A7;
  --sb-accent: #00E0B8; --sb-red: #F0525A; --sb-blue: #4F8BFF;
  --sb-font: Karla, ui-sans-serif, system-ui, sans-serif;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--sb-bg); color: var(--sb-ink); font-family: var(--sb-font);
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.sb[hidden] { display: none !important; }
.sb-board { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; cursor: grab; }
.sb-board.holding { cursor: grabbing; }
.sb-mini { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); width: 34px; pointer-events: none; opacity: .9; }

.sb-hud {
  position: absolute; left: 0; right: 0; top: 0;
  padding: calc(env(safe-area-inset-top, 0px) + 10px) 14px 10px;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: start; gap: 10px;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(11,17,32,.92), rgba(11,17,32,0));
}
.sb-side { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sb-side.blue { align-items: flex-end; text-align: right; }
.sb-side .name { font-family: var(--mono); font-size: 11px; letter-spacing: .14em; color: var(--sb-muted);
  text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.sb-side.red .name { color: var(--sb-red); } .sb-side.blue .name { color: var(--sb-blue); }
.sb-side .pts { font-weight: 800; font-size: 34px; line-height: 1; font-variant-numeric: tabular-nums; }
.sb-side .pucks { display: flex; gap: 4px; margin-top: 4px; }
.sb-side .pucks i { width: 9px; height: 9px; border-radius: 50%; background: currentColor; opacity: .25; }
.sb-side .pucks i.left { opacity: 1; }
.sb-side.red .pucks { color: var(--sb-red); } .sb-side.blue .pucks { color: var(--sb-blue); }
.sb-side.turn .name::after { content: " ●"; color: var(--sb-accent); }
.sb-hud .mid { text-align: center; padding-top: 2px; }
.sb-hud .round { font-family: var(--mono); font-size: 11px; letter-spacing: .18em; color: var(--sb-muted); }
.sb-hud .msg { margin-top: 4px; font-size: 14px; font-weight: 700; color: var(--sb-accent); max-width: 240px; min-height: 1.4em; }
.sb-hud .msg.dim { color: var(--sb-muted); font-weight: 500; }

.sb-tools { position: absolute; left: 12px; bottom: calc(env(safe-area-inset-bottom, 0px) + 12px); display: flex; gap: 8px; z-index: 5; }
.sb button {
  font: 700 15px var(--sb-font); padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--sb-ink);
  transition: border-color .15s, background .15s, transform .1s;
}
.sb button:hover { border-color: var(--sb-accent); }
.sb button:active { transform: scale(.98); }
.sb button.primary { background: var(--sb-accent); color: #062b25; border-color: var(--sb-accent); }
.sb-tools button {
  width: 40px; height: 40px; padding: 0; border-radius: 12px; border-color: rgba(255,255,255,.08);
  background: rgba(17,24,39,.85); font-size: 17px; display: grid; place-items: center; backdrop-filter: blur(6px);
}
.sb-tools button.on { border-color: var(--sb-accent); color: var(--sb-accent); }

.sb-toast {
  position: absolute; left: 50%; bottom: calc(env(safe-area-inset-bottom, 0px) + 64px);
  transform: translateX(-50%) translateY(10px); opacity: 0;
  background: var(--sb-bg2); border: 1px solid rgba(0,224,184,.35);
  padding: 8px 14px; border-radius: 10px; font-size: 13px; pointer-events: none;
  transition: opacity .25s, transform .25s; z-index: 20; white-space: nowrap;
}
.sb-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

.sb-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px); animation: sb-fade .25s ease-out;
}
.sb-sheet[hidden] { display: none; }
.sb-sheet.light { background: rgba(11,17,32,.35); pointer-events: none; }
.sb-sheet.light .sb-card { pointer-events: auto; }
@keyframes sb-fade { from { opacity: 0 } to { opacity: 1 } }
.sb-card {
  width: 100%; max-width: 420px; max-height: 100%; overflow: auto;
  background: var(--sb-bg2); border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px;
  box-shadow: 0 30px 80px rgba(0,0,0,.5); animation: sb-rise .3s cubic-bezier(.2,.9,.3,1.2);
}
@keyframes sb-rise { from { transform: translateY(14px); opacity: 0 } to { transform: none; opacity: 1 } }
.sb-card h2 { margin: 0 0 14px; font-size: 20px; font-weight: 800; color: var(--sb-ink); }
.sb-card label { display: block; font-size: 13px; font-weight: 700; margin: 12px 0; }
.sb-card label .val { float: right; font: 600 12px var(--mono); color: var(--sb-accent); }
.sb-card small { display: block; font-weight: 400; color: var(--sb-muted); font-size: 12px; margin-top: 2px; }
.sb-card input[type=range] { width: 100%; margin: 8px 0 2px; accent-color: var(--sb-accent); }
.sb-card select {
  display: block; width: 100%; margin-top: 6px; background: var(--sb-bg); color: var(--sb-ink);
  border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 8px 10px; font: 600 14px var(--mono);
}
.sb-card .row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.sb-card .acts { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.sb-card.result { text-align: center; max-width: 340px; }
.sb-card.result .big { font-size: 34px; font-weight: 800; letter-spacing: -.02em; line-height: 1.05; }
.sb-card.result .big.red { color: var(--sb-red); } .sb-card.result .big.blue { color: var(--sb-blue); }
.sb-card.result .sub { color: var(--sb-muted); margin-top: 8px; font-size: 14px; line-height: 1.5; }
.sb-card.result .sub b { color: var(--sb-ink); }
@media (max-width: 480px) {
  .sb-side .pts { font-size: 28px; }
  .sb-hud .msg { font-size: 13px; max-width: 170px; }
  .sb-card { padding: 20px; }
}
.sb-tools button[hidden] { display: none; }

/* ------------------------------------------------------ at the table ---

   Every game full-screen, the way shuffleboard came. Until it is, the frame
   is no box at all -- display: contents -- so a table in the room lays out
   exactly as it always has. */
.table-frame { display: contents; }
.frame-bar { display: flex; justify-content: flex-end; margin: 0 0 0.4rem; }

html.at-table, html.at-table body { overflow: hidden; overscroll-behavior: none; }
.table-frame.focused {
  display: block;
  position: fixed; inset: 0; z-index: 900;
  overflow: auto; overscroll-behavior: contain;
  background: var(--ink);
  background-image: radial-gradient(80rem 40rem at 15% -10%, #241c15 0%, var(--ink) 60%);
  /* Room at the bottom to scroll the last row of controls clear of the
     dock, and no more: the dock is a corner, not a band. */
  padding: calc(env(safe-area-inset-top, 0px) + 1rem) 1rem
           calc(env(safe-area-inset-bottom, 0px) + 4.5rem);
}
.table-frame.focused > * { max-width: 72rem; margin-inline: auto; }
.table-frame.focused > .panel { flex: none; overflow: visible; min-height: 0; }
.table-frame.focused > .panel + .panel { margin-top: 1rem; }

/* The corner of the call. Above everything, shuffleboard's board included. */
.table-dock {
  position: fixed; right: 12px; bottom: calc(env(safe-area-inset-bottom, 0px) + 12px);
  z-index: 1100;
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
  pointer-events: none;
}
.table-dock > * { pointer-events: auto; }
/* Specific enough to beat the room's own strip rule, `body.fixtured .stage .tiles`. */
.stage .table-dock .tiles.strip {
  display: flex; flex-wrap: nowrap; gap: 6px;
  margin: 0; padding: 0; flex: none;
  max-height: none; overflow: visible;
}
.stage .table-dock .tile {
  width: 104px; aspect-ratio: 4 / 3; padding: 0; gap: 0;
  position: relative; overflow: hidden;
  background: rgba(31, 26, 22, 0.92);
  box-shadow: 0 8px 20px -8px rgba(0, 0, 0, 0.8);
}
.stage .table-dock .tile .face { width: 2rem; height: 2rem; font-size: 0.8rem; }
.stage .table-dock .tile-name {
  position: absolute; left: 5px; right: 5px; bottom: 3px; z-index: 2;
  font-size: 0.66rem; text-align: left;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
}
.stage .table-dock .tile .you { display: none; }
.stage .table-dock .tile-mic { position: absolute; top: 3px; right: 3px; z-index: 2; }
.dock-btns { display: flex; gap: 8px; }
.dock-btn {
  width: 42px; height: 42px; padding: 0; border-radius: 12px;
  display: grid; place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(31, 26, 22, 0.9); color: var(--paper);
  font-size: 18px; line-height: 1; cursor: pointer;
  backdrop-filter: blur(6px);
  transition: border-color 150ms ease, transform 100ms ease;
}
.dock-btn:hover { border-color: var(--glow); }
.dock-btn:active { transform: scale(0.95); }
.dock-btn.live { border-color: var(--glow); color: var(--glow); background: rgba(232, 163, 61, 0.16); }
.dock-btn.speaking { box-shadow: 0 0 0 3px rgba(232, 163, 61, 0.35); }
.dock-btn:disabled { opacity: 0.5; cursor: progress; }
.dock-btn svg { width: 20px; height: 20px; }
.dock-warn {
  margin: 0; max-width: 16rem; padding: 0.3rem 0.5rem; border-radius: 8px;
  font-size: 0.75rem; color: var(--bad); text-align: right;
  background: rgba(31, 26, 22, 0.92);
}
@media (max-width: 34rem) {
  .stage .table-dock .tile { width: 72px; }
}

/* And what the room was too narrow for. Only on a screen with the width to
   spend: on a phone a table was never cramped by the chat, which is not
   beside it there, and bigger cards would only push a hand off the edge. */
/* The poker oval is sized by its width and is wider than tall, so on a
   laptop it grew until the hand and the bet buttons were below the fold --
   the one part of the table you act on. It gets the height that is left
   after them (about 24rem of head, hand, digest and buttons), and no more. */
.table-frame.focused .ring { max-width: calc((100dvh - 24rem) * 1.55); margin-inline: auto; }
@media (min-width: 44rem) {
  .table-frame.focused { --card-w: 3.3rem; }
  .table-frame.focused .card-r { font-size: 1.15rem; }
  .table-frame.focused .card-s { font-size: 1.05rem; }
  .table-frame.focused .ring .board { --card-w: min(3.4rem, 17cqw); }
  .table-frame.focused .ring .board .card-r { font-size: min(1.15rem, 6cqw); }
  .table-frame.focused .ring .board .card-s { font-size: min(1.05rem, 5.6cqw); }
  .table-frame.focused .ring .pew .felt-seat { width: min(7rem, 23cqw); }
  .table-frame.focused .ring .pew .chair-name { font-size: min(0.85rem, 3.4cqw); }
  .table-frame.focused .ring .pew .stack { font-size: min(0.9rem, 3.4cqw); }
}

/* ------------------------------------------------------ the account menu

   Your name, with what you can do about being you underneath it. A <details>
   so it opens without any script; the page closes it when you click away. */
.me-menu { position: relative; }
.me-menu > summary {
  list-style: none; cursor: pointer; user-select: none;
  padding: 0.3rem 0.6rem 0.3rem 0.35rem;
  border: 1px solid transparent; border-radius: 999px;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.me-menu > summary::-webkit-details-marker { display: none; }
.me-menu > summary:hover,
.me-menu[open] > summary { color: var(--paper); background: var(--panel); border-color: var(--line); }
.me-menu .caret { font-size: 0.7rem; opacity: 0.7; transition: transform 150ms ease; }
.me-menu[open] .caret { transform: rotate(180deg); }
.me-drop {
  position: absolute; right: 0; top: calc(100% + 0.4rem); z-index: 50;
  min-width: 11rem; padding: 0.35rem;
  display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 12px 30px -10px rgba(0, 0, 0, 0.7);
}
.me-drop form { margin: 0; }
.top .me-drop a,
.top .me-drop .me-item {
  display: block; width: 100%; box-sizing: border-box;
  padding: 0.5rem 0.7rem; border: 0; border-radius: 7px;
  background: none; color: var(--paper);
  font: inherit; font-size: 0.86rem; text-align: left; text-decoration: none;
  cursor: pointer;
}
.top .me-drop a:hover,
.top .me-drop .me-item:hover { background: var(--panel-lit); color: var(--glow); }

/* ---------------------------------------------------------- the glance ---

   The newest few things on the bulletin board, on the room itself. The same
   paper as the board -- tints, tilt, pinhead -- on the same cork, laid out
   as a row rather than a column: this is a look from the doorway, and the
   board is one click away for anybody who wants the rest. */
.glance { margin: 1.1rem 0 1.4rem; }
.glance-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 0.6rem; }
.glance-head h2 { margin: 0; }
.glance-head .linkish { font-size: 0.85rem; color: var(--muted); text-decoration: none; }
.glance-head .linkish:hover { color: var(--glow); }
.glance-row {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.1rem;
  align-items: start;
  padding: 1.1rem 1rem 1rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background:
    radial-gradient(circle at 20% 30%, rgba(0,0,0,0.16) 0 2px, transparent 3px),
    radial-gradient(circle at 70% 60%, rgba(0,0,0,0.13) 0 2px, transparent 3px),
    radial-gradient(circle at 45% 85%, rgba(255,255,255,0.05) 0 2px, transparent 3px),
    #6b4f36;
  background-size: 37px 41px, 53px 47px, 61px 59px, auto;
}
.glance-pin {
  display: block; width: auto; min-width: 0;
  color: #2a2119; text-decoration: none;
  transition: transform 140ms ease, box-shadow 140ms ease;
}
.glance-pin:hover {
  transform: rotate(var(--tilt, -1.5deg)) translateY(-3px);
  box-shadow: 0 10px 20px rgba(0,0,0,0.45);
}
/* A long note is the start of a note here; the board has the rest of it. */
.glance-pin .pin-body {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 6;
  overflow: hidden;
}
.glance-pin .shot img { cursor: pointer; }
.glance-pin footer { flex-wrap: wrap; }
.glance-pin footer span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.glance-said { margin-left: auto; }
/* Narrow: a row you slide along, rather than four notes the width of a stamp. */
@media (max-width: 40rem) {
  .glance-row {
    grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(9.5rem, 46%);
    overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity;
  }
  .glance-pin { scroll-snap-align: start; }
}

/* ------------------------------------------------------------ the room bar

   Who is here and the call's controls, together at the top of the room,
   above the banner. The faces take the slack; the controls keep to the
   right and drop underneath them when there is not room for both. */
.room-bar {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 0.6rem 1rem;
  margin: 0 0 1rem;
}
/* Specific enough to beat the strip's own margin and the rule that caps it
   at a table (`body.fixtured .stage .tiles`). */
body .stage .room-bar .tiles.strip {
  flex: 1 1 auto; min-width: 0;
  margin: 0; max-height: none; overflow: visible;
}
.room-bar .controls { flex: none; align-items: center; margin-left: auto; }
.room-bar .mic { padding: 0.5rem 0.95rem; }
.room-bar + .status.warn { margin: -0.4rem 0 0.8rem; }
.room-bar ~ .picker { margin: -0.3rem 0 1rem; }

.call-menu { position: relative; }
.call-menu > summary { list-style: none; }
.call-menu > summary::-webkit-details-marker { display: none; }
.call-menu > summary.mic {
  padding: 0.5rem 0.8rem; font-size: 1.1rem; line-height: 1; letter-spacing: 0.05em;
}
.call-drop {
  position: absolute; right: 0; top: calc(100% + 0.4rem); z-index: 60;
  min-width: 12rem; padding: 0.35rem;
  display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 12px 30px -10px rgba(0, 0, 0, 0.7);
}
.call-item {
  display: flex; align-items: center; gap: 0.6rem; width: 100%;
  padding: 0.55rem 0.7rem; border: 0; border-radius: 7px;
  background: none; color: var(--paper);
  font: inherit; font-size: 0.88rem; text-align: left; cursor: pointer;
}
.call-item:hover:not(:disabled) { background: var(--panel-lit); }
.call-item.on { color: var(--glow); }
.call-item.dim { opacity: 0.45; }
.call-item:disabled { opacity: 0.6; cursor: default; }


/* ------------------------------------------------------------------ darts ---

   GK's prototype's own look, carried across with it, the way shuffleboard's
   was: full-screen, the board on a dark ground, the HUD drawn on the canvas.
   Scoped under .db for the same reason .sb is -- the prototype styled bare
   buttons and labels, and the lounge has plenty of both. */
.db-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.db-line { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 1.1rem; }
.db-who { color: var(--muted); }
.db-who b { color: var(--paper); font-size: 1.2rem; font-variant-numeric: tabular-nums; }
.db-who.up { color: var(--glow); }
.db-go { display: flex; justify-content: center; }

html.db-open, html.db-open body { overflow: hidden; overscroll-behavior: none; }
.db {
  --db-bg: #0B1120; --db-bg2: #111827; --db-ink: #E5E7EB; --db-muted: #8B93A7; --db-accent: #00E0B8;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--db-bg); color: var(--db-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  touch-action: none;
}
.db[hidden] { display: none !important; }
.db-board { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.db-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.db-sheet[hidden] { display: none; }
.db-card {
  width: 100%; max-width: 380px; background: var(--db-bg2); border: 1px solid rgba(255,255,255,.08);
  border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.db-card h2 { margin: 0 0 14px; font-size: 20px; font-weight: 800; color: var(--db-ink); }
.db-card .row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: end; }
.db-card label { display: block; font-size: 13px; font-weight: 700; margin: 8px 0; }
.db-card label.check { display: flex; align-items: center; gap: 10px; margin: 8px 0 12px; }
.db-card input[type=checkbox] { width: 18px; height: 18px; accent-color: var(--db-accent); }
.db-card label.slider { margin-top: 14px; }
.db-card label .val { float: right; font: 600 12px var(--mono); color: var(--db-accent); }
.db-card small { display: block; font-weight: 400; font-size: 12px; color: var(--db-muted); margin-top: 2px; line-height: 1.4; }
.db-card input[type=range] { width: 100%; margin: 8px 0 2px; accent-color: var(--db-accent); }
.db-card select {
  display: block; width: 100%; margin-top: 6px; background: var(--db-bg); color: var(--db-ink);
  border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 9px 10px; font: 600 14px var(--mono);
}
.db-card .acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.db-card button {
  font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--db-ink);
}
.db-card button:hover { border-color: var(--db-accent); }
.db-card button.primary { background: var(--db-accent); color: #062b25; border-color: var(--db-accent); }

/* ------------------------------------------------------------------ bocce ---

   GK's prototype's own look, as darts' and shuffleboard's are: full-screen,
   the court in perspective under a night sky, the HUD drawn on the canvas.
   Scoped under .bocce (not .bc, which is a Bananagrams cell). */
.bocce-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.bocce-line { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline; gap: 0.3rem 1.1rem; }
.bocce-who { color: var(--muted); }
.bocce-who b { color: var(--paper); font-size: 1.2rem; font-variant-numeric: tabular-nums; }
.bocce-who.up { color: var(--glow); }
.bocce-go { display: flex; justify-content: center; }

html.bocce-open, html.bocce-open body { overflow: hidden; overscroll-behavior: none; }
.bocce {
  --bo-bg: #0B1120; --bo-bg2: #111827; --bo-ink: #E5E7EB; --bo-muted: #8B93A7; --bo-accent: #00E0B8;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--bo-bg); color: var(--bo-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  touch-action: none;
}
.bocce[hidden] { display: none !important; }
.bocce-court { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.bocce-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.bocce-sheet[hidden] { display: none; }
.bocce-card {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--bo-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.bocce-card h2 { margin: 0 0 14px; font-size: 20px; font-weight: 800; color: var(--bo-ink); }
.bocce-card .row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.bocce-card label { display: block; font-size: 13px; font-weight: 700; margin: 8px 0; }
.bocce-card label.slider { margin-top: 14px; }
.bocce-card label .val { float: right; font: 600 12px var(--mono); color: var(--bo-accent); }
.bocce-card small { display: block; font-weight: 400; font-size: 12px; color: var(--bo-muted); margin-top: 2px; line-height: 1.4; }
.bocce-card input[type=range] { width: 100%; margin: 8px 0 2px; accent-color: var(--bo-accent); }
.bocce-card select {
  display: block; width: 100%; margin-top: 6px; background: var(--bo-bg); color: var(--bo-ink);
  border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 9px 10px; font: 600 14px var(--mono);
}
.bocce-card .rules { color: var(--bo-muted); font-size: 12.5px; line-height: 1.5; margin-top: 14px; }
.bocce-card .acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.bocce-card button {
  font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--bo-ink);
}
.bocce-card button:hover { border-color: var(--bo-accent); }
.bocce-card button.primary { background: var(--bo-accent); color: #062b25; border-color: var(--bo-accent); }

/* --------------------------------------------------------------- cornhole ---

   GK's prototype's own look, as bocce's is: full-screen, the lane and board
   in perspective under a night sky, the HUD drawn on the canvas. */
.cornhole-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.cornhole-line { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline; gap: 0.3rem 1.1rem; }
.cornhole-who { color: var(--muted); }
.cornhole-who b { color: var(--paper); font-size: 1.2rem; font-variant-numeric: tabular-nums; }
.cornhole-who.up { color: var(--glow); }
.cornhole-go { display: flex; justify-content: center; }

html.cornhole-open, html.cornhole-open body { overflow: hidden; overscroll-behavior: none; }
.cornhole {
  --ch-bg: #0B1120; --ch-bg2: #111827; --ch-ink: #E5E7EB; --ch-muted: #8B93A7; --ch-accent: #00E0B8;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--ch-bg); color: var(--ch-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  touch-action: none;
}
.cornhole[hidden] { display: none !important; }
.cornhole-lane { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.cornhole-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.cornhole-sheet[hidden] { display: none; }
.cornhole-card {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--ch-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.cornhole-card h2 { margin: 0 0 14px; font-size: 20px; font-weight: 800; color: var(--ch-ink); }
.cornhole-card label { display: block; font-size: 13px; font-weight: 700; margin: 8px 0; }
.cornhole-card label.slider { margin-top: 14px; }
.cornhole-card label .val { float: right; font: 600 12px var(--mono); color: var(--ch-accent); }
.cornhole-card small { display: block; font-weight: 400; font-size: 12px; color: var(--ch-muted); margin-top: 2px; line-height: 1.4; }
.cornhole-card input[type=range] { width: 100%; margin: 8px 0 2px; accent-color: var(--ch-accent); }
.cornhole-card select {
  display: block; width: 100%; margin-top: 6px; background: var(--ch-bg); color: var(--ch-ink);
  border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 9px 10px; font: 600 14px var(--mono);
}
.cornhole-card .rules { color: var(--ch-muted); font-size: 12.5px; line-height: 1.5; margin-top: 14px; }
.cornhole-card .acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.cornhole-card button {
  font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--ch-ink);
}
.cornhole-card button:hover { border-color: var(--ch-accent); }
.cornhole-card button.primary { background: var(--ch-accent); color: #062b25; border-color: var(--ch-accent); }

.pool-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.pool-line { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline; gap: 0.3rem 1.1rem; }
.pool-who { color: var(--muted); }
.pool-who small { color: var(--muted); font-size: 0.8rem; }
.pool-who.up { color: var(--glow); }
.pool-go { display: flex; justify-content: center; }

html.pool-open, html.pool-open body { overflow: hidden; overscroll-behavior: none; }
.pool {
  --pl-bg: #0B1120; --pl-bg2: #111827; --pl-ink: #E5E7EB; --pl-muted: #8B93A7; --pl-accent: #00E0B8;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--pl-bg); color: var(--pl-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  touch-action: none;
}
.pool[hidden] { display: none !important; }
.pool-cloth { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.pool-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.pool-sheet[hidden] { display: none; }
.pool-card {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--pl-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.pool-card h2 { margin: 0 0 14px; font-size: 20px; font-weight: 800; color: var(--pl-ink); }
.pool-card label { display: block; font-size: 13px; font-weight: 700; margin: 8px 0; }
.pool-card label.slider { margin-top: 14px; }
.pool-card label .val { float: right; font: 600 12px var(--mono); color: var(--pl-accent); }
.pool-card small { display: block; font-weight: 400; font-size: 12px; color: var(--pl-muted); margin-top: 2px; line-height: 1.4; }
.pool-card input[type=range] { width: 100%; margin: 8px 0 2px; accent-color: var(--pl-accent); }
.pool-card select {
  display: block; width: 100%; margin-top: 6px; background: var(--pl-bg); color: var(--pl-ink);
  border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 9px 10px; font: 600 14px var(--mono);
}
.pool-card .rules { color: var(--pl-muted); font-size: 12.5px; line-height: 1.5; margin-top: 14px; }
.pool-card .acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.pool-card button {
  font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--pl-ink);
}
.pool-card button:hover { border-color: var(--pl-accent); }
.pool-card button.primary { background: var(--pl-accent); color: #062b25; border-color: var(--pl-accent); }
.pool-card .row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.pool-card label[hidden], .pool-card small[hidden] { display: none; }
.pool-card .rules b { color: var(--pl-ink); }

/* ------------------------------------------------------------- yahtzee ---
   From public/yahtzeetable.css, the port's own. */
/* -------------------------------------------------------------- yahtzee ---

   GK's prototype, full-screen: the tray, the dice and the held row live on
   the canvas in /yahtzeetable.js. What is here is the panel it leaves in the
   room, and the table's DOM parts -- the scorecard, the Roll bar and the
   settings sheet. The prototype's own look, carried across: its colours,
   its card, its buttons.

   `--yz-lift` is how far the Roll bar stands up off the bottom to clear
   table mode's dock, on a phone too narrow to put it beside it;
   `--yz-right` is how much room it leaves the dock when it can go beside
   it. `--yz-card-top` and `--yz-card-bottom` are where the card fits
   between the tray and the bar; the table works all four out from where
   the dock actually is. */
.yz-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.yz-line { margin: 0; color: var(--muted); }
.yz-scores { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 1rem; }
.yz-who { color: var(--muted); }
.yz-who b { color: var(--paper); font-variant-numeric: tabular-nums; }
.yz-who.yz-up { color: var(--glow); }
.yz-go { display: flex; justify-content: center; }

html.yz-open, html.yz-open body { overflow: hidden; overscroll-behavior: none; }
.yz-table {
  --yz-bg: #0B1120; --yz-bg2: #111827; --yz-ink: #E5E7EB; --yz-muted: #8B93A7; --yz-accent: #00E0B8;
  --yz-lift: 0px; --yz-right: 12px; --yz-card-top: 50vh; --yz-card-bottom: 62px;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--yz-bg); color: var(--yz-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  overscroll-behavior: none;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
.yz-table[hidden] { display: none !important; }
.yz-felt { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.yz-table button { font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--yz-ink); }
.yz-table button:hover { border-color: var(--yz-accent); }
.yz-table button[hidden] { display: none; }

/* The Roll bar. */
.yz-tools {
  position: absolute; left: 0; right: 0; bottom: var(--yz-lift); z-index: 5; display: flex; gap: 6px; align-items: center;
  padding: 8px var(--yz-right) calc(env(safe-area-inset-bottom, 0px) + 8px) 10px;
  background: var(--yz-bg2); border-top: 1px solid rgba(255,255,255,.08);
}
.yz-table .yz-roll { padding: 10px 12px; font-size: 14px; min-width: 150px;
  background: var(--yz-accent); color: #062b25; border-color: var(--yz-accent); }
.yz-table .yz-roll:disabled { opacity: .35; background: transparent; color: var(--yz-muted); border-color: rgba(255,255,255,.12); cursor: default; }

/* The scorecard. */
.yz-card {
  position: absolute; left: 10px; right: 10px; z-index: 4;
  top: var(--yz-card-top); bottom: calc(var(--yz-card-bottom) + env(safe-area-inset-bottom, 0px));
  overflow-y: auto; touch-action: pan-y; border-radius: 12px;
  background: rgba(17,24,39,.9); border: 1px solid rgba(255,255,255,.08);
}
.yz-card table { width: 100%; border-collapse: collapse; font: 500 12px var(--mono); }
.yz-card th { font: 700 11px Karla, system-ui, sans-serif; color: var(--yz-muted); padding: 8px 6px 6px; text-align: center;
  border-bottom: 1px solid rgba(255,255,255,.08); position: sticky; top: 0; background: var(--yz-bg2);
  max-width: 5.5em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.yz-card th.yz-me { color: var(--yz-ink); }
.yz-card th.yz-cur { color: var(--yz-accent); }
.yz-card td { padding: 3px 6px; border-bottom: 1px solid rgba(255,255,255,.05); text-align: center; height: 28px; }
.yz-card td.yz-l { text-align: left; font: 600 12px Karla, system-ui, sans-serif; color: var(--yz-ink); white-space: nowrap; }
.yz-card td.yz-l .yz-hint { color: var(--yz-muted); font: 500 10px var(--mono); margin-left: 4px; }
.yz-card td.yz-v { color: var(--yz-ink); }
.yz-card td.yz-dim { color: rgba(139,147,167,.6); }
.yz-card td.yz-gold { color: #F5C542; font-weight: 600; }
.yz-card td.yz-flash { color: var(--yz-accent); font-weight: 700; animation: yz-flash 1.4s ease-out; }
.yz-card tr.yz-sub td { color: var(--yz-muted); font-size: 11px; }
.yz-card tr.yz-total td { font-weight: 700; border-top: 1px solid rgba(255,255,255,.12); }
.yz-card td.yz-pick button { width: 100%; padding: 4px 2px; font: 700 12px var(--mono); border-radius: 7px;
  background: rgba(0,224,184,.12); color: var(--yz-accent); border: 1px solid rgba(0,224,184,.4); }
.yz-card td.yz-pick button.yz-zero { color: rgba(240,82,90,.9); border-color: rgba(240,82,90,.35); background: rgba(240,82,90,.08); }
.yz-card .yz-over { padding: 12px; text-align: center; font: 700 14px Karla, system-ui, sans-serif; color: #F5C542;
  display: flex; gap: 12px; align-items: center; justify-content: center; }
.yz-card .yz-over button { padding: 8px 14px; font-size: 13px; }
@keyframes yz-flash { 0% { background: rgba(0,224,184,.35); } 100% { background: transparent; } }

/* The settings sheet. Its bottom ends above table mode's dock, as the other
   full-screen games' do -- on a phone that is where Done is. */
.yz-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 66px);
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.yz-sheet[hidden] { display: none; }
.yz-menu {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--yz-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.yz-menu h2 { margin: 0 0 10px; font-size: 20px; font-weight: 800; color: var(--yz-ink); }
.yz-menu .yz-lede { color: var(--yz-muted); font-size: 14px; line-height: 1.5; margin: 0 0 12px; }
.yz-menu .yz-rules { color: var(--yz-muted); font-size: 12.5px; line-height: 1.5; margin: 0 0 8px; }
.yz-menu .yz-rules b { color: var(--yz-ink); }
.yz-menu label.yz-check { display: flex; align-items: center; gap: 10px; margin: 14px 0 2px; font-size: 13px; font-weight: 700; }
.yz-menu label.yz-check input { width: 18px; height: 18px; accent-color: var(--yz-accent); }
.yz-menu small { display: block; font-weight: 400; font-size: 12px; color: var(--yz-muted); margin-top: 2px; line-height: 1.4; }
.yz-menu .yz-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.yz-menu button.yz-primary { background: var(--yz-accent); color: #062b25; border-color: var(--yz-accent); }
.yz-menu :disabled { opacity: 0.5; }

/* ------------------------------------------------------------ rummikub ---
   From public/rummytable.css, the port's own. */
/* -------------------------------------------------------------- rummikub ---

   GK's prototype, full-screen: the table lives in /rummytable.js. What is
   here is the panel it leaves in the room, and the table's DOM parts -- the
   toolbar and the settings sheet. `--rk-lift` is how far the toolbar stands
   up off the bottom to clear table mode's dock. */
.rk-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.rk-line { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline; gap: 0.3rem 1.1rem; }
.rk-who { color: var(--muted); }
.rk-who b { color: var(--paper); font-variant-numeric: tabular-nums; }
.rk-who small { color: var(--muted); font-size: 0.8rem; }
.rk-who.rk-up { color: var(--glow); }
.rk-say { margin: 0; }
.rk-go { display: flex; justify-content: center; }

html.rk-open, html.rk-open body { overflow: hidden; overscroll-behavior: none; }
.rk-root {
  --rk-bg: #0B1120; --rk-bg2: #111827; --rk-ink: #E5E7EB; --rk-muted: #8B93A7; --rk-accent: #00E0B8; --rk-lift: 0px;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--rk-bg); color: var(--rk-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  touch-action: none; overscroll-behavior: none;
}
.rk-root[hidden] { display: none !important; }
.rk-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.rk-root button {
  font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--rk-ink);
}
.rk-root button:hover { border-color: var(--rk-accent); }
.rk-root button[hidden] { display: none; }

/* The prototype's toolbar, standing on table mode's dock where the two
   would otherwise overlap. */
.rk-tools {
  position: absolute; left: 0; right: 0; bottom: var(--rk-lift); z-index: 5;
  padding: 8px 10px calc(env(safe-area-inset-bottom, 0px) + 8px);
  background: var(--rk-bg2); border-top: 1px solid rgba(255,255,255,.08);
}
.rk-tools[hidden] { display: none; }
.rk-btns { display: flex; gap: 6px; align-items: center; max-width: 560px; margin: 0 auto; }
.rk-btns button { padding: 10px 12px; font-size: 14px; border-radius: 10px; min-width: 42px; }
.rk-gap { flex: 1; }
.rk-btns .rk-end { border-color: rgba(255,255,255,.15); color: var(--rk-muted); }
.rk-btns .rk-end.rk-ready { background: var(--rk-accent); color: #062b25; border-color: var(--rk-accent); animation: rk-pulse 1.2s ease-in-out infinite; }
@keyframes rk-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
/* Off your turn, the buttons that act on the turn go quiet; sorting your
   rack, tidying the table and zooming are yours at any time. */
.rk-idle button:not([data-rk="sort"]):not([data-rk="tidy"]):not([data-rk="in"]):not([data-rk="out"]) { opacity: .4; }
@media (max-width: 400px) {
  .rk-btns { gap: 4px; }
  .rk-btns button { padding: 9px 7px; min-width: 34px; font-size: 13px; }
}

/* The settings sheet ends above table mode's dock, like the other
   full-screen games' -- its mic, camera and way back sit bottom right, over
   the bottom of the sheet, which on a phone is where Done is. */
.rk-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 66px);
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.rk-sheet[hidden] { display: none; }
.rk-card {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--rk-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
  -webkit-user-select: text; user-select: text;
}
.rk-card h2 { margin: 0 0 12px; font-size: 20px; font-weight: 800; color: var(--rk-ink); }
.rk-lede { color: var(--rk-muted); font-size: 14px; line-height: 1.5; margin: 0 0 12px; }
.rk-rules { display: flex; flex-direction: column; gap: 8px; color: var(--rk-muted); font-size: 12.5px; line-height: 1.5; }
.rk-rules b { color: var(--rk-ink); }
.rk-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.rk-card .rk-primary { background: var(--rk-accent); color: #062b25; border-color: var(--rk-accent); }

/* -------------------------------------------------------------- farkle ---
   From public/farkletable.css, the port's own. */
/* ---------------------------------------------------------------- farkle ---

   GK's prototype, full-screen: the tray and the dice live in
   /farkletable.js. What is here is the panel it leaves in the room, and the
   table's DOM parts -- the card under the tray, the Bank and Roll bar, the
   settings behind the ⚙. `--fk-lift` is how far the bar and the card stand
   up off the bottom to clear table mode's dock. */
.fk-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.fk-line { margin: 0; color: var(--muted); }
.fk-scores { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 1rem; }
.fk-who { color: var(--muted); }
.fk-who b { color: var(--paper); font-variant-numeric: tabular-nums; }
.fk-who.fk-up { color: var(--glow); }
.fk-go { display: flex; justify-content: center; }

html.fk-open, html.fk-open body { overflow: hidden; overscroll-behavior: none; }
.fk {
  --fk-bg: #0B1120; --fk-bg2: #111827; --fk-ink: #E5E7EB; --fk-muted: #8B93A7; --fk-accent: #00E0B8; --fk-gold: #F5C542; --fk-lift: 0px;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--fk-bg); color: var(--fk-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  touch-action: none;
}
.fk[hidden] { display: none !important; }
.fk-tray { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.fk button { font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--fk-ink); }
.fk button:hover { border-color: var(--fk-accent); }
.fk button[hidden] { display: none; }

/* The ⚙, top right of the scores. */
.fk .fk-gear {
  position: absolute; top: 10px; right: 10px; z-index: 5; width: 40px; height: 40px; padding: 0;
  font-size: 18px; line-height: 1; border-color: transparent; color: rgba(229,231,235,.7);
}

/* The card under the tray: this turn, what the selection is worth, and the
   scoring. `top` is set from the tray's bottom. */
.fk-score {
  position: absolute; left: 10px; right: 10px; z-index: 4;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 62px + var(--fk-lift));
  overflow-y: auto; border-radius: 12px; background: rgba(17,24,39,.9); border: 1px solid rgba(255,255,255,.08);
}
.fk-status { padding: 8px 14px 0; font: 600 11px var(--mono); color: var(--fk-muted); letter-spacing: .04em; }
.fk-turn { display: grid; grid-template-columns: auto 1fr; gap: 4px 16px; align-items: center; padding: 6px 14px 10px; border-bottom: 1px solid rgba(255,255,255,.08); }
.fk-lbl { display: block; font: 700 10px var(--mono); color: var(--fk-muted); letter-spacing: .06em; }
.fk-turn b { font: 800 30px var(--mono); color: var(--fk-ink); line-height: 1.1; }
.fk-selv { font: 500 12px var(--mono); color: var(--fk-muted); text-align: right; line-height: 1.5; }
.fk-ok { color: var(--fk-accent); font-weight: 700; font-size: 15px; }
.fk-bad { color: #F0525A; font-weight: 700; }
.fk-dim { color: rgba(139,147,167,.8); }
.fk-over { padding: 12px; text-align: center; font: 700 14px Karla, system-ui, sans-serif; color: var(--fk-gold); border-bottom: 1px solid rgba(255,255,255,.08); }
.fk-ref { width: 100%; border-collapse: collapse; font: 500 11px var(--mono); }
.fk-ref td { padding: 3px 14px; height: 24px; border-bottom: 1px solid rgba(255,255,255,.04); }
.fk-ref td.fk-l { text-align: left; font: 600 12px Karla, system-ui, sans-serif; color: var(--fk-muted); }
.fk-ref td.fk-v { text-align: right; color: var(--fk-ink); }

/* Bank and Roll. */
.fk-tools {
  position: absolute; left: 0; right: 0; bottom: var(--fk-lift); z-index: 5;
  padding: 8px 10px calc(env(safe-area-inset-bottom, 0px) + 8px);
  background: var(--fk-bg2); border-top: 1px solid rgba(255,255,255,.08);
}
.fk-tools[hidden] { display: none; }
.fk-btns { display: flex; gap: 6px; align-items: center; max-width: 520px; margin: 0 auto; }
.fk-btns button { padding: 10px 12px; font-size: 14px; border-radius: 10px; }
.fk-gap { flex: 1; }
.fk .fk-roll { background: var(--fk-accent); color: #062b25; border-color: var(--fk-accent); min-width: 130px; }
.fk .fk-roll:disabled { opacity: .35; background: transparent; color: var(--fk-muted); border-color: rgba(255,255,255,.12); }
.fk .fk-bank { border-color: rgba(245,197,66,.5); color: var(--fk-gold); min-width: 120px; }
.fk .fk-bank:disabled { opacity: .35; color: var(--fk-muted); border-color: rgba(255,255,255,.12); }
.fk .fk-bank.fk-ready { background: var(--fk-gold); color: #2b2000; border-color: var(--fk-gold); animation: fk-pulse 1.2s ease-in-out infinite; }
@keyframes fk-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }

/* The settings. */
.fk-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.fk-sheet[hidden] { display: none; }
.fk-sheet-card {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--fk-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.fk-sheet-card h2 { margin: 0 0 10px; font-size: 20px; font-weight: 800; color: var(--fk-ink); }
.fk-lede { color: var(--fk-muted); font-size: 13.5px; line-height: 1.5; margin: 0 0 12px; }
.fk-sheet-card label { display: block; font-size: 13px; font-weight: 700; margin: 8px 0; }
.fk-sheet-card label.fk-check { display: flex; align-items: center; gap: 10px; margin: 12px 0 4px; }
.fk-sheet-card label.fk-check input { width: 18px; height: 18px; accent-color: var(--fk-accent); }
.fk-sheet-card small { display: block; font-weight: 400; font-size: 12px; color: var(--fk-muted); margin-top: 2px; line-height: 1.4; }
.fk-sheet-card select {
  display: block; width: 100%; margin-top: 6px; background: var(--fk-bg); color: var(--fk-ink);
  border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 9px 10px; font: 600 14px var(--mono);
}
.fk-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.fk-rules { color: var(--fk-muted); font-size: 12.5px; line-height: 1.5; margin-top: 14px; }
.fk-rules b { color: var(--fk-ink); }
.fk-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.fk .fk-primary { background: var(--fk-accent); color: #062b25; border-color: var(--fk-accent); }
.fk-sheet-card :disabled { opacity: 0.5; }

/* --------------------------------------------------------- bananagrams ---
   From public/bananatable.css, the port's own. */
/* ----------------------------------------------------------- bananagrams ---

   GK's prototype, full-screen: the board, the tiles and the rack live on a
   canvas in /bananatable.js. What is here is the panel it leaves in the
   room, and the table's DOM parts -- the toolbar, the words of a called
   grid, and the settings sheet. `--bn-lift` is how far the toolbar stands
   up off the bottom to clear table mode's dock. */

.bn-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.bn-line { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline; gap: 0.3rem 1.1rem; }
.bn-who { color: var(--muted); }
.bn-who small { color: var(--muted); font-size: 0.8rem; }
.bn-who.bn-up { color: var(--glow); }
.bn-tag { font-size: 0.75rem; color: var(--muted); border: 1px solid currentColor; border-radius: 999px; padding: 0 0.4rem; margin-left: 0.25rem; }
.bn-go { display: flex; justify-content: center; }

html.bn-open, html.bn-open body { overflow: hidden; overscroll-behavior: none; }
.bn-table {
  --bn-bg: #0B1120; --bn-bg2: #111827; --bn-ink: #E5E7EB; --bn-muted: #8B93A7; --bn-accent: #00E0B8; --bn-warn: #F5C542; --bn-bad: #F0525A;
  --bn-lift: 0px;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--bn-bg); color: var(--bn-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  touch-action: none; overscroll-behavior: none;
}
.bn-table[hidden] { display: none !important; }
.bn-table [hidden] { display: none !important; }
.bn-board { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.bn-table button {
  font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--bn-ink);
}
.bn-table button:hover { border-color: var(--bn-accent); }
.bn-table button:disabled { opacity: .5; cursor: default; }

/* The toolbar: the prototype's, centred and no wider than a phone so that
   on a wide screen it can sit beside the dock rather than under it. */
.bn-tools {
  position: absolute; left: 0; right: 0; bottom: var(--bn-lift); z-index: 5;
  display: flex; gap: 6px; align-items: center;
  max-width: 560px; margin: 0 auto;
  padding: 8px 10px calc(env(safe-area-inset-bottom, 0px) + 8px);
  background: var(--bn-bg2); border-top: 1px solid rgba(255,255,255,.08);
}
.bn-tools button { padding: 10px 12px; font-size: 14px; border-radius: 10px; min-width: 42px; }
.bn-gap { flex: 1; }
.bn-tools .bn-peel { border-color: rgba(255,255,255,.15); color: var(--bn-muted); }
.bn-tools .bn-peel.bn-ready {
  background: var(--bn-accent); color: #062b25; border-color: var(--bn-accent);
  animation: bn-pulse 1.2s ease-in-out infinite;
}
.bn-tools .bn-dump.bn-on { border-color: var(--bn-warn); color: var(--bn-warn); }
.bn-tools .bn-stuck { border-color: rgba(245,197,66,.5); color: var(--bn-warn); font-size: 13px; }
@keyframes bn-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
@media (prefers-reduced-motion: reduce) { .bn-tools .bn-peel.bn-ready { animation: none; } }

/* A called grid's words, above the toolbar: tap one you won't have. */
.bn-read {
  position: absolute; left: 0; right: 0; z-index: 4;
  max-width: 560px; margin: 0 auto; padding: 10px 12px;
  background: rgba(17,24,39,.94); border-top: 1px solid rgba(255,255,255,.08);
  backdrop-filter: blur(6px);
}
.bn-read-say { margin: 0 0 8px; font-size: 13px; font-weight: 700; color: var(--bn-ink); text-align: center; }
.bn-words { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; max-height: 28vh; overflow-y: auto; }
.bn-table .bn-word {
  font: 600 13px ui-monospace, SFMono-Regular, Menlo, monospace; text-transform: uppercase; letter-spacing: .04em;
  padding: 6px 10px; border-radius: 8px; border: 1px solid rgba(255,255,255,.15); color: var(--bn-ink); background: transparent;
}
span.bn-word { display: inline-block; }
.bn-table .bn-word.bn-odd { border-style: dashed; border-color: rgba(240,82,90,.6); }
.bn-table .bn-word.bn-mine { border-color: var(--bn-warn); color: var(--bn-warn); }
.bn-table .bn-word.bn-out { background: rgba(240,82,90,.2); border-color: var(--bn-bad); color: #ff9aa0; text-decoration: line-through; }
.bn-read-acts { display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 8px; flex-wrap: wrap; }
.bn-table .bn-happy { padding: 9px 14px; font-size: 14px; background: var(--bn-accent); color: #062b25; border-color: var(--bn-accent); }
.bn-table .bn-happy:disabled { background: transparent; color: var(--bn-muted); border-color: rgba(255,255,255,.15); }
.bn-read-wait { font: 500 11px ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--bn-muted); }

/* The settings sheet ends above table mode's dock, like every other
   full-screen game's: on a phone the dock sits where Done would be. */
.bn-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 66px);
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.bn-card {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--bn-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
  -webkit-user-select: text; user-select: text; touch-action: auto;
}
.bn-card h2 { margin: 0 0 10px; font-size: 20px; font-weight: 800; color: var(--bn-ink); }
.bn-lede { color: var(--bn-muted); font-size: 14px; line-height: 1.5; margin: 0 0 12px; }
.bn-rules { color: var(--bn-muted); font-size: 12.5px; line-height: 1.5; display: grid; gap: 6px; margin-bottom: 8px; }
.bn-rules b { color: var(--bn-ink); }
.bn-card label.bn-check { display: flex; align-items: center; gap: 10px; margin: 14px 0 4px; font-size: 13px; font-weight: 700; }
.bn-card label.bn-check input { width: 18px; height: 18px; accent-color: var(--bn-accent); }
.bn-card small { display: block; font-weight: 400; font-size: 12px; color: var(--bn-muted); margin-top: 2px; line-height: 1.4; }
.bn-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.bn-table .bn-primary { background: var(--bn-accent); color: #062b25; border-color: var(--bn-accent); }

/* ---------------------------------------------------------- war on war ---

   GK's prototype, full-screen: the nations, the stage, the wheel and the
   hand live on the canvas in /waronwartable.js. What is here is the panel it
   leaves in the room, and the table's DOM parts -- the commentary, the Queue
   bar and the settings sheet. The prototype's own look, carried across.

   `--wow-lift` is how far the Queue bar stands up off the bottom to clear
   table mode's dock, on a phone too narrow to put it beside it;
   `--wow-right` is how much room it leaves the dock when it can go beside
   it. The table works both out from where the dock actually is. */
.wow-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.wow-line { margin: 0; color: var(--muted); }
.wow-pops { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 1rem; }
.wow-who { color: var(--muted); }
.wow-who b { color: var(--paper); font-variant-numeric: tabular-nums; }
.wow-who.up { color: var(--glow); }
.wow-who.gone b { color: var(--muted); }
.wow-go { display: flex; justify-content: center; }

html.wow-open, html.wow-open body { overflow: hidden; overscroll-behavior: none; }
.wow-table {
  --wow-bg: #0B1120; --wow-bg2: #111827; --wow-ink: #E5E7EB; --wow-muted: #8B93A7; --wow-accent: #00E0B8;
  --wow-lift: 0px; --wow-right: 10px;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--wow-bg); color: var(--wow-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  overscroll-behavior: none;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
.wow-table[hidden] { display: none !important; }
.wow-felt { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.wow-table button { font: 700 15px Karla, system-ui, sans-serif; padding: 12px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--wow-ink); }
.wow-table button:hover { border-color: var(--wow-accent); }
.wow-table button[hidden] { display: none; }

/* The commentary, between the stage and the hand. Tap it to read back. */
.wow-log {
  position: absolute; left: 10px; right: 10px; z-index: 3;
  overflow-y: auto; padding: 5px 10px; border-radius: 10px; cursor: pointer;
  background: rgba(17,24,39,.78); border: 1px solid rgba(255,255,255,.08); backdrop-filter: blur(6px);
  font: 500 11px var(--mono); color: var(--wow-muted); line-height: 1.45; transition: height .2s;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 10px);
}
.wow-log.open { height: 50vh !important; top: 60px !important; }
.wow-log .line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wow-log .line.hl { color: var(--wow-accent); }
.wow-log b { font-weight: 700; }
.wow-log b.red { color: #ff7b84; }
.wow-log b.win { color: #F5C542; }

.wow-tools {
  position: absolute; left: 0; right: 0; bottom: var(--wow-lift); z-index: 5; display: flex; gap: 6px; align-items: center;
  padding: 8px var(--wow-right) calc(env(safe-area-inset-bottom, 0px) + 8px) 10px;
  background: var(--wow-bg2); border-top: 1px solid rgba(255,255,255,.08);
}
.wow-tools .wow-gap { flex: 1; }
.wow-table .wow-tools button { padding: 10px 12px; font-size: 14px; min-width: 42px; }
.wow-table .wow-queue, .wow-table .wow-again { min-width: 150px;
  background: var(--wow-accent); color: #062b25; border-color: var(--wow-accent); }
.wow-table .wow-queue:disabled { opacity: .35; background: transparent; color: var(--wow-muted); border-color: rgba(255,255,255,.12); cursor: default; }

/* The settings sheet. Its bottom ends above table mode's dock, as the other
   full-screen games' do -- on a phone that is where Done is. */
.wow-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 66px);
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.wow-sheet[hidden] { display: none; }
.wow-menu {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--wow-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.wow-menu h2 { margin: 0 0 10px; font-size: 20px; font-weight: 800; color: var(--wow-ink); }
.wow-menu .wow-lede { color: var(--wow-muted); font-size: 14px; line-height: 1.5; margin: 0 0 12px; }
.wow-menu .wow-rules { color: var(--wow-muted); font-size: 12.5px; line-height: 1.5; margin: 0 0 8px; }
.wow-menu .wow-rules b { color: var(--wow-ink); }
.wow-menu label.wow-check { display: flex; align-items: center; gap: 10px; margin: 14px 0 2px; font-size: 13px; font-weight: 700; }
.wow-menu label.wow-check input { width: 18px; height: 18px; accent-color: var(--wow-accent); }
.wow-menu small { display: block; font-weight: 400; font-size: 12px; color: var(--wow-muted); margin-top: 2px; line-height: 1.4; }
.wow-menu .wow-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.wow-menu button.wow-primary { background: var(--wow-accent); color: #062b25; border-color: var(--wow-accent); }
.wow-menu :disabled { opacity: 0.5; }

/* ---------------------------------------------------------------- qwirkle ---

   GK's prototype, full-screen: the board, the tiles and the rack live on the
   canvas in /qwirkletable.js. What is here is the panel it leaves in the
   room, and the table's DOM parts -- the toolbar and the settings sheet.

   `--qw-lift` is how far the toolbar stands up off the bottom to clear table
   mode's dock, on a phone too narrow to put it beside it; `--qw-right` is
   how much room it leaves the dock when it can go beside it. */
.qw-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.qw-line { margin: 0; color: var(--muted); }
.qw-scores { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 1rem; }
.qw-who { color: var(--muted); }
.qw-who b { color: var(--paper); font-variant-numeric: tabular-nums; }
.qw-who.up { color: var(--glow); }
.qw-star { color: #F5C542; font-size: 0.75em; }
.qw-go { display: flex; justify-content: center; }

html.qw-open, html.qw-open body { overflow: hidden; overscroll-behavior: none; }
.qw-table {
  --qw-bg: #0B1120; --qw-bg2: #111827; --qw-ink: #E5E7EB; --qw-muted: #8B93A7; --qw-accent: #00E0B8;
  --qw-lift: 0px; --qw-right: 10px;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--qw-bg); color: var(--qw-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  overscroll-behavior: none;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
.qw-table[hidden] { display: none !important; }
.qw-felt { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.qw-table button { font: 700 15px Karla, system-ui, sans-serif; padding: 10px 12px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--qw-ink); min-width: 42px; }
.qw-table button:hover { border-color: var(--qw-accent); }
.qw-table button:disabled { opacity: .35; cursor: default; }

.qw-tools {
  position: absolute; left: 0; right: 0; bottom: var(--qw-lift); z-index: 5; display: flex; gap: 6px; align-items: center;
  padding: 8px var(--qw-right) calc(env(safe-area-inset-bottom, 0px) + 8px) 10px;
  background: var(--qw-bg2); border-top: 1px solid rgba(255,255,255,.08);
}
.qw-tools .qw-gap { flex: 1; }
/* Nobody's turn but somebody else's: the controls step back rather than
   disappearing, so the toolbar does not change shape under your thumb. */
.qw-tools.idle .qw-play, .qw-tools.idle .qw-swap, .qw-tools.idle [data-qw="undo"] { opacity: .4; }
.qw-table .qw-play { min-width: 96px; }
.qw-table .qw-play.ready { background: var(--qw-accent); color: #062b25; border-color: var(--qw-accent); }
.qw-table .qw-swap.on { border-color: #F5C542; color: #F5C542; }

.qw-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 66px);
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.qw-sheet[hidden] { display: none; }
.qw-menu {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--qw-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.qw-menu h2 { margin: 0 0 10px; font-size: 20px; font-weight: 800; color: var(--qw-ink); }
.qw-menu .qw-lede { color: var(--qw-muted); font-size: 14px; line-height: 1.5; margin: 0 0 12px; }
.qw-menu .qw-rules { color: var(--qw-muted); font-size: 12.5px; line-height: 1.5; margin: 0 0 8px; }
.qw-menu .qw-rules b { color: var(--qw-ink); }
.qw-menu label.qw-check { display: flex; align-items: center; gap: 10px; margin: 14px 0 2px; font-size: 13px; font-weight: 700; }
.qw-menu label.qw-check input { width: 18px; height: 18px; accent-color: var(--qw-accent); }
.qw-menu small { display: block; font-weight: 400; font-size: 12px; color: var(--qw-muted); margin-top: 2px; line-height: 1.4; }
.qw-menu .qw-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.qw-menu button.qw-primary { background: var(--qw-accent); color: #062b25; border-color: var(--qw-accent); }
.qw-menu :disabled { opacity: 0.5; }

/* ---------------------------------------------------------------- jigsaw ---

   GK's prototype, full-screen: the frame, the pieces and the picture live on
   the canvas in /jigsawtable.js. What is here is the panel it leaves in the
   room -- how far along it is, who has put what in, and the way in -- and the
   table's DOM parts, the toolbar and the settings sheet.

   `--jig-lift` is how far the toolbar stands up off the bottom to clear table
   mode's dock, on a phone too narrow to put it beside it; `--jig-right` is
   how much room it leaves the dock when it can go beside it. */
.jig-panel { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }
.jig-line { margin: 0; color: var(--muted); }
.jig-bar { width: min(22rem, 100%); height: 0.5rem; border-radius: 0.25rem; background: rgba(255,255,255,.08); overflow: hidden; }
.jig-bar > span { display: block; height: 100%; background: var(--glow); border-radius: 0.25rem; }
.jig-board { margin: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 1rem; }
.jig-who { color: var(--muted); }
.jig-who b { color: var(--paper); font-variant-numeric: tabular-nums; }
.jig-go { display: flex; justify-content: center; }
.jig-shot { width: min(18rem, 100%); border-radius: 0.6rem; overflow: hidden; border: 1px solid var(--line); }
.jig-shot img { display: block; width: 100%; }
.jig-picks { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.4rem; }

html.jig-open, html.jig-open body { overflow: hidden; overscroll-behavior: none; }
.jig-table {
  --jig-bg: #0B1120; --jig-bg2: #111827; --jig-ink: #E5E7EB; --jig-muted: #8B93A7; --jig-accent: #00E0B8;
  --jig-lift: 0px; --jig-right: 10px;
  position: fixed; inset: 0; z-index: 1000;
  background: var(--jig-bg); color: var(--jig-ink);
  font-family: Karla, ui-sans-serif, system-ui, sans-serif;
  overscroll-behavior: none;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
.jig-table[hidden] { display: none !important; }
.jig-felt { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.jig-table button { font: 700 15px Karla, system-ui, sans-serif; padding: 10px 12px; border-radius: 10px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: var(--jig-ink); min-width: 42px; }
.jig-table button:hover { border-color: var(--jig-accent); }
.jig-tools {
  position: absolute; left: 0; right: 0; bottom: var(--jig-lift); z-index: 5; display: flex; gap: 6px; align-items: center;
  padding: 8px var(--jig-right) calc(env(safe-area-inset-bottom, 0px) + 8px) 10px;
  background: var(--jig-bg2); border-top: 1px solid rgba(255,255,255,.08);
}
.jig-tools .jig-gap { flex: 1; }
.jig-table .jig-ghost.on { border-color: var(--jig-accent); color: var(--jig-accent); }
.jig-sheet {
  position: absolute; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: 20px;
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 66px);
  background: rgba(11,17,32,.82); backdrop-filter: blur(8px);
}
.jig-sheet[hidden] { display: none; }
.jig-menu {
  width: 100%; max-width: 380px; max-height: 100%; overflow: auto; background: var(--jig-bg2);
  border: 1px solid rgba(255,255,255,.08); border-radius: 18px; padding: 24px; box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.jig-menu h2 { margin: 0 0 10px; font-size: 20px; font-weight: 800; color: var(--jig-ink); }
.jig-menu .jig-lede { color: var(--jig-muted); font-size: 14px; line-height: 1.5; margin: 0 0 12px; }
.jig-menu .jig-rules { color: var(--jig-muted); font-size: 12.5px; line-height: 1.5; margin: 0 0 8px; }
.jig-menu .jig-rules b { color: var(--jig-ink); }
.jig-menu label.jig-check { display: flex; align-items: center; gap: 10px; margin: 14px 0 2px; font-size: 13px; font-weight: 700; }
.jig-menu label.jig-check input { width: 18px; height: 18px; accent-color: var(--jig-accent); }
.jig-menu small { display: block; font-weight: 400; font-size: 12px; color: var(--jig-muted); margin-top: 2px; line-height: 1.4; }
.jig-menu .jig-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.jig-menu button.jig-primary { background: var(--jig-accent); color: #062b25; border-color: var(--jig-accent); }

/* ------------------------------------------------------------------ admin ---

   The owner's page: people and their keys, rooms and their furniture, the
   invitations, and the two things that run themselves -- with a bar across
   the top to jump between them, because it is a long page and the section you
   want is rarely the first. */
.adm-nav {
  position: sticky; top: 0; z-index: 5;
  display: flex; flex-wrap: wrap; gap: 0.35rem;
  margin: 0.2rem 0 1.4rem; padding: 0.55rem 0;
  background: var(--ink);
  border-bottom: 1px solid var(--line);
}
.adm-nav a {
  padding: 0.3rem 0.75rem; border: 1px solid var(--line); border-radius: 999px;
  font-size: 0.85rem; color: var(--paper); text-decoration: none;
}
.adm-nav a:hover { border-color: var(--glow); }
.adm-section { margin-bottom: 2.6rem; scroll-margin-top: 4rem; }
.adm-section > .lede { max-width: 62ch; }
.adm-section h3 { margin: 1.4rem 0 0.6rem; font-size: 1rem; }
.adm-small { font-size: 0.85rem; margin-top: 0.7rem; max-width: 62ch; }

/* Saved: said quietly in the corner, and loudly only when it did not. */
.adm-saved {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 20; margin: 0;
  padding: 0.45rem 0.85rem; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--line);
  font-size: 0.85rem; color: var(--paper);
  opacity: 0; transform: translateY(0.4rem); pointer-events: none;
  transition: opacity 160ms ease, transform 160ms ease;
}
.adm-saved.on { opacity: 1; transform: none; }
.adm-saved.bad { border-color: var(--bad); color: var(--bad); }

/* People against rooms: a tick is a key. */
.adm-scroll { overflow-x: auto; }
.adm-access { border-collapse: collapse; min-width: 100%; }
.adm-access th, .adm-access td {
  padding: 0.5rem 0.6rem; border-bottom: 1px solid var(--line); text-align: center;
}
.adm-access th:first-child, .adm-access td:first-child { text-align: left; }
.adm-access thead th { vertical-align: bottom; font-weight: 600; font-size: 0.85rem; }
.adm-access thead th span { display: block; }
.adm-access thead th small { display: block; font-weight: 400; font-size: 0.7rem; color: var(--dim); }
.adm-access thead th.shut small { color: var(--glow); }
.adm-who strong { display: block; }
.adm-who small { display: block; color: var(--dim); font-size: 0.75rem; }
.adm-key.open { opacity: 0.6; }
.adm-key.always { font-family: var(--mono); font-size: 0.72rem; color: var(--dim); }
.adm-key input { width: 1.15rem; height: 1.15rem; accent-color: var(--glow); cursor: pointer; }

/* A room, and its furniture in the order you meet it. */
.adm-add { margin-bottom: 1rem; }
.adm-room {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 0.8rem 0.9rem; margin-bottom: 0.8rem; scroll-margin-top: 4rem;
}
.adm-room-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 0.5rem 1rem; }
.adm-room-name { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.6rem; }
.adm-icon { font-size: 1.2rem; }
.adm-badge {
  font-family: var(--mono); font-size: 0.7rem; padding: 0.05rem 0.45rem;
  border: 1px solid var(--line); border-radius: 999px; color: var(--dim);
}
.adm-badge.shut { color: var(--glow); border-color: var(--glow); }

.adm-furniture, .adm-games { list-style: none; margin: 0; padding: 0; }
.adm-furniture { margin-top: 0.7rem; border-top: 1px dashed var(--line); }
.adm-fixture { border-bottom: 1px dashed var(--line); padding: 0.45rem 0; background: var(--ink); }
.adm-fixture-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.6rem; }
.adm-kind { font-size: 0.72rem; color: var(--dim); min-width: 6.5rem; }
.adm-rename { display: flex; align-items: center; gap: 0.4rem; flex: 1; min-width: 12rem; }
.adm-rename input[type="text"] { flex: 1; min-width: 8rem; }
.adm-moves { display: inline-flex; gap: 0.1rem; }
.adm-moves .linkish { padding: 0 0.3rem; color: var(--dim); }
.adm-moves .linkish:hover { color: var(--glow); }
.adm-takeout .linkish { color: var(--dim); }

/* The handle. Touch is left to the script while it is held, or dragging a row
   on a phone would scroll the page instead. */
.adm-grip {
  touch-action: none; cursor: grab; flex: none;
  background: none; border: 0; padding: 0.2rem 0.35rem;
  color: var(--dim); font-size: 1.05rem; line-height: 1;
}
.adm-grip:hover { color: var(--glow); }
.adm-furniture .dragging, .adm-games .dragging {
  outline: 1px dashed var(--glow); outline-offset: 2px; border-radius: 6px;
}
.dragging .adm-grip { cursor: grabbing; color: var(--glow); }

/* The games on a piece of furniture, in the order its table offers them. */
.adm-games-wrap { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.3rem 0.8rem; margin: 0.45rem 0 0.1rem 2rem; }
.adm-games-label { font-size: 0.78rem; padding-top: 0.25rem; min-width: 3.8rem; }
.adm-games { display: flex; flex-direction: column; gap: 0.1rem; flex: 1; min-width: 14rem; }
.adm-game { display: flex; align-items: center; gap: 0.35rem; padding: 0.1rem 0.25rem; background: var(--ink); }
.adm-game label { display: inline-flex; align-items: center; gap: 0.45rem; flex: 1; font-size: 0.88rem; cursor: pointer; }
.adm-game input { accent-color: var(--glow); }
.adm-game.off label { color: var(--dim); }
.adm-furnish { margin: 0.6rem 0 0; }

/* Folded away: how a room looks, and the parts that run themselves. */
.adm-fold { margin-top: 0.7rem; }
.adm-fold > summary { cursor: pointer; color: var(--muted); font-size: 0.88rem; }
.adm-fold[open] > summary { color: var(--paper); margin-bottom: 0.5rem; }
.adm-house .adm-fold { border: 1px solid var(--line); border-radius: var(--radius); padding: 0.6rem 0.8rem; }


/* ---- call health ------------------------------------------------------ */

/* A dot on a face whose connection is less than good. Nothing when it is. */
.tile[data-link]::after {
  content: ""; position: absolute; top: 0.45rem; left: 0.45rem;
  width: 0.6rem; height: 0.6rem; border-radius: 50%;
  background: #d9a441; box-shadow: 0 0 0 2px var(--panel);
}
.tile[data-link="poor"]::after, .tile[data-link="down"]::after { background: var(--bad); }
.tile[data-link="connecting"]::after { background: var(--muted); animation: link-wait 1.2s ease-in-out infinite; }
@keyframes link-wait { 50% { opacity: 0.35; } }

.hear {
  display: inline-flex; align-items: center; gap: 0.4rem;
  margin: -0.3rem 0 0.8rem; padding: 0.5rem 0.95rem;
  border-radius: 999px; border: 1px solid var(--glow);
  background: var(--glow-soft); color: var(--paper);
  font: inherit; font-weight: 600; cursor: pointer;
}
.table-dock .hear { margin: 0 0 0.4rem; }

.call-health {
  margin: -0.3rem 0 1rem; padding: 0.7rem 0.9rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); font-size: 0.85rem;
}
.call-health-head { display: flex; align-items: baseline; gap: 0.7rem; margin-bottom: 0.4rem; }
.call-health .note { color: var(--muted); }
.call-health-head .note { flex: 1; min-width: 0; }
.call-health-scroll { overflow-x: auto; }
.call-health table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 0.78rem; }
.call-health th { text-align: left; font-weight: 600; color: var(--muted); padding: 0.2rem 0.7rem 0.2rem 0; white-space: nowrap; }
.call-health td { padding: 0.3rem 0.7rem 0.3rem 0; white-space: nowrap; border-top: 1px solid var(--line); }
.link-dot { display: inline-block; width: 0.5rem; height: 0.5rem; margin-right: 0.35rem; border-radius: 50%; background: #5fb37a; vertical-align: middle; }
.call-health tr[data-grade="fair"] .link-dot { background: #d9a441; }
.call-health tr[data-grade="poor"] .link-dot, .call-health tr[data-grade="down"] .link-dot { background: var(--bad); }
.call-health tr[data-grade="connecting"] .link-dot { background: var(--muted); }

/* ---- who's about: a dot on a face ------------------------------------ */

.live-face { position: relative; display: inline-flex; flex: none; }
.faces .live-face:not(:first-child) { margin-left: -0.6rem; }
.activity-dot {
  position: absolute; right: -1px; bottom: -1px;
  width: 32%; height: 32%; min-width: 0.5rem; min-height: 0.5rem; max-width: 0.8rem; max-height: 0.8rem;
  border-radius: 50%; background: #3fb96b; box-shadow: 0 0 0 2px var(--panel);
}
.live-face[data-activity="idle"] .activity-dot { background: #e2b340; }
.live-face[data-activity="away"] .activity-dot { background: #7c7f86; }
.live-face[data-activity="offline"] .activity-dot { background: var(--panel); box-shadow: inset 0 0 0 2px #7c7f86, 0 0 0 2px var(--panel); }
.tile.has-video .live-face { display: none; }

/* ---- photos dragged over the window ------------------------------------ */

.dropzone {
  position: fixed; inset: 0.75rem; z-index: 900;
  display: grid; place-items: center; text-align: center;
  border: 3px dashed var(--glow); border-radius: calc(var(--radius) * 2);
  background: rgba(18, 13, 10, 0.78); color: var(--paper);
  font-size: 1.35rem; font-weight: 700;
  pointer-events: none; opacity: 0; transition: opacity 120ms ease;
}
.dropzone.on { opacity: 1; }
.dropzone.no { border-color: var(--muted); }
.dropzone small { display: block; margin-top: 0.4rem; font-size: 0.9rem; font-weight: 500; color: var(--muted); }

/* ---- the call's dock, moved and tucked -------------------------------- */

.table-dock[data-side="left"] { right: auto; left: 12px; align-items: flex-start; }
.dock-tools { display: flex; gap: 6px; align-self: flex-end; }
.table-dock[data-side="left"] .dock-tools { align-self: flex-start; }
.dock-tool {
  display: inline-flex; align-items: center; justify-content: center; gap: 2px;
  min-width: 1.6rem; height: 1.6rem; padding: 0 0.35rem;
  border: 1px solid var(--line); border-radius: 999px;
  background: rgba(17, 12, 9, 0.75); color: var(--muted);
  font: inherit; font-size: 0.8rem; line-height: 1; cursor: pointer;
}
.dock-tool:hover { border-color: var(--glow); color: var(--paper); }
.dock-count { font-family: var(--mono); font-size: 0.7rem; }

/* ---- the call's rail, across the top of a game ------------------------ */

.table-rail {
  position: fixed; top: 0; left: 0; right: 0; height: var(--rail-h, 0px);
  z-index: 1100; box-sizing: border-box;
  display: flex; align-items: center; gap: 0.6rem; padding: 0 0.6rem;
  background: rgba(12, 9, 7, 0.9); border-bottom: 1px solid var(--line);
  backdrop-filter: blur(8px);
}
.rail-faces { flex: 1; min-width: 0; overflow-x: auto; overflow-y: hidden; }
.rail-faces::-webkit-scrollbar { display: none; }
.table-rail .tiles.strip { flex-wrap: nowrap; justify-content: flex-start; margin: 0; }
/* Kept at their size and scrolled past, rather than squeezed thinner with
   every person who joins: five faces in a row were five slivers. */
.table-rail .tiles.strip .tile { flex: 0 0 92px; width: 92px; height: calc(var(--rail-h, 84px) - 20px); }
.rail-note { color: var(--muted); font-size: 0.8rem; white-space: nowrap; }
.rail-note.warn { color: var(--bad); margin: 0; }
.rail-btns { display: flex; gap: 8px; margin-left: auto; flex: none; }
.table-rail[data-small] { justify-content: flex-end; }
.table-rail[data-small] .dock-btn { width: 2rem; height: 2rem; }
.table-rail[data-small] .dock-btn svg { width: 15px; height: 15px; }
.table-rail .hear { margin: 0; padding: 0.3rem 0.7rem; font-size: 0.8rem; white-space: nowrap; }

/* And the page below it, when the rail is over a room rather than a game. */
html.rail-on body { padding-top: var(--rail-h, 0px); }

/* Every table, below it. The roots are all fixed at inset 0, so moving the
   top down is the whole of it. */
html.rail-on .tt, html.rail-on .pk, html.rail-on .cornhole, html.rail-on .bocce,
html.rail-on .db, html.rail-on .golf, html.rail-on .pool, html.rail-on .sb,
html.rail-on .bn-table, html.rail-on .qw-table, html.rail-on .rk-root,
html.rail-on .fk, html.rail-on .yz-table, html.rail-on .wow-table,
html.rail-on .jig-table {
  top: var(--rail-h, 0px);
}

/* ------------------------------------------------------------- bots, asked for */

/* Who is at the table, one chip each, with the two things you can do about a
   chair before the deal: pick a partner, or send a bot back to the room. */
.seat-list {
  list-style: none; margin: 0.2rem 0 0.8rem; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.45rem;
}
.seat-chip {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.3rem 0.35rem 0.3rem 0.7rem;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel-lit); font-size: 0.85rem;
}
.seat-chip.me { border-color: var(--glow); }
.seat-chip > span:first-child { padding-right: 0.3rem; }
.seat-btn {
  font: inherit; font-size: 0.72rem; line-height: 1;
  padding: 0.3rem 0.55rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--panel); color: var(--muted);
  cursor: pointer;
}
.seat-btn:hover { color: var(--paper); border-color: var(--glow); }
.seat-tag {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.05em;
  color: var(--glow); padding-right: 0.4rem;
}
.seat-sides { margin: 0 0 0.8rem; font-size: 0.9rem; color: var(--paper); }
.seat-sides span { color: var(--dim); font-family: var(--mono); font-size: 0.7rem; margin: 0 0.4rem; }
.seat-note { margin: 0 0 0.8rem; font-size: 0.8rem; color: var(--muted); }

.bot-add { display: inline-flex; align-items: center; gap: 0.4rem; }
.bot-add select, .bot-invite select { max-width: 12rem; padding: 0.35rem 0.5rem; font-size: 0.85rem; }

.bot-picker { max-height: 20rem; }
.bot-invite {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--line);
}
.bot-invite select { flex: 1; max-width: none; }
.bot-picker .status { margin: 0.5rem 0.8rem; }
.bot-row { cursor: default; }
.bot-row:hover { background: none; }

/* What a bot just said, under its face. */
.talk-layer { position: fixed; inset: 0; pointer-events: none; z-index: 400; }
.talk-bubble {
  position: absolute;
  padding: 0.45rem 0.7rem; border-radius: 12px;
  background: var(--paper); color: var(--ink);
  font-size: 0.85rem; line-height: 1.3;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  opacity: 0; transform: translateY(-4px);
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.talk-bubble.on { opacity: 1; transform: none; }
.talk-bubble b { display: block; font-size: 0.68rem; font-weight: 600; color: #7a5a2e; letter-spacing: 0.02em; }
.talk-bubble.pointed::before {
  content: ""; position: absolute; top: -6px; left: 18px;
  border: 6px solid transparent; border-top: 0; border-bottom-color: var(--paper);
}
.tt-log .line.talk { color: var(--tt-ink); font-style: italic; white-space: normal; }
.tt-log .line.talk b { font-style: normal; }

/* ------------------------------------------------------------ admin: bots */

/* ------------------------------------------------------------ cryptics ---

   The admin's measuring instrument: a batch of clues, and which of them the
   checker would have let through. */
.cry-run { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.8rem; margin: 1rem 0 0.4rem; }
.cry-run label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.8rem; color: var(--muted); }
.cry-run input { min-width: 18rem; }
.cry-run small { flex-basis: 100%; color: var(--muted); font-size: 0.78rem; }
.cry-rate { color: var(--glow); font-variant-numeric: tabular-nums; }
.cry-bar { display: block; width: 8rem; height: 6px; border-radius: 3px; background: rgba(255,255,255,0.08); overflow: hidden; }
.cry-bar i { display: block; height: 100%; background: var(--glow); }

.cry-list { list-style: none; margin: 0.6rem 0 1.2rem; padding: 0; display: grid; gap: 0.6rem; }
.cry {
  padding: 0.7rem 0.9rem; border-radius: 10px;
  background: rgba(255, 255, 255, 0.03); border-left: 3px solid var(--line);
}
.cry-ok { border-left-color: var(--glow); }
.cry-no { border-left-color: #e0a326; }
.cry-clue { margin: 0 0 0.3rem; font-size: 1rem; color: var(--paper); }
.cry-how { margin: 0; font-size: 0.78rem; color: var(--muted); font-family: var(--mono); line-height: 1.6; }
.cry-ans { color: var(--glow); font-weight: 700; letter-spacing: 0.06em; margin-right: 0.4rem; }
.cry-faults { margin: 0.4rem 0 0; padding-left: 1.1rem; font-size: 0.8rem; color: #f5c542; }

/* What the voices have cost today, and whether a room has gone quiet. */
/* ---------------------------------------------------- today's three ---

   Three separate little locks under the puzzle: each clue its own box, each
   either open or not. Getting one wrong says nothing about the other two,
   which is how a cryptic works at a kitchen table. */
.cryptics { margin-top: 2.5rem; }
.cryptics h2 { margin-bottom: 0.3rem; }
.cry-set { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: 0.8rem; counter-reset: cry; }
.cry-q {
  position: relative; padding: 0.9rem 1rem 0.9rem 2.6rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.02);
}
.cry-q::before {
  counter-increment: cry; content: counter(cry);
  position: absolute; left: 1rem; top: 0.95rem;
  font-family: var(--mono); font-size: 0.8rem; color: var(--muted);
}
.cry-q.got { border-color: rgba(0, 224, 184, 0.35); background: rgba(0, 224, 184, 0.04); }
.cry-q.shake { animation: shake 420ms; }
.cry-text { margin: 0; font-size: 1.05rem; color: var(--paper); line-height: 1.45; }
.cry-try { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-top: 0.6rem; }
.cry-in {
  flex: 1 1 10rem; min-width: 0; max-width: 16rem;
  font-family: var(--mono); letter-spacing: 0.14em; text-transform: uppercase;
}
.cry-in::placeholder { letter-spacing: 0.3em; text-transform: none; color: var(--dim); }
.cry-got { margin: 0.5rem 0 0; font-size: 0.95rem; }
.cry-got strong { color: var(--glow); letter-spacing: 0.1em; font-family: var(--mono); }
/* How it worked, which is at least half of why anybody does these. */
.cry-got .cry-how { display: block; margin-top: 0.2rem; font-size: 0.82rem; color: var(--muted); }

/* A ring that came from a piece of furniture says so, and one nobody heard
   leaves a link behind instead. */
.picker-say { margin: 0 0 0.5rem; font-size: 0.82rem; color: var(--muted); }
.picker-say strong { color: var(--paper); }
.ring-link { display: block; margin-top: 0.3rem; font-size: 0.78rem; color: var(--muted); }
.ring-link code {
  display: block; margin-top: 0.15rem; font-size: 0.72rem; color: var(--paper);
  word-break: break-all; line-height: 1.35;
}

.voice-spend { margin: 0.4rem 0 0.8rem; font-size: 0.9rem; color: var(--muted); line-height: 1.5; }
.voice-spend b { color: var(--paper); font-variant-numeric: tabular-nums; }
.voice-spend .bar {
  display: block; height: 5px; margin: 0.45rem 0; border-radius: 3px;
  background: rgba(255, 255, 255, 0.08); overflow: hidden;
}
.voice-spend .bar i { display: block; height: 100%; background: var(--glow); }
.voice-spend.spent .bar i { background: #e0a326; }
.voice-spend strong { color: #f5c542; font-weight: 700; }

.bots-usage { width: 100%; border-collapse: collapse; font-size: 0.85rem; margin: 0.8rem 0 0.4rem; }
.bots-usage th, .bots-usage td { text-align: left; padding: 0.35rem 0.5rem; border-bottom: 1px solid var(--line); }
.bots-usage th { color: var(--muted); font-weight: 500; }
.bots-ai pre { padding: 0.6rem 0.8rem; border: 1px solid var(--line); border-radius: 10px; background: var(--panel); overflow-x: auto; }

.bots-edit { display: grid; grid-template-columns: 14rem 1fr; gap: 1rem; align-items: start; }
.bots-list { display: flex; flex-direction: column; gap: 0.25rem; }
.bots-list h3 { margin: 0.8rem 0 0.2rem; font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.bots-list h3:first-child { margin-top: 0; }
.bots-item {
  display: flex; align-items: center; gap: 0.55rem; width: 100%;
  padding: 0.35rem 0.5rem; border-radius: 10px; border: 1px solid transparent;
  background: none; color: var(--paper); font: inherit; font-size: 0.9rem; text-align: left; cursor: pointer;
}
.bots-item:hover { background: var(--panel-lit); }
.bots-item.on { border-color: var(--glow); background: var(--panel-lit); }
.bots-item.is-hidden { opacity: 0.55; }
.bots-item img, .bots-item .face { width: 1.9rem; height: 1.9rem; border-radius: 50%; object-fit: cover; flex: none; }
.bots-item span { flex: 1; }
.bots-item em { font-style: normal; font-family: var(--mono); font-size: 0.6rem; color: var(--glow); }
.bots-new { margin-top: 0.7rem; align-self: flex-start; }

.bots-editor { min-width: 0; }
.bots-head { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; flex-wrap: wrap; }
.bots-acts { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.bots-panes { display: grid; grid-template-columns: 1fr 1fr; gap: 0.8rem; margin-top: 0.4rem; }
.bots-panes textarea {
  width: 100%; min-height: 26rem; resize: vertical; box-sizing: border-box;
  padding: 0.8rem; border-radius: 10px; border: 1px solid var(--line);
  background: var(--panel); color: var(--paper); font-size: 0.82rem; line-height: 1.5;
}
.bots-preview {
  padding: 0.8rem 1rem; border-radius: 10px; border: 1px solid var(--line);
  background: var(--panel-lit); font-size: 0.9rem; line-height: 1.5; min-height: 26rem; box-sizing: border-box;
}
.bots-preview h3, .bots-preview h4, .bots-preview h5 { margin: 0.8rem 0 0.3rem; }
.bots-card { display: flex; flex-direction: column; gap: 0.15rem; padding-bottom: 0.6rem; margin-bottom: 0.6rem; border-bottom: 1px solid var(--line); }
.bots-card span { font-family: var(--mono); font-size: 0.7rem; color: var(--muted); }
.bots-errors { color: var(--bad); margin: 0 0 0.6rem; padding-left: 1.1rem; }

@media (max-width: 48rem) {
  .bots-edit, .bots-panes { grid-template-columns: 1fr; }
  .bots-panes textarea, .bots-preview { min-height: 14rem; }
}

/* A bot's line in the chat: the same, with a quiet mark that it is a bot. */
.line.from-bot .line-name::after {
  content: "bot"; margin-left: 0.4rem; padding: 0 0.3rem; border-radius: 4px;
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.05em;
  color: var(--glow); border: 1px solid var(--glow-soft); vertical-align: 1px;
}

.mention-list {
  position: fixed; z-index: 450; display: flex; flex-direction: column;
  min-width: 10rem; padding: 0.25rem; border-radius: 10px;
  border: 1px solid var(--line); background: var(--panel-lit);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.mention-list[hidden] { display: none; }
.mention-item {
  font: inherit; font-size: 0.88rem; text-align: left; color: var(--paper);
  background: none; border: 0; border-radius: 7px; padding: 0.35rem 0.6rem; cursor: pointer;
}
.mention-item.on, .mention-item:hover { background: var(--glow-soft); }

.picker-close {
  flex: none; width: 1.9rem; height: 1.9rem; border-radius: 50%;
  border: 1px solid var(--line); background: none; color: var(--muted);
  font: inherit; font-size: 1.1rem; line-height: 1; cursor: pointer;
}
.picker-close:hover { color: var(--paper); border-color: var(--glow); }

.bots-say textarea {
  width: 100%; box-sizing: border-box; padding: 0.7rem; border-radius: 10px;
  border: 1px solid var(--line); background: var(--panel); color: var(--paper); font-size: 0.85rem;
}
.bots-say .bots-voice-try input { flex: 1; min-width: 10rem; }

/* Sending a bot home from its face. */
.tile { position: relative; }
.tile-home {
  position: absolute; top: 0.35rem; right: 0.35rem; z-index: 3;
  width: 1.6rem; height: 1.6rem; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.25); background: rgba(20, 16, 13, 0.7);
  color: var(--paper); font: inherit; font-size: 1rem; line-height: 1; cursor: pointer;
  opacity: 0; transition: opacity 0.15s ease;
}
.tile:hover .tile-home, .tile-home:focus-visible { opacity: 1; }
@media (hover: none) { .tile-home { opacity: 0.85; } }
.tile-home:hover { border-color: var(--glow); }
.table-rail .tile-home { top: 0.15rem; right: 0.15rem; width: 1.3rem; height: 1.3rem; font-size: 0.85rem; }
.talk-bubble.page-note { transform: translate(-50%, -4px); background: var(--panel-lit); color: var(--paper); border: 1px solid var(--line); }
.talk-bubble.page-note.on { transform: translate(-50%, 0); }

/* A face on camera is its own layer, so a chat line arriving -- or anything
   else the page repaints -- cannot make it flash. */
.tile-video, .tile-video video, .tile-video canvas, .screen-video video {
  transform: translateZ(0);
  backface-visibility: hidden;
}
.tiles { contain: layout paint; }

/* Fading a tile in is for somebody arriving in the room. A tile with a
   picture in it is a face already on screen, and if anything ever rebuilds
   it, the fade is a flash rather than an arrival -- so it does not fade.
   Neither does the rail, which is a strip of people who are already here. */
.tile.has-video, .tiles.strip .tile { animation: none; }

/* ---------------------------------------------------------------- uno */

/* The table itself is a canvas (/unotable.js) and wears the .tt styles with
   everything else. What is left here is the panel it leaves in the room, and
   the four colours -- on the buttons that name one, and in the commentary. */
.tt-panel-uno { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }

.tt-btns .u-r { background: #d2463f; border-color: #d2463f; color: #fff; }
.tt-btns .u-y { background: #e0a326; border-color: #e0a326; color: #3a2a06; }
.tt-btns .u-g { background: #3f8f56; border-color: #3f8f56; color: #fff; }
.tt-btns .u-b { background: #3a6fb0; border-color: #3a6fb0; color: #fff; }

.tt-log b.u-r { color: #ff8b84; }
.tt-log b.u-y { color: #f5c542; }
.tt-log b.u-g { color: #5fd18a; }
.tt-log b.u-b { color: #7fb0ff; }
.tt-log b.u-w { color: #e5e7eb; }
.tt-log .line.talk { color: var(--tt-ink); }

/* ------------------------------------------------------------- skip-bo --- */

/* The table is a canvas (/skipbotable.js) and wears the .tt styles with
   everything else; this is the panel it leaves in the room, the commentary's
   card names, and the line of text the action bar carries instead of buttons
   for a game you play by tapping the table itself. */
.tt-panel-skipbo { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; }

.tt-log b.sb-c { color: #F3F4F6; }
.tt-log b.sb-cw { color: #b49bff; }
.tt-log .line.dim { color: #6f7789; }

.sb-tt .tt-log { bottom: calc(env(safe-area-inset-bottom, 0px) + 112px + var(--tt-lift)); }
@media (min-aspect-ratio: 1/1) { .sb-tt .tt-log { bottom: calc(112px + var(--tt-lift)); } }

.tt-hint {
  flex: 1; align-self: center; text-align: center; padding: 0 10px;
  font: 600 12.5px var(--mono); color: var(--tt-muted); line-height: 1.35;
}
