:root {
  --bg: #f4f5f7;
  --panel: #ffffff;
  --ink: #14161a;
  --muted: #6b7280;
  --line: #d8dbe0;
  --accent: #e5197f;   /* logo pink */
  --green: #1f6f43;
  --green-soft: #e6f2ea;
  --gold: #f6c445;
  --radius: 10px;
  --shadow: 0 1px 3px rgba(0, 0, 0, .12);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--ink);
  -webkit-text-size-adjust: 100%;
}

h1, h2, h3 { margin: 0 0 .5rem; line-height: 1.2; }
h1 { font-size: 1.4rem; }
h2 { font-size: 1.05rem; }
.muted { color: var(--muted); }
.small { font-size: .85rem; }

button {
  font: inherit;
  font-weight: 600;
  border: 0;
  border-radius: var(--radius);
  padding: .6rem 1.1rem;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
}
button:hover { filter: brightness(1.06); }
button:disabled { opacity: .45; cursor: not-allowed; filter: none; }
button.ghost { background: #fff; color: var(--ink); border: 1px solid var(--line); }
button.dark { background: #2b2f36; }
button.good { background: var(--green); }
button.big { font-size: 1.15rem; padding: .9rem 1.6rem; }

/* The Discord sign-in is a link, not a button - it is a real page navigation -
   so it borrows the button's look, in Discord's own blurple. */
a.big.discord {
  display: inline-block;
  font-weight: 600;
  border-radius: var(--radius);
  padding: .9rem 1.6rem;
  font-size: 1.15rem;
  background: #5865f2;
  color: #fff;
  text-decoration: none;
}
a.big.discord:hover { filter: brightness(1.06); }

input {
  font: inherit;
  padding: .55rem .7rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  color: inherit;
  min-width: 0;
}

.bar {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  align-items: center;
  background: #ececee;
  border-bottom: 1px solid var(--line);
  padding: .8rem 1rem;
}
.bar .spacer { flex: 1 1 auto; }

/* Logo: the ten-ball lockup. Swap public/logo.svg for your own file to
   rebrand - nothing else references the artwork. */
.logo { height: 40px; width: auto; display: block; flex: none; }
.logo-big {
  display: block;
  width: min(340px, 100%);
  height: auto;
  margin: 0 auto 1rem;
}

.wrap { max-width: 1180px; margin: 0 auto; padding: 1rem; }
.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1rem;
  margin-bottom: 1rem;
}
.row { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }

/* ---------------------------------------------------------- bingo ticket */

/* The printed sheet: six tickets on a coloured card under one BINGO header.
   Change --sheet to recolour it (the logo pink is #e5197f). */
.sheet {
  /* The sheet colour is the guest's own pick, falling back to the printed red. */
  --sheet: var(--sheet-pick, #fa2b4e);
  background: var(--sheet);
  border-radius: 4px;
  padding: .55rem .6rem;
  /* Narrow like the printed sheet, centred on wider screens. */
  max-width: 420px;
  margin: 0 auto;
}
/* No card around the sheet - the sheet itself is the card. */
.sheet-card {
  padding: 0;
  background: none;
  border: 0;
  box-shadow: none;
  margin-bottom: .5rem;
}
.sheet-title {
  color: #fff;
  text-align: center;
  font-weight: 900;
  letter-spacing: .3em;
  text-indent: .3em; /* keeps the word optically centred despite the spacing */
  font-size: clamp(1.3rem, 6vw, 2rem);
  padding: .1rem 0 .5rem;
}
/* The footer line on a printed sheet - names the strip for screenshot checks. */
.sheet-foot {
  color: #fff;
  text-align: center;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .04em;
  padding-top: .45rem;
}

.ticket {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  background: #1a1a1a;   /* shows through as the grid lines */
  border: 2px solid #1a1a1a;
  gap: 1px;
  user-select: none;
}
/* Square-ish like the printed sheet, but height is capped to whatever the
   screen has left, so all 18 rows stay visible without zooming or scrolling.
   The 250px allows for the top bar, the name card and the claim bar. */
.ticket .cell {
  height: clamp(16px, calc((100dvh - 330px) / 18), 37px);
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  font-size: clamp(.75rem, 3.2vw, 1.2rem);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
/* Blanks are plain white on a printed ticket, same as any other square. */
.ticket .cell.blank { background: #fff; }
.ticket .cell.num { cursor: pointer; }
/* The daub. Colour and shape are the guest's own pick; the ring is the one a
   real dabber leaves, and the rest are drawn in its place. */
.ticket .cell.marked {
  background: var(--dab-pick, var(--gold));
  position: relative;
}
.ticket .cell.marked::after {
  content: "";
  position: absolute;
  inset: 8%;
  border: 3px solid rgba(0, 0, 0, .55);
  border-radius: 50%;
}
/* A solid blot rather than a ring. */
body[data-dab="blot"] .ticket .cell.marked::after {
  inset: 12%;
  border: 0;
  background: rgba(0, 0, 0, .3);
}
/* Drawn shapes: the character sits in the square in place of the ring. */
body[data-dab="star"] .ticket .cell.marked::after,
body[data-dab="heart"] .ticket .cell.marked::after,
body[data-dab="cross"] .ticket .cell.marked::after {
  inset: 0;
  border: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.15em;
  line-height: 1;
  color: rgba(0, 0, 0, .6);
}
body[data-dab="star"] .ticket .cell.marked::after { content: "★"; }
body[data-dab="heart"] .ticket .cell.marked::after { content: "♥"; }
body[data-dab="cross"] .ticket .cell.marked::after { content: "✕"; }
/* Nothing drawn at all - the colour alone says it is daubed. */
body[data-dab="plain"] .ticket .cell.marked::after { content: none; }

/* ------------------------------------------------------- card style panel */

/* The customisation box: the same centred layout as the other overlays, with
   each row of swatches centred under its own label. */
.style-box h2 { font-size: 1.3rem; }
.style-box .swatches { justify-content: center; }
.style-box .style-row.row { justify-content: center; }
.style-row { margin-top: .6rem; }
.style-row > .small.muted { display: block; margin-bottom: .3rem; }
.swatches { display: flex; gap: .4rem; flex-wrap: wrap; }
/* Big enough for a thumb, and the chosen one is ringed rather than recoloured
   so the colour itself is never in doubt. */
.swatch {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  border: 2px solid var(--line);
  padding: 0;
  cursor: pointer;
}
.swatch[aria-pressed="true"] { box-shadow: 0 0 0 3px var(--ink); }
/* Names the colour for a screen reader without printing it on the swatch. */
.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}
.shape {
  min-width: 2.5rem;
  padding: .35rem .5rem;
  font-size: 1rem;
  background: #fff;
  color: var(--ink);
  border: 1px solid var(--line);
}
.shape[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--ink); }
/* Host-side check view: which numbers were actually called */
.ticket .cell.hit { background: var(--green-soft); color: var(--green); }
.ticket .cell.miss { color: #b9bec6; }
/* Called but the player never ticked it, and ticked though never called. */
.ticket .cell.undaubed { background: #fff; color: var(--green); box-shadow: inset 0 0 0 2px var(--green); }
.ticket .cell.wrong { background: #fdecec; color: var(--accent); box-shadow: inset 0 0 0 2px var(--accent); }

.legend { display: flex; flex-wrap: wrap; gap: .75rem; margin-bottom: .5rem; }
.legend .sw {
  display: inline-block;
  width: .7rem;
  height: .7rem;
  border-radius: 3px;
  margin-right: .3rem;
  vertical-align: -1px;
}
.legend .sw.hit { background: var(--green-soft); box-shadow: inset 0 0 0 2px var(--green); }
.legend .sw.undaubed { background: #fff; box-shadow: inset 0 0 0 2px var(--green); }
.legend .sw.wrong { background: #fdecec; box-shadow: inset 0 0 0 2px var(--accent); }

/* Check-in-progress banner, on both panels. */
.card.checking {
  background: #fff8e1;
  border-color: var(--gold);
  border-left: 5px solid var(--gold);
}

/* Board beside the sheet on a wide screen, under it on a phone. */
.play-layout {
  display: flex;
  gap: .75rem;
  align-items: flex-start;
  justify-content: center;
  flex-wrap: wrap;
  position: relative;
}
.play-layout .sheet-card { flex: 0 1 420px; max-width: 100%; order: 1; }
.board-card { flex: 0 1 380px; max-width: 100%; order: 2; padding: .75rem; }
.prizes-card { flex: 0 1 380px; max-width: 100%; order: 3; padding: .75rem; }
@media (min-width: 800px) {
  /* Board on the left once there is room for it. */
  .board-card { order: 0; position: sticky; top: 1rem; }
  /* Prizes float against the right edge, out of the flex flow, so it does not
     throw off the sheet's own centring. */
  .prizes-card {
    position: absolute;
    top: 0;
    right: 0;
  }
  /* Room reserved on the right while the prizes card is showing, so the sheet
     centres itself clear of it instead of sitting underneath. */
  .play-layout.prizes-open { padding-right: 405px; }
}
/* Bigger than the host's board - this one is read at arm's length on a sofa. */
.board-card .board { gap: 4px; }
.board-card .board div {
  font-size: clamp(.8rem, 1.9vw, 1.15rem);
  font-weight: 700;
  aspect-ratio: 1 / .9;
}

/* The player's own header: just who they are - what they are playing for now
   lives at the top of the prizes card instead. */
.play-head { flex-wrap: nowrap; align-items: flex-start; }
.play-who { min-width: 0; }
/* The name reads as a name rather than a heading - a touch bigger, tighter,
   and off the top edge. */
.play-who h1 {
  margin: .1rem 0 0;
  font-size: 1.5rem;
  letter-spacing: -.01em;
  overflow-wrap: anywhere;
}
/* Sits at the top of the prizes card, so it reads as one block with the
   rundown underneath rather than a separate strip in the header. */
.play-stage { margin-bottom: .8rem; }
.prizes-rule { border: 0; border-top: 1px solid var(--line); margin: 0 0 .8rem; }

/* The prize being played for, on the guest's screen. */
.prize-now { color: var(--accent); font-weight: 700; }

/* A break: the same held-breath feel as a bingo check, with its own clock. */
.card.break {
  background: #fdeef6;
  border-color: var(--accent);
  border-left: 5px solid var(--accent);
  text-align: center;
}
.break-clock { font-size: 2.4rem; font-weight: 800; line-height: 1.1; margin: .2rem 0; }

/* A quiet line pinned to the bottom of the screen. It is taken out of the flow
   entirely, so it has no say in how anything above it is laid out - the page
   just reserves room for it underneath. */
.foot {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;            /* under the result overlays, over the page */
  background: var(--panel);
  border-top: 1px solid var(--line);
  padding: .7rem 1rem calc(.7rem + env(safe-area-inset-bottom));
  color: var(--muted);
  line-height: 1.4;
  text-align: center;
}
.foot strong { color: var(--ink); }
/* Room at the foot of the page so the last card is never sat on. */
.wrap { padding-bottom: 5.5rem; }

/* Frozen cards while the host checks - visibly dead, not just unresponsive. */
.sheet.locked .ticket { opacity: .55; }
.sheet.locked .cell.num { cursor: default; }

/* Claim button beside each ticket: no vertical cost, no picking from a list. */
.strip-ticket { display: flex; align-items: stretch; gap: .35rem; }
.strip-ticket .ticket { flex: 1; min-width: 0; }
.claim-btn {
  flex: none;
  width: 3.4rem;
  background: #1a1a1a;
  color: #fff;
  font-size: .72rem;
  font-weight: 900;
  letter-spacing: .04em;
  padding: 0;
  border-radius: 3px;
}
.claim-btn:disabled { background: #1a1a1a; opacity: .35; }
/* A strip: six tickets stacked down the sheet. */
.strip-ticket { margin-bottom: .45rem; }
.strip-ticket:last-child { margin-bottom: 0; }
.strip-ticket.highlight .ticket { box-shadow: 0 0 0 3px var(--accent); }
.ticket-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  font-size: .8rem;
  font-weight: 700;
  color: var(--muted);
  margin-bottom: .2rem;
}
.pill {
  background: #e3e5e8;
  color: #414750;
  border-radius: 999px;
  padding: .1rem .5rem;
  font-weight: 700;
}
.pill.ok { background: var(--green); color: #fff; }

/* ------------------------------------------------- 75-ball minigame card */

/* The American card: 5x5 under the word BINGO, one letter per column, with the
   free square in the middle. Same red sheet as the 90-ball strip. */
.sheet.card75 { max-width: 380px; padding: .5rem .55rem .6rem; }

.card75-head {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1px;
  padding: 0 0 .35rem;
}
.card75-head div {
  color: #fff;
  text-align: center;
  font-weight: 900;
  font-size: clamp(1.3rem, 8vw, 2.4rem);
  line-height: 1;
}

.ticket75 { grid-template-columns: repeat(5, 1fr); }
.ticket75 .cell {
  /* Five rows rather than eighteen, so each square can be far bigger - but
     still capped to the screen so the whole card is visible at once. */
  height: clamp(38px, calc((100dvh - 300px) / 5), 68px);
  font-size: clamp(1.1rem, 5.5vw, 1.9rem);
}
/* The free square: the number small, FREE beneath it, and already daubed. */
.ticket75 .cell.free {
  flex-direction: column;
  gap: 0;
  font-size: clamp(.85rem, 3.6vw, 1.2rem);
  background: var(--gold);
}
.ticket75 .cell.free span {
  font-size: .58em;
  font-weight: 800;
  letter-spacing: .06em;
  color: rgba(0, 0, 0, .6);
}

/* A set of cards on one sheet: five separate cards with the sheet colour
   showing between them, so it reads as five cards rather than one long grid. */
.sheet.set { display: flex; flex-direction: column; gap: 10px; }
.sheet.set .ticket75 .cell { height: clamp(26px, calc((100dvh - 320px) / 10), 46px); font-size: clamp(.9rem, 4vw, 1.4rem); }
.sheet .single-card { display: block; }

/* Speed Bingo: nine loose balls rather than a grid of squares, laid out as
   a ring - one in the middle, eight evenly around it. */
.ticket75.ring {
  display: block;              /* the balls place themselves, not a grid */
  position: relative;
  background: none;
  border: 0;
  /* One size drives the lot: the card, and how far out the ring of balls sits.
     Kept as a length rather than a percentage, because a percentage in a
     transform would be measured against the ball, not the card. */
  --size: min(88vw, 340px);
  --ring-r: calc(var(--size) * .355);
  width: var(--size);
  aspect-ratio: 1;
  margin: .3rem auto .1rem;
}
.ticket75.ring .cell {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 27%;
  height: 27%;
  border-radius: 50%;
  aspect-ratio: 1;
  font-size: clamp(1.1rem, 5.5vw, 1.7rem);
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, .12);
  /* Out from the middle at its own angle, then turned back upright so the
     number never reads sideways. */
  transform:
    translate(-50%, -50%)
    rotate(calc(var(--i) * 45deg))
    translate(0, calc(var(--ring-r) * -1))
    rotate(calc(var(--i) * -45deg));
  transform-origin: center;
}
/* The middle ball stays put, and is the biggest thing on the card. */
.ticket75.ring .cell.centre {
  width: 32%;
  height: 32%;
  font-size: clamp(1.3rem, 7vw, 2.1rem);
  transform: translate(-50%, -50%);
}
/* The daub is the whole ball, so the ring sits inside it rather than in a
   square corner. */
.ticket75.ring .cell.marked::after { inset: 14%; }

/* The starred number on a 90-ball strip: the star is drawn behind the number
   itself, half faded, so the square still reads as a number first. An SVG
   rather than a font glyph, so it is the same shape on every phone and can be
   placed to the pixel. */
.cell.starred { position: relative; }
.star-mark {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 88%;
  height: 88%;
  transform: translate(-50%, -50%);
  fill: var(--gold);
  opacity: .5;
  pointer-events: none;
}
/* The number is lifted over its star, so the star never reads as part of it. */
.cell.starred .num-text { position: relative; z-index: 1; }

/* Cash Dash (5x2) and Speed Bingo (3x3): far fewer squares than BINGO, so
   each one is bigger - these are read across a room at speed. */
.sheet.card75 .sheet-title { padding: .1rem 0 .45rem; font-size: clamp(1rem, 4.5vw, 1.5rem); }
.sheet.cols3 { max-width: 380px; }
/* Two rows only, so they can be deep without pushing anything off screen. */
.sheet.card75 .ticket75:not(.cols3) .cell.tall { height: auto; }
.sheet:not(.set) .ticket75.cols5.rows2 .cell { height: clamp(48px, 14vw, 84px); }

/* One card, so one full-width claim button under it. */
.claim-btn.wide {
  width: 100%;
  margin-top: .45rem;
  padding: .7rem 0;
  font-size: 1.05rem;
  letter-spacing: .12em;
}

/* On the host panel the card sits in a white claim box, not on the red sheet,
   and it is read at a desk - so a fixed, modest square. */
.card75-head.dark div { color: var(--ink); font-size: 1.1rem; }
.ticket75.flat .cell { height: 2.4rem; font-size: 1rem; }
/* The ring, read at a desk rather than held in a hand. */
.ticket75.ring.flat { --size: 230px; margin: .2rem 0 .4rem; }
.ticket75.ring.flat .cell { height: auto; font-size: .95rem; }
.ticket75.ring.flat .cell.centre { font-size: 1.05rem; }
.ticket75.flat .cell.free { font-size: .8rem; }

/* The host's button for the switch - a colour of its own, so it is never hit
   by accident while reaching for "Call next number". */
button.mini { background: #7c3aed; }

/* The ball on the host panel carries the letter in the minigame. */
.callout .ball.lettered { flex-direction: column; gap: 0; font-size: 2rem; line-height: 1; }
.callout .ball .ball-letter {
  font-size: .95rem;
  font-weight: 900;
  letter-spacing: .18em;
  text-indent: .18em;
  opacity: .85;
}

/* The banner announcing the switch, on both panels. */
.card.minigame {
  background: #efe7ff;
  border-color: #7c3aed;
  border-left: 5px solid #7c3aed;
}

/* The 75-ball wall board: a lettered row of fifteen for each of B I N G O. */
.board.board75 {
  grid-template-columns: auto repeat(15, 1fr);
  gap: 2px;
}
.board.board75 .board-letter {
  background: none;
  color: var(--ink);
  font-weight: 900;
  font-size: clamp(.75rem, 1.8vw, 1.05rem);
  padding-right: .25rem;
  aspect-ratio: auto;
}

/* ------------------------------------------------------------ call board */

.board {
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  gap: 3px;
}
.board div {
  aspect-ratio: 1 / .78;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #e9eaec;
  border-radius: 4px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: #9aa0a8;
  font-size: clamp(.7rem, 1.5vw, 1.05rem);
}
.board div.called { background: var(--green); color: #fff; }
.board div.latest { background: var(--accent); color: #fff; box-shadow: 0 0 0 3px rgba(229, 25, 127, .3); }

.callout {
  display: flex;
  align-items: center;
  gap: 1rem;
}
.callout .ball {
  width: 5.5rem;
  height: 5.5rem;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.5rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  flex: none;
}
.recent { display: flex; gap: .35rem; flex-wrap: wrap; }
.recent span {
  background: #2b2f36;
  color: #fff;
  border-radius: 6px;
  padding: .2rem .5rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- claims */

.claim {
  border: 1px solid var(--line);
  border-left: 5px solid var(--muted);
  border-radius: var(--radius);
  padding: .6rem .8rem;
  margin-bottom: .5rem;
}
.claim.valid { border-left-color: var(--green); background: var(--green-soft); }
.claim.invalid { border-left-color: var(--accent); background: #fdecec; }
.claim.done { opacity: .5; }

.badge {
  display: inline-block;
  padding: .15rem .5rem;
  border-radius: 999px;
  font-size: .78rem;
  font-weight: 700;
  background: #e3e5e8;
  color: #414750;
}
.badge.ok { background: var(--green); color: #fff; }
.badge.no { background: var(--accent); color: #fff; }
/* A prize won on top of the one being claimed - the host pays both. */
.badge.bonus { background: var(--gold); color: #1a1a1a; }

/* The last ball out, on the claimed ticket and in the claim's summary line. */
.claim-meta {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: center;
  margin: .35rem 0;
}
.claim-meta .lastcall {
  padding: .1rem .45rem;
  border-radius: 999px;
  background: #fff;
  box-shadow: inset 0 0 0 2px var(--muted);
}
.claim-meta .lastcall.ok { box-shadow: inset 0 0 0 2px var(--green); color: var(--green); }
.ticket .cell.lastcall { outline: 3px solid var(--gold); outline-offset: -3px; }

table { width: 100%; border-collapse: collapse; font-size: .9rem; }
th, td { text-align: left; padding: .35rem .5rem; border-bottom: 1px solid var(--line); }
th { color: var(--muted); font-weight: 600; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------ host player table */

#players td { padding: .45rem .5rem; vertical-align: middle; }
#players tr:last-child td { border-bottom: 0; }
#players tr:hover td { background: #f7f8fa; }
#players tr.offline .pname { color: var(--muted); }
#players .pname { font-weight: 600; }
#players .strip-link {
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px dotted var(--line);
}
#players .strip-link:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* Discord id reveal: click a signed-in player's name to swap it for their
   Discord id (and copy it), so the host can look someone up without leaving
   the panel. Click again to swap back. */
#players .pname.clickable {
  cursor: pointer;
  border-bottom: 1px dotted var(--line);
}
#players .pname.clickable:hover { color: var(--accent); border-bottom-color: var(--accent); }
#players .pname.showing-id {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--accent);
}

/* Online light, so a name that has dropped off reads at a glance. */
.dot {
  display: inline-block;
  width: .5rem;
  height: .5rem;
  border-radius: 50%;
  margin-right: .4rem;
  vertical-align: middle;
}
.dot.on { background: var(--green); }
.dot.off { background: transparent; box-shadow: inset 0 0 0 2px var(--line); }

/* How much of the strip is called - the bar is read faster than the count. */
.meter {
  display: inline-block;
  width: 3.5rem;
  height: .35rem;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
  margin-right: .4rem;
  vertical-align: middle;
}
.meter i { display: block; height: 100%; background: var(--green); }

#players .kick {
  padding: .1rem .45rem;
  color: var(--muted);
  line-height: 1.4;
}
#players .kick:hover { color: var(--accent); border-color: var(--accent); }

.grid2 { display: grid; grid-template-columns: 1.4fr 1fr; gap: 1rem; align-items: start; }
@media (max-width: 900px) { .grid2 { grid-template-columns: 1fr; } }

.toast {
  position: fixed;
  left: 50%;
  bottom: 1.2rem;
  transform: translateX(-50%);
  background: #14161a;
  color: #fff;
  padding: .7rem 1.1rem;
  border-radius: var(--radius);
  font-weight: 600;
  box-shadow: var(--shadow);
  z-index: 50;
}
.hidden { display: none !important; }

/* ------------------------------------------------------------------ rules */

.rules h2 { margin-top: 1.2rem; }
.rules h2:first-of-type { margin-top: .5rem; }
.rules ul, .rules ol { padding-left: 1.2rem; margin: .5rem 0; }
.rules li { margin-bottom: .4rem; line-height: 1.45; }
.rules ul ul { margin: .35rem 0 .1rem; }
.rules p { line-height: 1.5; margin: .5rem 0; }

.agree {
  display: flex;
  align-items: center;
  gap: .6rem;
  background: var(--green-soft);
  border: 1px solid var(--green);
  border-radius: var(--radius);
  padding: .8rem 1rem;
  margin: 1.2rem 0 .8rem;
  font-weight: 600;
  cursor: pointer;
}
.agree input { width: 1.3rem; height: 1.3rem; flex: none; accent-color: var(--green); }
.rules > button { width: 100%; }

/* ------------------------------------------------- win / lose celebrations */

#confetti {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 90;
}

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(10, 12, 16, .72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  z-index: 100;
}
.overlay-box {
  background: var(--panel);
  border-radius: var(--radius);
  padding: 1.5rem;
  text-align: center;
  max-width: 460px;
  width: 100%;
  box-shadow: 0 12px 40px rgba(0, 0, 0, .35);
}
.overlay-box h2 { font-size: 1.9rem; margin-bottom: .4rem; }
.overlay-box img {
  display: block;
  width: 100%;
  max-width: 340px;
  border-radius: var(--radius);
  margin: .9rem auto;
}
.overlay-box button { margin-top: .6rem; }

/* The guest-facing prize rundown: what each prize is worth tonight, read-only. */
#prizesList { text-align: left; margin: .9rem 0; }
.prize-line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: .7rem 0;
  border-bottom: 1px solid var(--line);
}
.prize-line:last-child { border-bottom: 0; }
.prize-line > span { font-weight: 600; }
.prize-value {
  flex: 0 0 auto;
  min-width: 8rem;
  padding: .55rem .7rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  text-align: right;
}

/* Marks a player whose name came from a Discord sign-in. */
.badge-discord {
  display: inline-block;
  font-size: .7rem;
  line-height: 1;
  padding: .2rem .35rem;
  border-radius: 4px;
  vertical-align: middle;
  color: #fff;
  background: #5865f2;
}

/* ------------------------------------------------------------ timed kicks */

/* Host: how long a removal lasts, chosen at the moment of kicking. */
.kick-options { justify-content: center; margin: .8rem 0 .2rem; }
.kick-options button { flex: 1 1 8rem; }

/* Host: who is currently sitting one out, and how long is left. */
.ban-row {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .4rem 0;
  border-bottom: 1px solid var(--line);
}
.ban-row:last-child { border-bottom: 0; }
.ban-row .spacer { flex: 1 1 auto; }
.ban-row .countdown {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--accent);
}

/* Player: the whole screen while they are out. */
.card.banned { border-left: 5px solid var(--accent); text-align: center; }
.ban-clock {
  font-size: clamp(2.6rem, 12vw, 4.5rem);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  color: var(--accent);
  margin: .5rem 0;
}

/* Host: how many times a player's page has blocked something. */
.badge-warn {
  display: inline-block;
  font-size: .7rem;
  font-weight: 700;
  line-height: 1;
  padding: .2rem .35rem;
  border-radius: 4px;
  vertical-align: middle;
  cursor: help;
  color: #7a4a00;
  background: #ffe9b3;
  box-shadow: inset 0 0 0 1px #e0b64d;
}

/* Player: the card is not long-press-copied, but text can still be selected -
   right-click and the browser's own menus are the player's business. */
.sheet { -webkit-touch-callout: none; }
