/* ════════════════════════════════════════════════════════════
   PopKuiz — Design System
   ════════════════════════════════════════════════════════════ */

/* ─── Lucide icons ─── */
.feature-icon [data-lucide], .feature-icon svg { width: 2rem; height: 2rem; }

/* ─── Fonts (self-hosted) ───────────────────────────────────────────────
   Outfit + Inter are served from public/vendor/fonts, NOT from Google.

   Two reasons, and the second was a surprise:

   1. HOTSPOT MODE HAS NO INTERNET BY DESIGN. Anything fetched from a CDN —
      these fonts, and Lucide's icon script — simply never arrives, so the app
      loses its icons and its typography in exactly the mode built for a room
      with no network. Same for Anywhere Mode on a player's flaky mobile data.

   2. The @import this replaces NEVER WORKED, online either. It sat after a
      style rule, and the CSS spec requires @import to precede all other rules
      (bar @charset/@layer) — so browsers dropped it silently. Verified live:
      document.fonts.size was 0, zero requests to gstatic, and a width probe
      showed 'Outfit' measuring identically to a font that does not exist.
      The whole app had been rendering in fallback sans the entire time.

   So switching to @font-face both fixes offline AND turns the intended
   typography on for the first time — the app will visibly change.

   Google's own subset split is kept (unicode-range per block), so a browser
   still only pulls the ranges a page actually uses. Outfit ships latin +
   latin-ext only, with no Greek — Greek headings still fall back, which is
   upstream and unchanged. Inter covers Greek, so Greek body text is fine.
   Both are SIL Open Font License; self-hosting is expressly permitted. */

/* cyrillic-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/vendor/fonts/inter-UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJuktqUYLkn8BJ.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJtEtqUYLknw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJuktqUYLkn8BJ.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJtEtqUYLknw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJuktqUYLkn8BJ.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJtEtqUYLknw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJuktqUYLkn8BJ.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJtEtqUYLknw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJuktqUYLkn8BJ.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJtEtqUYLknw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJuktqUYLkn8BJ.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJtEtqUYLknw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJuktqUYLkn8BJ.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url(/vendor/fonts/outfit-QGYvz_MVcBeNP4NJtEtqUYLknw.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --- CSS Custom Properties --- */
:root {
  /* Background */
  --bg-primary: #13192b;
  --bg-secondary: #1b223c;
  --bg-card: rgba(255, 255, 255, 0.05);
  --bg-card-hover: rgba(255, 255, 255, 0.08);
  --bg-glass: rgba(255, 255, 255, 0.07);
  --bg-glass-border: rgba(255, 255, 255, 0.12);

  /* Text */
  --text-primary: #fffffe;
  --text-secondary: #a7a9be;
  --text-muted: #6b6d80;

  /* Accent */
  --accent: #dc4091;
  --accent-glow: rgba(220, 64, 145, 0.4);
  --accent-light: #f472b6;

  /* Answer Colors (PopKuiz Theme) */
  --answer-red: #dc4091;
  --answer-red-glow: rgba(220, 64, 145, 0.3);
  --answer-blue: #00f3ff;
  --answer-blue-glow: rgba(0, 243, 255, 0.3);
  --answer-yellow: #ff9f1c;
  --answer-yellow-glow: rgba(255, 159, 28, 0.3);
  --answer-green: #79f487;
  --answer-green-glow: rgba(121, 244, 135, 0.3);

  /* Status */
  --correct: #79f487;
  --correct-glow: rgba(121, 244, 135, 0.4);
  --incorrect: #dc4091;
  --incorrect-glow: rgba(220, 64, 145, 0.4);

  /* Misc */
  --gradient-hero: linear-gradient(135deg, #dc4091 0%, #79f487 100%);
  --gradient-warm: linear-gradient(135deg, #ff9f1c, #dc4091);
  --gradient-cool: linear-gradient(135deg, #00f3ff, #79f487);
  --gradient-success: linear-gradient(135deg, #79f487, #00f3ff);
  --shadow-xl: 0 25px 50px -12px rgba(0, 0, 0, 0.6);
  --shadow-glow: 0 0 40px var(--accent-glow);
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 20px;
  --radius-xl: 28px;
  --radius-full: 9999px;

  /* Font */
  --font-heading: 'Outfit', sans-serif;
  --font-body: 'Inter', sans-serif;
}

/* --- Reset & Base --- */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

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

body {
  font-family: var(--font-body);
  background: var(--bg-primary);
  color: var(--text-primary);
  min-height: 100vh;
  overflow-x: hidden;
  line-height: 1.6;
}

/* --- Animated Background --- */
.bg-animated {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: var(--bg-primary);
}

.bg-animated::before {
  content: '';
  position: absolute;
  width: 600px;
  height: 600px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--accent-glow), transparent 70%);
  top: -200px;
  right: -200px;
  animation: floatBlob1 20s ease-in-out infinite;
}

.bg-animated::after {
  content: '';
  position: absolute;
  width: 500px;
  height: 500px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(52, 152, 219, 0.2), transparent 70%);
  bottom: -150px;
  left: -150px;
  animation: floatBlob2 25s ease-in-out infinite;
}

@keyframes floatBlob1 {

  0%,
  100% {
    transform: translate(0, 0) scale(1);
  }

  33% {
    transform: translate(-100px, 80px) scale(1.1);
  }

  66% {
    transform: translate(50px, -50px) scale(0.9);
  }
}

@keyframes floatBlob2 {

  0%,
  100% {
    transform: translate(0, 0) scale(1);
  }

  33% {
    transform: translate(80px, -60px) scale(1.15);
  }

  66% {
    transform: translate(-60px, 40px) scale(0.85);
  }
}

/* --- Typography --- */
h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-heading);
  font-weight: 700;
  line-height: 1.2;
}

.text-gradient {
  background: var(--gradient-hero);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

/* --- Layout --- */
.container {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 24px;
}

.container-sm {
  max-width: 600px;
}

.container-md {
  max-width: 800px;
}

.flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

.flex-col {
  display: flex;
  flex-direction: column;
}

.flex-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.gap-sm {
  gap: 8px;
}

.gap-md {
  gap: 16px;
}

.gap-lg {
  gap: 24px;
}

.gap-xl {
  gap: 32px;
}

/* --- Glass Card --- */
.glass-card {
  background: var(--bg-glass);
  border: 1px solid var(--bg-glass-border);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  padding: 32px;
  transition: all 0.3s ease;
}

.glass-card:hover {
  background: var(--bg-card-hover);
  border-color: rgba(255, 255, 255, 0.18);
}

/* --- Buttons --- */
/* Buttons — sticker style (2026-09-28). A solid face sitting on a darker base
   (a hard, unblurred shadow), so every button reads as a chunky physical button
   from the same family as the mascots, the Kaos wheel and the Pop-Up cards.
   Hover lifts it a touch; pressing pushes it down onto its base.

   NO GLOW. The old primary had a pink glow and grew 5% on hover — on a library
   of 40 quiz cards that was 40 glowing buttons shouting at once. Emphasis now
   comes from the fill alone, so the one pink button on a screen is the loud one.

   A variant only sets --btn-face / --btn-edge (and its colours); the lift,
   press and base are shared here. */
.btn {
  --btn-face: transparent;
  --btn-edge: rgba(0, 0, 0, 0.38);
  --btn-lift: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1rem;
  padding: 12px 26px;
  border: 2px solid transparent;
  border-radius: var(--radius-full);
  background: var(--btn-face);
  box-shadow: 0 var(--btn-lift) 0 var(--btn-edge);
  transform: translateY(0);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  text-decoration: none;
  position: relative;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
}

.btn:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 calc(var(--btn-lift) + 2px) 0 var(--btn-edge);
}

.btn:active:not(:disabled) {
  transform: translateY(var(--btn-lift));
  box-shadow: 0 0 0 var(--btn-edge);
  transition-duration: 0.05s;
}

.btn:focus-visible {
  outline: 3px solid var(--accent-light);
  outline-offset: 3px;
}

.btn-primary {
  --btn-face: var(--accent);
  --btn-edge: #8a1f58;
  color: #fff;
  border-color: rgba(255, 255, 255, 0.2);
  /* a sheen across the top half, like the gloss on the stickers */
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0) 55%);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.18);
}

.btn-primary:hover:not(:disabled) {
  --btn-face: #e5519d;
}

.btn-large {
  --btn-lift: 5px;
  font-size: 1.2rem;
  padding: 16px 36px;
}

.btn-xl {
  --btn-lift: 6px;
  padding: 20px 48px;
  font-size: 1.4rem;
  letter-spacing: 0.1em;
}

#startGameBtn {
  text-transform: uppercase;
  font-weight: 900;
  border-radius: var(--radius-full);
}

#startGameBtn:not(:disabled) {
  --btn-face: #ffffff;
  --btn-edge: #9aa3c7;
  background-image: none;
  color: #13192b;
  border-color: #ffffff;
  text-shadow: none;
  animation: buttonPulse 2s infinite;
}

/* The pulse rings out beyond the base, which stays put underneath it. */
@keyframes buttonPulse {
  0% {
    box-shadow: 0 var(--btn-lift) 0 var(--btn-edge), 0 0 0 0 rgba(255, 255, 255, 0.4);
  }

  70% {
    box-shadow: 0 var(--btn-lift) 0 var(--btn-edge), 0 0 0 20px rgba(255, 255, 255, 0);
  }

  100% {
    box-shadow: 0 var(--btn-lift) 0 var(--btn-edge), 0 0 0 0 rgba(255, 255, 255, 0);
  }
}

.btn-outline {
  --btn-face: rgba(255, 255, 255, 0.05);
  --btn-edge: rgba(0, 0, 0, 0.42);
  color: var(--text-primary);
  border-color: rgba(255, 255, 255, 0.16);
}

.btn-outline:hover:not(:disabled) {
  --btn-face: rgba(255, 255, 255, 0.09);
  border-color: rgba(255, 255, 255, 0.3);
}

/* Ghost buttons are text links in button clothes: no base, no lift. */
.btn-ghost {
  color: var(--text-secondary);
  box-shadow: none;
}

.btn-ghost:hover:not(:disabled),
.btn-ghost:active:not(:disabled) {
  color: var(--text-primary);
  --btn-face: var(--bg-card);
  transform: none;
  box-shadow: none;
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none !important;
}

/* --- Inputs --- */
.input {
  font-family: var(--font-body);
  font-size: 1rem;
  padding: 14px 20px;
  background: var(--bg-card);
  border: 2px solid var(--bg-glass-border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  width: 100%;
  transition: all 0.25s ease;
  outline: none;
}

.input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-glow);
}

.input-large {
  font-size: 1.5rem;
  padding: 18px 24px;
  text-align: center;
  font-family: var(--font-heading);
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

.input::placeholder {
  color: var(--text-muted);
}

/* --- Game Code Display --- */
.game-code {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 5rem;
  letter-spacing: 0.2em;
  text-align: center;
  background: var(--gradient-hero);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 30px var(--accent-glow));
  animation: codePulse 2s ease-in-out infinite;
}

@keyframes codePulse {

  0%,
  100% {
    filter: drop-shadow(0 0 30px var(--accent-glow));
  }

  50% {
    filter: drop-shadow(0 0 50px var(--accent-glow));
  }
}

/* --- Answer Buttons (Kahoot-style) --- */
.answer-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  width: 100%;
}

.answer-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.4rem;
  padding: 32px 24px;
  border: none;
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all 0.2s ease;
  color: white;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
  position: relative;
  overflow: hidden;
  min-height: 100px;
}

.answer-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, transparent 100%);
}

.answer-btn:hover {
  transform: scale(1.03);
  filter: brightness(1.1);
}

.answer-btn:active {
  transform: scale(0.97);
}

.answer-btn.selected {
  outline: 4px solid white;
  outline-offset: -4px;
}

.answer-btn-0 {
  background: #c2307a;
  box-shadow: 0 6px 20px rgba(194, 48, 122, 0.3);
}

.answer-btn-1 {
  /* Teal and green were darkened a step (was #00b8c8 / #3db84e): white text on
     them sat near 2.4:1, under the 3:1 large text needs from across a room. */
  background: #0097a7;
  box-shadow: 0 6px 20px rgba(0, 151, 167, 0.3);
}

.answer-btn-2 {
  background: #d97f00;
  box-shadow: 0 6px 20px rgba(217, 127, 0, 0.3);
}

.answer-btn-3 {
  background: #2ea043;
  box-shadow: 0 6px 20px rgba(46, 160, 67, 0.3);
}

/* Options 5–12. A question allows twelve options, but only four ever had a
   colour — the rest drew as bare, colourless tiles. The shape (below) is what
   keeps 1 and 9 apart; these just need to differ from their neighbours. */
.answer-btn-4  { background: #7b4fd1; }
.answer-btn-5  { background: #2d6fd2; }
.answer-btn-6  { background: #d2463b; }
.answer-btn-7  { background: #a67c00; }
.answer-btn-8  { background: #0e8f78; }
.answer-btn-9  { background: #9c3fb5; }
.answer-btn-10 { background: #4a6fa5; }
.answer-btn-11 { background: #b85a1f; }

/* The option's shape (optShape in socket-client.js), top-right of its tile on
   both screens. Lucide draws outlines; filled reads from further away. */
.opt-shape {
  position: absolute;
  top: 0.5em;
  right: 0.55em;
  display: flex;
  width: var(--opt-shape-size, 20px);
  height: var(--opt-shape-size, 20px);
  color: rgba(255, 255, 255, 0.92);
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
  pointer-events: none;
  z-index: 2;
}
.opt-shape svg {
  width: 100% !important;
  height: 100% !important;
  fill: currentColor;
  stroke: currentColor;
}

.answer-shapes {
  font-size: 1.6rem;
  flex-shrink: 0;
}

/* Mobile answer grid */
@media (max-width: 600px) {
  .answer-grid {
    grid-template-columns: 1fr;
  }

  .answer-btn {
    font-size: 1.1rem;
    padding: 24px 20px;
    min-height: 72px;
  }
}

/* --- True/False Buttons --- */
.tf-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}

.tf-btn {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 2rem;
  padding: 48px 32px;
  border: none;
  border-radius: var(--radius-lg);
  cursor: pointer;
  color: white;
  transition: all 0.2s ease;
}

.tf-btn:hover {
  transform: scale(1.04);
}

.tf-btn:active {
  transform: scale(0.96);
}

.tf-true {
  background: #0097a7;
  box-shadow: 0 6px 20px rgba(0, 151, 167, 0.3);
}

.tf-false {
  background: #c2307a;
  box-shadow: 0 6px 20px rgba(194, 48, 122, 0.3);
}

/* --- Timer Ring --- */
.timer-container {
  position: relative;
  width: 120px;
  height: 120px;
}

.timer-ring {
  transform: rotate(-90deg);
}

.timer-ring circle {
  fill: none;
  stroke-width: 8;
}

.timer-ring .ring-bg {
  stroke: var(--bg-card);
}

.timer-ring .ring-fill {
  stroke: var(--accent);
  stroke-linecap: round;
  transition: stroke-dashoffset 1s linear, stroke 0.3s;
}

.timer-ring .ring-fill.warning {
  stroke: var(--answer-yellow);
}

.timer-ring .ring-fill.danger {
  stroke: var(--answer-red);
}

.timer-number {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 2.5rem;
  transition: color 0.3s ease;
}

/* The clock running out is the tensest moment of a question and, on the big
   screen, the only one with nothing else moving. The number beats — slowly at
   ten seconds, hard at five — so the room feels the clock instead of reading it. */
.timer-number.warning {
  color: var(--answer-yellow);
  animation: timerBeat 1s ease-in-out infinite;
}

.timer-number.danger {
  color: var(--answer-red);
  text-shadow: 0 0 18px rgba(220, 64, 145, 0.7);
  animation: timerBeat 0.5s ease-in-out infinite;
}

@keyframes timerBeat {
  0%, 100% { transform: scale(1); }
  30%      { transform: scale(1.14); }
}

@media (prefers-reduced-motion: reduce) {
  .timer-number.warning,
  .timer-number.danger { animation: none; }
}

/* --- Player List --- */
.players-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  width: 100%;
  perspective: 1000px;
  /* Sizing and spacing live with the lobby layout in game.html. */
}

/* Every dimension is in `em`, so the ONE thing fitLobbyToScreen() changes is
   the grid's font-size and the whole chip — padding, gap, radius, avatar —
   scales with it. */
.player-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.28em;
  padding: 0.34em 0.8em;
  border-radius: 0.22em;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: rgba(255, 255, 255, 0.8);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1em;            /* inherited from .players-grid's --chip-font */
  line-height: 1.1;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
  /* NOT `transition: all`. The fit pass rewrites font-size several times in a
     row and measures between each, so a tweened size would feed it numbers
     that are still in flight — the same reason the Most Likely To card is
     forbidden from transitioning its width. */
  transition: background 0.3s ease, border-color 0.3s ease,
              color 0.3s ease, transform 0.3s ease;
}

/* Only genuinely new arrivals animate in — updatePlayerList reuses the chips
   that were already there. Re-running this on every join would set the whole
   lobby flashing each time somebody turns up.

   `backwards`, never `both`: a `both`-filled animation ending on a transform
   keeps applying it for the element's whole life, so the hover lift below
   would never once reach the screen. */
.player-chip--new {
  animation: nameReveal 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}

.player-chip:hover {
  background: rgba(255, 255, 255, 0.1);
  transform: translateY(-4px) scale(1.05);
  color: white;
}

.player-chip .dot {
  display: none;
  /* Hide dots for monochromatic frosty look */
}

.player-chip.kickable {
  cursor: pointer;
}

.player-chip.kickable:hover .chip-name {
  text-decoration: line-through;
  color: rgba(255, 255, 255, 0.5);
}

.player-chip.kickable:hover {
  background: rgba(231, 76, 60, 0.15);
  border-color: rgba(231, 76, 60, 0.3);
}

/* ---- Test bots -------------------------------------------------------- */
/* Deliberately understated: this is a rehearsal tool, and it sits on the same
   screen a room of real people is looking at while they wait to start. */
/* Lives in the lobby's control bar, which never scrolls away. */
.bot-controls {
  display: flex;
  align-items: center;
  gap: 8px;
}

.btn-bot {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 18px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: rgba(255, 255, 255, 0.62);
  font-family: var(--font-body, inherit);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease, transform 0.18s ease;
}

.btn-bot:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.28);
  color: rgba(255, 255, 255, 0.92);
  transform: translateY(-1px);
}

.btn-bot:active:not(:disabled) { transform: translateY(0) scale(0.97); }

.btn-bot:disabled { opacity: 0.35; cursor: not-allowed; }

.btn-bot--clear {
  color: rgba(231, 76, 60, 0.75);
  border-color: rgba(231, 76, 60, 0.25);
}

.btn-bot--clear:hover:not(:disabled) {
  background: rgba(231, 76, 60, 0.14);
  border-color: rgba(231, 76, 60, 0.45);
  color: rgba(255, 140, 130, 0.95);
}

/* A bot's chip reads dimmer than a person's, so a glance at the lobby still
   tells you how many real players have actually joined. */
.player-chip--bot {
  opacity: 0.58;
  border-style: dashed;
}

/* Sized in em like the rest of the chip, so it stays level with the name at
   whatever size the lobby fit pass settles on. It used to be a fixed 16px
   (lcIcon's inline size beat the old 0.5em rule), a speck beside a big name. */
.player-chip--bot .chip-bot-mark {
  display: inline-flex;
  align-items: center;
  font-size: 0.9em;
  margin-right: 0.12em;
  color: rgba(255, 255, 255, 0.6);
}

@keyframes nameReveal {
  0% {
    transform: scale(0.6) rotateX(-20deg) translateY(20px);
    opacity: 0;
  }

  100% {
    transform: scale(1) rotateX(0deg) translateY(0);
    opacity: 1;
  }
}

/* --- Lobby Header Layout --- */
/* Layout, sizing and the shrink-on-join behaviour live in game.html with the
   rest of the three-band lobby. */
.lobby-header {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  text-align: left;
}

.lobby-info {
  margin-bottom: 0;
}

.game-code {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 9rem;
  letter-spacing: 0.1em;
  margin-top: 8px;
  color: white;
  text-shadow: 0 0 30px rgba(255, 255, 255, 0.2);
}

/* --- Leaderboard --- */
.leaderboard-list {
  list-style: none;
  width: 100%;
}

.leaderboard-item {
  display: flex;
  align-items: center;
  gap: 0;
  margin-bottom: 8px;
  animation: slideInRight 0.5s ease both;
  will-change: transform;
}

.leaderboard-item-card {
  display: flex;
  align-items: center;
  gap: 16px;
  flex: 1;
  min-width: 0;
  padding: 16px 24px;
  border-radius: var(--radius-md);
  background: var(--bg-card);
  transition: background 0.3s ease;
}

/* Right-side score group — keeps name from shifting */
.leaderboard-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

/* The name is centred in whatever space is left over, so ANY change to the
   width of this group slides it. The +points badge appears mid-reveal, which
   moved every gaining player's name left while the others stayed put. Reserving
   the slot whether or not there is a badge in it keeps the column of names
   dead straight — the same trick .leaderboard-streak-slot already uses. */
.leaderboard-gain-slot {
  width: 104px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

/* Fixed-width slot for fire — always reserves space so card width stays constant */
.leaderboard-streak-slot {
  width: 64px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-size: 1.5rem;
}

.leaderboard-item:nth-child(1) .leaderboard-item-card {
  background: linear-gradient(135deg, rgba(243, 156, 18, 0.2), rgba(243, 156, 18, 0.05));
  border: 1px solid rgba(243, 156, 18, 0.3);
}

.leaderboard-item:nth-child(2) .leaderboard-item-card {
  background: linear-gradient(135deg, rgba(189, 195, 199, 0.15), rgba(189, 195, 199, 0.05));
  border: 1px solid rgba(189, 195, 199, 0.2);
}

.leaderboard-item:nth-child(3) .leaderboard-item-card {
  background: linear-gradient(135deg, rgba(205, 127, 50, 0.15), rgba(205, 127, 50, 0.05));
  border: 1px solid rgba(205, 127, 50, 0.2);
}

.leaderboard-rank {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 1.5rem;
  min-width: 40px;
  text-align: center;
}

.leaderboard-name {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 1.2rem;
  flex: 1;
}

.leaderboard-score {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.3rem;
  color: var(--accent-light);
  min-width: 80px;
  text-align: right;
}

.streak-count {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 1.1rem;
  background: linear-gradient(135deg, #ff8c42, #ffd700);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

@keyframes slideInRight {
  from {
    transform: translateX(60px);
    opacity: 0;
  }

  to {
    transform: translateX(0);
    opacity: 1;
  }
}

/* --- Leaderboard: Rank change arrows --- */
.rank-change {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.8rem;
  min-width: 32px;
  text-align: center;
}

.rank-change--up {
  color: #79f487;
  text-shadow: 0 0 8px rgba(121, 244, 135, 0.5);
}

.rank-change--down {
  color: #dc4091;
}

.rank-change--same {
  color: var(--text-muted);
}

/* --- Leaderboard: Points gained badge --- */
.points-gained-badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  background: linear-gradient(135deg, rgba(121, 244, 135, 0.2), rgba(0, 243, 255, 0.12));
  border: 1px solid rgba(121, 244, 135, 0.35);
  border-radius: var(--radius-full);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.88rem;
  color: #79f487;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.2s ease;
}

/* A Minefield round can take points away — same shape, opposite story. */
.points-gained-badge--loss {
  background: linear-gradient(135deg, rgba(220, 64, 145, 0.22), rgba(168, 85, 247, 0.12));
  border-color: rgba(220, 64, 145, 0.45);
  color: #ff7ab8;
}

@keyframes badgePop {
  0% {
    transform: scale(0) rotate(-10deg);
    opacity: 0;
  }

  70% {
    transform: scale(1.25) rotate(3deg);
    opacity: 1;
  }

  100% {
    transform: scale(1) rotate(0);
    opacity: 1;
  }
}

.badge-pop {
  animation: badgePop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes badgeMergeOut {
  0% {
    transform: scale(1);
    opacity: 1;
  }

  60% {
    transform: scale(1.3) translateX(8px);
    opacity: 0.5;
  }

  100% {
    transform: scale(0);
    opacity: 0;
  }
}

.badge-merge-out {
  animation: badgeMergeOut 0.35s ease-in forwards;
}

/* --- Leaderboard: Score count-up flash --- */
@keyframes scoreFlash {
  0% {
    color: var(--accent-light);
    transform: scale(1);
  }

  40% {
    color: #79f487;
    transform: scale(1.18);
  }

  100% {
    color: var(--accent-light);
    transform: scale(1);
  }
}

.score-updated {
  animation: scoreFlash 0.5s ease both;
  display: inline-block;
}

/* --- Leaderboard subtitle --- */
.leaderboard-subtitle {
  font-family: var(--font-body);
  color: var(--text-muted);
  font-size: 0.9rem;
  margin-bottom: 20px;
}

/* --- Results Bar Chart --- */
.results-bar-container {
  width: 100%;
}

.results-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 16px;
}

.results-bar-label {
  font-family: var(--font-heading);
  font-weight: 600;
  min-width: 150px;
  text-align: right;
  font-size: 1.1rem;
}

.results-bar-track {
  flex: 1;
  min-height: 64px;
  height: auto;
  background: var(--bg-card);
  border-radius: var(--radius-sm);
  overflow: hidden;
  position: relative;
  padding: 10px 14px;
}

.results-bar-fill {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  border-radius: var(--radius-sm);
  animation: barGrow 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
  transition: width 0.5s ease;
}

.results-bar-track--correct {
  box-shadow: 0 0 0 3px var(--correct), 0 0 28px 8px var(--correct-glow);
}

/* Dim wrong answer bars — opacity on children only so name pills are unaffected */
.results-bar--wrong {
  filter: saturate(0.6);
  transition: filter 0.5s ease;
}

.results-bar--wrong .results-bar-label,
.results-bar--wrong .results-bar-count,
.results-bar--wrong .results-bar-fill {
  opacity: 0.45;
  transition: opacity 0.5s ease;
}

/* Glass name pills container — sits above the fill */
.player-name-pills {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  min-height: 42px;
}

/* Individual glass name pill — dark glass so white text pops */
.player-name-pill {
  background: rgba(0, 0, 0, 0.3);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 20px;
  padding: 4px 12px;
  font-size: 0.85rem;
  font-weight: 600;
  color: white;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
  white-space: nowrap;
  animation: pillPop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  cursor: default;
}

.player-name-pill--overflow {
  background: rgba(0, 0, 0, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.15);
}

@keyframes pillPop {
  from {
    transform: scale(0.6);
    opacity: 0;
  }

  to {
    transform: scale(1);
    opacity: 1;
  }
}

/* Checkmark icon on correct bar label */
.bar-correct-icon {
  color: var(--correct);
  font-weight: 900;
  margin-right: 5px;
}

@keyframes barGrow {
  from {
    width: 0 !important;
  }
}

.results-bar-count {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.15rem;
  min-width: 36px;
}

/* --- Question Display --- */
.question-text {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 2.2rem;
  text-align: center;
  line-height: 1.3;
  margin-bottom: 32px;
}

@media (max-width: 600px) {
  .question-text {
    font-size: 1.4rem;
  }
}

/* A video-gap question held back until the pause, arriving as the clip stops. */
.question-text--revealed { animation: questionTextReveal 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) backwards; }
@keyframes questionTextReveal {
  from { opacity: 0; transform: translateY(12px) scale(0.94); }
}
@media (prefers-reduced-motion: reduce) { .question-text--revealed { animation: none; } }

.question-type-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border-radius: var(--radius-full);
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  background: var(--bg-card);
  border: 1px solid var(--bg-glass-border);
  color: var(--text-secondary);
}

/* --- Progress Bar --- */
.progress-bar {
  width: 100%;
  height: 6px;
  background: var(--bg-card);
  border-radius: var(--radius-full);
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: var(--gradient-cool);
  border-radius: var(--radius-full);
  transition: width 0.5s ease;
}

/* --- Ordering (Drag & Drop) --- */
/* A phone screen doesn't grow when the list does, so the rows shrink instead.
   Everything sizes off these three vars; the tiers are applied in player.js
   from the item count. Scrolling still backstops the very long ones. */
.ordering-list {
  list-style: none;
  width: 100%;
  max-width: 400px;
  margin: 0 auto;
  --order-pad-y: 16px;
  --order-gap: 8px;
  --order-font: 1.1rem;
  --order-num: 28px;
}

.ordering-list--compact {
  --order-pad-y: 8px;
  --order-gap: 5px;
  --order-font: 1rem;
  --order-num: 23px;
}

.ordering-list--dense {
  --order-pad-y: 6px;
  --order-gap: 4px;
  --order-font: 0.9rem;
  --order-num: 20px;
}

/* The rows are the content; the chrome around them gives up its room first. */
.ordering-list--compact + .btn,
.ordering-list--dense + .btn {
  margin-top: 10px;
  padding-top: 14px;
  padding-bottom: 14px;
}

.ordering-item {
  display: flex;
  align-items: center;
  gap: 12px;
  /* Grown into the space a short list leaves (_fitOrderingToScreen). */
  min-height: var(--order-row-h, 0px);
  padding: var(--order-pad-y) 20px;
  margin-bottom: var(--order-gap);
  background: var(--bg-card);
  border: 2px solid var(--bg-glass-border);
  border-radius: var(--radius-md);
  cursor: grab;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: var(--order-font);
  transition: all 0.2s ease;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

.ordering-item:active,
.ordering-item.dragging {
  cursor: grabbing;
  transform: scale(1.05) rotate(-1deg);
  background: var(--bg-card-hover);
  border-color: var(--accent);
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5), 0 0 20px var(--accent-glow);
  z-index: 100;
}

.ordering-item .drag-handle {
  color: var(--text-muted);
  font-size: 1.2rem;
}

/* When the list is short enough to fit, a row is draggable anywhere — the
   nicest thing to touch. When it overflows, the row has to be able to scroll
   instead, so the handle takes over the drag and the rest of the row pans.
   (`touch-action` is decided by the browser at touch-start, so this can't be a
   per-gesture choice in JS — it has to be a class set once, after measuring.) */
.ordering-list--scroll .ordering-item {
  touch-action: pan-y;
}

.ordering-list--scroll .ordering-item .drag-handle {
  touch-action: none;
  color: var(--text-secondary);
  font-size: 1.45rem;
  padding: 0 10px;
  margin: calc(var(--order-pad-y) * -1) 0;
  align-self: stretch;
  display: flex;
  align-items: center;
  cursor: grab;
}

.ordering-item .order-number {
  width: var(--order-num);
  height: var(--order-num);
  border-radius: 50%;
  background: var(--accent);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  font-weight: 700;
  flex-shrink: 0;
}

/* --- Feedback Animations --- */
.feedback-correct {
  animation: correctPulse 0.6s ease;
}

.feedback-incorrect {
  animation: incorrectShake 0.6s ease;
}

@keyframes correctPulse {
  0% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.08);
  }

  100% {
    transform: scale(1);
  }
}

@keyframes incorrectShake {

  0%,
  100% {
    transform: translateX(0);
  }

  20% {
    transform: translateX(-12px);
  }

  40% {
    transform: translateX(12px);
  }

  60% {
    transform: translateX(-8px);
  }

  80% {
    transform: translateX(8px);
  }
}

/* --- Confetti --- */
.confetti-piece {
  position: fixed;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  z-index: 9999;
  pointer-events: none;
  animation: confettiFall var(--fall-duration, 3s) linear forwards;
}

@keyframes confettiFall {
  0% {
    transform: translateY(0) rotate(0deg);
    opacity: 1;
  }

  100% {
    transform: translateY(100vh) rotate(var(--rotation, 720deg));
    opacity: 0;
  }
}

/* --- Screen transitions --- */
.screen {
  display: none !important;
  animation: fadeIn 0.4s ease;
}

.screen.active {
  display: flex !important;
  flex-direction: column;
  align-items: center;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes zoomIn {
  from {
    opacity: 0;
    transform: scale(0.8);
  }

  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes fadeInDown {
  from {
    opacity: 0;
    transform: translateY(-12px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}


/* --- Waiting Spinner --- */
.spinner {
  width: 48px;
  height: 48px;
  border: 4px solid var(--bg-card);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* --- Streaks --- */
.streak-badge {
  display: contents;
  animation: chipPop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* --- Host Controls --- */
.host-controls {
  position: fixed;
  bottom: 24px;
  right: 24px;
  display: flex;
  gap: 12px;
  z-index: 100;
}

/* --- Score Counter Animation --- */
.score-counter {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 3rem;
  color: var(--accent-light);
  transition: all 0.3s;
}

.score-counter.bump {
  animation: scoreBump 0.4s ease;
}

@keyframes scoreBump {
  0% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.3);
    color: var(--correct);
  }

  100% {
    transform: scale(1);
  }
}

.score-counter.bump-red {
  animation: scoreRedPulse 0.5s ease;
}

@keyframes scoreRedPulse {
  0% {
    transform: scale(1);
  }

  40% {
    transform: scale(1.35);
    color: #ef4444;
    text-shadow: 0 0 18px rgba(239, 68, 68, 0.85);
  }

  100% {
    transform: scale(1);
    color: var(--accent-light);
    text-shadow: none;
  }
}

/* --- Type Answer Input (Player) --- */
.type-answer-input {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 1.8rem;
  padding: 20px 28px;
  text-align: center;
  background: var(--bg-card);
  border: 3px solid var(--bg-glass-border);
  border-radius: var(--radius-lg);
  color: var(--text-primary);
  width: 100%;
  max-width: 500px;
  outline: none;
  transition: all 0.25s;
}

/* The placeholder shrinks on a narrow phone so "Type your answer..." isn't cut off. */
.type-answer-input::placeholder { font-size: min(1.8rem, 6vw); }

.type-answer-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 6px var(--accent-glow);
}

/* --- Notification Toast --- */
.toast {
  position: fixed;
  top: 24px;
  right: 24px;
  padding: 16px 24px;
  border-radius: var(--radius-md);
  background: var(--bg-glass);
  border: 1px solid var(--bg-glass-border);
  backdrop-filter: blur(20px);
  font-family: var(--font-heading);
  font-weight: 600;
  z-index: 1000;
  animation: toastSlide 0.4s ease, toastFade 0.4s ease 2.6s forwards;
}

@keyframes toastSlide {
  from {
    transform: translateX(100px);
    opacity: 0;
  }

  to {
    transform: translateX(0);
    opacity: 1;
  }
}

@keyframes toastFade {
  to {
    opacity: 0;
    transform: translateY(-20px);
  }
}

/* --- Utility --- */
.text-center {
  text-align: center;
}

.text-left {
  text-align: left;
}

.w-full {
  width: 100%;
}

.mt-sm {
  margin-top: 8px;
}

.mt-md {
  margin-top: 16px;
}

.mt-lg {
  margin-top: 32px;
}

.mt-xl {
  margin-top: 48px;
}

.mb-sm {
  margin-bottom: 8px;
}

.mb-md {
  margin-bottom: 16px;
}

.mb-lg {
  margin-bottom: 32px;
}

.p-md {
  padding: 16px;
}

.p-lg {
  padding: 32px;
}

.hidden {
  display: none !important;
}

/*
 * The `hidden` ATTRIBUTE only carries a default `display: none`, so any rule
 * that sets display (e.g. `.double-points-chip { display: inline-flex }`) beats
 * it and the element stays visible. This makes `el.hidden = true` reliable.
 */
[hidden] {
  display: none !important;
}

.visible {
  display: block;
}

/* Visually hidden, still read by screen readers */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --- Responsive --- */
@media (max-width: 768px) {
  .game-code {
    font-size: 3rem;
  }

  .glass-card {
    padding: 20px;
  }

  .question-text {
    font-size: 1.5rem;
  }

  .container {
    padding: 0 16px;
  }

  .timer-container {
    width: 80px;
    height: 80px;
  }

  .timer-number {
    font-size: 1.8rem;
  }
}

@media (max-width: 480px) {
  .game-code {
    font-size: 2.2rem;
    letter-spacing: 0.15em;
  }

  .tf-grid {
    gap: 12px;
  }

  .tf-btn {
    font-size: 1.4rem;
    padding: 32px 24px;
  }
}

/* --- Mascot Wander Animation --- */
@keyframes mascotWander {
  0% {
    transform: translate(0, 0) rotate(0deg);
  }

  25% {
    transform: translate(70vw, 20vh) rotate(5deg);
  }

  50% {
    transform: translate(20vw, 70vh) rotate(-5deg);
  }

  75% {
    transform: translate(80vw, 50vh) rotate(10deg);
  }

  100% {
    transform: translate(0, 0) rotate(0deg);
  }
}

.mascot-wander {
  position: fixed;
  top: 10vh;
  left: 10vw;
  width: 15vw;
  max-width: 200px;
  min-width: 100px;
  pointer-events: none;
  opacity: 0.15;
  z-index: 0;
  animation: mascotWander 60s linear infinite;
  filter: blur(2px) brightness(0.8);
}

@keyframes mascotWander2 {
  0% {
    transform: translate(80vw, 80vh) rotate(0deg);
  }

  25% {
    transform: translate(10vw, 60vh) rotate(-10deg);
  }

  50% {
    transform: translate(60vw, 10vh) rotate(5deg);
  }

  75% {
    transform: translate(20vw, 30vh) rotate(-5deg);
  }

  100% {
    transform: translate(80vw, 80vh) rotate(0deg);
  }
}

.mascot-wander-2 {
  position: fixed;
  top: 0;
  left: 0;
  width: 12vw;
  max-width: 180px;
  min-width: 90px;
  pointer-events: none;
  opacity: 0.12;
  z-index: 0;
  animation: mascotWander2 50s linear infinite;
  filter: blur(3px) brightness(0.7);
}

/* --- Player Feedback Mascots --- */
.player-feedback-mascot {
  /* Peeks up from the bottom edge, into the empty space under the result card.
     It used to sit dead centre BEHIND the card, blurred at 25% — a green smear
     that read as a green glow behind a pink "Wrong!", and stayed behind the
     rank number on the next screen, muddying both. */
  position: fixed;
  left: 50%;
  bottom: 0;
  transform: translate(-50%, 75%);
  width: 42vw;
  max-width: 180px;
  pointer-events: none;
  opacity: 0;
  z-index: -1;
  transition: opacity 0.4s ease, transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.player-feedback-mascot.active {
  opacity: 0.95;
  transform: translate(-50%, 22%);
  animation: playerMascotFloat 4s ease-in-out 0.7s infinite;
}

@keyframes playerMascotFloat {
  0%, 100% { transform: translate(-50%, 22%) rotate(0deg); }
  50%      { transform: translate(-50%, 18%) rotate(4deg); }
}

@media (prefers-reduced-motion: reduce) {
  .player-feedback-mascot.active { animation: none; }
}

/* --- End Session UI --- */
.btn-end-session {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 400;
  /* Above screens, below overlay */
  padding: 10px 18px;
  font-size: 0.9rem;
  background: rgba(220, 64, 145, 0.1);
  border: 1px solid rgba(220, 64, 145, 0.3);
  color: #dc4091;
  /* answer-red/accent */
  transition: all 0.2s ease;
}

/* Tucked away while the mouse is still (game.html) — see wakeEndBtn. */
.btn-end-session--idle {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s ease;
}

.btn-end-session:hover {
  background: #dc4091;
  color: white;
  box-shadow: 0 0 20px var(--answer-red-glow);
  transform: translateY(-2px);
}

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 14, 23, 0.85);
  backdrop-filter: blur(12px);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}

.modal-overlay.active {
  opacity: 1;
  pointer-events: auto;
}

.modal-card {
  max-width: 400px;
  width: 90%;
  text-align: center;
  transform: scale(0.9);
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.modal-overlay.active .modal-card {
  transform: scale(1);
}

.modal-title {
  font-size: 1.5rem;
  margin-bottom: 12px;
  color: var(--text-primary);
}

.modal-desc {
  color: var(--text-secondary);
  margin-bottom: 32px;
  line-height: 1.5;
}

.modal-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
}

/* --- Music Control Panel --- */
/* ── Music dock ─────────────────────────────────────────────────────────
   A single icon in the lobby's control bar: click mutes, hover opens the full
   panel upwards. It used to be a permanently expanded card pinned over the
   top-left corner, which is a lot of chrome for something touched twice a
   night — and it sat on top of the join details players are trying to read. */
.music-dock {
  position: relative;
  display: flex;
  align-items: center;
  text-align: left;
}

.music-dock-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: rgba(255, 255, 255, 0.72);
  cursor: pointer;
  padding: 0;
  transition: background 0.18s ease, color 0.18s ease,
              border-color 0.18s ease, transform 0.18s ease;
}

.music-dock-btn:hover {
  background: rgba(255, 255, 255, 0.11);
  border-color: rgba(255, 255, 255, 0.3);
  color: white;
  transform: translateY(-1px);
}

/* Muted reads at a glance, without opening anything. */
.music-dock-btn.active {
  color: var(--answer-red, #dc4091);
  border-color: rgba(220, 64, 145, 0.45);
  background: rgba(220, 64, 145, 0.12);
}

.music-panel {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 0;
  min-width: 230px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 11px;
  border-radius: 14px;
  background: rgba(14, 14, 28, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.45);
  z-index: 70;
  /* Hidden by transform/opacity rather than display, so it can animate — and
     `visibility` so it cannot be tabbed into or hovered while closed. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px) scale(0.97);
  transform-origin: bottom left;
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s;
}

/* Opens on hovering anywhere in the dock — including the panel itself, so the
   pointer can travel up into it without it closing underneath. Focus-within
   keeps it reachable from the keyboard. */
.music-dock:hover .music-panel,
.music-dock:focus-within .music-panel {
  opacity: 1;
  visibility: visible;
  transform: translateY(0) scale(1);
}

/* An invisible bridge across the gap, or the panel closes mid-reach. */
.music-dock::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 100%;
  width: 230px;
  height: 12px;
}

@media (prefers-reduced-motion: reduce) {
  .music-panel { transition: opacity 0.16s ease, visibility 0.16s; transform: none; }
  .music-dock:hover .music-panel,
  .music-dock:focus-within .music-panel { transform: none; }
}

.music-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--accent-light);
}

.music-title .track-name {
  color: white;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.music-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.music-panel .volume-slider { width: 100%; }

.volume-slider {
  flex: 1;
  height: 6px;
  -webkit-appearance: none;
  appearance: none;
  background: var(--bg-card);
  border-radius: var(--radius-full);
  outline: none;
}

.volume-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  background: var(--accent);
  border-radius: 50%;
  cursor: pointer;
  box-shadow: 0 0 10px var(--accent-glow);
}

.mute-toggle {
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 1.2rem;
  padding: 0;
  display: flex;
}

.mute-toggle.active {
  color: var(--answer-red);
}

.track-selector {
  display: flex;
  gap: 8px;
  width: 100%;
}

.track-btn {
  flex: 1;
  padding: 8px 4px;
  font-size: 0.7rem;
  border-radius: var(--radius-sm);
  background: var(--bg-glass);
  border: 1px solid var(--bg-glass-border);
  color: var(--text-secondary);
  font-family: var(--font-heading);
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
  text-transform: uppercase;
  white-space: nowrap;
}

.track-btn.active {
  background: var(--accent);
  color: white;
  border-color: var(--accent);
  box-shadow: 0 0 15px var(--accent-glow);
}

/* --- Mascot Flood Overlay --- */
.mascot-flood-overlay {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

#resultsScreen {
  position: relative;
  z-index: 1;
}

.mascot-flood-overlay .flood-mascot {
  position: fixed;
  width: 10vw;
  min-width: 70px;
  max-width: 140px;
  opacity: 0;
  bottom: -15vh;
}

@keyframes mascotFloodFly {
  0% {
    opacity: 0;
    transform: translateY(0) scale(0.7);
  }

  10% {
    opacity: 0.55;
  }

  75% {
    opacity: 0.5;
  }

  100% {
    opacity: 0;
    transform: translateY(-120vh) rotate(var(--end-rot, 10deg)) scale(1.1);
  }
}

/* ═══════════════════════════════════════════
   EMOTE SYSTEM
═══════════════════════════════════════════ */

/* Toggle button — fixed bottom-right on player screen */
.emote-toggle-btn {
  position: fixed;
  bottom: 20px;
  right: 20px;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--bg-glass);
  border: 2px solid var(--bg-glass-border);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  cursor: pointer;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  line-height: 1;
  transition: transform 0.15s ease, opacity 0.2s ease;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}

.emote-toggle-btn.hidden {
  display: none;
}

.emote-toggle-btn.cooldown {
  opacity: 0.35;
  pointer-events: none;
}

.emote-toggle-btn:active {
  transform: scale(0.88);
}

/* Emote carousel — pops up above the button */
.emote-carousel {
  position: fixed;
  bottom: 84px;
  right: 12px;
  background: rgba(8, 6, 18, 0.88);
  border: 1px solid rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
  border-radius: var(--radius-lg);
  padding: 12px;
  z-index: 500;
  width: min(88vw, 320px);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.emote-carousel.hidden {
  display: none;
}

.emote-scroll {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 4px;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
  /* Allow horizontal pan for scrolling but keep taps crisp */
  touch-action: pan-x;
  -webkit-overflow-scrolling: touch;
}

.emote-scroll::-webkit-scrollbar {
  height: 4px;
}

.emote-scroll::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.2);
  border-radius: 2px;
}

.emote-option {
  flex-shrink: 0;
  width: 58px;
  height: 58px;
  border: none;
  background: transparent;
  cursor: pointer;
  border-radius: 14px;
  padding: 6px;
  transition: background 0.15s ease, transform 0.15s ease;
  /* Remove tap delay and prevent double-tap zoom */
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}

.emote-option:hover {
  background: rgba(255, 255, 255, 0.12);
}

.emote-option:active {
  transform: scale(0.82);
}

.emote-option img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  /* Don't let the image absorb the tap — pass it through to the button */
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
}

/* Emote stage — full-screen overlay on host display */
#emoteStage {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 800;
}

/* Individual floating emote */
.floating-emote {
  position: absolute;
  bottom: 10%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  animation: emoteFloat 3.2s ease-out forwards;
}

.floating-emote-img {
  width: 84px;
  height: 84px;
  filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.45));
}

.floating-emote-name {
  font-family: var(--font-heading);
  font-size: 0.72rem;
  font-weight: 700;
  color: white;
  padding: 3px 10px;
  border-radius: 20px;
  white-space: nowrap;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}

@keyframes emoteFloat {
  0% {
    transform: translateY(0) scale(0.4);
    opacity: 0;
  }

  12% {
    transform: translateY(-24px) scale(1.15);
    opacity: 1;
  }

  75% {
    transform: translateY(-200px) scale(1);
    opacity: 0.85;
  }

  100% {
    transform: translateY(-260px) scale(0.9);
    opacity: 0;
  }
}

/* ════════════════════════════════════════════════════════════
   Reading Phase — Question Intro Before Answers
   ════════════════════════════════════════════════════════════ */

/* --- Keyframes --- */

@keyframes logoEntrance {
  0% {
    transform: scale(0) rotate(-20deg);
    opacity: 0;
  }

  52% {
    transform: scale(1.18) rotate(8deg);
    opacity: 1;
  }

  73% {
    transform: scale(0.93) rotate(-4deg);
  }

  88% {
    transform: scale(1.05) rotate(1.5deg);
  }

  100% {
    transform: scale(1) rotate(0deg);
    opacity: 1;
  }
}

@keyframes logoFloat {

  0%,
  100% {
    transform: translateY(0px) rotate(-1.5deg) scale(1);
  }

  35% {
    transform: translateY(-10px) rotate(2.5deg) scale(1.03);
  }

  70% {
    transform: translateY(-5px) rotate(-0.5deg) scale(0.99);
  }
}

@keyframes readingTextIn {
  0% {
    transform: translateY(32px) scale(0.94);
    opacity: 0;
  }

  58% {
    transform: translateY(-5px) scale(1.01);
    opacity: 1;
  }

  100% {
    transform: translateY(0) scale(1);
    opacity: 1;
  }
}

@keyframes readingProgressAppear {
  from {
    opacity: 0;
    transform: scaleX(0.85);
  }

  to {
    opacity: 1;
    transform: scaleX(1);
  }
}

@keyframes bubbleRise {
  0% {
    transform: translateY(0) scale(1);
    opacity: 0.65;
  }

  100% {
    transform: translateY(-115vh) scale(0.2);
    opacity: 0;
  }
}

@keyframes readingPlayerExit {
  0% {
    transform: scale(1) translateY(0);
    opacity: 1;
  }

  35% {
    transform: scale(1.04) translateY(-6px);
    opacity: 1;
  }

  100% {
    transform: scale(0.84) translateY(20px);
    opacity: 0;
  }
}

@keyframes readingOverlayExit {
  0% {
    transform: translateY(0) scale(1);
    opacity: 1;
  }

  25% {
    transform: translateY(-10px) scale(1.02);
    opacity: 1;
  }

  100% {
    transform: translateY(-102%) scale(0.96);
    opacity: 0;
  }
}

@keyframes glowPulse {

  0%,
  100% {
    opacity: 0.07;
    transform: translate(-50%, -50%) scale(1);
  }

  50% {
    opacity: 0.13;
    transform: translate(-50%, -50%) scale(1.15);
  }
}

/* --- Timebomb Timer --- */
.timebomb-container {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.timebomb-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 0 12px rgba(239, 68, 68, 0.55));
}

.timebomb-number {
  position: absolute;
  font-family: 'Courier New', Courier, monospace;
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  color: #ff2d00;
  text-shadow: 0 0 6px rgba(255, 45, 0, 0.9), 0 0 16px rgba(255, 45, 0, 0.5);
  letter-spacing: 3px;
  pointer-events: none;
  /* Positioned over the LCD display panel (lower-center of bomb body) */
  top: 47%;
  left: 50%;
  transform: translateX(-50%);
  font-size: 2.2rem;
  white-space: nowrap;
}

/* Host screen size (150×150 container) */
#displayTimer .timebomb-number {
  font-size: 2rem;
}

/* Player screen size (90×125 container) */
.player-timer-bomb .timebomb-number {
  font-size: 1.45rem;
  letter-spacing: 2px;
}

/* --- Point Multiplier Badge --- */
@keyframes multiplierBounceIn {
  0% {
    transform: translateY(-50px) rotate(-10deg) scale(0);
    opacity: 0;
  }

  60% {
    transform: translateY(6px) rotate(4deg) scale(1.15);
    opacity: 1;
  }

  80% {
    transform: translateY(-3px) rotate(-1deg) scale(0.97);
  }

  100% {
    transform: translateY(0) rotate(0deg) scale(1);
    opacity: 1;
  }
}

@keyframes multiplierFloat {

  0%,
  100% {
    transform: translateY(0) rotate(-2deg);
  }

  50% {
    transform: translateY(-7px) rotate(2deg);
  }
}

.multiplier-badge {
  position: absolute;
  top: 28px;
  right: 36px;
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background: linear-gradient(135deg, #fbbf24, #f59e0b);
  box-shadow: 0 0 28px rgba(251, 191, 36, 0.75), 0 4px 14px rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-heading);
  font-size: 1.8rem;
  font-weight: 900;
  color: #1c1a2e;
  z-index: 40;
  pointer-events: none;
  animation: multiplierBounceIn 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) both,
    multiplierFloat 2s ease-in-out 0.7s infinite;
}

.multiplier-badge--sm {
  width: 52px;
  height: 52px;
  font-size: 1.2rem;
  top: 16px;
  right: 16px;
  box-shadow: 0 0 18px rgba(251, 191, 36, 0.7), 0 3px 8px rgba(0, 0, 0, 0.3);
}

/* --- Minefield Reading Phase --- */
@keyframes mineRise {
  0% {
    transform: translateY(0) scale(1) rotate(0deg);
    opacity: 0.85;
  }

  50% {
    transform: translateY(-55vh) scale(0.75) rotate(18deg);
    opacity: 0.65;
  }

  100% {
    transform: translateY(-115vh) scale(0.25) rotate(-12deg);
    opacity: 0;
  }
}

@keyframes dangerPulse {

  0%,
  100% {
    box-shadow: inset 0 0 0 3px rgba(239, 68, 68, 0);
  }

  50% {
    box-shadow: inset 0 0 0 4px rgba(239, 68, 68, 0.65);
  }
}

#readingOverlay.minefield-active,
#readingScreen.minefield-active {
  animation: dangerPulse 1.1s ease-in-out infinite;
}

/* --- Penalty (Minefield wrong answer) feedback --- */
@keyframes explosionBurst {
  0% {
    filter: brightness(1);
  }

  12% {
    filter: brightness(2.2) saturate(0.3);
    background-color: #7f1d1d !important;
  }

  28% {
    filter: brightness(1.4);
    background-color: #991b1b !important;
    transform: translate(-3px, 2px);
  }

  42% {
    background-color: #7f1d1d !important;
    transform: translate(3px, -2px);
  }

  56% {
    background-color: #991b1b !important;
    transform: translate(-2px, 1px);
  }

  72% {
    background-color: #7f1d1d !important;
    transform: translate(0);
  }

  100% {
    filter: brightness(1);
  }
}

.penalty-feedback {
  animation: explosionBurst 0.6s ease-out both !important;
}

/* --- Full-page Explosion Overlay (Minefield wrong answer) --- */
@keyframes explosionExpandBg {
  0% {
    opacity: 0;
    transform: scale(0.2);
  }

  20% {
    opacity: 1;
    transform: scale(1.05);
    background: radial-gradient(circle, #fbbf24 0%, #ef4444 50%, #991b1b 100%);
  }

  60% {
    transform: scale(1);
    background: radial-gradient(circle, #ef4444 0%, #7f1d1d 60%, #1c0a0a 100%);
  }

  100% {
    opacity: 0;
    transform: scale(1);
  }
}

@keyframes explosionBoomIn {
  0% {
    transform: translate(-50%, -50%) scale(0) rotate(-15deg);
    opacity: 0;
  }

  35% {
    transform: translate(-50%, -50%) scale(1.3) rotate(6deg);
    opacity: 1;
  }

  55% {
    transform: translate(-50%, -50%) scale(0.92) rotate(-3deg);
  }

  70% {
    transform: translate(-50%, -50%) scale(1.07) rotate(1deg);
  }

  100% {
    transform: translate(-50%, -50%) scale(1) rotate(0deg);
    opacity: 1;
  }
}

@keyframes shardFly {
  0% {
    opacity: 1;
    transform: translate(0, 0) rotate(0deg) scale(1);
  }

  100% {
    opacity: 0;
    transform: translate(var(--sx), var(--sy)) rotate(var(--sr)) scale(0.3);
  }
}

.explosion-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0;
  animation: explosionExpandBg 1.4s ease-out both;
  background: radial-gradient(circle, #fbbf24 0%, #ef4444 50%, #991b1b 100%);
  pointer-events: none;
}

.explosion-text {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font-heading);
  font-size: clamp(4rem, 18vw, 9rem);
  font-weight: 900;
  color: #fff;
  text-shadow: 0 0 30px #fbbf24, 0 0 60px #ef4444, 0 4px 0 #7f1d1d;
  letter-spacing: 4px;
  white-space: nowrap;
  animation: explosionBoomIn 0.65s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

.explosion-shard {
  position: absolute;
  top: 50%;
  left: 50%;
  font-size: 2.2rem;
  animation: shardFly 1.1s ease-out both;
}

/* --- Player Reading Screen --- */

#readingScreen {
  overflow: hidden;
  justify-content: center;
  padding-bottom: 6px;
}

.reading-bubbles-layer {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

.reading-bubble {
  position: absolute;
  bottom: -40px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.13);
  animation: bubbleRise linear infinite;
}

.reading-screen-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 36px 24px 80px;
  gap: 18px;
  width: 100%;
  max-width: 500px;
  position: relative;
  z-index: 1;
}

.reading-logo-wrap {
  /* The logos are 1.4–4× wider than tall: full width so the wide ones come out big,
     and a height that lets the squarer ones (fyi…, VOTE!) grow too. */
  width: 88%;
  height: clamp(110px, 32vw, 150px);
  animation: logoEntrance 0.78s cubic-bezier(0.34, 1.15, 0.64, 1) both,
    logoFloat 3.2s ease-in-out 0.8s infinite;
}

.reading-logo {
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 6px 20px var(--r-shadow, rgba(220, 64, 145, 0.55)));
}

.reading-question-text {
  font-family: var(--font-heading);
  font-size: clamp(1.6rem, 7.4vw, 2.1rem);
  font-weight: 800;
  text-align: center;
  color: var(--text-primary);
  line-height: 1.3;
  animation: readingTextIn 0.56s cubic-bezier(0.34, 1.15, 0.64, 1) 0.52s both;
  max-width: 100%;
  overflow-wrap: anywhere;
}
/* Long questions (set in player.js) step down so they still fit above the bar. */
.reading-question-text.is-long { font-size: clamp(1.25rem, 5.4vw, 1.5rem); line-height: 1.4; }

.reading-progress-track {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 6px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 0;
  overflow: hidden;
}

.reading-progress-fill {
  height: 100%;
  width: 0%;
  border-radius: 0;
  background: linear-gradient(90deg, var(--r-grad-from, #dc4091), var(--r-grad-to, #79f487));
  box-shadow: 0 0 10px var(--r-shadow, rgba(220, 64, 145, 0.6));
}

/* --- Game Display Reading Overlay --- */

#readingOverlay {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--bg-primary);
  padding: 48px 64px 80px;
  overflow: hidden;
}

#readingOverlay::before {
  content: '';
  position: absolute;
  top: 38%;
  left: 50%;
  width: 700px;
  height: 700px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--r-grad-from, #dc4091) 0%, transparent 68%);
  opacity: 0.07;
  pointer-events: none;
  animation: glowPulse 3s ease-in-out infinite;
}

.display-reading-logo {
  width: min(62vw, 820px);
  height: min(26vh, 280px);
  object-fit: contain;
  filter: drop-shadow(0 10px 40px var(--r-shadow, rgba(220, 64, 145, 0.6)));
  animation: logoEntrance 0.78s cubic-bezier(0.34, 1.15, 0.64, 1) both,
    logoFloat 3.2s ease-in-out 0.8s infinite;
  position: relative;
  z-index: 1;
}

.display-reading-question {
  margin-top: 32px;
  font-family: var(--font-heading);
  font-size: clamp(2.4rem, min(5vw, 8.5vh), 5rem);
  font-weight: 800;
  text-align: center;
  color: var(--text-primary);
  line-height: 1.22;
  max-width: min(90vw, 1500px);
  overflow-wrap: anywhere;
  animation: readingTextIn 0.56s cubic-bezier(0.34, 1.15, 0.64, 1) 0.52s both;
  position: relative;
  z-index: 1;
}

/* Long questions (set in game-display.js) step down so they still fit on screen. */
.display-reading-question.is-long { font-size: clamp(1.8rem, min(3.4vw, 5.6vh), 3.3rem); line-height: 1.28; }

.display-reading-progress-track {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 8px;
  background: rgba(255, 255, 255, 0.09);
  border-radius: 0;
  overflow: hidden;
}

.display-reading-progress-fill {
  height: 100%;
  width: 0%;
  border-radius: var(--radius-full);
  background: linear-gradient(90deg, var(--r-grad-from, #dc4091), var(--r-grad-to, #79f487));
  box-shadow: 0 0 14px var(--r-shadow, rgba(220, 64, 145, 0.6));
}

/* Reading overlay bubbles layer (reused class) */
#readingOverlay .reading-bubbles-layer {
  z-index: 0;
}

/* --- Type-Answer Results: wrong answer dangling tags --- */

@keyframes wrongAnswerDangle {

  0%,
  100% {
    transform: rotate(-4deg) translateY(0);
  }

  50% {
    transform: rotate(4deg) translateY(-5px);
  }
}

@keyframes wrongTagAppear {
  0% {
    transform: scale(0.5) rotate(-12deg);
    opacity: 0;
  }

  65% {
    transform: scale(1.08) rotate(3deg);
    opacity: 1;
  }

  100% {
    transform: scale(1) rotate(var(--final-rot, -2deg));
    opacity: 1;
  }
}

.type-answer-results {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
  width: 100%;
}

.type-answer-correct-card {
  background: rgba(121, 244, 135, 0.1);
  border: 2px solid rgba(121, 244, 135, 0.4);
  border-radius: var(--radius-lg);
  padding: 32px 48px;
  text-align: center;
  width: 100%;
  max-width: 700px;
}

.type-answer-correct-label {
  font-size: 1rem;
  color: var(--text-muted);
  margin-bottom: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
}

.type-answer-correct-text {
  font-family: var(--font-heading);
  font-size: 2.4rem;
  font-weight: 800;
  color: var(--correct);
  text-shadow: 0 0 20px rgba(121, 244, 135, 0.4);
}

.type-answer-stats {
  margin-top: 12px;
  font-size: 0.9rem;
  color: var(--text-muted);
}

.type-answer-wrong-section {
  width: 100%;
  max-width: 900px;
}

.type-answer-wrong-label {
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 14px;
  text-align: center;
}

.type-answer-wrong-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
  align-items: flex-start;
}

.type-answer-wrong-tag {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  background: rgba(220, 64, 145, 0.1);
  border: 1.5px solid rgba(220, 64, 145, 0.32);
  border-radius: 14px;
  padding: 8px 16px;
  transform-origin: top center;
  animation:
    wrongTagAppear 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) var(--delay, 0s) both,
    wrongAnswerDangle var(--swing-dur, 2.6s) ease-in-out var(--swing-delay, 0.5s) infinite;
}

.type-answer-wrong-author {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 2px;
}

.type-answer-wrong-text {
  font-family: var(--font-heading);
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--incorrect);
}

/* --- Pixel Reveal canvas --- */
.pixel-reveal-canvas {
  max-width: 100%;
  max-height: 280px;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-glow);
  border: 2px solid var(--bg-glass-border);
  display: block;
}

/* --- TunedIn Vinyl Spin --- */
@keyframes tunedInSpin {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}

/* ════ LEADERBOARD REDESIGN ════ */

/* Player colour avatar */
.lb-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--avatar-color, #6b6d80);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4), 0 0 0 2px rgba(255, 255, 255, 0.12);
}

/* Uploaded profile picture fills the avatar circle, replacing the initial */
.lb-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  display: block;
}

/* --- Player profile pictures --- */

/* Lobby chip on the host screen.
   Sized in em, not px: the chip is font-size 4rem, so the avatar tracks the
   name automatically and stays proportional if that size ever changes. */
.player-chip .chip-avatar {
  width: 1.45em;
  height: 1.45em;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.22), 0 6px 22px rgba(0, 0, 0, 0.45);
}

/* A big round avatar needs less breathing room on its side than bare text does */
.player-chip:has(.chip-avatar) {
  padding-left: 24px;
}

/* Picker on the player's phone */
.avatar-picker {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin: 20px 0 8px;
}

.avatar-bubble {
  position: relative;
  width: 104px;
  height: 104px;
  border-radius: 50%;
  border: 2px dashed rgba(255, 255, 255, 0.28);
  background: var(--avatar-color, var(--bg-glass));
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 2.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  overflow: visible;
  transition: transform 0.15s ease, border-color 0.2s ease;
  -webkit-tap-highlight-color: transparent;
}

.avatar-bubble:active { transform: scale(0.95); }

.avatar-bubble img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}

.avatar-badge {
  position: absolute;
  right: -2px;
  bottom: -2px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 1.4rem;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45), 0 0 0 3px var(--bg-primary);
}

.avatar-hint {
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.avatar-remove {
  background: none;
  border: 0;
  color: var(--text-muted);
  font-size: 0.8rem;
  text-decoration: underline;
  cursor: pointer;
  padding: 2px 6px;
}

.lb-avatar-slot {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

/* Score progress bar */
.lb-progress-track {
  height: 4px;
  background: rgba(255, 255, 255, 0.06);
  overflow: hidden;
}

/* Width is this round's points as a share of the round's best — not the running
   total, which converges to a full bar for everyone within a question or two. */
.lb-progress-bar {
  height: 100%;
  background: linear-gradient(90deg, #dc4091, #a855f7);
  border-radius: 0 2px 2px 0;
  width: 0;
}

/* Gold goes to whoever won THIS round, which is what the bar now measures —
   medal colours by overall rank would just fight the number beside them. */
.lb-progress-bar--best {
  background: linear-gradient(90deg, #f5c518, #ff9f1c);
  box-shadow: 0 0 10px rgba(245, 197, 24, 0.55);
}

/* Rank change badge — prominent pill */
.lb-rank-badge {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 0.95rem;
  min-width: 44px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  padding: 0 10px;
  flex-shrink: 0;
}

.lb-rank-badge--up {
  background: rgba(121, 244, 135, 0.18);
  border: 1.5px solid rgba(121, 244, 135, 0.5);
  color: #79f487;
  animation: lbBadgeUpPulse 1.8s ease-in-out infinite;
}

.lb-rank-badge--down {
  background: rgba(220, 64, 145, 0.15);
  border: 1.5px solid rgba(220, 64, 145, 0.4);
  color: #dc4091;
}

.lb-rank-badge--same {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: var(--text-muted);
}

/* Held back until the reorder — see lbRevealRanks. Opacity rather than display,
   so letting it in doesn't shunt the row's layout sideways. */
.lb-rank-badge {
  opacity: 1;
  transition: opacity 0.3s ease;
}

.lb-rank-badge--pending {
  opacity: 0;
  animation: none !important;   /* the --up pulse must not run while invisible */
}

/* The new number landing as its row moves */
.leaderboard-rank.rank-flip {
  animation: lbRankFlip 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes lbRankFlip {
  0% { transform: scale(1); }
  35% { transform: scale(1.45); color: var(--accent-light); }
  100% { transform: scale(1); }
}

/* The standings' copy of the double-points warning */
.lb-double-chip {
  margin: 0 auto 10px;
}

@media (prefers-reduced-motion: reduce) {
  .leaderboard-rank.rank-flip { animation: none; }
  .lb-rank-badge { transition: none; }
}

@keyframes lbBadgeUpPulse {

  0%,
  100% {
    box-shadow: 0 0 10px rgba(121, 244, 135, 0.3);
  }

  50% {
    box-shadow: 0 0 22px rgba(121, 244, 135, 0.65);
  }
}

/* Epic streak fire (3+) */
.streak-fire {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: 1.5rem;
}

.streak-fire--epic {
  animation: fireScaleIn 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) both,
    fireGlow 1.6s ease-in-out 0.7s infinite;
  filter: drop-shadow(0 0 6px rgba(255, 140, 66, 0.7));
}

@keyframes fireScaleIn {
  from {
    transform: scale(0.5);
    opacity: 0;
  }

  to {
    transform: scale(1.3);
    opacity: 1;
  }
}

@keyframes fireGlow {

  0%,
  100% {
    filter: drop-shadow(0 0 6px rgba(255, 140, 66, 0.6));
    transform: scale(1.3);
  }

  50% {
    filter: drop-shadow(0 0 18px rgba(255, 200, 66, 0.9));
    transform: scale(1.45);
  }
}

/* Player phone rank movement */
.personal-rank-move {
  min-height: 2rem;
  margin-bottom: 8px;
}

.rank-move {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.1rem;
  display: inline-block;
  padding: 6px 16px;
  border-radius: var(--radius-full);
  animation: rankMoveIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

.rank-move--up {
  color: #79f487;
  background: rgba(121, 244, 135, 0.12);
  border: 1px solid rgba(121, 244, 135, 0.3);
}

.rank-move--down {
  color: #dc4091;
  background: rgba(220, 64, 145, 0.10);
  border: 1px solid rgba(220, 64, 145, 0.25);
}

.rank-move--same {
  color: var(--text-muted);
}

.rank-move--top {
  color: #f5c518;
  background: rgba(245, 197, 24, 0.12);
  border: 1px solid rgba(245, 197, 24, 0.35);
}

@keyframes rankMoveIn {
  from {
    transform: translateY(10px) scale(0.85);
    opacity: 0;
  }

  to {
    transform: translateY(0) scale(1);
    opacity: 1;
  }
}

/* ── Full-screen leaderboard layout ──────────────────────────
   Override the centred game-wrapper so the leaderboard fills
   the entire viewport top-to-bottom with no dead space.
──────────────────────────────────────────────────────────────── */
#leaderboardScreen {
  justify-content: flex-start !important;
  padding: 20px 48px 20px !important;
}

/* Compact header so max space goes to the rows */
#leaderboardScreen h2 {
  font-size: 2rem !important;
  margin-bottom: 2px !important;
}

#leaderboardScreen .leaderboard-subtitle {
  margin-bottom: 10px;
  font-size: 0.85rem;
}

/* Container stretches to fill remaining height.
   Two columns once there is a chase to show; the left keeps its original width
   so a small game looks exactly as it always did. */
#leaderboardScreen .leaderboard-container {
  flex: 1;
  width: 100%;
  max-width: 1000px;
  display: flex;
  flex-direction: column;
  margin-bottom: 14px;
  min-height: 0;
}

#leaderboardScreen .leaderboard-container--split {
  flex-direction: row;
  align-items: stretch;
  gap: 22px;
  max-width: 1380px;
}

/* Direct child ONLY: the chase list also carries `.leaderboard-list`, and a
   descendant selector here forced it to the left column's min-width, pushing its
   rows clean off the right of the screen. */
#leaderboardScreen .leaderboard-container--split > .leaderboard-list {
  flex: 1 1 auto;
  /* A full-size row's fixed parts (rank, badge, avatar, score, padding, streak
     slot) come to ~460px; below this the score overflows the card, which clips
     it. The JS width gate should prevent ever getting here, but this keeps the
     failure visible as a tighter chase rather than a missing score. */
  min-width: 520px;
}

/* ── The chase column ──────────────────────────────────────────────
   Deliberately the SAME row markup as the left column, only smaller: a row
   that climbs into the top 5 is the same DOM node moving between lists, which
   is what lets it animate across instead of vanishing and reappearing.
──────────────────────────────────────────────────────────────────── */
.lb-chase-col {
  /* Shrinkable (0 1, not 0 0): between the split threshold and a wide display
     the chase gives room back to the top-5 rows rather than starving them. */
  flex: 0 1 clamp(290px, 30%, 420px);
  display: flex;
  flex-direction: column;
  /* Head + rows + "+N more" centre as ONE block against the tall left column,
     so the header stays attached to its list instead of floating at the top. */
  justify-content: center;
  min-height: 0;
  min-width: 0;
}

.lb-chase-head {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-align: left;
  padding: 0 4px 7px;
  flex-shrink: 0;
}

/* Specificity matters here: `#leaderboardScreen .leaderboard-list` sets
   `flex: 1` + `justify-content: center`, and it is defined LATER in this file,
   so an equally-specific rule would lose the tie and leave the rows floating in
   the middle of a full-height box. The extra class outranks it outright. */
#leaderboardScreen .lb-chase-col .lb-chase-list {
  flex: 0 1 auto;
  gap: var(--chase-gap, 5px);
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  min-height: 0;
  overflow: hidden;
}

.lb-chase-more {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 0.82rem;
  color: var(--text-muted);
  text-align: center;
  padding-top: 7px;
  flex-shrink: 0;
}

/* Chase rows: same structure, dialled down. `flex: 0 0 auto` so they keep a
   constant height instead of stretching like the left column's rows — a
   two-player chase should not produce two enormous rows. */
#leaderboardScreen .lb-chase-list .leaderboard-item {
  flex: 0 0 auto;
  max-height: none;
}

#leaderboardScreen .lb-chase-list .leaderboard-item-card {
  padding: 0 12px;
  gap: 9px;
  min-height: var(--chase-h, 42px);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.04);
}

/* A host screen can't scroll, so the rows are sized to the space that's actually
   there — lbFitChase() in game-display.js measures the column and sets these,
   rather than stepping through fixed tiers that shrink further than they need to. */

/* The top-3 medal treatments are positional (nth-child) and must never paint a
   chase row gold just because it happens to sit first in that column. */
.lb-chase-list .leaderboard-item:nth-child(1) .leaderboard-item-card,
.lb-chase-list .leaderboard-item:nth-child(2) .leaderboard-item-card,
.lb-chase-list .leaderboard-item:nth-child(3) .leaderboard-item-card {
  background: rgba(255, 255, 255, 0.04);
  border: none;
}

.lb-chase-list .leaderboard-rank {
  font-size: var(--chase-font, 0.95rem) !important;
  min-width: 26px !important;
  color: var(--text-muted);
}

.lb-chase-list .leaderboard-name {
  font-size: var(--chase-font, 0.95rem) !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: left;
}

.lb-chase-list .leaderboard-score {
  font-size: var(--chase-font, 0.95rem) !important;
  min-width: 58px !important;
}

.lb-chase-list .lb-avatar {
  width: var(--chase-av, 26px) !important;
  height: var(--chase-av, 26px) !important;
  font-size: 0.72rem !important;
}

.lb-chase-list .lb-rank-badge {
  font-size: 0.68rem !important;
  min-width: 30px !important;
  height: 20px !important;
  padding: 0 5px;
}

/* No room for these at this size — the rank badge carries the story instead.
   Scoped with #leaderboardScreen because that id sets `display: flex` on the
   streak slot; a plain class rule loses to it and the fire leaks into the
   chase rows, floating outside their cards. */
#leaderboardScreen .lb-chase-list .leaderboard-streak-slot,
#leaderboardScreen .lb-chase-list .points-gained-badge,
#leaderboardScreen .lb-chase-list .lb-progress-track {
  display: none !important;
}

/* The gain slot's fixed 104px exists to keep the LEFT column's centred names
   from sliding. Chase names are left-aligned, so nothing slides here — and the
   reserved width, holding a points badge the chase never shows, squeezed every
   chase name to nothing on a 1280px screen. Sized to its content instead:
   empty, or the one thing the chase does show, the Kaos badge. */
#leaderboardScreen .lb-chase-list .leaderboard-gain-slot { width: auto; }
/* Number only: the Kaos border already says what it is, and every pixel the
   badge gives up goes to the name beside it. */
#leaderboardScreen .lb-chase-list .kaos-delta-badge {
  font-size: calc(var(--chase-font, 0.95rem) * 0.8);
  padding: 1px 6px;
  box-shadow: 0 0 8px rgba(220, 64, 145, 0.35);
}
#leaderboardScreen .lb-chase-list .kaos-delta-icon { display: none; }

/* ── Crossing between the columns ──────────────────────────────────
   Breaking into the top 5 is the best thing that happens on this screen, so it
   gets marked. Animated on the CARD, never the <li> — the row itself is
   carrying the FLIP transform and a competing animation would fight it.
──────────────────────────────────────────────────────────────────── */
.lb-crossed-in .leaderboard-item-card {
  animation: lbCrossedIn 1s ease-out;
}

@keyframes lbCrossedIn {
  0%   { box-shadow: 0 0 0 0 rgba(121, 244, 135, 0); }
  25%  { box-shadow: 0 0 26px 5px rgba(121, 244, 135, 0.65); }
  100% { box-shadow: 0 0 0 0 rgba(121, 244, 135, 0); }
}

.lb-crossed-out .leaderboard-item-card {
  animation: lbCrossedOut 1s ease-out;
}

@keyframes lbCrossedOut {
  0%   { box-shadow: 0 0 0 0 rgba(220, 64, 145, 0); }
  25%  { box-shadow: 0 0 22px 4px rgba(220, 64, 145, 0.5); }
  100% { box-shadow: 0 0 0 0 rgba(220, 64, 145, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .lb-crossed-in .leaderboard-item-card,
  .lb-crossed-out .leaderboard-item-card { animation: none; }
}

/* List is a vertical flex — items share available height, centred when capped */
#leaderboardScreen .leaderboard-list {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
}

/* Each row grows to fill its share but never exceeds the cap.
   The cap used to be 155px, which on a 900px screen let five rows divide the
   whole column into slabs — a 44px avatar and a 24px name adrift in 130px of
   empty card. The row is only as tall as it needs to be now, and the contents
   below are sized up to match. */
#leaderboardScreen .leaderboard-item {
  flex: 1;
  max-height: 155px;
  margin-bottom: 0;
  align-items: stretch;
}

/* The TOP-5 rows only. Scoped to the left list by id, because
   `.leaderboard-item` also matches every row in the chase column — a min-height
   here leaked into those and doubled their height. */
#leaderboardScreen #leaderboardList > .leaderboard-item {
  /* Grows to share the column, but stops at the cap instead of turning into a
     slab — the leftover space goes above and below the block, not inside it. */
  flex: 1 1 auto;
  max-height: 104px;
  min-height: 74px;
}

/* The block of rows sits centred in the column rather than stretched down it. */
#leaderboardScreen .leaderboard-container--split > .leaderboard-list {
  justify-content: center;
  gap: 12px;
}

/* Card fills the full height of the row; vertically centres its content */
#leaderboardScreen .leaderboard-item-card {
  align-items: center;
  padding: 0 28px;
}

/* Streak slot stretches to match row height */
#leaderboardScreen .leaderboard-streak-slot {
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Progress bar — absolute inside the card, clipped by overflow:hidden */
#leaderboardScreen .leaderboard-item-card {
  position: relative;
  overflow: hidden;
}

#leaderboardScreen .lb-progress-track {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  margin-top: 0;
  border-radius: 0;
}

/* ── Scaling by player count ────────────────────────────────
   Items already grow vertically — these rules scale the text
   and avatar sizes so they look proportional at each count.
──────────────────────────────────────────────────────────────── */

/* 5 players — the standard full board, and the one people actually look at.
   Sized against the 108px row above rather than the old 155px slab. */
.leaderboard-list[data-count="5"] .leaderboard-rank {
  font-size: 2.3rem;
  min-width: 56px;
}

.leaderboard-list[data-count="5"] .leaderboard-name {
  font-size: 1.95rem;
}

.leaderboard-list[data-count="5"] .leaderboard-score {
  font-size: 1.9rem;
  min-width: 112px;
}

.leaderboard-list[data-count="5"] .lb-avatar {
  width: 62px;
  height: 62px;
  font-size: 1.6rem;
}

.leaderboard-list[data-count="5"] .lb-rank-badge {
  font-size: 1.05rem;
  min-width: 50px;
  height: 32px;
}

/* 4 players */
.leaderboard-list[data-count="4"] .leaderboard-rank {
  font-size: 2.4rem;
  min-width: 58px;
}

.leaderboard-list[data-count="4"] .leaderboard-name {
  font-size: 1.8rem;
}

.leaderboard-list[data-count="4"] .leaderboard-score {
  font-size: 1.9rem;
  min-width: 120px;
}

.leaderboard-list[data-count="4"] .lb-avatar {
  width: 52px;
  height: 52px;
  font-size: 1.3rem;
}

.leaderboard-list[data-count="4"] .lb-rank-badge {
  font-size: 1.15rem;
  min-width: 52px;
  height: 34px;
}

/* 3 players */
.leaderboard-list[data-count="3"] .leaderboard-rank {
  font-size: 3rem;
  min-width: 68px;
}

.leaderboard-list[data-count="3"] .leaderboard-name {
  font-size: 2.2rem;
}

.leaderboard-list[data-count="3"] .leaderboard-score {
  font-size: 2.3rem;
  min-width: 140px;
}

.leaderboard-list[data-count="3"] .lb-avatar {
  width: 64px;
  height: 64px;
  font-size: 1.6rem;
}

.leaderboard-list[data-count="3"] .lb-rank-badge {
  font-size: 1.35rem;
  min-width: 60px;
  height: 40px;
}

.leaderboard-list[data-count="3"] .leaderboard-streak-slot {
  width: 88px;
  font-size: 2rem;
}

/* 2 players */
.leaderboard-list[data-count="2"] .leaderboard-rank {
  font-size: 4rem;
  min-width: 80px;
}

.leaderboard-list[data-count="2"] .leaderboard-name {
  font-size: 2.8rem;
}

.leaderboard-list[data-count="2"] .leaderboard-score {
  font-size: 3rem;
  min-width: 170px;
}

.leaderboard-list[data-count="2"] .lb-avatar {
  width: 80px;
  height: 80px;
  font-size: 2rem;
}

.leaderboard-list[data-count="2"] .lb-rank-badge {
  font-size: 1.6rem;
  min-width: 70px;
  height: 48px;
}

.leaderboard-list[data-count="2"] .leaderboard-streak-slot {
  width: 100px;
  font-size: 2.5rem;
}

/* 1 player */
.leaderboard-list[data-count="1"] .leaderboard-rank {
  font-size: 5rem;
  min-width: 90px;
}

.leaderboard-list[data-count="1"] .leaderboard-name {
  font-size: 3.5rem;
}

.leaderboard-list[data-count="1"] .leaderboard-score {
  font-size: 3.8rem;
  min-width: 200px;
}

.leaderboard-list[data-count="1"] .lb-avatar {
  width: 100px;
  height: 100px;
  font-size: 2.5rem;
}

.leaderboard-list[data-count="1"] .lb-rank-badge {
  display: none;
}

.leaderboard-list[data-count="1"] .leaderboard-streak-slot {
  width: 120px;
  font-size: 3rem;
}
/* ─── Number Input: Player Keypad ─── */

.numpad-display {
  width: 100%;
  background: rgba(255,255,255,0.08);
  border: 2px solid rgba(6,182,212,0.4);
  border-radius: var(--radius-sm);
  padding: 16px 20px;
  font-size: 2.4rem;
  font-family: var(--font-heading);
  font-weight: 700;
  text-align: right;
  color: var(--text-secondary);
  letter-spacing: 0.05em;
  margin-bottom: 12px;
  min-height: 72px;
  word-break: break-all;
}

.numpad-hint {
  font-size: 0.88rem;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.numpad-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.numpad-key {
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 1.6rem;
  font-family: var(--font-heading);
  font-weight: 700;
  height: 100%;
  padding: 0;
  cursor: pointer;
  transition: background 0.12s, transform 0.08s;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

.numpad-key:active {
  background: rgba(6,182,212,0.22);
  transform: scale(0.94);
}

.numpad-key:disabled {
  opacity: 0.4;
  cursor: default;
}

/* The number dial's Submit is the standard pill (.btn .btn-primary .btn-large,
   set in renderNumberInputUI) like every other type's; this only keeps it
   full width and unshrinkable in the dial's column. */
.numpad-submit {
  width: 100%;
  flex-shrink: 0;
}

/* ─── Number Input: host reveal (applyInlineResultsNumber) ───
   Every position is set from JS for the current view; `--nr-move` is 0 for a
   plain re-layout and the zoom's duration while the camera moves, so ONE set
   of transitions serves both. Nothing here transitions a size the layout
   measures (the tags' width/height), only where things sit. */

.nr {
  --nr-move: 0ms;
  --nr-ease: cubic-bezier(0.65, 0, 0.35, 1);
  --nr-in: #2ecc71;
  --nr-gold: #fbbf24;
  --nr-close: #22d3ee;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* The answer — the payoff, so it gets the size */
.nr-answer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  text-align: center;
}
.nr-answer-label {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.nr-answer-value {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(2.4rem, 7vh, 6rem);
  line-height: 1;
  color: rgba(255, 255, 255, 0.28);
  font-variant-numeric: tabular-nums;
  transition: color 0.3s ease;
}
.nr-answer-value--rolling { color: #fff; }
.nr-answer-value--landed {
  color: var(--nr-gold);
  text-shadow: 0 0 34px rgba(251, 191, 36, 0.45);
  animation: nrLand 0.55s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.nr-answer-value--dark { color: var(--text-muted); }
.nr-answer-range {
  min-height: 1.3em;
  font-size: 0.98rem;
  font-weight: 700;
  color: #6ee7a0;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.4s ease, transform 0.4s ease;
}
.nr-answer-range.is-shown { opacity: 1; transform: none; }

/* The line. The stage IS the plot: 0%–100% is the ends of the track, and the
   side margins are the room a tag may overhang (NR_EDGE in JS). */
.nr-stage {
  position: relative;
  z-index: 0;
  height: clamp(190px, 29vh, 380px);
  margin: 0 52px;
}

.nr-track {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 37px;
  height: 6px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.18);
  transform-origin: left center;
  animation: nrTrackIn 0.55s cubic-bezier(0.25, 1, 0.5, 1) backwards;
}

.nr-axis {
  position: absolute;
  left: -52px;
  right: -52px;
  bottom: 0;
  height: 37px;
  overflow: hidden;
  animation: nrFadeIn 0.5s ease 0.15s backwards;
}
.nr-tick {
  position: absolute;
  top: 0;
  width: 0;
  transition: left var(--nr-move) var(--nr-ease), opacity var(--nr-move) ease;
}
.nr-tick::before {
  content: '';
  position: absolute;
  left: -1px;
  top: 0;
  width: 2px;
  height: 8px;
  background: rgba(255, 255, 255, 0.28);
}
.nr-tick-label {
  position: absolute;
  top: 12px;
  left: 0;
  transform: translateX(-50%);
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--text-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* The accepted zone: a column of light the right guesses stand in */
.nr-band {
  position: absolute;
  top: 0;
  bottom: 30px;
  border-left: 1.5px dashed rgba(46, 204, 113, 0.6);
  border-right: 1.5px dashed rgba(46, 204, 113, 0.6);
  border-radius: 8px 8px 0 0;
  background: linear-gradient(to top, rgba(46, 204, 113, 0.24), rgba(46, 204, 113, 0.03));
  opacity: 0;
  transition: left var(--nr-move) var(--nr-ease), width var(--nr-move) var(--nr-ease), opacity 0.4s ease;
}
.nr-band--on {
  opacity: 1;
  animation: nrBandIn 0.55s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.nr-band-label {
  /* Beside the zone's top-left corner, clear of the needle's head, which
     always stands in the middle of the zone. */
  position: absolute;
  top: 2px;
  right: 100%;
  margin-right: 6px;
  font-size: 0.75rem;
  font-weight: 800;
  color: #6ee7a0;
  white-space: nowrap;
}

/* The needle: sweeps in with the counter, then stays on the answer */
.nr-needle {
  position: absolute;
  top: 0;
  bottom: 37px;
  left: 0;
  width: 0;
  z-index: 2;
  opacity: 0;
  transition: left var(--nr-move) var(--nr-ease), opacity 0.3s ease;
}
.nr-needle--on { opacity: 1; }
.nr-needle::before {
  content: '';
  position: absolute;
  left: -1.5px;
  top: 10px;
  bottom: -12px;
  width: 3px;
  border-radius: 2px;
  background: var(--nr-gold);
  box-shadow: 0 0 12px rgba(251, 191, 36, 0.7);
}
.nr-needle::after {
  content: '';
  position: absolute;
  left: -7px;
  top: 0;
  width: 14px;
  height: 14px;
  border-radius: 2px;
  background: var(--nr-gold);
  transform: rotate(45deg);
}

/* A guess: dot on the line, stem up, tag on top. The pin itself makes no
   stacking context (no z-index/transform/opacity), so every stem sits under
   every tag. */
.nr-pin {
  position: absolute;
  bottom: 40px;
  left: 0;
  width: 0;
  height: 0;
  transition: left var(--nr-move) var(--nr-ease), opacity var(--nr-move) ease;
}
.nr-pin--gone { opacity: 0; }
.nr-dot {
  position: absolute;
  left: -8px;
  bottom: -8px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--pin-color, #fff);
  border: 2.5px solid #fff;
  box-sizing: border-box;
  z-index: 2;
  animation: nrDotPop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) var(--pin-delay, 0ms) backwards;
  transition: box-shadow 0.35s ease, opacity 0.35s ease;
}
.nr-stem {
  position: absolute;
  left: -1px;
  bottom: 0;
  width: 2px;
  height: 0;
  background: rgba(255, 255, 255, 0.3);
  z-index: 1;
  transform-origin: bottom center;
  animation: nrStemGrow 0.3s ease calc(var(--pin-delay, 0ms) + 60ms) backwards;
  transition: height var(--nr-move) var(--nr-ease), background 0.35s ease, opacity 0.35s ease;
}
.nr-tag {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 3;
  transform: translateX(-50%);
  pointer-events: none;
  transition: bottom var(--nr-move) var(--nr-ease), transform var(--nr-move) var(--nr-ease), opacity 0.3s ease;
}
.nr-pin--untagged .nr-tag { opacity: 0; }
.nr-tag-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 5px 11px 6px;
  border-radius: 10px;
  background: rgba(14, 14, 28, 0.9);
  border: 1.5px solid rgba(255, 255, 255, 0.16);
  box-shadow: inset 0 3px 0 var(--pin-color, #fff);
  white-space: nowrap;
  animation: nrTagDrop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--pin-delay, 0ms) + 110ms) backwards;
  transition: border-color 0.35s ease, box-shadow 0.35s ease, opacity 0.35s ease, filter 0.35s ease;
}
.nr-tag-name {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.82rem;
  line-height: 1.2;
  color: rgba(255, 255, 255, 0.78);
}
.nr-tag-value {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.1rem;
  line-height: 1.15;
  color: #fff;
  font-variant-numeric: tabular-nums;
  transition: color 0.35s ease;
}
.nr-tag-more {
  /* On the right edge, not the top corner: the top is where a sharpshooter's
     "+500" badge sits, and a column of joint winners carries one on every tag. */
  position: absolute;
  top: 50%;
  right: -12px;
  transform: translateY(-50%);
  min-width: 22px;
  height: 22px;
  padding: 0 5px;
  border-radius: 11px;
  background: #fff;
  color: #14142a;
  font-size: 0.72rem;
  font-weight: 800;
  line-height: 22px;
  text-align: center;
}

/* A group: everyone who guessed the same number. The number once, on the
   left; their names beside it, in columns of up to four (--rows). */
.nr-tag-card--group {
  flex-direction: row;
  align-items: center;
  gap: 10px;
  padding: 6px 12px 6px 10px;
}
.nr-group-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1;
}
.nr-group-count {
  margin-top: 3px;
  font-size: 0.72rem;
  font-weight: 800;
  color: rgba(255, 255, 255, 0.55);
}
.nr-group-names {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(var(--rows, 4), auto);
  column-gap: 12px;
  row-gap: 1px;
  padding-left: 10px;
  border-left: 1px solid rgba(255, 255, 255, 0.14);
}
.nr-group-name {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.8rem;
  line-height: 1.25;
  color: rgba(255, 255, 255, 0.85);
  animation: nrNameIn 0.3s ease calc(var(--pin-delay, 0ms) + 160ms + var(--name-i, 0) * 70ms) backwards;
}
.nr-group-dot {
  flex-shrink: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--pin-color, #fff);
}
.nr-group-name--more { color: var(--text-muted); }
.nr-pin--group .nr-dot { border-color: rgba(255, 255, 255, 0.95); background: #9aa0b8; }

/* Verdict */
.nr-pin--in .nr-tag-card {
  border-color: var(--nr-in);
  box-shadow: inset 0 3px 0 var(--pin-color, #fff), 0 0 18px rgba(46, 204, 113, 0.4);
  animation: nrTagBounce 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--in-i, 0) * 45ms);
}
.nr-pin--in .nr-tag-value { color: #6ee7a0; }
.nr-pin--in .nr-stem { background: rgba(46, 204, 113, 0.65); }
.nr-pin--in .nr-dot { box-shadow: 0 0 0 3px var(--nr-in), 0 0 14px rgba(46, 204, 113, 0.7); }
.nr-pin--out .nr-tag-card { opacity: 0.5; filter: saturate(0.5); }
.nr-pin--out .nr-dot { opacity: 0.6; }
.nr-pin--out .nr-stem { opacity: 0.35; }
.nr-pin--sharp .nr-tag { z-index: 4; }
.nr-pin--sharp .nr-tag-card {
  border-color: var(--nr-gold);
  box-shadow: inset 0 3px 0 var(--pin-color, #fff), 0 0 0 2px rgba(251, 191, 36, 0.4), 0 0 26px rgba(251, 191, 36, 0.55);
}
.nr-pin--sharp .nr-tag-value { color: #fcd34d; }
.nr-pin--sharp .nr-dot { box-shadow: 0 0 0 3px var(--nr-gold), 0 0 16px rgba(251, 191, 36, 0.8); }
.nr-pin--in .nr-group-name { color: #d7ffe6; }
/* Closest — the flat bonus when nobody was exact. Teal, not gold: gold means
   the exact number. */
.nr-pin--closest .nr-tag { z-index: 4; }
.nr-pin--closest .nr-tag-card {
  border-color: var(--nr-close);
  box-shadow: inset 0 3px 0 var(--pin-color, #fff), 0 0 0 2px rgba(34, 211, 238, 0.35), 0 0 22px rgba(34, 211, 238, 0.45);
}
.nr-tag-badge.nr-tag-badge--closest { background: var(--nr-close); color: #053540; }
.nr-tag-badge {
  position: absolute;
  top: -13px;
  left: 50%;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--nr-gold);
  color: #3b2300;
  font-size: 0.72rem;
  font-weight: 800;
  line-height: 1.3;
  transform: translateX(-50%);
  animation: nrBadgePop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}

/* Guesses the zoom left behind — counted, not deleted */
.nr-edge {
  position: absolute;
  bottom: 24px;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 4px 8px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.16);
  font-size: 0.75rem;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.72);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
}
.nr-edge--left { right: calc(100% + 6px); }
.nr-edge--right { left: calc(100% + 6px); }
.nr-edge.is-shown { opacity: 1; transition-delay: calc(var(--nr-move) * 0.6); }

.nr-zoom-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 1.4em;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
  opacity: 0;
  transition: opacity 0.5s ease 0.4s;
}
.nr-zoom-note.is-shown { opacity: 1; }

/* Who got closest */
.nr-cards {
  /* One row, always: a wrapped second row pushes the screen past the fold
     when a picture shares the width. */
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 12px;
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
}
.nr-cards:empty { display: none; }
.nr-card {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  text-align: left;
  padding: 8px 14px 10px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.06);
  border: 1.5px solid rgba(255, 255, 255, 0.14);
  animation: nrCardIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--card-i, 0) * 120ms) backwards;
}
.nr-card--first { border-color: rgba(46, 204, 113, 0.7); }
.nr-card--sharp { border-color: var(--nr-gold); box-shadow: 0 0 22px rgba(251, 191, 36, 0.22); }
.nr-card--wild { border-style: dashed; background: rgba(255, 255, 255, 0.03); }
.nr-card--closest { border-color: var(--nr-close); box-shadow: 0 0 22px rgba(34, 211, 238, 0.18); }
.nr-card-bonus.nr-card-bonus--closest { color: var(--nr-close); }
.nr-card-rank {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.nr-card-name {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.1rem;
  color: #fff;
}
.nr-card-dot {
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--pin-color, #fff);
}
.nr-card-nick { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nr-card-guess {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.3rem;
  line-height: 1.2;
  color: #fff;
  font-variant-numeric: tabular-nums;
}
.nr-card-off {
  margin-left: 8px;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text-secondary);
}
.nr-card-off.is-in { color: #6ee7a0; }
.nr-card-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 10px;
}
.nr-card-points {
  font-size: 0.85rem;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.7);
}
.nr-card-bonus {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.8rem;
  font-weight: 800;
  color: var(--nr-gold);
}

.nr-stats {
  text-align: center;
  font-size: 0.9rem;
  color: var(--text-muted);
}
.nr-stats.is-final { animation: nrFadeIn 0.4s ease backwards; }

@keyframes nrTrackIn { from { transform: scaleX(0); opacity: 0; } }
@keyframes nrFadeIn  { from { opacity: 0; } }
@keyframes nrDotPop  { from { transform: scale(0); opacity: 0; } }
@keyframes nrStemGrow { from { transform: scaleY(0); } }
@keyframes nrTagDrop { from { opacity: 0; transform: translateY(-14px) scale(0.9); } }
@keyframes nrNameIn  { from { opacity: 0; transform: translateX(-6px); } }
@keyframes nrTagBounce {
  0% { transform: scale(1); }
  40% { transform: scale(1.12) translateY(-4px); }
  100% { transform: scale(1); }
}
@keyframes nrBandIn { from { transform: scaleX(0); } }
@keyframes nrLand {
  0% { transform: scale(0.85); }
  55% { transform: scale(1.1); }
  100% { transform: scale(1); }
}
@keyframes nrBadgePop { from { opacity: 0; transform: translateX(-50%) scale(0.4); } }
@keyframes nrCardIn { from { opacity: 0; transform: translateY(16px) scale(0.96); } }

/* Narrow — a picture beside the line, or a small screen. Sized off the
   reveal's own width, not the window's, because the answer picture takes
   width away after the reveal is already on screen. */
.nr { container-type: inline-size; }
.nr-short { display: none; }
@container (max-width: 720px) {
  .nr-stage { height: clamp(170px, 25vh, 300px); }
  .nr-card--wild { display: none; }
  .nr-card { padding: 6px 10px 8px; }
  .nr-card-name { font-size: 1rem; }
  .nr-card-guess { font-size: 1.1rem; }
  .nr-card-off { margin-left: 5px; font-size: 0.78rem; }
  .nr-long { display: none; }
  .nr-short { display: inline; }
}

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

/* ─── Number Input: Builder ─── */
.number-input-builder {
  padding: 12px 0;
}

/* ─── Number Input: Range display on player ─── */
.numpad-range {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 6px 12px;
  margin-bottom: 8px;
  background: rgba(6,182,212,0.08);
  border: 1px solid rgba(6,182,212,0.25);
  border-radius: var(--radius-sm);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.1rem;
  color: #06b6d4;
}
.numpad-range-arrow {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--text-muted);
  letter-spacing: 0.03em;
}

/* ─── Number Input: Circular Slider tweak buttons ─── */
.numslider-tweak-row {
  display: flex;
  gap: 24px;
  justify-content: center;
  flex-shrink: 0;
}

.numslider-tweak-btn {
  width: 76px;
  height: 54px;
  border-radius: 27px;
  background: rgba(255,255,255,0.09);
  border: 1.5px solid rgba(255,255,255,0.16);
  color: white;
  font-size: 2rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s, transform 0.08s;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  user-select: none;
}

.numslider-tweak-btn:active {
  background: rgba(6,182,212,0.25);
  transform: scale(0.92);
}

.numslider-tweak-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Type-it-in: the keyboard button sits between − and +, the field opens under them. */
.numslider-type-btn {
  display: flex;
  align-items: center;
  justify-content: center;
}

.numslider-type-row {
  display: flex;
  gap: 10px;
  width: 100%;
  max-width: 300px;
  flex-shrink: 0;
}

.numslider-type-input {
  flex: 1;
  min-width: 0;
  height: 54px;
  padding: 0 16px;
  border-radius: 27px;
  border: 2px solid #06b6d4;
  background: rgba(6,182,212,0.1);
  color: white;
  font-family: var(--font-heading);
  font-size: 1.6rem;
  font-weight: 700;
  text-align: center;
  outline: none;
}

.numslider-type-ok {
  height: 54px;
  padding: 0 22px;
  border-radius: 27px;
  border: none;
  background: #06b6d4;
  color: #06222a;
  font-family: var(--font-heading);
  font-size: 1.1rem;
  font-weight: 800;
  cursor: pointer;
  touch-action: manipulation;
}

.numslider-type-note {
  margin: 0;
  color: #fbbf24;
}

/* ─── Player: Sharpshooter feedback badge ─── */
.sharpshooter-badge {
  margin-top: 12px;
  padding: 8px 20px;
  background: rgba(251,191,36,0.12);
  border: 2px solid rgba(251,191,36,0.5);
  border-radius: 12px;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.1rem;
  color: #fbbf24;
  text-shadow: 0 0 8px rgba(251,191,36,0.6);
  animation: multiplierBounceIn 0.5s cubic-bezier(0.34,1.56,0.64,1) both;
}

/* ─── Image Pin: Builder zone editor ─── */
.image-pin-builder {
  padding: 4px 0;
}

/* ─── Image Pin: Player answer UI ─── */
.image-pin-wrapper {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
}

.image-pin-instruction {
  font-size: 0.85rem;
  color: var(--text-muted);
  text-align: center;
  margin-bottom: 8px;
}

.image-pin-submit {
  margin-top: 10px;
  width: 100%;
}

/* Quiz builder: media thumbnail hover overlay (Change/Remove buttons).
   Replaces the former inline onmouseover/onmouseout handlers. */
.media-upload-box:hover .media-overlay {
  opacity: 1 !important;
}

/* --- Double points (closing questions) --- */

.double-points-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 14px;
  border-radius: var(--radius-full);
  background: linear-gradient(135deg, var(--answer-yellow), var(--accent));
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 0.82rem;
  letter-spacing: 0.08em;
  white-space: nowrap;
  box-shadow: 0 0 18px rgba(255, 159, 28, 0.5);
  animation: doublePointsPulse 1.6s ease-in-out infinite;
}

@keyframes doublePointsPulse {
  0%, 100% { transform: scale(1);    box-shadow: 0 0 18px rgba(255, 159, 28, 0.5); }
  50%      { transform: scale(1.05); box-shadow: 0 0 28px rgba(255, 159, 28, 0.8); }
}

@media (prefers-reduced-motion: reduce) {
  .double-points-chip { animation: none; }
}

/* --- Most Likely To --- */

/* Host screen: the players themselves are the options, and the reveal is the
   whole point of the round — so faces grow with their vote count and the voters
   are named underneath. Sizing hangs off two custom properties:
     --mlt-base  on the grid, shrinking as the room grows
     --mlt-face  per card, set from its share of the vote at results time      */
/* The default lives on the container, not here: _mltFitToScreen sets --mlt-base
   as an inline style on the container so the grid AND the verdict line (its
   sibling) both inherit it. A declaration on .mlt-grid itself would win over
   that inherited value for everything inside the grid. */
.display-answers { --mlt-base: 110px; }

.mlt-grid {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: calc(var(--mlt-base) * 0.08) calc(var(--mlt-base) * 0.05);
  width: 100%;
  /* Room for the glow and the crown, which sit outside the cards */
  padding: calc(var(--mlt-base) * 0.2) 6px 4px;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
}

.mlt-card {
  --mlt-face: var(--mlt-base);
  --mlt-glow: 0.22;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--mlt-base) * 0.055);
  padding: calc(var(--mlt-base) * 0.07) 8px calc(var(--mlt-base) * 0.09);
  width: clamp(120px, calc(var(--mlt-face) + 86px + (var(--mlt-vcols, 1) - 1) * var(--mlt-base) * 0.9), 540px);
  border-radius: var(--radius-lg);
  background: transparent;
  /* The reshuffle animates transform; nothing else here may set one, or it
     would fight the FLIP in applyMostLikelyResults. Width is deliberately NOT
     transitioned — the reveal measures layout synchronously, and a width tween
     would feed it mid-flight numbers. */
  transition:
    transform 0.64s cubic-bezier(0.22, 1, 0.36, 1),
    background 0.45s ease,
    opacity 0.5s ease,
    filter 0.5s ease;
  animation: mltPopIn 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--mlt-i, 0) * 0.07s) both;
}

@keyframes mltPopIn {
  0% { opacity: 0; transform: scale(0.5) translateY(22px); }
  100% { opacity: 1; transform: none; }
}

/* The bubble carries the idle bob, so the card's transform stays free for FLIP */
.mlt-bubble {
  position: relative;
  width: var(--mlt-face);
  height: var(--mlt-face);
  flex-shrink: 0;
  /* Negative delay: every bubble is already somewhere else in its cycle, so
     they never bob in lockstep */
  animation: mltFloat 4.2s ease-in-out calc(var(--mlt-i, 0) * -0.43s) infinite;
}

@keyframes mltFloat {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-8px) rotate(1.4deg); }
}

/* Soft aura in the player's own colour — brightens with the vote count */
.mlt-bubble::before {
  content: '';
  position: absolute;
  inset: -16%;
  border-radius: 50%;
  background: radial-gradient(circle, var(--mlt-color, #6b6d80) 0%, transparent 68%);
  opacity: var(--mlt-glow, 0.22);
  filter: blur(12px);
  transition: opacity 0.6s ease, transform 0.6s ease;
  pointer-events: none;
}

.mlt-face {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: var(--mlt-color, #6b6d80);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: calc(var(--mlt-face) * 0.42);
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, 0.16),
    0 12px 28px rgba(0, 0, 0, 0.45);
  transition: box-shadow 0.5s ease;
}

.mlt-face img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Results: the bubble is already at its new size, so this is what makes it read
   as growing into it. */
.mlt-card--result .mlt-face {
  animation: mltGrow 0.72s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes mltGrow {
  0% { transform: scale(0.5); }
  100% { transform: scale(1); }
}

.mlt-name {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(0.95rem, calc(var(--mlt-face) * 0.17), 1.7rem);
  line-height: 1.15;
  color: var(--text-primary);
  text-align: center;
  overflow-wrap: anywhere;
  max-width: 100%;
  transition: color 0.4s ease, text-shadow 0.4s ease;
}

/* Vote count badge — hidden until results, so it gives nothing away mid-round */
.mlt-badge {
  position: absolute;
  right: -4%;
  bottom: 0;
  z-index: 2;
  min-width: calc(var(--mlt-face) * 0.36);
  height: calc(var(--mlt-face) * 0.36);
  padding: 0 calc(var(--mlt-face) * 0.07);
  border-radius: 999px;
  background: linear-gradient(135deg, #f472b6, #a855f7);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: calc(var(--mlt-face) * 0.2);
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5), 0 0 0 3px var(--bg-primary);
  opacity: 0;
  transform: scale(0.3);
  transition: opacity 0.3s ease, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.mlt-badge--shown { opacity: 1; transform: scale(1); }
.mlt-badge--pop { animation: mltBadgePop 0.3s ease; }

@keyframes mltBadgePop {
  50% { transform: scale(1.3); }
}

/* Crown drops in on the winner(s) once the counts have landed */
.mlt-crown {
  position: absolute;
  top: calc(var(--mlt-face) * -0.24);
  left: 50%;
  z-index: 3;
  font-size: calc(var(--mlt-face) * 0.38);
  line-height: 1;
  opacity: 0;
  transform: translate(-50%, -70px) scale(0.4) rotate(-30deg);
  pointer-events: none;
  filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.55));
}

.mlt-card--crowned .mlt-crown {
  animation: mltCrownDrop 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}

@keyframes mltCrownDrop {
  0% { opacity: 0; transform: translate(-50%, -70px) scale(0.4) rotate(-30deg); }
  55% { opacity: 1; transform: translate(-50%, 6px) scale(1.15) rotate(8deg); }
  100% { opacity: 1; transform: translate(-50%, 0) scale(1) rotate(0deg); }
}

.mlt-card--winner {
  background: linear-gradient(180deg, rgba(220, 64, 145, 0.2), rgba(168, 85, 247, 0.08));
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.14);
}

.mlt-card--winner .mlt-face {
  box-shadow:
    0 0 0 5px var(--accent),
    0 16px 36px rgba(0, 0, 0, 0.5),
    0 0 48px var(--accent-glow);
}

.mlt-card--winner .mlt-bubble::before { animation: mltAuraPulse 1.9s ease-in-out infinite; }

@keyframes mltAuraPulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.18); }
}

.mlt-card--winner .mlt-name {
  color: #fff;
  text-shadow: 0 0 20px var(--accent-glow);
}

/* Nobody picked them — dimmed, but still on screen and still bobbing. In a big
   room these are dropped instead; see MLT_HIDE_ZEROS_ABOVE. */
.mlt-card--zero { opacity: 0.42; filter: saturate(0.45); }

/* Dropped from a big room's reveal. Pinned out of the flow by _mltRetireCard, so
   this plays over the top while everyone else closes ranks. */
.mlt-card--gone {
  pointer-events: none;
  animation: mltCardGone 0.44s cubic-bezier(0.4, 0, 1, 1) forwards;
}

@keyframes mltCardGone {
  0% { opacity: 0.8; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.4); }
}

/* Who voted for whom. Built at results time; each chip pops in on a stagger.
   Columns rather than wrapping: --mlt-vcols is set per card from the number of
   voters, so eight of them read as three tidy rows instead of eight lines. */
.mlt-voters {
  display: grid;
  grid-template-columns: repeat(var(--mlt-vcols, 1), minmax(0, 1fr));
  justify-items: center;
  gap: calc(var(--mlt-base) * 0.04);
  width: 100%;
}

.mlt-voter {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  min-width: 0;
  padding: 2px calc(var(--mlt-base) * 0.075) 2px 2px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.16);
  font-family: var(--font-heading);
  font-size: clamp(0.6rem, calc(var(--mlt-base) * 0.088), 1rem);
  font-weight: 700;
  color: var(--text-primary);
  opacity: 0;
  transform: scale(0.3) translateY(12px);
}

.mlt-voter--in { animation: mltVoterIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; }

@keyframes mltVoterIn {
  0% { opacity: 0; transform: scale(0.3) translateY(12px); }
  70% { opacity: 1; transform: scale(1.12) translateY(0); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}

.mlt-voter-face {
  width: clamp(15px, calc(var(--mlt-base) * 0.185), 28px);
  height: clamp(15px, calc(var(--mlt-base) * 0.185), 28px);
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--mlt-color, #6b6d80);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(0.5rem, calc(var(--mlt-base) * 0.075), 0.85rem);
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.mlt-voter-face img { width: 100%; height: 100%; object-fit: cover; display: block; }

.mlt-voter-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: clamp(70px, calc(var(--mlt-base) * 1.05), 170px);
  line-height: 1.35;
}

.mlt-voter--more {
  padding: 2px calc(var(--mlt-base) * 0.085);
  color: var(--text-secondary);
}

/* The jokes already in the votes: picking yourself, and two people picking each
   other. Marked on the chips themselves so they cost no vertical room. */
.mlt-voter-mark {
  font-size: 0.9em;
  line-height: 1;
  flex-shrink: 0;
}

.mlt-voter--self {
  background: rgba(255, 159, 28, 0.22);
  border-color: rgba(255, 159, 28, 0.55);
  color: #ffd8a1;
}

.mlt-voter--mutual {
  background: rgba(0, 243, 255, 0.18);
  border-color: rgba(0, 243, 255, 0.5);
  color: #b6f4ff;
}

.mlt-card--winner .mlt-voter {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.3);
  font-size: clamp(0.68rem, calc(var(--mlt-base) * 0.098), 1.1rem);
}

.mlt-card--winner .mlt-voter-face {
  width: clamp(17px, calc(var(--mlt-base) * 0.21), 32px);
  height: clamp(17px, calc(var(--mlt-base) * 0.21), 32px);
}

/* The room's verdict, said out loud under the grid */
.mlt-verdict {
  margin-top: calc(var(--mlt-base) * 0.11);
  text-align: center;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(1.05rem, calc(var(--mlt-base) * 0.23), 2.1rem);
  color: var(--text-secondary);
  opacity: 0;
  transform: translateY(16px);
}

.mlt-verdict--in { animation: mltVerdictIn 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; }

@keyframes mltVerdictIn {
  to { opacity: 1; transform: none; }
}

.mlt-verdict em {
  font-style: normal;
  color: var(--accent-light);
  text-shadow: 0 0 24px var(--accent-glow);
}

/* Round framing — only rendered when the round earned it (see _mltBanner) */
.mlt-flavour {
  display: inline-block;
  margin-right: 0.6em;
  padding: 0.14em 0.7em;
  border-radius: 999px;
  font-size: 0.72em;
  letter-spacing: 0.08em;
  vertical-align: middle;
  animation: mltFlavourIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes mltFlavourIn {
  0% { opacity: 0; transform: scale(0.4) rotate(-8deg); }
  100% { opacity: 1; transform: none; }
}

.mlt-flavour--landslide {
  background: linear-gradient(135deg, #ff9f1c, #dc4091);
  color: #fff;
  box-shadow: 0 0 26px rgba(255, 159, 28, 0.5);
}

.mlt-flavour--deadlock {
  background: linear-gradient(135deg, #00f3ff, #79f487);
  color: #10202b;
}

.mlt-flavour--squeaker {
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.3);
  color: var(--text-primary);
}

.mlt-flavour--repeat {
  background: linear-gradient(135deg, #a855f7, #dc4091);
  color: #fff;
  box-shadow: 0 0 26px rgba(168, 85, 247, 0.55);
}

.mlt-mutuals {
  margin-top: 0.35em;
  font-size: 0.62em;
  font-weight: 700;
  color: #b6f4ff;
}

/* Player phone: tap a face to vote */
.mlt-vote-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  width: 100%;
}

.mlt-vote-btn {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 14px 10px;
  border-radius: var(--radius-lg);
  border: 2px solid var(--bg-glass-border);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-primary);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.14s cubic-bezier(0.34, 1.56, 0.64, 1), border-color 0.2s ease,
    background 0.2s ease, opacity 0.25s ease, box-shadow 0.25s ease;
  /* `backwards`, not `both`: mltPopIn ends on `transform: none`, and a
     forwards-filled animation keeps applying that final value at a higher
     cascade level than normal declarations — which silently cancelled the
     :active press and the .selected scale below for the element's whole life. */
  animation: mltPopIn 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--mlt-i, 0) * 0.05s) backwards;
}

.mlt-vote-btn:active { transform: scale(0.94); }

.mlt-vote-btn.selected {
  border-color: var(--accent);
  background: rgba(220, 64, 145, 0.22);
  transform: scale(1.04);
  box-shadow: 0 0 26px var(--accent-glow);
}

.mlt-vote-btn.selected .mlt-vote-face {
  box-shadow: 0 0 0 3px var(--accent), 0 0 22px var(--accent-glow);
}

/* Options not chosen, once the vote is in. The equivalent rule for
   .mobile-answer-btn lives in play.html and is scoped to that class. */
.mlt-vote-btn.disabled {
  opacity: 0.4;
  pointer-events: none;
}

.mlt-vote-face {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--mlt-color, #6b6d80);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.16);
  transition: box-shadow 0.25s ease;
}

.mlt-vote-face img { width: 100%; height: 100%; object-fit: cover; display: block; }

.mlt-vote-name {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1rem;
  text-align: center;
  overflow-wrap: anywhere;
}

/* Two columns on a normal phone (a 375px iPhone included) so a full party of
   players doesn't turn into a long scroll. Only the narrowest phones drop to one. */
@media (max-width: 330px) {
  .mlt-vote-grid { grid-template-columns: 1fr; }
}

/* --- Most Likely To: your own result, on your phone --- */

.mltp-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 26px 20px 22px;
  text-align: center;
  animation: zoomIn 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

.mltp-card--crowned {
  background: linear-gradient(180deg, rgba(220, 64, 145, 0.22), rgba(168, 85, 247, 0.1));
  border-color: var(--accent);
  box-shadow: 0 0 44px var(--accent-glow);
}

.mltp-card--ignored { opacity: 0.9; }

.mltp-bubble { position: relative; }

.mltp-face {
  width: 128px;
  height: 128px;
  border-radius: 50%;
  background: var(--mlt-color, #6b6d80);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 3.2rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.18), 0 12px 30px rgba(0, 0, 0, 0.45);
}

.mltp-face img { width: 100%; height: 100%; object-fit: cover; display: block; }

.mltp-card--crowned .mltp-face {
  box-shadow: 0 0 0 5px var(--accent), 0 0 48px var(--accent-glow);
}

.mltp-crown {
  position: absolute;
  top: -30px;
  left: 50%;
  font-size: 3rem;
  line-height: 1;
  pointer-events: none;
  filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.5));
  animation: mltpCrownDrop 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes mltpCrownDrop {
  0% { opacity: 0; transform: translate(-50%, -60px) scale(0.4) rotate(-28deg); }
  60% { opacity: 1; transform: translate(-50%, 5px) scale(1.12) rotate(7deg); }
  100% { opacity: 1; transform: translate(-50%, 0) scale(1) rotate(0deg); }
}

.mltp-count {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.55rem;
  line-height: 1.2;
  color: var(--text-primary);
}

.mltp-card--crowned .mltp-count { color: var(--accent-light); }

.mltp-sub {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-secondary);
}

.mltp-voters {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 7px;
}

.mltp-voter {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 12px 4px 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.16);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.92rem;
  color: var(--text-primary);
  animation: mltVoterIn 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

.mltp-voter-face {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--mlt-color, #6b6d80);
  color: #fff;
  font-size: 0.82rem;
  font-weight: 800;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.mltp-voter-face img { width: 100%; height: 100%; object-fit: cover; display: block; }

.mltp-notes { display: flex; flex-direction: column; gap: 5px; }

.mltp-note {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--answer-blue);
}

/* The reaction window — the round has no leaderboard, so this is what happens
   next. Emotes float onto the host screen. */
.mltp-react {
  margin-top: 20px;
  width: 100%;
  text-align: center;
  animation: mltpReactIn 0.4s ease both;
}

@keyframes mltpReactIn {
  0% { opacity: 0; transform: translateY(14px); }
  100% { opacity: 1; transform: none; }
}

.mltp-react-label {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1rem;
  color: var(--text-secondary);
  margin-bottom: 10px;
}

/* Three to a row: six reactions wrapped 4 + 2 as a flex row. */
.mltp-react-row {
  display: grid;
  grid-template-columns: repeat(3, auto);
  justify-content: center;
  gap: 10px 14px;
}

.mltp-react-btn { transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s ease; }
.mltp-react-btn:active { transform: scale(0.88); }
.mltp-react-btn--sent { transform: scale(1.25); opacity: 0.5; }

@media (prefers-reduced-motion: reduce) {
  .mltp-card,
  .mltp-crown,
  .mltp-voter,
  .mltp-react,
  .mltp-react-btn {
    animation: none !important;
    transition: none !important;
  }

  .mltp-crown { opacity: 1; transform: translate(-50%, 0); }
}

/* Motion is the whole point of this round, so what's left when it's off has to
   still read: everything lands in its final state, instantly. */
@media (prefers-reduced-motion: reduce) {
  .mlt-card,
  .mlt-flavour,
  .mlt-card--gone,
  .mlt-card--result .mlt-face,
  .mlt-bubble,
  .mlt-bubble::before,
  .mlt-card--winner .mlt-bubble::before,
  .mlt-badge,
  .mlt-voter,
  .mlt-verdict,
  .mlt-vote-btn {
    animation: none !important;
    transition: none !important;
  }

  .mlt-voter,
  .mlt-verdict {
    opacity: 1;
    transform: none;
  }

  .mlt-badge--shown { opacity: 1; transform: none; }

  .mlt-card--crowned .mlt-crown {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   KAOS — the Wheel of Misfortune
   ---------------------------------------------------------------------------
   Sits over the settled standings, so the room watches the board it just read
   come under threat. The mascot is the app icon (the laugh) — it arrives from
   the side, cackles, and spins the wheel.
   ═══════════════════════════════════════════════════════════════════════════ */

.kaos-overlay {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;          /* the veil and the button opt back in */
  overflow: hidden;
}

/* Darkens the standings without hiding them */
.kaos-veil {
  position: absolute;
  inset: 0;
  /* Deep enough that the wheel and rules read cleanly against it, shallow
     enough that the standings stay a ghost underneath — the room should still
     see the board that is about to be attacked. The first value was 0.72, which
     let the medal gradients on the top three punch straight through. */
  background: radial-gradient(circle at 50% 45%, rgba(30, 0, 24, 0.88), rgba(3, 1, 9, 0.97));
  opacity: 0;
  transition: opacity 0.7s ease;
  pointer-events: auto;
}

.kaos-overlay--lit .kaos-veil { opacity: 1; }

/* ── The mascot: the Imp (kaos-imp.js) ──────────────────────────────────────
   The slot is moved ONLY by transform, from wherever it is to wherever it's
   going — kaos-imp.js sets its width (the biggest the Imp ever gets, so it is
   only ever scaled down and stays crisp) and every position. It used to be
   pinned to a different screen edge per pose, with its own keyframes each,
   which teleported it between beats and cut moves off halfway in test mode. */
.kaos-mascot-slot {
  position: absolute;
  left: 0;
  top: 0;
  width: 600px;
  opacity: 0;
  z-index: 6;
  transform-origin: 50% 50%;
  will-change: transform, opacity;
  pointer-events: none;
}
.imp-squash { transform-origin: 50% 88%; }
.imp-nod { transform-origin: 50% 80%; }

/* …the element inside does the cackling, so the two never fight over one transform. */
.kaos-mascot {
  display: block;
  width: 100%;
  height: auto;
  will-change: transform;
}

/* The fire (kaos-imp.js): a canvas right above the veil, under the wheel, the
   card and the Imp. The Imp's glow is drawn there too, as a light — a CSS
   drop-shadow on the animated rig re-rendered every frame. */
.kaos-fire {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  pointer-events: none;
}

/* The floor on fire (kaos-imp.js buildFloor): cartoon tongues in the Imp's
   palette. Its height is one scaleY, set by blaze() — the overshoot on the
   transition is the flames leaping when the floor catches. */
.kaos-floor {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3vh;
  height: 36vh;
  z-index: 1;
  opacity: 0;
  transform: scaleY(0);
  transform-origin: 50% 100%;
  transition: transform 0.7s cubic-bezier(0.3, 1.5, 0.5, 1), opacity 0.35s ease;
  pointer-events: none;
}
.kaos-floor__row { position: absolute; inset: 0; }
.kaos-floor__row--front { transform: scaleY(0.62); transform-origin: 50% 100%; }
.kaos-tongue {
  position: absolute;
  bottom: 0;
  aspect-ratio: 100 / 216;
  background: no-repeat center bottom / 100% 100%;
  transform-origin: 50% 100%;
  animation: kaosTongue 0.8s linear infinite;
  will-change: transform;
}
@keyframes kaosTongue {
  0%   { transform: scale(1, var(--h, 1)) skewX(0deg); }
  25%  { transform: scale(0.9, calc(var(--h, 1) * 1.14)) skewX(-5deg); }
  50%  { transform: scale(1.04, calc(var(--h, 1) * 0.9)) skewX(2deg); }
  75%  { transform: scale(0.94, calc(var(--h, 1) * 1.08)) skewX(5deg); }
  100% { transform: scale(1, var(--h, 1)) skewX(0deg); }
}

/* Eyes opening in the dark, before anything is seen. */
.imp-dark-eyes {
  position: absolute;
  width: min(19vw, 30vh);
  z-index: 5;
  opacity: 0;
  pointer-events: none;
  filter: drop-shadow(0 0 10px rgba(255, 160, 40, 0.95)) drop-shadow(0 0 34px rgba(255, 80, 20, 0.7));
}
.imp-dark-eyes svg { display: block; width: 100%; height: auto; overflow: visible; }

/* The cackle — a shake with a scale pulse, fired with the laugh audio */
.kaos-mascot--cackle { animation: kaosCackle 0.62s ease-in-out 3; }

@keyframes kaosCackle {
  0%, 100% { transform: rotate(0deg) scale(1); }
  20%      { transform: rotate(-9deg) scale(1.09); }
  45%      { transform: rotate(7deg) scale(1.04); }
  70%      { transform: rotate(-5deg) scale(1.07); }
}

/* ── The Imp (kaos-imp.js) ──────────────────────────────────────────────────
   Kaos's mascot as a rig: the head's SVG holds every eye set and mouth, and one
   of each is shown through data-eyes / data-mouth. It fills .kaos-mascot, so
   the slot's pose classes still decide where it is and how big. Hands are sized
   and placed in cqw (the rig is the container) so they scale with it too. */
.kaos-mascot-fallback { display: block; width: 100%; height: auto; filter: drop-shadow(0 0 34px rgba(253, 87, 39, 0.7)); }
.imp-rig { position: relative; width: 100%; aspect-ratio: 1; container-type: inline-size; }
.imp-bob { position: absolute; inset: 0; animation: impBob 2.2s ease-in-out infinite; }
@keyframes impBob {
  0%, 100% { transform: translateY(0) rotate(-1.5deg); }
  50%      { transform: translateY(-2.5%) rotate(1.5deg); }
}
.imp-head { display: block; width: 100%; height: 100%; overflow: visible; }
.imp-head .imp-eyes,
.imp-head .imp-mouth { display: none; }
.imp-head[data-eyes="menace"] .imp-eyes--menace,
.imp-head[data-eyes="sly"] .imp-eyes--sly,
.imp-head[data-eyes="wide"] .imp-eyes--wide,
.imp-head[data-eyes="glee"] .imp-eyes--glee,
.imp-head[data-eyes="closed"] .imp-eyes--closed,
.imp-head[data-mouth="grin"] .imp-mouth--grin,
.imp-head[data-mouth="smirk"] .imp-mouth--smirk,
.imp-head[data-mouth="open"] .imp-mouth--open,
.imp-head[data-mouth="o"] .imp-mouth--o,
.imp-head[data-mouth="cackle"] .imp-mouth--cackle,
.imp-head[data-mouth="roar"] .imp-mouth--roar { display: inline; }
.imp-pupil {
  transform: translate(var(--imp-lx, 0px), var(--imp-ly, 0px));
  transition: transform 0.22s cubic-bezier(0.3, 1.4, 0.5, 1);
}
/* Each flame's rim, ink and fill carry the same class, so they flicker as one.
   The origin is in the head's own units (view-box), at the base of each flame. */
.imp-flame { transform-box: view-box; animation: impFlicker 0.8s ease-in-out infinite alternate; }
.imp-flame--c { transform-origin: 128px 104px; }
.imp-flame--l { transform-origin: 90px 114px; animation-duration: 0.66s; animation-delay: -0.25s; }
.imp-flame--r { transform-origin: 166px 114px; animation-duration: 0.73s; animation-delay: -0.5s; }
@keyframes impFlicker {
  0%   { transform: scale(1, 1) skewX(0deg); }
  50%  { transform: scale(0.95, 1.07) skewX(-3deg); }
  100% { transform: scale(1.03, 0.95) skewX(2.5deg); }
}
.imp-ear { transform-box: view-box; }
.imp-ear--l { transform-origin: 64px 158px; animation: impTwitchL 5.2s ease-in-out infinite; }
.imp-ear--r { transform-origin: 192px 158px; animation: impTwitchR 6.1s ease-in-out -2.1s infinite; }
@keyframes impTwitchL { 0%, 88%, 100% { transform: rotate(0deg); } 91% { transform: rotate(10deg); } 94% { transform: rotate(-4deg); } }
@keyframes impTwitchR { 0%, 88%, 100% { transform: rotate(0deg); } 91% { transform: rotate(-10deg); } 94% { transform: rotate(4deg); } }

/* position (WAAPI) → gesture (the rub) → mirror → the hand */
.imp-hand {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 34cqw;
  height: 34cqw;
  margin: -17cqw 0 0 -17cqw;
  transform: scale(0);
}
.imp-hand__gest, .imp-hand__flip { width: 100%; height: 100%; }
.imp-hand__gest { transform-origin: 50% 80%; }
.imp-hand--l .imp-hand__flip { transform: scaleX(-1); }
.imp-hand svg { display: block; width: 100%; height: 100%; overflow: visible; }
.imp-hand .imp-pose { display: none; }
.imp-hand[data-pose="claw"] .imp-pose--claw,
.imp-hand[data-pose="point"] .imp-pose--point,
.imp-hand[data-pose="fist"] .imp-pose--fist { display: inline; }
/* Rubbing its hands together: the two meet and part, out of step. */
.imp-rig--rubbing .imp-hand__gest { animation: impRub 0.32s ease-in-out infinite alternate; }
.imp-rig--rubbing .imp-hand--l .imp-hand__gest { animation-direction: alternate-reverse; }
@keyframes impRub {
  from { transform: translate(-3cqw, 1cqw) rotate(-7deg); }
  to   { transform: translate(3cqw, -1cqw) rotate(7deg); }
}

/* Its words arrive one at a time, as it says them (kaos-imp.js talk()). */
.kaos-word {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.35em) scale(0.8);
  transition: opacity 0.12s ease-out, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.kaos-word.is-in { opacity: 1; transform: none; }
/* Player names in what the Imp says (game/ImpLines.js marks them). */
.kaos-name { color: #ffb347; text-shadow: 0 0 14px rgba(253, 87, 39, 0.65); }

/* The Kaos badge on the reading card. Sits under the type name so a round that
   is BOTH a Minefield and a Kaos round says so — the background effect is the
   authored modifier's, this is the wheel's, and the score is scaled by both. */
.display-reading-kaos {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding: 7px 16px;
  border-radius: 999px;
  background: rgba(10, 4, 16, 0.55);
  border: 1.5px solid var(--kaos-colour, #dc4091);
  color: var(--kaos-colour, #dc4091);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(0.75rem, 1.3vw, 1.05rem);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  box-shadow: 0 0 22px var(--kaos-glow, rgba(220, 64, 145, 0.5));
  animation: kaosBadgeThrob 1.5s ease-in-out infinite;
}

.display-reading-kaos svg { width: 1.1em; height: 1.1em; }

/* The reading card's "what's at stake" line: Minefield / 2x / 3x, in words. */
.display-reading-rule {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
  padding: 8px 20px;
  border-radius: 999px;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(1.1rem, 3vh, 1.9rem);
  color: white;
  animation: fadeInUp 0.45s ease 0.25s backwards;
}
.display-reading-rule[hidden] { display: none; }
.display-reading-rule svg { width: 1.2em !important; height: 1.2em !important; color: #ff8a8a; }
.display-reading-rule--mine {
  background: rgba(70, 8, 12, 0.9);
  border: 2px solid #ff4d4d;
  box-shadow: 0 0 26px rgba(255, 60, 60, 0.5);
}
.display-reading-rule--mult {
  background: linear-gradient(135deg, #f5b800, #ff7a1a);
  box-shadow: 0 0 26px rgba(255, 159, 28, 0.55);
}
@media (prefers-reduced-motion: reduce) { .display-reading-rule { animation: none; } }

@keyframes kaosBadgeThrob {
  0%, 100% { box-shadow: 0 0 18px var(--kaos-glow, rgba(220, 64, 145, 0.4)); }
  50%      { box-shadow: 0 0 34px var(--kaos-glow, rgba(220, 64, 145, 0.85)); }
}

@media (prefers-reduced-motion: reduce) {
  .display-reading-kaos { animation: none; }
}

/* The host is held back until the standings have finished revealing. Dimmed
   rather than hidden, so the button stays where the eye expects it. */
.btn-waiting {
  opacity: 0.4;
  cursor: progress;
  filter: grayscale(0.4);
  transition: opacity 0.25s ease, filter 0.25s ease;
}

/* The standings are only a new scene the first time. See _leaderboardSeen. */
#leaderboardScreen.no-fade { animation: none; }

/* ...and neither are the rows. The staggered slide-in costs 120ms PER ROW and
   phase 2 waits it out, so from the second leaderboard on it is pure delay in
   front of the gains. Scoped to the board id so it outranks `.leaderboard-item`
   in both columns. */
#leaderboardBoard.lb-no-entrance .leaderboard-item { animation: none; }

/* iOS tap haptics: an invisible `switch` parked over each tappable button.
   The finger lands here, iOS buzzes, and the change handler forwards the tap.
   Only created on devices with no Vibration API — see _attachTapHaptics. */
.ios-haptic-veil {
  position: absolute;
  margin: 0;
  padding: 0;
  opacity: 0;
  z-index: 5;
  border-radius: inherit;
  -webkit-appearance: none;
  appearance: none;
  /* Never let it take focus rings or a caret; it is not a real control. */
  outline: none;
}

/* A submitted answer has to LOOK submitted. `disabled` on its own barely
   changes a button, and on the types that go through an explicit Submit there
   is nothing else on screen that moves — so the button dims out hard and says
   so itself. */
.submitted,
button.submitted:disabled {
  opacity: 0.45 !important;
  filter: grayscale(0.55);
  background: rgba(255, 255, 255, 0.1) !important;
  border-color: rgba(255, 255, 255, 0.16) !important;
  color: rgba(255, 255, 255, 0.72) !important;
  box-shadow: none !important;
  cursor: default;
  transition: opacity 0.22s ease, filter 0.22s ease, background 0.22s ease;
}

/* ── Kaos on the phone ──────────────────────────────────────────────────────
   Shown on the personal-score screen while the wheel is being spun on the big
   screen. Says nothing about the mode — only that it is coming, and what that
   means for you. */
.kaos-incoming {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-top: 26px;
  padding: 18px 20px;
  border-radius: 18px;
  background: rgba(20, 8, 26, 0.72);
  border: 1px solid rgba(220, 64, 145, 0.35);
  animation: kaosIncomingIn 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}

.kaos-incoming-mark {
  width: min(210px, 62vw);
  height: auto;
  filter: drop-shadow(0 0 18px rgba(220, 64, 145, 0.6));
  animation: kaosIncomingThrob 1.6s ease-in-out infinite;
}

.kaos-incoming-line {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1rem;
  line-height: 1.3;
  text-align: center;
  color: rgba(255, 255, 255, 0.86);
}

/* The people with something to lose get the louder version. */
/* The leader's phone IS the lever. Big enough to find without looking, because
   the room is watching the big screen, not this. */
.kaos-spin-btn {
  width: 100%;
  margin-top: 4px;
  padding: 16px 20px;
  border-radius: 14px;
  border: none;
  background: linear-gradient(135deg, #ff6bd6, #dc4091);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 1.15rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 8px 26px rgba(220, 64, 145, 0.45);
  animation: kaosSpinPulse 1.2s ease-in-out infinite;
}

.kaos-spin-btn:active { transform: scale(0.97); }

.kaos-spin-btn:disabled {
  animation: none;
  opacity: 0.55;
  background: rgba(255, 255, 255, 0.12);
  box-shadow: none;
  cursor: default;
}

/* Confirmed by the server: this press is the one that turned the wheel. */
.kaos-spin-btn--done:disabled {
  opacity: 1;
  background: linear-gradient(135deg, #2ecc71, #17a05a);
  color: #fff;
  box-shadow: 0 0 28px rgba(46, 204, 113, 0.45);
  animation: kaosSpinConfirm 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes kaosSpinConfirm {
  0%   { transform: scale(0.9); }
  60%  { transform: scale(1.06); }
  100% { transform: scale(1); }
}

@keyframes kaosSpinPulse {
  0%, 100% { box-shadow: 0 8px 26px rgba(220, 64, 145, 0.45); transform: scale(1); }
  50%      { box-shadow: 0 8px 40px rgba(255, 107, 214, 0.75); transform: scale(1.025); }
}

@media (prefers-reduced-motion: reduce) {
  .kaos-spin-btn { animation: none; }
}

.kaos-incoming--top {
  border-color: rgba(239, 68, 68, 0.55);
  background: rgba(38, 8, 14, 0.8);
  box-shadow: 0 0 34px rgba(239, 68, 68, 0.28);
}

.kaos-incoming--top .kaos-incoming-line { color: #ff8b8b; }

/* While the phone has a Kaos job (spin, summon, brace), Kaos takes over the
   score screen: the rank and points were read before the Imp turned up, and
   left in place they pushed the button below the fold on a real phone. */
#scoreScreen.kaos-takeover > :not(.kaos-incoming) { display: none !important; }
#scoreScreen.kaos-takeover .kaos-incoming { margin-top: 0; }
#scoreScreen.kaos-takeover .kaos-incoming-mark { width: min(150px, 44vw); }

/* The phones' job while the wheel is called up and turning (player.js
   _showKaosCharge): one big buzzer to mash. Fire for SUMMON, ice for BRACE. */
.kaos-charge {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
  margin-top: 4px;
}
.kaos-charge[data-side="summon"] {
  --charge-bg: radial-gradient(circle at 35% 30%, #ffe680, #fd7a27 55%, #bc1831);
  --charge-rim: #ffd23f;
  --charge-glow: rgba(253, 87, 39, 0.65);
}
.kaos-charge[data-side="brace"] {
  --charge-bg: radial-gradient(circle at 35% 30%, #eaf8ff, #6cb8ee 55%, #2b5fae);
  --charge-rim: #cfeeff;
  --charge-glow: rgba(108, 184, 238, 0.65);
}
.kaos-charge-btn {
  position: relative;
  width: min(56vw, 220px);
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  border-radius: 50%;
  border: 5px solid #201e2e;
  background: var(--charge-bg);
  color: #201e2e;
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 1.55rem;
  letter-spacing: 0.08em;
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  box-shadow: 0 0 0 6px var(--charge-rim), 0 10px 0 #201e2e, 0 0 44px var(--charge-glow);
}
.kaos-charge-icon svg { width: 44px; height: 44px; stroke-width: 2.6; }
.kaos-charge-btn.is-hit { animation: kaosChargeHit 0.14s ease-out; }
@keyframes kaosChargeHit {
  0%   { transform: translateY(7px) scale(0.95); box-shadow: 0 0 0 6px var(--charge-rim), 0 3px 0 #201e2e, 0 0 70px var(--charge-glow); }
  100% { transform: none; }
}
.kaos-charge.is-closed .kaos-charge-btn { opacity: 0.4; filter: grayscale(0.7); cursor: default; }
.kaos-charge-hint {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 0.95rem;
  line-height: 1.3;
  text-align: center;
  color: rgba(255, 255, 255, 0.85);
}
.kaos-charge.is-closed .kaos-charge-hint { opacity: 0.5; }
.kaos-charge-tug { display: flex; align-items: center; gap: 8px; width: 100%; }
.kaos-charge-tug__end {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.kaos-charge-tug__end--summon { color: #ff9a4a; }
.kaos-charge-tug__end--brace { color: #8fcfff; }
.kaos-charge-tug__bar {
  flex: 1;
  height: 12px;
  overflow: hidden;
  border-radius: var(--radius-full);
  border: 2px solid #201e2e;
  background: linear-gradient(90deg, #6cb8ee, #2b5fae);
}
.kaos-charge-tug__fill {
  width: 50%;
  height: 100%;
  background: linear-gradient(90deg, #bc1831, #fd7a27);
  transition: width 0.3s ease;
}
.kaos-charge-spark {
  position: absolute;
  bottom: 48%;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  pointer-events: none;
  animation: kaosChargeSpark 0.7s ease-out forwards;
}
.kaos-charge[data-side="summon"] .kaos-charge-spark { background: radial-gradient(circle, #fff6c8, #ffb02e 50%, rgba(253, 87, 39, 0)); }
.kaos-charge[data-side="brace"] .kaos-charge-spark { background: radial-gradient(circle, #fff, #9fdcff 50%, rgba(108, 184, 238, 0)); }
@keyframes kaosChargeSpark {
  from { transform: translate(-50%, 0) scale(1); opacity: 1; }
  to   { transform: translate(calc(-50% + var(--drift, 0px)), -170px) scale(0.3); opacity: 0; }
}

/* The verdict, on the phone: which face landed, and this player's own part in
   it — sent as the big screen's demo marks who it's aimed at. */
.kaos-verdict-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 14px;
  border-radius: var(--radius-full);
  background: rgba(10, 4, 16, 0.6);
  border: 1.5px solid var(--kaos-colour, #dc4091);
  color: var(--kaos-colour, #dc4091);
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 0.9rem;
  letter-spacing: 0.08em;
  animation: kaosIncomingIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}
.kaos-verdict-chip svg { width: 1.1em; height: 1.1em; }
.kaos-incoming--you {
  border-color: rgba(239, 68, 68, 0.75);
  background: rgba(46, 8, 14, 0.88);
  box-shadow: 0 0 40px rgba(239, 68, 68, 0.4);
  animation: kaosYouPulse 1.1s ease-in-out 3;
}
.kaos-incoming--you .kaos-incoming-line { color: #fff; font-size: 1.12rem; }
@keyframes kaosYouPulse {
  0%, 100% { box-shadow: 0 0 40px rgba(239, 68, 68, 0.4); }
  50%      { box-shadow: 0 0 64px rgba(239, 68, 68, 0.75); }
}

@keyframes kaosIncomingIn {
  0%   { opacity: 0; transform: translateY(16px) scale(0.92); }
  100% { opacity: 1; transform: none; }
}

@keyframes kaosIncomingThrob {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.05); }
}

@media (prefers-reduced-motion: reduce) {
  .kaos-incoming-mark { animation: none; }
}

/* Its RETURN (a second Kaos in the same game): the lights stutter before it's
   back. The peeks and the long jumpscare only work once. */
.kaos-overlay--flicker .kaos-veil { animation: kaosFlicker 0.75s steps(1, end); }
@keyframes kaosFlicker {
  0%   { opacity: 1; }
  12%  { opacity: 0.25; }
  22%  { opacity: 1; }
  40%  { opacity: 0.4; }
  48%  { opacity: 1; }
  70%  { opacity: 0.15; }
  78%  { opacity: 1; }
}

/* The whole overlay recoils on the scare. */
.kaos-overlay--shake { animation: kaosShake 0.6s cubic-bezier(0.36, 0.07, 0.19, 0.97); }

@keyframes kaosShake {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(-14px, 8px); }
  22% { transform: translate(12px, -10px); }
  36% { transform: translate(-10px, -6px); }
  50% { transform: translate(8px, 9px); }
  66% { transform: translate(-6px, -4px); }
  82% { transform: translate(4px, 3px); }
}

/* ── Speech ─────────────────────────────────────────────────────────────────
   One line at a time. Sits beside the mascot's resting corner. */
.kaos-speech {
  position: absolute;
  left: min(22vw, 330px);
  bottom: 13vh;
  max-width: min(640px, 52vw);
  padding: 20px 26px;
  border-radius: 20px 20px 20px 4px;
  background: rgba(20, 8, 26, 0.93);
  border: 2px solid rgba(220, 64, 145, 0.5);
  box-shadow: 0 0 40px rgba(220, 64, 145, 0.3);
  z-index: 7;
}

.kaos-speech-text {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(1.1rem, 2.2vw, 2rem);
  line-height: 1.25;
  color: #fff;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
}

/* The mascot crosses to the right when the wheel takes the left-hand side, and
   the bubble has to go with it — left where it was, it sits on top of the disc. */
.kaos-speech--right {
  left: auto;
  right: min(18vw, 300px);
  bottom: 11vh;
  max-width: min(460px, 34vw);
  border-radius: 20px 20px 4px 20px;
}

.kaos-speech--in { animation: kaosSpeechIn 0.32s cubic-bezier(0.34, 1.56, 0.64, 1) backwards; }

@keyframes kaosSpeechIn {
  0%   { opacity: 0; transform: translateY(14px) scale(0.9); }
  100% { opacity: 1; transform: none; }
}

/* ── The storm ──────────────────────────────────────────────────────────────
   Bolts are struck when the wheel lands. Hidden by opacity, not display, so the
   flash can be re-triggered without a reflow. */
.kaos-storm {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 8;
}

.kaos-flash {
  position: absolute;
  inset: 0;
  background: #fff;
  opacity: 0;
}

.kaos-flash--strike { animation: kaosFlash 0.72s ease-out; }

@keyframes kaosFlash {
  0%   { opacity: 0; }
  4%   { opacity: 0.92; }
  10%  { opacity: 0.12; }
  16%  { opacity: 0.78; }
  26%  { opacity: 0.05; }
  38%  { opacity: 0.5; }
  100% { opacity: 0; }
}

.kaos-bolt {
  position: absolute;
  top: -4vh;
  height: 62vh;
  width: clamp(70px, 9vw, 150px);
  fill: #fdf6ff;
  filter: drop-shadow(0 0 24px rgba(190, 220, 255, 0.95))
          drop-shadow(0 0 60px rgba(150, 190, 255, 0.7));
  opacity: 0;
}

.kaos-bolt--a { left: 12vw; }
.kaos-bolt--b { left: 47vw; height: 74vh; }
.kaos-bolt--c { left: 78vw; }

.kaos-bolt--strike { animation: kaosBolt 0.6s steps(1, end); }

@keyframes kaosBolt {
  0%   { opacity: 0; }
  8%   { opacity: 1; }
  20%  { opacity: 0; }
  32%  { opacity: 1; }
  46%  { opacity: 0; }
  58%  { opacity: 0.85; }
  100% { opacity: 0; }
}

/* ── The invitation ─────────────────────────────────────────────────────────
   The leader is called up by name, and the hub becomes the button. */
.kaos-invite {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-top: 10px;
  font-family: var(--font-heading);
  font-weight: 800;
  animation: kaosSpeechIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}

.kaos-invite-name {
  font-size: clamp(1.2rem, 2.6vw, 2.2rem);
  color: #ff6bd6;
  text-shadow: 0 0 18px rgba(255, 107, 214, 0.65);
}

.kaos-invite-call {
  font-size: clamp(0.8rem, 1.5vw, 1.15rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}

/* Only clickable once the mascot has actually called somebody up. */
.kaos-hub { border: none; padding: 0; font: inherit; cursor: default; }

.kaos-hub--armed {
  cursor: pointer;
  animation: kaosHubPulse 1.15s ease-in-out infinite;
}

@keyframes kaosHubPulse {
  0%, 100% { transform: translate(-50%, -50%) scale(1); filter: drop-shadow(0 0 0 rgba(253, 87, 39, 0)); }
  55%      { transform: translate(-50%, -50%) scale(1.08); filter: drop-shadow(0 0 3vh rgba(253, 87, 39, 0.9)); }
}

/* ── The die ────────────────────────────────────────────────────────────────
   1, 2 or 3 — how many rounds the mode rides for. The Imp's own die: a dark
   cube with a fire-orange sticker rim and glowing ember pips, thrown from the
   Imp's claw onto the card (game-display.js _kaosRollDie).
   die (the throw) → squash (the bounces) → cube (the tumble) → six faces. */
.kaos-die-row {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin: 10px 0 4px;
}
.kaos-die {
  --die: clamp(58px, 5.8vw, 86px);
  position: relative;
  z-index: 2;
  width: var(--die);
  height: var(--die);
  filter: drop-shadow(0 0 16px rgba(253, 87, 39, 0.5)) drop-shadow(0 10px 12px rgba(0, 0, 0, 0.55));
}
.kaos-die__squash { width: 100%; height: 100%; transform-origin: 50% 100%; perspective: 520px; }
.kaos-die__cube { position: relative; width: 100%; height: 100%; transform-style: preserve-3d; }
.kaos-die__face {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template: repeat(3, 1fr) / repeat(3, 1fr);
  gap: 6%;
  padding: 16%;
  border-radius: 16%;
  background: radial-gradient(circle at 30% 24%, #5a2240, #2a0d1b 60%, #16060e);
  border: calc(var(--die) * 0.065) solid #fd5727;
  box-shadow:
    inset 0 0 0 calc(var(--die) * 0.03) #201e2e,
    inset 0 calc(var(--die) * -0.09) calc(var(--die) * 0.14) rgba(0, 0, 0, 0.55);
  backface-visibility: hidden;
}
.kaos-die__face--front  { transform: translateZ(calc(var(--die) / 2)); }
.kaos-die__face--back   { transform: rotateY(180deg) translateZ(calc(var(--die) / 2)); }
.kaos-die__face--right  { transform: rotateY(90deg) translateZ(calc(var(--die) / 2)); }
.kaos-die__face--left   { transform: rotateY(-90deg) translateZ(calc(var(--die) / 2)); }
.kaos-die__face--top    { transform: rotateX(90deg) translateZ(calc(var(--die) / 2)); }
.kaos-die__face--bottom { transform: rotateX(-90deg) translateZ(calc(var(--die) / 2)); }
/* The sides a little darker, so the cube reads as a solid in the light. */
.kaos-die__face--right, .kaos-die__face--left { background: radial-gradient(circle at 30% 24%, #461a31, #210a15 60%, #12050b); }
.kaos-die__face--top, .kaos-die__face--bottom { background: radial-gradient(circle at 30% 24%, #6a2a4a, #331121 60%, #1a0710); }
.kaos-die__face i { display: block; }
.kaos-die__pip {
  border-radius: 50%;
  background: radial-gradient(circle at 38% 34%, #fff6c8, #ffd23f 34%, #fd7a27 66%, #bc1831);
  box-shadow: 0 0 calc(var(--die) * 0.08) rgba(255, 150, 40, 0.9);
}
.kaos-die-row.is-landed .kaos-die__pip { animation: kaosPipFlare 1s ease-out; }
@keyframes kaosPipFlare {
  0%   { box-shadow: 0 0 calc(var(--die) * 0.3) #ffd23f, 0 0 calc(var(--die) * 0.55) #fd5727; filter: brightness(1.7); }
  100% { box-shadow: 0 0 calc(var(--die) * 0.08) rgba(255, 150, 40, 0.9); filter: none; }
}
.kaos-die-spark {
  position: absolute;
  z-index: 3;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff6c8, #ffb02e 45%, rgba(253, 87, 39, 0));
  opacity: 0;
  pointer-events: none;
}

.kaos-die-label {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  min-width: 9ch;
  font-family: var(--font-heading);
  text-align: left;
}
.kaos-die-label__small {
  font-weight: 800;
  font-size: clamp(0.72rem, 1.1vw, 0.95rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}
.kaos-die-label__big {
  font-weight: 900;
  font-size: clamp(1.5rem, 2.6vw, 2.3rem);
  line-height: 1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  background: linear-gradient(180deg, #ffe680 8%, #fd7a27 55%, #d42a2a);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 2px 0 #201e2e) drop-shadow(0 0 14px rgba(253, 87, 39, 0.55));
  transform-origin: 0% 60%;
  animation: kaosDieWord 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}
@keyframes kaosDieWord {
  from { transform: scale(1.6) rotate(-6deg); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* ── Titles ─────────────────────────────────────────────────────────────── */
.kaos-stage {
  position: relative;
  z-index: 2;                    /* over the fire, which burns behind the wheel and card */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  pointer-events: none;
}

/* The wordmark, strapline and invitation travel together — a column of their
   own so they can sit under the wheel normally and beside it during the spin. */
.kaos-side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(6px, 1.4vh, 18px);
}

/* ── The spin ───────────────────────────────────────────────────────────────
   While the wheel is up it owns the screen: a huge disc on the left, with the
   wordmark and the call to spin beside it. The mascot steps over to the right
   in the same beat (--host-right), so nothing fights the wheel for the left. */
.kaos-stage--wheel {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-areas: 'wheel side';
  grid-template-columns: minmax(0, 1.05fr) minmax(280px, 0.95fr);
  align-items: center;
  justify-items: center;
  gap: clamp(12px, 2.5vw, 48px);
  padding: 0 clamp(12px, 2.5vw, 48px);
}

.kaos-stage--wheel .kaos-wheel-wrap {
  grid-area: wheel;
  width: min(90vh, 50vw);
  height: min(90vh, 50vw);
  margin: 0;
}

.kaos-stage--wheel .kaos-side {
  grid-area: side;
  width: 100%;
  /* Up from centre, so the tug meter at the bottom of this column clears the
     Imp's speech bubble in the corner below it. */
  align-self: start;
  margin-top: 7vh;
}

.kaos-stage--wheel .kaos-title {
  width: clamp(200px, 28vw, 500px);
  max-height: 24vh;
}

/* The wordmark, with the mascot's laugh built in as the O */
.kaos-title {
  width: clamp(200px, 27vw, 440px);
  max-height: 17vh;          /* the art is 1.66:1, so cap the axis that matters */
  height: auto;
  object-fit: contain;
  opacity: 0;
  transform: scale(0.6);
  /* drop-shadow, not text-shadow: this is artwork now, so the glow has to
     follow the letterforms rather than a box */
  filter: drop-shadow(0 0 24px rgba(253, 87, 39, 0.6));
}

.kaos-title--in { animation: kaosTitleIn 0.7s cubic-bezier(0.2, 1.6, 0.4, 1) forwards; }

/* The settled state written out as normal declarations, NOT left to the
   entrance animation's forwards-fill: kaos-title--throb below sets `animation`
   too, and a second class replaces that property outright rather than adding to
   it — taking the fill with it and dropping the title back to opacity 0. */
.kaos-title--shown { opacity: 1; transform: none; }

@keyframes kaosTitleIn {
  0%   { opacity: 0; transform: scale(0.5) rotate(-6deg); }
  60%  { opacity: 1; transform: scale(1.12) rotate(2deg); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
}

/* Slow ominous breathing once it has landed */
.kaos-title--throb { animation: kaosThrob 2.4s ease-in-out infinite; }

@keyframes kaosThrob {
  0%, 100% { filter: drop-shadow(0 0 20px rgba(253, 87, 39, 0.55)); }
  50%      { filter: drop-shadow(0 0 46px rgba(253, 87, 39, 0.95)); }
}

.kaos-sub {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(0.9rem, 1.7vw, 1.4rem);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.7);
  opacity: 0;
  transition: opacity 0.5s ease 0.25s;
}

/* Tied to the WHEEL appearing, not to the lights going out. It used to ride on
   --lit, which is set in the first 40ms — so "The Wheel of Misfortune" was sat
   on screen through the peeks and the jumpscare, announcing the thing the
   mascot had not introduced yet. */
.kaos-sub--shown { opacity: 1; }

/* ── The wheel ──────────────────────────────────────────────────────────── */
.kaos-wheel-wrap {
  position: relative;
  width: clamp(260px, 34vh, 430px);
  height: clamp(260px, 34vh, 430px);
  margin-top: 14px;
  opacity: 0;
  transform: scale(0.55);
  transition: opacity 0.5s ease, transform 0.6s cubic-bezier(0.2, 1.5, 0.4, 1);
}

.kaos-wheel-wrap--in { opacity: 1; transform: scale(1); }

/* The wheel has done its job. It goes, rather than hanging around shrunken
   beside the answer it already gave — the lightning is the hand-off. */
.kaos-wheel-wrap--gone {
  animation: kaosWheelOut 0.55s cubic-bezier(0.55, 0, 0.35, 1) forwards;
  pointer-events: none;
}

@keyframes kaosWheelOut {
  0%   { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.35); }
}

/* Taken out of the flow once it has faded, so the verdict card centres on the
   screen instead of around a hole where the wheel used to be. */
.kaos-wheel-wrap--hidden { display: none; }

.kaos-stage--verdict .kaos-title { max-height: 13vh; }

/* 720p is a very common projector/TV resolution and the verdict state only just
   fits there — the longest rules card (The Duel) ran 10px past the top. Tighten
   the whole stage rather than let the wordmark clip. */
@media (max-height: 820px) {
  .kaos-stage { gap: 6px; }
  .kaos-stage--verdict .kaos-title { max-height: 10vh; }
  .kaos-verdict { padding: 14px 24px 18px; margin-top: 10px; }
  .kaos-verdict-icon { font-size: 2rem; }
  .kaos-verdict-rules { margin-bottom: 12px; }
}

.kaos-wheel {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: 50%;
  /* The Imp's sticker style: an ink edge, like every outline it's drawn in. The
     fire-coloured rim is the fixed frame around it (.kaos-wheel-frame). */
  border: 0.8vh solid #201e2e;
  box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.4);
  /* spokes, shading and slices are set from JS so they always match the wheel order */
  will-change: transform;
}

/* The halo behind the wheel: it burns hotter as the room SUMMONS (--fury) and
   frosts over as the top five BRACE (--frost) — set from the tug-of-war. Two
   layers whose opacity follows a variable, so the compositor does the work. */
.kaos-wheel-halo { position: absolute; inset: -16%; z-index: 0; pointer-events: none; }
.kaos-wheel-halo::before,
.kaos-wheel-halo::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  transition: opacity 0.35s ease;
}
.kaos-wheel-halo::before {
  background: radial-gradient(circle, rgba(253, 87, 39, 0.75) 42%, rgba(188, 24, 49, 0.35) 56%, transparent 71%);
  opacity: var(--fury, 0.3);
}
.kaos-wheel-halo::after {
  background: radial-gradient(circle, rgba(108, 184, 238, 0.8) 42%, rgba(43, 95, 174, 0.35) 56%, transparent 71%);
  opacity: var(--frost, 0);
}

/* The frame: a fixed fire-gradient rim round the disc, in ink, studded with
   carnival bulbs (built by buildKaosWheel). It doesn't turn — the disc does. */
.kaos-wheel-frame {
  position: absolute;
  inset: -6%;
  z-index: 2;
  border-radius: 50%;
  pointer-events: none;
  box-shadow: 0 0 0 0.7vh #201e2e, 0 12px 40px rgba(0, 0, 0, 0.55);
}
.kaos-wheel-frame::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: linear-gradient(135deg, #ffe066, #fd5727 48%, #a8142a);
  -webkit-mask: radial-gradient(circle closest-side, transparent 88.6%, #000 89.3%);
  mask: radial-gradient(circle closest-side, transparent 88.6%, #000 89.3%);
}
.kaos-bulb {
  position: absolute;
  width: 2.7%;
  aspect-ratio: 1;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle at 36% 34%, #fffbe6, #ffd23f 55%, #c98a00);
  box-shadow: 0 0 0 0.25vh #201e2e, 0 0 1.4vh rgba(255, 210, 63, 0.95);
  animation: kaosBulb 1.1s steps(1, end) infinite;
  animation-delay: calc(var(--i, 0) * -0.55s);
}
@keyframes kaosBulb {
  0%   { opacity: 1; }
  50%  { opacity: 0.28; }
}
/* While it spins, the lights chase round the rim. */
.kaos-wheel-wrap--spinning .kaos-bulb {
  animation-duration: 0.48s;
  animation-timing-function: steps(1, end);
  animation-delay: calc(var(--i, 0) * -0.02s);
}


/* Segment labels ride the wheel, each rotated to its own slice */
/* ICONS ONLY. Words on a spinning wheel are unreadable at any size that fits a
   slice, and at nine faces a slice is 44deg — "DOUBLE OR NOTHING" never had a
   hope. The mode is named in full on the verdict card a moment later, which is
   where reading actually happens. */
.kaos-seg-label {
  position: absolute;
  /* left/top AND the box size are percentages of the wheel — so the ring and the
     glyphs both track the disc through its size changes with no JS re-layout.
     The disc is ~800px while it spins and ~250px on the verdict card. */
  display: flex;
  align-items: center;
  justify-content: center;
  /* Set per wheel by buildKaosWheel: a five-face wheel has far wider slices than
     a ten-face one and can carry a bigger token. */
  width: var(--kaos-seg-size, 14%);
  height: var(--kaos-seg-size, 14%);
  transform: translate(-50%, -50%);
  pointer-events: none;
  /* A STICKER TOKEN: cream, ringed in ink, the glyph in the face's own colour. */
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #fffdf6, #f3e6d0);
  box-shadow: 0 0 0 0.45vh #201e2e, 0 0.5vh 0 0.45vh rgba(32, 30, 46, 0.55);
  color: var(--seg-colour, #201e2e);
}

.kaos-seg-label .kaos-seg-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64%;
  height: 64%;
}

/*
 * !important IS LOAD-BEARING HERE, and it is the only thing that can do this job.
 *
 * lcIcon() emits `<i data-lucide style="width:24px;height:24px">`, and
 * lucide.createIcons() COPIES the placeholder's attributes onto the <svg> it
 * swaps in — the element's own attributes are spread last, so `style` survives.
 * An inline declaration outranks any selector, however specific, so the
 * `width: 100%` below silently did nothing for the life of this screen: the
 * glyphs rendered at a flat 24px inside a 102px box on an 800px disc — 3% of the
 * wheel — which is why the faces were unreadable from across a room.
 *
 * Overriding an inline style is the one thing !important exists for. Don't
 * "clean this up" without also changing what lcIcon writes.
 */
.kaos-seg-label .kaos-seg-icon svg {
  width: 100% !important;
  height: 100% !important;
  /* Drawn big, on a token: a bold stroke in the face's own colour. */
  stroke-width: 2.4;
}

/* A mode the room is too small for is still drawn, but visibly out of play */
/* The wheel now only draws faces the room is big enough for, so nothing needs
   dimming — a mode that cannot happen is simply not on it, which keeps the
   visible odds honest. */

/* A sliver too narrow to carry a word: icon only, and dark, because The
   Levelling's slice is near-white and a white glyph on it would vanish. */
/* A sliver is ~8deg wide — about 45px of arc even out at the wider radius it
   rides on — so it takes its own, smaller box. Sized off the wheel like
   everything else, and still more than twice what it used to render at.
   SIZE ONLY: the ink is a separate question, answered below. */
.kaos-seg-label--sliver {
  width: var(--kaos-sliver-size, 8%);
  height: var(--kaos-sliver-size, 8%);
}

.kaos-seg-label--sliver .kaos-seg-icon svg { stroke-width: 2.8; }

/* A light slice (The Levelling is near-white) would swallow a white glyph.
   Applied by luminance in buildKaosWheel, NOT by how narrow the slice is — The
   Tithe is a sliver too and is mid-purple, and inking it dark made it vanish. */
/* On a token the glyph sits on cream, so a pale face's own colour would vanish
   into it: ink instead. */
.kaos-seg-label--dark-ink { color: #201e2e; }

.kaos-hub {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 21%;
  height: 21%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  /* The Imp's own face at the middle of its wheel — it doesn't turn with it. */
  background: radial-gradient(circle at 36% 30%, #3a1428, #1a0710 70%);
  box-shadow:
    0 0 0 0.7vh #201e2e,
    0 0 0 1.4vh #fd5727,
    0 0 0 2vh #201e2e,
    0 8px 30px rgba(0, 0, 0, 0.6);
  z-index: 4;
}
.kaos-hub-face { width: 92%; height: auto; aspect-ratio: 1; pointer-events: none; }  /* the SVG has no size of its own: in a flex box its height collapsed to 0 */

.kaos-pointer {
  position: absolute;
  top: -10%;
  left: 50%;
  /* A flame flapper, pinned near its top — sized off the wheel, so it's never
     a speck on a disc that fills half the screen. */
  width: 11%;
  transform: translateX(-50%);
  transform-origin: 50% 24%;
  filter: drop-shadow(0 0.6vh 0.8vh rgba(0, 0, 0, 0.6));
  z-index: 5;
}
.kaos-pointer svg { display: block; width: 100%; height: auto; }

/* Kicked back a touch each time a segment goes past, like a real flapper */
.kaos-pointer--tick { animation: kaosPointerTick 0.1s ease-out; }

@keyframes kaosPointerTick {
  0%   { transform: translateX(-50%) rotate(0deg); }
  45%  { transform: translateX(-50%) rotate(-17deg); }
  100% { transform: translateX(-50%) rotate(0deg); }
}

/* ── The room at the wheel: the tug meter (game-display.js _kaosSetTug) ────── */
.kaos-tug {
  width: min(460px, 100%);
  margin-top: 14px;
  padding: 14px 18px 12px;
  border-radius: 18px;
  background: rgba(16, 8, 26, 0.82);
  border: 2px solid rgba(255, 255, 255, 0.12);
  font-family: var(--font-heading);
  pointer-events: none;
  animation: kaosIncomingIn 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
}
.kaos-tug-row { display: flex; justify-content: space-between; margin-bottom: 8px; }
.kaos-tug-side {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 900;
  font-size: clamp(0.85rem, 1.4vw, 1.15rem);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.kaos-tug-side svg { width: 1.3em; height: 1.3em; }
.kaos-tug-side b { font-size: 1.25em; color: #fff; }
.kaos-tug-side--summon { color: #ff9a4a; }
.kaos-tug-side--brace { color: #8fcfff; }
.kaos-tug-bar {
  position: relative;
  height: clamp(16px, 2.2vh, 24px);
  border-radius: var(--radius-full);
  border: 0.35vh solid #201e2e;
  background: linear-gradient(90deg, #6cb8ee, #2b5fae);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1);
}
.kaos-tug-fill {
  width: 50%;
  height: 100%;
  border-radius: var(--radius-full) 0 0 var(--radius-full);
  background: linear-gradient(90deg, #bc1831, #fd7a27 70%, #ffd23f);
  transition: width 0.28s ease;
}
.kaos-tug-mark {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(20px, 2.8vh, 30px);
  aspect-ratio: 1;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle at 35% 30%, #fff, #ffe680);
  border: 0.35vh solid #201e2e;
  transition: left 0.28s ease;
}
.kaos-tug-hint {
  margin-top: 8px;
  font-weight: 700;
  font-size: clamp(0.7rem, 1vw, 0.9rem);
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.72);
  text-align: center;
}

/* ── The verdict ────────────────────────────────────────────────────────── */
.kaos-verdict {
  margin-top: 18px;
  /* Wider than it was: the headline is one sentence and wants to stay one
     line, and a 660px card wrapped it into three on a projector. */
  max-width: min(1040px, 92vw);
  padding: 20px 38px 24px;
  border-radius: var(--radius-lg);
  background: rgba(16, 8, 26, 0.93);
  border: 2px solid var(--kaos-colour, #dc4091);
  box-shadow: 0 0 46px var(--kaos-glow, rgba(220, 64, 145, 0.5));
  text-align: center;
  pointer-events: auto;
  animation: kaosVerdictIn 0.55s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}

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

.kaos-verdict-icon { font-size: 2.6rem; line-height: 1; margin-bottom: 4px; }

.kaos-verdict-name {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(1.7rem, 3.4vw, 2.9rem);
  letter-spacing: 0.07em;
  color: var(--kaos-colour, #dc4091);
  text-shadow: 0 0 20px var(--kaos-glow, rgba(220, 64, 145, 0.6));
}

.kaos-verdict-tag {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: clamp(0.85rem, 1.5vw, 1.1rem);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
  margin-bottom: 10px;
}

/*
 * THE HEADLINE IS THE CARD.
 *
 * This screen is up for about eight seconds and is being read from across a
 * room, usually off a projector. The rule that is about to cost people points
 * has to be legible at that distance, which a 1.1rem paragraph never was — so
 * one sentence carries it, at roughly three times the size, and the paragraph
 * below is demoted to detail for whoever is close enough to want it.
 */
.kaos-verdict-head {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(1.25rem, 2.5vw, 2.15rem);
  line-height: 1.22;
  letter-spacing: 0.01em;
  color: #fff;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.7);
  margin: 2px 0 12px;
  text-wrap: balance;
}

.kaos-verdict-rules {
  font-size: clamp(0.82rem, 1.15vw, 1rem);
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.55);
  margin: 0 0 14px;
  max-width: 74ch;
  margin-inline: auto;
}

/* The tax rides on every face, so it gets a permanent line of its own rather
   than being folded into a rule that changes every time. */
.kaos-verdict-tax {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 0 0 16px;
  padding: 7px 16px;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.14);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(0.72rem, 1.15vw, 0.98rem);
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.82);
}

.kaos-verdict-tax svg { width: 1.1em !important; height: 1.1em !important; opacity: 0.8; }

/* ── The verdict, acted out (kaos-demos.js) ───────────────────────────────────
   Each rule played with the room's own faces instead of a paragraph. The card
   widens for it; the icon goes (the demo is the picture) and the headline
   stays as the caption. Faces are sized by one variable, --kd, per group. */
.kaos-verdict--demo { width: min(1180px, 92vw); max-width: min(1180px, 92vw); padding-top: 16px; }
.kaos-verdict--demo .kaos-verdict-icon { display: none; }
.kaos-verdict--demo .kaos-verdict-tag { margin-bottom: 2px; }
.kaos-verdict--demo .kaos-verdict-head { margin-top: 6px; }
/* A demo card is taller than a paragraph card, and must still fit a 900px
   screen with the wordmark above it: the name and the wordmark give a little,
   and the die and the button share one line. */
.kaos-verdict--demo .kaos-verdict-name { font-size: clamp(1.5rem, 2.8vw, 2.4rem); }
.kaos-verdict--demo .kaos-verdict-tax { margin-bottom: 10px; }
.kaos-stage--verdict:has(.kaos-verdict--demo) .kaos-title { max-height: 10vh; }
.kaos-verdict--demo .kaos-die-row { display: inline-flex; vertical-align: middle; margin: 0 22px 0 0; }
.kaos-verdict--demo #kaosContinueBtn { display: inline-flex; vertical-align: middle; }

.kaos-demo {
  --kd: clamp(46px, 5.4vw, 88px);
  position: relative;
  height: clamp(180px, 29vh, 290px);
  margin: 4px 0 8px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.kd-row {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(10px, 1.6vw, 28px);
  width: 100%;
  height: 100%;
  padding-bottom: 30px;
}
.kd-group { position: relative; display: flex; align-items: flex-end; gap: clamp(8px, 1vw, 16px); min-width: 0; }
.kd-p {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: calc(var(--kd) * 1.3);
  min-width: 0;
  flex-shrink: 1;          /* a crowded row squeezes the names, never the card */
  transition: filter 0.35s ease, opacity 0.35s ease, transform 0.3s ease;
}
.kd-face.lb-avatar { width: var(--kd); height: var(--kd); font-size: calc(var(--kd) * 0.44); transition: box-shadow 0.3s ease, filter 0.35s ease; }
.kd-p--big { --kd: clamp(64px, 7.4vw, 118px); }
.kd-p--small { --kd: clamp(38px, 4.2vw, 64px); opacity: 0.85; }
.kd-name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(0.72rem, 1.05vw, 1rem);
  color: rgba(255, 255, 255, 0.85);
}
.kd-more {
  align-self: center;
  padding: 6px 10px;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.08);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(0.75rem, 1vw, 0.95rem);
  color: rgba(255, 255, 255, 0.6);
}
.kd-tag {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(0.68rem, 1vw, 0.95rem);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.6);
}
.kd-tag--under { position: absolute; left: 50%; bottom: -27px; transform: translateX(-50%); }
.kd-tag--danger { color: #ff7a7a; }
.kd-tag--ice { color: #9fdcff; }
.kd-tag--rebel { color: #7be38f; }
.kd-tag--note { position: absolute; left: 50%; bottom: 0; transform: translateX(-50%); opacity: 0; transition: opacity 0.4s ease; color: rgba(255, 255, 255, 0.72); }
.kd-tag--note.is-on { opacity: 1; }
/* Points on the move: lost as they leave, won as they land. */
.kd-chip {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 3;
  padding: 4px 12px;
  border-radius: var(--radius-full);
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(0.9rem, 1.4vw, 1.25rem);
  white-space: nowrap;
  color: #fff;
  pointer-events: none;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5);
}
.kd-chip--lost { background: #d64545; }
.kd-chip--won { background: #2fa84f; }
.kd-p.is-marked .kd-face { box-shadow: 0 0 0 3px #ff4d4d, 0 0 26px rgba(255, 60, 60, 0.85); }

/* Bounty: the mark, a crosshair on their face, hunters either side. */
.kd-mark-wrap { --kd: clamp(64px, 7.4vw, 118px); position: relative; display: flex; flex-direction: column; align-items: center; gap: 8px; margin: 0 clamp(4px, 1vw, 18px); }
/* Room under the face for the crosshair's lower tick, so it never sits on the name. */
.kd-mark-wrap .kd-p--big { gap: calc(var(--kd) * 0.3); }
.kd-crosshair {
  position: absolute;
  left: 50%;
  top: calc(var(--kd) / 2);
  width: calc(var(--kd) * 1.5);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  color: #ff4d4d;
  opacity: 0;
  pointer-events: none;
  filter: drop-shadow(0 0 10px rgba(255, 60, 60, 0.8));
}
.kd-crosshair svg { display: block; width: 100%; height: 100%; }
.kd-crosshair.is-on { opacity: 1; animation: kdSight 7s linear infinite; }
@keyframes kdSight {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(360deg); }
}
.kd-mark-wrap .kd-tag { opacity: 0; transition: opacity 0.4s ease; }
.kd-mark-wrap .kd-tag.is-on { opacity: 1; }
.kd-hunter.is-fast .kd-face { box-shadow: 0 0 0 3px #ffd23f, 0 0 20px rgba(255, 210, 63, 0.8); }

/* The Uprising: the top three raised up, the rest sitting it out, the mob with torches. */
.kd-lords {
  position: relative;
  align-self: flex-start;
  display: flex;
  gap: 12px;
  margin-top: 6px;
  padding: 10px 14px 12px;
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(255, 210, 63, 0.2), rgba(255, 210, 63, 0.04));
  border: 1px solid rgba(255, 210, 63, 0.35);
}
.kd-idle { position: relative; display: flex; gap: 8px; opacity: 0.6; filter: grayscale(0.7); }
.kd-mob { --kd: clamp(38px, 4.2vw, 62px); position: relative; display: grid; grid-template-columns: repeat(4, auto); gap: 22px 10px; }
.kd-mob .kd-p { width: calc(var(--kd) * 1.2); }
.kd-torch {
  position: absolute;
  top: -20px;
  right: -4px;
  color: #ff9a2e;
  transform-origin: 50% 100%;
  filter: drop-shadow(0 0 8px rgba(255, 140, 40, 0.9));
  animation: kdTorch 0.5s ease-in-out infinite alternate;
}
.kd-torch svg { width: 22px !important; height: 22px !important; }
.kd-mob.is-rising .kd-torch { color: #ffd23f; }
@keyframes kdTorch {
  from { transform: rotate(-9deg) scale(1); }
  to   { transform: rotate(9deg) scale(1.18); }
}

/* Thin Ice: a slab that cracks under the top five; solid ground for the rest. */
.kd-icebox, .kd-groundbox { position: relative; display: flex; align-items: flex-end; gap: 10px; padding: 0 14px 20px; }
.kd-icebox .kd-p, .kd-groundbox .kd-p { z-index: 1; }
.kd-ice, .kd-ground { position: absolute; left: 0; right: 0; bottom: 0; height: 22px; border-radius: 11px; }
.kd-ice {
  overflow: hidden;
  background: linear-gradient(180deg, #e2f6ff, #86cdf0 55%, #4a9ad0);
  box-shadow: 0 0 24px rgba(124, 199, 238, 0.55), inset 0 2px 0 rgba(255, 255, 255, 0.85);
}
.kd-ice svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.kd-ice path { stroke-dasharray: 1px; stroke-dashoffset: 1px; transition: stroke-dashoffset 0.55s ease-out; }
.kd-ice path:nth-child(2) { transition-delay: 0.08s; }
.kd-ice path:nth-child(3) { transition-delay: 0.2s; }
.kd-ice path:nth-child(4) { transition-delay: 0.28s; }
.kd-ice.is-cracked path { stroke-dashoffset: 0; }
.kd-ground { background: linear-gradient(180deg, #5b4a44, #34282a); box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.12); }
.kd-p.is-falling { transform: translateY(30%) scale(0.86); opacity: 0.45; filter: saturate(0.4) brightness(0.8) hue-rotate(170deg); }

/* Snap: a stopwatch on each of the top five, running out; the full clock for the rest. */
.kd-ring { position: absolute; left: 50%; top: calc(var(--kd) / 2); width: calc(var(--kd) * 1.38); aspect-ratio: 1; transform: translate(-50%, -50%); pointer-events: none; }
.kd-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.kd-ring-bg { fill: none; stroke: rgba(255, 255, 255, 0.14); stroke-width: 3.2; }
.kd-ring-fill { fill: none; stroke: #ffd23f; stroke-width: 3.2; stroke-linecap: round; stroke-dasharray: 100px; stroke-dashoffset: 0; }
.kd-ring-n {
  position: absolute;
  right: -6px;
  top: -6px;
  min-width: 1.7em;
  padding: 2px 5px;
  border-radius: var(--radius-full);
  background: #ffd23f;
  color: #201e2e;
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(0.7rem, 1vw, 0.92rem);
  text-align: center;
}
.kd-p.is-out .kd-ring-fill { stroke: #ff4d4d; }
.kd-p.is-out .kd-ring-n { background: #ff4d4d; color: #fff; }
.kd-p.is-out .kd-face { filter: grayscale(0.6) brightness(0.7); }
.kd-calm-clock { align-self: center; color: #7be38f; filter: drop-shadow(0 0 10px rgba(123, 227, 143, 0.5)); }
.kd-calm-clock svg { width: 34px !important; height: 34px !important; }

/* Handicap: by rank up a ramp — the further down the board, the bigger the stamp. */
.kd-handicap { gap: clamp(6px, 1.2vw, 18px); }
.kd-ramp {
  position: absolute;
  left: 3%;
  right: 3%;
  bottom: 22px;
  height: 62%;
  clip-path: polygon(0 100%, 100% 0, 100% 100%);
  background: linear-gradient(90deg, rgba(214, 69, 69, 0.4), rgba(47, 168, 79, 0.4));
  pointer-events: none;
}
.kd-rated { --kd: calc(clamp(44px, 5vw, 78px) * (0.78 + var(--t, 0.5) * 0.45)); z-index: 1; margin-bottom: calc(var(--t, 0) * clamp(34px, 9vh, 90px)); }
.kd-stamp {
  position: absolute;
  top: -10px;
  right: -12px;
  padding: 3px 8px;
  border-radius: 8px;
  border: 2px solid rgba(255, 255, 255, 0.9);
  background: color-mix(in srgb, #2fa84f calc(var(--t, 0.5) * 100%), #d64545);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(0.78rem, 1.15vw, 1.08rem);
  transform: rotate(-8deg);
  opacity: 0;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
}
.kd-rated.is-stamped .kd-stamp { opacity: 1; }

/* The Duel: pairs drawn at random, like a fruit machine, again and again. */
.kd-duel { align-items: center; }
.kd-pairs { display: grid; grid-template-columns: repeat(3, auto); gap: 10px 18px; }
.kd-pair {
  --kd: clamp(40px, 4.4vw, 66px);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 14px;
  background: rgba(142, 68, 173, 0.16);
  border: 1px solid rgba(142, 68, 173, 0.45);
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.kd-slot { width: calc(var(--kd) * 1.3); }
.kd-slot .kd-p { width: 100%; }
.kd-vs { font-family: var(--font-heading); font-weight: 900; font-size: clamp(0.8rem, 1.2vw, 1.05rem); letter-spacing: 0.06em; color: #d08bf0; }
.kd-pair.is-spinning .kd-face { filter: blur(1.5px) brightness(1.25); }
.kd-pair.is-fighting { border-color: #ffd23f; box-shadow: 0 0 18px rgba(255, 210, 63, 0.4); }
.kd-bye { position: relative; display: flex; flex-direction: column; align-items: center; margin-left: 10px; opacity: 0.75; }

/* Double or Nothing: everyone flips a coin — safe, or risky. */
.kd-flipper { perspective: 500px; }
.kd-coin {
  order: -1;
  position: relative;
  width: calc(var(--kd) * 1.15);
  height: calc(var(--kd) * 0.6);
  transform-style: preserve-3d;
  transition: transform 0.45s cubic-bezier(0.3, 1.4, 0.5, 1);
}
.kd-coin__face {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border-radius: var(--radius-full);
  backface-visibility: hidden;
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(0.62rem, 0.95vw, 0.86rem);
}
.kd-coin__face svg { width: 1.2em !important; height: 1.2em !important; }
.kd-coin__safe { background: #34344a; color: #cfd2e6; border: 2px solid rgba(255, 255, 255, 0.25); }
.kd-coin__risky { background: linear-gradient(135deg, #ffb347, #e08a1e); color: #201e2e; transform: rotateY(180deg); box-shadow: 0 0 16px rgba(224, 138, 30, 0.7); }
.kd-flipper.is-risky .kd-coin { transform: rotateY(180deg); }
.kd-flipper.is-risky .kd-face { box-shadow: 0 0 0 3px #e08a1e, 0 0 18px rgba(224, 138, 30, 0.6); }

/* Blackout: the lights go out on every answer and every face. */
.kd-verdict {
  order: -1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--kd) * 0.46);
  height: calc(var(--kd) * 0.46);
  border-radius: 50%;
  color: #fff;
  transition: background 0.3s ease, color 0.3s ease;
}
.kd-verdict svg { width: 60% !important; height: 60% !important; }
.kd-verdict.is-right { background: #2fa84f; }
.kd-verdict.is-wrong { background: #d64545; }
/* Faces go dark; names stay — the blackout hides answers and standings, not who's playing. */
.kd-lit.is-dark .kd-face { filter: brightness(0.12) saturate(0); }
.kd-lit.is-dark .kd-verdict { background: rgba(255, 255, 255, 0.08); color: transparent; }
.kd-lit.is-dark .kd-verdict::after { content: '?'; position: absolute; font-family: var(--font-heading); font-weight: 900; color: rgba(255, 255, 255, 0.45); }
.kd-verdict { position: relative; }
.kaos-demo.kd-flicker .kd-lit.is-dark .kd-face { filter: brightness(0.7); }
.kd-eye {
  position: absolute;
  left: 50%;
  top: 18%;
  z-index: 2;
  color: #a78bfa;
  opacity: 0;
  transform: translate(-50%, -50%);
  pointer-events: none;
  filter: drop-shadow(0 0 18px rgba(167, 139, 250, 0.85));
}
.kd-eye svg { width: 44px !important; height: 44px !important; }
.kd-eye.is-on { opacity: 1; }

@media (max-height: 820px) {
  .kaos-demo { height: clamp(150px, 24vh, 210px); --kd: clamp(36px, 4vw, 56px); }
  .kaos-verdict--demo .kaos-verdict-name { font-size: clamp(1.4rem, 2.8vw, 2.2rem); }
}
@media (prefers-reduced-motion: reduce) {
  .kd-crosshair.is-on, .kd-torch { animation: none; }
}

/* ── The banner carried through the question itself ─────────────────────── */
.kaos-round-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 15px;
  border-radius: var(--radius-full);
  background: var(--kaos-colour, #dc4091);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 0.9rem;
  letter-spacing: 0.09em;
  box-shadow: 0 0 20px var(--kaos-glow, rgba(220, 64, 145, 0.6));
  animation: kaosChipPulse 1.7s ease-in-out infinite;
}

@keyframes kaosChipPulse {
  0%, 100% { box-shadow: 0 0 16px var(--kaos-glow, rgba(220,64,145,0.5)); }
  50%      { box-shadow: 0 0 34px var(--kaos-glow, rgba(220,64,145,0.95)); }
}

/* A row the wheel has singled out */
.leaderboard-item--marked .leaderboard-item-card {
  animation: kaosMarked 1.1s ease-in-out infinite;
}

@keyframes kaosMarked {
  0%, 100% { box-shadow: inset 0 0 0 2px rgba(255, 79, 163, 0.35); }
  50%      { box-shadow: inset 0 0 0 3px rgba(255, 79, 163, 0.95), 0 0 26px rgba(255, 79, 163, 0.55); }
}

/* ── Blackout ───────────────────────────────────────────────────────────── */
.blackout-screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  text-align: center;
}

.blackout-eye {
  color: #a78bfa;
  animation: kaosThrob 2.6s ease-in-out infinite, blackoutDrift 7s ease-in-out infinite;
}

.blackout-eye svg {
  width: clamp(4rem, 11vw, 8rem);
  height: auto;
  filter: drop-shadow(0 0 26px rgba(167, 139, 250, 0.75));
}

@keyframes blackoutDrift {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50%      { transform: translateY(-14px) rotate(2deg); }
}

.blackout-title {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(2rem, 5.5vw, 4rem);
  letter-spacing: 0.2em;
  color: #a78bfa;
  text-shadow: 0 0 30px rgba(167, 139, 250, 0.8);
}

.blackout-sub {
  font-size: clamp(1rem, 2vw, 1.4rem);
  color: var(--text-secondary);
  max-width: 620px;
}

/* Motion is the delivery here, but every state still has to be readable
   without it: the wheel lands on its answer, the mascot is simply present. */
@media (prefers-reduced-motion: reduce) {
  .kaos-mascot--in,
  .kaos-mascot--cackle,
  .imp-bob,
  .imp-flame,
  .imp-ear,
  .imp-rig--rubbing .imp-hand__gest,
  .kaos-overlay--flicker .kaos-veil,
  .kaos-tongue,
  .kaos-die-row.is-landed .kaos-die__pip,
  .kaos-bulb,
  .kaos-die-label__big,
  .kaos-title--in,
  .kaos-title--throb,
  .kaos-pointer--tick,
  .kaos-verdict,
  .kaos-round-chip,
  .leaderboard-item--marked .leaderboard-item-card,
  .blackout-eye {
    animation: none !important;
  }
  .kaos-mascot { transform: none; }
  .kaos-title { opacity: 1; transform: none; }
  .kaos-wheel-wrap { opacity: 1; transform: none; }
}

/* ── Builder: suggested free-to-use images ──────────────────────────────────
   A button under the media drop zone opens a picker. Nothing is fetched until
   the host asks — searching on every keystroke meant a round trip and a wall of
   thumbnails for questions that never wanted a picture. */
.img-suggest-btn {
  width: 100%;
  margin-top: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font-size: 0.85rem;
  border: 1px dashed var(--bg-glass-border);
  color: var(--text-secondary);
  transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease;
}

.img-suggest-btn:hover {
  border-color: var(--accent);
  color: var(--text-primary);
  background: rgba(220, 64, 145, 0.08);
}

/* Results grid, in the picker */
.img-picker-results {
  max-height: 46vh;
  overflow-y: auto;
  padding-right: 4px;
}

.img-picker-empty {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.88rem;
  padding: 26px 0;
  margin: 0;
}

.img-suggest-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 9px;
}

.img-suggest-item {
  position: relative;
  padding: 0;
  border: 1px solid var(--bg-glass-border);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  aspect-ratio: 4 / 3;
  overflow: hidden;
  cursor: pointer;
  transition: transform 0.14s cubic-bezier(0.34, 1.56, 0.64, 1),
              border-color 0.2s ease, box-shadow 0.2s ease;
}

.img-suggest-item:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4), 0 0 0 1px var(--accent);
}

.img-suggest-item:active { transform: translateY(0) scale(0.97); }

.img-suggest-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The licence rides on the thumbnail — the host should be able to see what
   they are picking without hunting for it. */
.img-suggest-lic {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 2px 5px;
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #fff;
  background: rgba(0, 0, 0, 0.66);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.img-suggest-item.is-loading {
  opacity: 0.5;
  pointer-events: none;
}

.img-suggest-item.is-loading::after {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(45deg,
    rgba(255,255,255,0.08) 0 8px, rgba(255,255,255,0) 8px 16px);
  animation: imgAdopt 0.7s linear infinite;
}

@keyframes imgAdopt {
  to { background-position: 22px 0; }
}

@media (prefers-reduced-motion: reduce) {
  .img-suggest-item, .img-suggest-item.is-loading::after { transition: none; animation: none; }
}

/* ── Anywhere Mode dock ──────────────────────────────────────────────────
   Sits beside the music dock in the lobby bar and follows the same shape:
   a 42px icon that opens a panel upwards on hover or focus. Anywhere Mode
   swaps the join address from the LAN IP to a public URL, so players in a
   pub can join over their own mobile data with no shared WiFi at all. */
.net-dock {
  position: relative;
  display: flex;
  align-items: center;
  text-align: left;
}

.net-dock-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: rgba(255, 255, 255, 0.72);
  cursor: pointer;
  padding: 0;
  transition: background 0.18s ease, color 0.18s ease,
              border-color 0.18s ease, transform 0.18s ease;
}

.net-dock-btn:hover {
  background: rgba(255, 255, 255, 0.11);
  border-color: rgba(255, 255, 255, 0.3);
  color: white;
  transform: translateY(-1px);
}

/* Anywhere Mode live — readable at a glance without opening the panel,
   because it changes what the QR code on screen actually points at. */
.net-dock-btn.is-anywhere {
  color: var(--answer-green, #26890c);
  border-color: rgba(38, 137, 12, 0.5);
  background: rgba(38, 137, 12, 0.16);
}

.net-dock-btn.is-busy {
  color: rgba(255, 255, 255, 0.9);
  border-color: rgba(255, 255, 255, 0.35);
}

.net-dock-btn.is-busy > * { animation: netPulse 1s ease-in-out infinite; }

@keyframes netPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

.net-panel {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 0;
  width: 290px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 11px;
  border-radius: 14px;
  background: rgba(14, 14, 28, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.45);
  z-index: 70;
  /* Hidden by transform/opacity so it can animate, plus visibility so it
     cannot be tabbed into or hovered while closed. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px) scale(0.97);
  transform-origin: bottom left;
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s;
}

.net-dock:hover .net-panel,
.net-dock:focus-within .net-panel {
  opacity: 1;
  visibility: visible;
  transform: translateY(0) scale(1);
}

/* The same invisible bridge the music dock needs, or the panel closes as the
   pointer travels up into it. */
.net-dock::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 100%;
  width: 290px;
  height: 12px;
}

@media (prefers-reduced-motion: reduce) {
  .net-panel { transition: opacity 0.16s ease, visibility 0.16s; transform: none; }
  .net-dock:hover .net-panel,
  .net-dock:focus-within .net-panel { transform: none; }
  .net-dock-btn.is-busy > * { animation: none; }
}

.net-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}

/* The switch itself: two mutually exclusive ways to join. */
.net-choice {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px;
}

.net-opt {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  padding: 9px 10px;
  border-radius: 11px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.6);
  cursor: pointer;
  text-align: left;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.net-opt:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.24);
  color: rgba(255, 255, 255, 0.85);
}

.net-opt.is-active {
  background: rgba(220, 64, 145, 0.16);
  border-color: rgba(220, 64, 145, 0.5);
  color: white;
}

.net-opt:disabled { opacity: 0.4; cursor: not-allowed; }

.net-opt-name {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 0.86rem;
  line-height: 1.1;
}

.net-opt-sub {
  font-size: 0.66rem;
  line-height: 1.25;
  opacity: 0.65;
}

.net-status {
  font-size: 0.74rem;
  line-height: 1.4;
  padding: 8px 10px;
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.05);
  color: rgba(255, 255, 255, 0.72);
  word-break: break-word;
}

.net-status.is-error {
  background: rgba(220, 64, 145, 0.14);
  color: #ffb9d9;
}

.net-status.is-live {
  background: rgba(38, 137, 12, 0.16);
  color: #b6f0a0;
}

.net-install-lead,
.net-install-note {
  margin: 0;
  font-size: 0.7rem;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.55);
}

.net-cmd {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 6px 0;
  padding: 7px 9px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.net-cmd code {
  flex: 1;
  font-size: 0.72rem;
  color: #9fe6ff;
  word-break: break-all;
}

.net-copy {
  display: inline-flex;
  padding: 4px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
}

.net-copy:hover { background: rgba(255, 255, 255, 0.14); color: white; }

.net-fallback {
  font-size: 0.72rem;
  color: rgba(255, 255, 255, 0.6);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  padding-top: 9px;
}

.net-fallback summary {
  cursor: pointer;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.72);
  list-style: none;
}

.net-fallback summary::-webkit-details-marker { display: none; }
.net-fallback summary::before { content: '▸ '; opacity: 0.6; }
.net-fallback[open] summary::before { content: '▾ '; }

.net-fallback ol {
  margin: 8px 0 6px;
  padding-left: 18px;
  line-height: 1.5;
}

/* Tells the room which mode is live, right beside the address they're typing. */
.lobby-anywhere-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding: 4px 11px;
  border-radius: 999px;
  background: rgba(38, 137, 12, 0.18);
  border: 1px solid rgba(38, 137, 12, 0.45);
  color: #b6f0a0;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
}

/* Three ways to join now, so the tiles get their own row rather than being
   squeezed to a third of the panel width each. */
.net-choice--3 { grid-template-columns: 1fr 1fr 1fr; gap: 5px; }
.net-choice--3 .net-opt { padding: 8px 7px; }
.net-choice--3 .net-opt-name { font-size: 0.78rem; }
.net-choice--3 .net-opt-sub { font-size: 0.6rem; }

/* Hotspot Mode is live — the Mac is broadcasting its own network. */
.net-dock-btn.is-hotspot {
  color: #ffc857;
  border-color: rgba(255, 200, 87, 0.5);
  background: rgba(255, 200, 87, 0.14);
}

.net-hotspot {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.09);
}

.net-open-settings {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 10px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.09);
  border: 1px solid rgba(255, 255, 255, 0.16);
  color: white;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.74rem;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.net-open-settings:hover {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.3);
}

.net-steps {
  margin: 0;
  padding-left: 16px;
  font-size: 0.7rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.62);
}

.net-steps strong { color: rgba(255, 255, 255, 0.85); }

/* Hotspot Mode's badge, alongside Anywhere Mode's. */
.lobby-anywhere-badge--hotspot {
  background: rgba(255, 200, 87, 0.16);
  border-color: rgba(255, 200, 87, 0.45);
  color: #ffe0a3;
}

/* ══════════════════════════════════════════════════════════════════════════
   KAOS AFTERMATH — the duel bracket and the Levelling collapse
   ══════════════════════════════════════════════════════════════════════════
   The payoff the wheel spends forty seconds promising. Everything here sizes
   off --kaft-base, one number set inline by _kaftFitToScreen, for the same
   reason the Most Likely To reveal does: a host screen cannot scroll, so the
   reveal has to shrink to the viewport rather than run off the bottom of it.
   Declared on .kaftermath-stage, NOT on .kaftermath-body — a declaration on the
   inner box would beat the inherited value for everything inside it. */
.kaftermath {
  position: fixed;
  inset: 0;
  z-index: 880;               /* under the Kaos wheel (900), over everything else */
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  overflow: hidden;
}

.kaftermath-veil {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 45%, rgba(10, 4, 16, 0.86), rgba(6, 2, 10, 0.97));
  opacity: 0;
  transition: opacity 0.42s ease;
}
.kaftermath--lit .kaftermath-veil { opacity: 1; }

.kaftermath-stage {
  --kaft-base: 20px;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.9);
  padding: calc(var(--kaft-base) * 1.1);
  max-width: 96vw;
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 0.34s ease, transform 0.34s cubic-bezier(0.22, 1.2, 0.36, 1);
}
.kaftermath--lit .kaftermath-stage { opacity: 1; transform: scale(1); }

.kaftermath-head {
  display: flex;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.5);
  color: var(--kaft-colour, #dc4091);
}

.kaftermath-icon { display: flex; }
.kaftermath-icon svg {
  /* Same trap as the wheel: lcIcon writes an inline width/height that lucide
     copies onto the <svg>, and an inline style outranks any selector. */
  width: calc(var(--kaft-base) * 1.9) !important;
  height: calc(var(--kaft-base) * 1.9) !important;
  stroke-width: 1.8;
}

.kaftermath-title {
  font-family: var(--font-display, inherit);
  font-size: calc(var(--kaft-base) * 1.75);
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-shadow: 0 0 calc(var(--kaft-base) * 1.2) var(--kaft-glow, rgba(220, 64, 145, 0.55));
}

.kaftermath-foot {
  margin: 0;
  font-size: calc(var(--kaft-base) * 0.78);
  letter-spacing: 0.04em;
  color: var(--text-muted, #a8a4b8);
  opacity: 0;
  transition: opacity 0.4s ease;
}
.kaftermath-foot.is-shown { opacity: 1; }

/* ── THE DUEL BRACKET ─────────────────────────────────────────────────────
   Every pairing at once, in a grid. A celebrity duel between two people is
   dead air for everyone else, and the same is true of its reveal: the room
   wants to find its own name, so all of them land together and then resolve
   one at a time.

   WHO WON HAS TO READ FROM THE BACK OF THE ROOM. A settled card shows the
   reason under each face (right + time / wrong / no answer), a crown and a
   green ring on the winner, the card lit toward them, chevrons pointing at
   them where the VS was, the loser greyed and shrunk — and the amounts that
   changed hands staying put under both faces. */
.kaft-duels {
  display: grid;
  /* Every card the width of the widest: auto tracks sized each column to its
     own longest names, and a bracket filling the screen showed it — Sprocket
     vs Maximilian half as wide again as Ana vs Jo beside it. The column count
     is set by _kaftFitDuels. */
  grid-template-columns: repeat(var(--kaft-cols, 2), minmax(0, 1fr));
  gap: calc(var(--kaft-base) * 0.5) calc(var(--kaft-base) * 0.8);
  justify-content: center;
}

.kaft-duel {
  position: relative;
  display: grid;
  /* minmax(0, …): a long name must ellipsise inside its own half, never push
     into the middle — side-by-side text used to collide over the VS. */
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: calc(var(--kaft-base) * 0.34);
  /* The top padding is the crown's room above the winner's face. */
  padding: calc(var(--kaft-base) * 0.95) calc(var(--kaft-base) * 0.6) calc(var(--kaft-base) * 0.5);
  border-radius: calc(var(--kaft-base) * 0.6);
  /* Opaque, not a white wash: the results screen under the veil (the question
     in big white type) showed straight through a see-through card and ran
     across the middle of every pairing. */
  background: rgb(26, 18, 36);
  border: 1px solid rgba(255, 255, 255, 0.1);
  /* MUST NOT transition width/height: the fit pass measures this box
     synchronously and a size tween feeds it numbers still in flight. */
  transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
  /* `backwards`, never `both`: a both-filled entrance keeps applying its final
     transform for the element's whole life and silently kills every later
     state change on it. */
  animation: kaftDuelLand 0.36s cubic-bezier(0.22, 1.2, 0.36, 1) backwards;
}
@keyframes kaftDuelLand {
  from { opacity: 0; transform: translateY(calc(var(--kaft-base) * 0.5)); }
  to   { opacity: 1; transform: none; }
}

.kaft-duel--settled { background: rgb(32, 23, 44); }

/* The card lights up toward whoever won. */
.kaft-duel--won-left {
  background: linear-gradient(90deg, rgb(28, 62, 45), rgb(32, 23, 44) 60%);
  border-color: rgba(74, 222, 128, 0.5);
}
.kaft-duel--won-right {
  background: linear-gradient(270deg, rgb(28, 62, 45), rgb(32, 23, 44) 60%);
  border-color: rgba(74, 222, 128, 0.5);
}

/* Each player is a stack — crown, face, name, reason, amount — so the two
   face each other across the card with only the VS between them. Side by side,
   the two text columns grew toward the middle and overlapped at any width
   short of a projector. */
.kaft-side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.2);
  min-width: 0;
  text-align: center;
  transition: opacity 0.34s ease, transform 0.34s cubic-bezier(0.22, 1.2, 0.36, 1), filter 0.34s ease;
}

.kaft-face { position: relative; flex-shrink: 0; }
.kaft-side .lb-avatar {
  width: calc(var(--kaft-base) * 1.85);
  height: calc(var(--kaft-base) * 1.85);
  font-size: calc(var(--kaft-base) * 0.85);
  transition: box-shadow 0.3s ease;
}

/* Waiting over every face, shown only on the winner's. */
.kaft-crown {
  position: absolute;
  top: calc(var(--kaft-base) * -0.78);
  left: 50%;
  display: flex;
  color: #ffcf33;
  filter: drop-shadow(0 0 calc(var(--kaft-base) * 0.3) rgba(255, 207, 51, 0.85));
  opacity: 0;
  transform: translateX(-50%) scale(0.2);
  transition: opacity 0.2s ease, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
  pointer-events: none;
}
/* lcIcon's inline size again — see .kaos-seg-icon for why this needs !important. */
.kaft-crown svg { width: calc(var(--kaft-base) * 1.05) !important; height: calc(var(--kaft-base) * 1.05) !important; }

.kaft-side-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
  max-width: calc(var(--kaft-base) * 6.2);
  gap: calc(var(--kaft-base) * 0.06);
}

.kaft-name {
  max-width: 100%;
  font-weight: 700;
  font-size: calc(var(--kaft-base) * 0.8);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* WHY: right and how fast, wrong, or no answer. Shown on the settle, not
   before — up front it would give every result away at once. */
.kaft-res {
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  font-weight: 800;
  font-size: calc(var(--kaft-base) * 0.56);
  opacity: 0;
  transition: opacity 0.25s ease;
}
.kaft-res svg { width: 1.05em !important; height: 1.05em !important; }
.kaft-res--right { color: #4ade80; }
.kaft-res--wrong { color: #ff6b81; }
.kaft-res--none  { color: rgba(255, 255, 255, 0.5); }
.kaft-duel--revealed .kaft-res { opacity: 1; }

/* What changed hands — and it STAYS. It used to fly across and fade out, so a
   settled card said nothing about the stake at all. */
.kaft-amt {
  min-height: 1.15em;
  font-weight: 900;
  font-size: calc(var(--kaft-base) * 0.8);
  font-variant-numeric: tabular-nums;
  opacity: 0;
  transition: opacity 0.3s ease;
}
.kaft-duel--paid .kaft-amt { opacity: 1; }

/* The verdict. */
.kaft-side--won { transform: scale(1.08); }
.kaft-side--won .kaft-crown { opacity: 1; transform: translateX(-50%) scale(1) rotate(-10deg); }
.kaft-side--won .lb-avatar {
  box-shadow: 0 0 0 calc(var(--kaft-base) * 0.12) #4ade80, 0 0 calc(var(--kaft-base) * 0.9) rgba(74, 222, 128, 0.6);
}
.kaft-side--won .kaft-name { color: #4ade80; }
.kaft-side--won .kaft-amt { color: #4ade80; }

/* The loser visibly gives way. Dimming alone reads as "less important";
   shrinking and greying reads as "beaten", which is what happened. */
.kaft-side--lost { opacity: 0.7; transform: scale(0.88); }
/* Grey the FACE, not the whole side — greying the side turned the red −500
   grey too, and the loss is half of what the card has to say. */
.kaft-side--lost .lb-avatar { filter: grayscale(1) brightness(0.75); }
.kaft-side--lost .kaft-name { color: rgba(255, 255, 255, 0.6); }
.kaft-side--lost .kaft-amt { color: #ff6b81; }

.kaft-vs {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(var(--kaft-base) * 0.62);
  font-weight: 800;
  letter-spacing: 0.08em;
  color: var(--text-muted, #a8a4b8);
  opacity: 0.7;
  min-width: calc(var(--kaft-base) * 2.6);
  text-align: center;
}
/* The VS turns into an arrow at the winner: the points go that way. */
.kaft-vs--arrow { color: #4ade80; opacity: 1; }
.kaft-vs--arrow svg {
  width: calc(var(--kaft-base) * 1.3) !important;
  height: calc(var(--kaft-base) * 1.3) !important;
  stroke-width: 2.6;
  filter: drop-shadow(0 0 calc(var(--kaft-base) * 0.3) rgba(74, 222, 128, 0.7));
}

.kaft-duel--stalemate .kaft-vs { color: var(--text-muted, #a8a4b8); opacity: 0.45; }
.kaft-duel--stalemate .kaft-side { opacity: 0.7; }

/* A pairing where nothing moved says so, rather than just sitting there
   looking unresolved. */
.kaft-drawn {
  font-size: calc(var(--kaft-base) * 0.62);
  font-weight: 900;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted, #a8a4b8);
}

.kaft-duel--flash {
  box-shadow: 0 0 0 2px rgba(74, 222, 128, 0.6), 0 0 calc(var(--kaft-base) * 1.4) rgba(74, 222, 128, 0.35);
}

/* ── THE LEVELLING COLLAPSE ───────────────────────────────────────────────
   Every bar falls (or rises) to the same height at once. The single most
   dramatic thing this game can draw, and it was arriving as a silent number. */
.kaft-level {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: calc(var(--kaft-base) * 0.4);
  /* Tall enough for the collapse to be a real fall, short enough to leave the
     title and the closing line room to breathe. At 11 the stage measured 819px
     against a 900px viewport — inside the fit's limit, but only just, and it
     read as a wall of bars rather than a chart falling over. */
  height: calc(var(--kaft-base) * 8.2);
  position: relative;
  padding-top: calc(var(--kaft-base) * 0.6);
}

.kaft-bar-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: calc(var(--kaft-base) * 0.26);
  height: 100%;
}

/* A DEFINITE track for the bars to be a percentage OF.
   `flex: 1 1 0` inside a column of known height gives this box a definite
   height, which is what lets `.kaft-bar { height: N% }` resolve at all — a
   percentage height against an auto-height parent is simply ignored, and the
   bars would all collapse to nothing. */
.kaft-bar-track {
  flex: 1 1 0;
  min-height: 0;
  width: 100%;
  /* THE SCORE LIVES IN HERE, directly on top of its own bar, so it rides the
     bar down during the collapse. Sitting outside the track it was pushed to
     the top of the column instead: eight numbers on one straight line above
     eight bars of different heights, none of them visibly attached to the bar
     they belonged to — and during the collapse they would not have moved. */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: calc(var(--kaft-base) * 0.16);
}

.kaft-bar {
  width: calc(var(--kaft-base) * 2.1);
  border-radius: calc(var(--kaft-base) * 0.22) calc(var(--kaft-base) * 0.22) 0 0;
  background: linear-gradient(180deg, var(--kaft-bar-colour, #6b6d80), rgba(255, 255, 255, 0.12));
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
  /* HEIGHT IS THE WHOLE POINT HERE, so this one does transition it — and it is
     measured before the transition is armed, never during. */
  transition: height 1.15s cubic-bezier(0.62, 0, 0.3, 1);
  position: relative;
}

.kaft-bar-score {
  font-size: calc(var(--kaft-base) * 0.62);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: #fff;
  white-space: nowrap;
}

.kaft-bar-name {
  font-size: calc(var(--kaft-base) * 0.54);
  color: var(--text-muted, #a8a4b8);
  max-width: calc(var(--kaft-base) * 2.8);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The line everyone ends up on. Drawn across the whole set so the collapse has
   something to collapse TO — without it the bars just move. */
/* Positioned from the bottom in px by _kaftPlaceLevelLine, measured off the
   real track — the only way to land it exactly on the height the bars are
   travelling to. */
.kaft-levelline {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.75), transparent);
  opacity: 0;
  transition: opacity 0.5s ease;
  pointer-events: none;
}
.kaft-levelline.is-shown { opacity: 1; }

/* The line carries NO label of its own. It used to print the share at its right
   end, which lands exactly on the rightmost bar's own score — two numbers on
   top of each other, at the one moment the reveal is asking the room to read a
   number. The closing line underneath already says it, once. */

@media (prefers-reduced-motion: reduce) {
  .kaftermath-stage,
  .kaftermath-veil,
  .kaft-side,
  .kaft-duel,
  .kaft-bar { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ── THE TITHE — a transfer, not a collapse ───────────────────────────────
   Deliberately a different shape from the Levelling: two named groups and a pot
   between them, so which mode fired is readable without the title. */
.kaftermath--over-kaos {
  /* The Kaos overlay (900) is still up — this plays over the rules card the
     host just dismissed, on the same layer the room is already looking at. */
  z-index: 920;
}

.ktithe {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.5);
  position: relative;
}

.ktithe-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: calc(var(--kaft-base) * 0.34);
  max-width: 92vw;
}

.ktithe-chip {
  display: flex;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.26);
  padding: calc(var(--kaft-base) * 0.24) calc(var(--kaft-base) * 0.46)
           calc(var(--kaft-base) * 0.24) calc(var(--kaft-base) * 0.24);
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.05);
  /* No width/height transition: the fit pass measures these synchronously. */
  transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.22, 1.2, 0.36, 1),
              border-color 0.3s ease, background 0.3s ease;
}

.ktithe-chip .lb-avatar {
  width: calc(var(--kaft-base) * 1.5);
  height: calc(var(--kaft-base) * 1.5);
  font-size: calc(var(--kaft-base) * 0.7);
  flex-shrink: 0;
}

.ktithe-who {
  font-weight: 700;
  font-size: calc(var(--kaft-base) * 0.68);
  max-width: calc(var(--kaft-base) * 5);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ktithe-amt {
  font-weight: 800;
  font-size: calc(var(--kaft-base) * 0.68);
  font-variant-numeric: tabular-nums;
}

.ktithe-chip--payer { border-color: rgba(214, 69, 69, 0.4); }
.ktithe-chip--payer .ktithe-amt { color: #ff8095; }
.ktithe-chip--taker { border-color: rgba(47, 168, 79, 0.32); }
.ktithe-chip--taker .ktithe-amt { color: var(--correct, #2fa84f); opacity: 0.35; }

/* Paid up: the leader visibly gives way, the same language the duel's loser
   uses — dimming alone reads as "less important", shrinking reads as "taken". */
.ktithe-chip--spent {
  opacity: 0.45;
  transform: scale(0.92);
  border-color: rgba(255, 255, 255, 0.07);
}

/* ...and collected. */
.ktithe-chip--paid {
  background: rgba(47, 168, 79, 0.16);
  border-color: rgba(47, 168, 79, 0.6);
  transform: scale(1.04);
}
.ktithe-chip--paid .ktithe-amt { opacity: 1; }

.ktithe-pot {
  display: flex;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.36);
  padding: calc(var(--kaft-base) * 0.3) calc(var(--kaft-base) * 0.8);
  border-radius: calc(var(--kaft-base) * 0.7);
  border: 2px solid rgba(142, 107, 217, 0.55);
  background: radial-gradient(circle at 50% 30%, rgba(142, 107, 217, 0.3), rgba(142, 107, 217, 0.08));
  box-shadow: 0 0 calc(var(--kaft-base) * 1.4) rgba(142, 107, 217, 0.3);
  transition: transform 0.18s cubic-bezier(0.22, 1.4, 0.36, 1),
              box-shadow 0.18s ease;
}

.ktithe-pot--hit {
  transform: scale(1.07);
  box-shadow: 0 0 calc(var(--kaft-base) * 2.2) rgba(142, 107, 217, 0.6);
}

.ktithe-pot-icon svg {
  /* The wheel's trap again: lcIcon writes an inline size lucide copies onto the
     <svg>, and an inline style outranks any selector. */
  width: calc(var(--kaft-base) * 1.3) !important;
  height: calc(var(--kaft-base) * 1.3) !important;
  color: #c9aaff;
}

.ktithe-pot-num {
  font-weight: 800;
  font-size: calc(var(--kaft-base) * 1.25);
  font-variant-numeric: tabular-nums;
  color: #fff;
  text-shadow: 0 0 calc(var(--kaft-base) * 0.7) rgba(142, 107, 217, 0.7);
}

/* The money itself, crossing the gap. Positioned against .kaftermath-body, which
   is why the flight is measured against that box rather than the viewport. */
.kaftermath-body { position: relative; }

.ktithe-bead {
  position: absolute;
  font-weight: 800;
  font-size: calc(var(--kaft-base) * 0.72);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  white-space: nowrap;
  z-index: 3;
}

.ktithe-bead--out { color: #ff8095; text-shadow: 0 0 calc(var(--kaft-base) * 0.5) rgba(255, 128, 149, 0.6); }
.ktithe-bead--in  { color: var(--correct, #2fa84f); text-shadow: 0 0 calc(var(--kaft-base) * 0.5) rgba(47, 168, 79, 0.6); }

@media (prefers-reduced-motion: reduce) {
  .ktithe-chip, .ktithe-pot { transition-duration: 0.01ms !important; }
}

/* ── BOUNTY — one target, and the hunters converge ────────────────────────
   Radial, because the mode is about a single named player. Unmistakable
   against the Tithe's two rows and the Levelling's chart. */
.kbounty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.7);
}

.kbounty-centre { display: flex; flex-direction: column; align-items: center; gap: calc(var(--kaft-base) * 0.2); }

.kbounty-mark {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.22s cubic-bezier(0.22, 1.4, 0.36, 1), filter 0.3s ease;
}

/* The mark is drawn at twice a hunter's size — the room has to see instantly
   who this is about. */
.kbounty-mark .lb-avatar {
  width: calc(var(--kaft-base) * 4.2);
  height: calc(var(--kaft-base) * 4.2);
  font-size: calc(var(--kaft-base) * 1.9);
}

.kbounty-ring {
  position: absolute;
  inset: calc(var(--kaft-base) * -0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ff5f7a;
  opacity: 0.85;
  animation: kbountySpin 9s linear infinite;
}
.kbounty-ring svg {
  width: calc(var(--kaft-base) * 5.2) !important;
  height: calc(var(--kaft-base) * 5.2) !important;
  stroke-width: 1.1;
}
@keyframes kbountySpin { to { transform: rotate(360deg); } }

.kbounty-mark--hit { transform: scale(0.94); filter: brightness(1.5); }
.kbounty-mark--down .lb-avatar { filter: grayscale(0.4); }
.kbounty-mark--held .kbounty-ring { color: var(--correct, #2fa84f); opacity: 0.5; animation-duration: 3s; }

.kbounty-mark-name {
  font-weight: 800;
  font-size: calc(var(--kaft-base) * 0.95);
  margin-top: calc(var(--kaft-base) * 0.2);
}

.kbounty-mark-amt {
  font-weight: 900;
  font-size: calc(var(--kaft-base) * 1.3);
  font-variant-numeric: tabular-nums;
  min-height: calc(var(--kaft-base) * 1.4);
}
.kbounty-mark-amt--down { color: #ff8095; text-shadow: 0 0 calc(var(--kaft-base) * 0.6) rgba(255, 128, 149, 0.6); }
.kbounty-mark-amt--up   { color: var(--correct, #2fa84f); text-shadow: 0 0 calc(var(--kaft-base) * 0.6) rgba(47, 168, 79, 0.6); }

.kbounty-hunters {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: calc(var(--kaft-base) * 0.34);
  max-width: 92vw;
}

.kbounty-hunter {
  display: flex;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.24);
  padding: calc(var(--kaft-base) * 0.2) calc(var(--kaft-base) * 0.44) calc(var(--kaft-base) * 0.2) calc(var(--kaft-base) * 0.2);
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.05);
  opacity: 0.55;
  transition: opacity 0.26s ease, transform 0.26s cubic-bezier(0.22, 1.3, 0.36, 1),
              border-color 0.26s ease, background 0.26s ease;
}
.kbounty-hunter .lb-avatar {
  width: calc(var(--kaft-base) * 1.4);
  height: calc(var(--kaft-base) * 1.4);
  font-size: calc(var(--kaft-base) * 0.66);
}
.kbounty-hunter--strike {
  opacity: 1;
  transform: translateY(calc(var(--kaft-base) * -0.16)) scale(1.05);
  border-color: rgba(47, 168, 79, 0.55);
  background: rgba(47, 168, 79, 0.14);
}

.kbounty-who { font-weight: 700; font-size: calc(var(--kaft-base) * 0.64); }
.kbounty-amt { font-weight: 800; font-size: calc(var(--kaft-base) * 0.64); color: var(--correct, #2fa84f); font-variant-numeric: tabular-nums; }

/* ── THE UPRISING — a revolt, not a tax ───────────────────────────────────
   The risk is looking like the Tithe: both move points from the top of the
   board to the bottom. There is no pot here and no queue — the mob surges as
   one and hits the leaders directly. */
.kuprising {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--kaft-base) * 1.1);
}

.kuprising-top, .kuprising-mob {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: calc(var(--kaft-base) * 0.34);
  max-width: 92vw;
}

.kuprising-lord, .kuprising-rebel {
  display: flex;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.24);
  padding: calc(var(--kaft-base) * 0.22) calc(var(--kaft-base) * 0.46) calc(var(--kaft-base) * 0.22) calc(var(--kaft-base) * 0.22);
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.05);
  transition: transform 0.3s cubic-bezier(0.22, 1.3, 0.36, 1), opacity 0.3s ease,
              border-color 0.3s ease, background 0.3s ease;
}

.kuprising-lord .lb-avatar {
  width: calc(var(--kaft-base) * 2.1);
  height: calc(var(--kaft-base) * 2.1);
  font-size: calc(var(--kaft-base) * 0.95);
}
.kuprising-rebel .lb-avatar {
  width: calc(var(--kaft-base) * 1.4);
  height: calc(var(--kaft-base) * 1.4);
  font-size: calc(var(--kaft-base) * 0.66);
}

.kuprising-who { font-weight: 700; font-size: calc(var(--kaft-base) * 0.7); }
.kuprising-amt { font-weight: 800; font-size: calc(var(--kaft-base) * 0.7); font-variant-numeric: tabular-nums; }
.kuprising-amt--loss { color: rgba(255, 255, 255, 0.5); }
.kuprising-amt--gain { color: var(--correct, #2fa84f); opacity: 0.4; }

/* The rebels rise. */
.kuprising-rebel--surge {
  transform: translateY(calc(var(--kaft-base) * -0.5)) scale(1.06);
  border-color: rgba(47, 168, 79, 0.6);
  background: rgba(47, 168, 79, 0.16);
}
.kuprising-rebel--surge .kuprising-amt--gain { opacity: 1; }

/* ...and the leaders take it. */
.kuprising-lord--struck {
  transform: scale(0.9);
  opacity: 0.5;
  border-color: rgba(214, 69, 69, 0.5);
  background: rgba(214, 69, 69, 0.1);
}
.kuprising-lord--struck .kuprising-amt--loss { color: #ff8095; }

/* ...or held: right, and faster than every rebel who was right. Nobody could
   touch them, and that is the counterplay the mode exists to allow. */
.kuprising-lord--held {
  transform: scale(1.04);
  border-color: rgba(74, 222, 128, 0.6);
  background: rgba(74, 222, 128, 0.12);
  box-shadow: 0 0 calc(var(--kaft-base) * 0.8) rgba(74, 222, 128, 0.3);
}
.kuprising-lord--held .kuprising-amt--loss {
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  color: #4ade80;
  letter-spacing: 0.08em;
}
.kuprising-lord--held .kuprising-amt--loss svg { width: 1em !important; height: 1em !important; }

@media (prefers-reduced-motion: reduce) {
  .kbounty-ring { animation: none !important; }
  .kbounty-mark, .kbounty-hunter, .kuprising-lord, .kuprising-rebel { transition-duration: 0.01ms !important; }
}

/* The Kaos tax on the phone: your own rate, for a mechanic that otherwise ran
   on every Kaos round without ever admitting it existed. */
.kaos-rate-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding: 4px 11px;
  border-radius: var(--radius-full);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  border: 1px solid currentColor;
}
.kaos-rate-chip--down { color: #ff8095; background: rgba(214, 69, 69, 0.14); }
.kaos-rate-chip--up   { color: var(--correct, #2fa84f); background: rgba(47, 168, 79, 0.14); }
.kaos-rate-chip svg { width: 1em !important; height: 1em !important; }

/* The aftermath waits for the host rather than fading itself out. */
.kaftermath-btn {
  /* The overlay is pointer-events: none so the screen underneath stays live —
     the button has to opt back in, exactly as the Kaos verdict's does. */
  pointer-events: auto;
  margin-top: calc(var(--kaft-base) * 0.2);
  font-size: calc(var(--kaft-base) * 0.72);
  padding: calc(var(--kaft-base) * 0.42) calc(var(--kaft-base) * 1.2);
  /* Not .btn's `transition: all`: the fit passes stand this button in while
     they change --kaft-base, and a size still tweening from the last base is
     the size they would measure. */
  transition: transform 0.25s ease, box-shadow 0.25s ease, background 0.25s ease, opacity 0.25s ease;
  animation: kaftBtnIn 0.42s cubic-bezier(0.2, 1.4, 0.4, 1) backwards;
}

@keyframes kaftBtnIn {
  from { opacity: 0; transform: translateY(calc(var(--kaft-base) * 0.5)); }
  to   { opacity: 1; transform: none; }
}

/* While the reveal is up, the veil swallows clicks so a stray one cannot skip
   it by hitting the advance arrow underneath. Space is handled separately —
   see _spaceDismissesAftermath, which dismisses rather than advancing, so an
   impatient host can never accidentally jump a whole question. */
.kaftermath--lit .kaftermath-veil { pointer-events: auto; }

/* WHAT KAOS TOOK, on its own badge beside the round's points.
   The +points badge is the NET of the question and the Kaos hit together, so a
   leader who earned 849 and lost 1,200 to a bounty read as "−351" — and one who
   earned more than they lost read as a green gain with the theft nowhere on the
   board. Stacked inside .leaderboard-gain-slot, which is a fixed 104px, so the
   name beside it still cannot move. */
.leaderboard-gain-slot {
  flex-direction: column;
  gap: 3px;
}

/* DRESSED AS KAOS, NOT AS POINTS. It used to be a second green (or pink) pill
   under the first, a little smaller, with an 11px icon — so the pair read as
   the same thing said twice. Every Kaos badge now wears the Kaos brand (dark,
   magenta-edged, the face's own icon in Kaos pink) whatever the face, and only
   the NUMBER carries the gain/loss colour. Not each face's own colour: The
   Uprising's green would collide with the points badge. Same type size as the
   points badge, so neither reads as a footnote to the other. */
.kaos-delta-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px 3px 7px;
  border-radius: var(--radius-full);
  background: rgba(18, 5, 14, 0.92);
  border: 1.5px solid #dc4091;
  box-shadow: 0 0 12px rgba(220, 64, 145, 0.45);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 0.88rem;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.kaos-delta-icon { display: inline-flex; color: #ff6bd6; }
/* lcIcon's inline size again — see .kaos-seg-icon for why this needs !important. */
.kaos-delta-icon svg { width: 1.05em !important; height: 1.05em !important; }

.kaos-delta-badge--loss .kaos-delta-num { color: #ff5a78; }
.kaos-delta-badge--gain .kaos-delta-num { color: #4ade80; }

/* The second step of the count-up: this is the moment Kaos's amount lands on
   the score, so the badge that says so flares as it does. */
.kaos-delta-badge--hit { animation: kaosDeltaHit 0.55s cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes kaosDeltaHit {
  0%   { transform: scale(1); box-shadow: 0 0 12px rgba(220, 64, 145, 0.45); }
  40%  { transform: scale(1.22); box-shadow: 0 0 26px rgba(255, 107, 214, 0.95); }
  100% { transform: scale(1); box-shadow: 0 0 12px rgba(220, 64, 145, 0.45); }
}
@media (prefers-reduced-motion: reduce) {
  .kaos-delta-badge--hit { animation: none; }
}

/* The phone's own version: what Kaos did to YOU this round. */
.personal-kaos-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  padding: 7px 16px 7px 12px;
  border-radius: var(--radius-full);
  background: rgba(18, 5, 14, 0.92);
  border: 1.5px solid #dc4091;
  box-shadow: 0 0 16px rgba(220, 64, 145, 0.4);
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 0.95rem;
  color: #fff;
  animation: kaosIncomingIn 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) 0.6s backwards;
}
.personal-kaos-chip svg { width: 18px !important; height: 18px !important; color: #ff6bd6; }
.personal-kaos-name { letter-spacing: 0.06em; text-transform: uppercase; font-size: 0.82rem; opacity: 0.85; }
.personal-kaos-amt { color: #4ade80; font-variant-numeric: tabular-nums; }
.personal-kaos-chip--loss .personal-kaos-amt { color: #ff5a78; }

/* ── Phone: the big screen is reconnecting ──────────────────────────────────
   A pill across the top rather than a full-screen block: the game is on hold,
   not over, and whatever the phone was showing stays readable underneath. */
.host-reconnecting {
  position: fixed;
  top: max(12px, env(safe-area-inset-top));
  left: 16px;
  right: 16px;
  z-index: 250;
  display: flex;
  justify-content: center;
  pointer-events: none;
}
.host-reconnecting__card {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 12px;
  align-items: center;
  max-width: 420px;
  width: 100%;
  padding: 12px 16px;
  border-radius: 16px;
  background: rgba(15, 14, 23, 0.94);
  border: 1px solid var(--bg-glass-border, rgba(255, 255, 255, 0.12));
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
  animation: fadeInDown 0.35s ease backwards;
}
.host-reconnecting__icon {
  grid-row: span 2;
  width: 44px;
  height: 44px;
  animation: codePulse 1.4s ease-in-out infinite;
}
.host-reconnecting__title { font-weight: 800; }
.host-reconnecting__sub { font-size: 0.85rem; color: var(--text-muted); }

/* ── Big screen: a Video Gap clip played from YouTube ───────────────────────
   Sized like the <video> the file path uses (65vh tall at most), 16:9. The
   iframe takes no clicks: YouTube's own overlay would otherwise appear on
   hover and could pause the clip out from under the game. */
.videogap-yt-frame {
  width: min(100%, calc(65vh * 16 / 9));
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 2px solid var(--bg-glass-border);
  box-shadow: var(--shadow-glow);
  background: #000;
}
.videogap-yt-frame iframe,
.videogap-yt-frame > div {
  width: 100%;
  height: 100%;
  border: 0;
  pointer-events: none;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE TRIBUNAL — the phone ballot, the big screen's lineup, and the trial
   ---------------------------------------------------------------------------
   Deliberately NOT Most Likely To's look. That round is a celebration: bright
   tiles, a crown, confetti. This is an accusation — dark, crimson, and every
   player drawn as a shard of pottery (an ostrakon, what the Athenians scratched
   a name on to exile someone). A vote scratches the name; a stamp says it.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The three shard outlines. Neighbouring cards differ, so the grid reads as a
   pile of broken pottery rather than a sheet of identical tiles. */
.trib-shard--v0::before, .ktrial-shard--v0 > div {
  clip-path: polygon(3% 12%, 30% 2%, 62% 7%, 97% 0%, 100% 44%, 95% 88%, 64% 100%, 30% 94%, 0% 100%, 4% 55%);
}
.trib-shard--v1::before, .ktrial-shard--v1 > div {
  clip-path: polygon(0% 6%, 41% 0%, 72% 5%, 100% 14%, 96% 58%, 100% 96%, 58% 92%, 22% 100%, 2% 86%, 5% 40%);
}
.trib-shard--v2::before, .ktrial-shard--v2 > div {
  clip-path: polygon(6% 0%, 50% 5%, 94% 0%, 100% 36%, 97% 78%, 82% 100%, 40% 95%, 4% 100%, 0% 62%, 3% 24%);
}

/* ── The phone: a ballot ──────────────────────────────────────────────────── */
.tribunal-screen {
  justify-content: flex-start !important;
  gap: 14px;
  padding-top: max(22px, env(safe-area-inset-top)) !important;
  background:
    radial-gradient(120% 70% at 50% 0%, rgba(194, 24, 91, 0.32), transparent 62%),
    radial-gradient(90% 50% at 50% 110%, rgba(110, 6, 38, 0.4), transparent 60%),
    #0b0308;
}

.tribunal-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

.tribunal-gavel {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: linear-gradient(150deg, #ff5c9a, #8e0e3c);
  color: #fff;
  box-shadow: 0 0 28px rgba(194, 24, 91, 0.65);
}
.tribunal-gavel svg {
  width: 30px !important;
  height: 30px !important;
  transform-origin: 70% 70%;
  animation: tribGavelKnock 2.4s ease-in-out infinite;
}
@keyframes tribGavelKnock {
  0%, 62%, 100% { transform: rotate(0deg); }
  70%           { transform: rotate(-32deg); }
  78%           { transform: rotate(6deg); }
  84%           { transform: rotate(0deg); }
}

.tribunal-title {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 1.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #ff5c9a;
  text-shadow: 0 0 22px rgba(255, 92, 154, 0.6);
}

.tribunal-sub {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.95rem;
  color: rgba(255, 255, 255, 0.74);
}

.tribunal-clock {
  width: 100%;
  max-width: 460px;
  height: 6px;
  flex-shrink: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  overflow: hidden;
}
.tribunal-clock-fill {
  height: 100%;
  width: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #ff5c9a, #c2185b);
  box-shadow: 0 0 12px rgba(255, 92, 154, 0.7);
}

.tribunal-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px 10px;
  width: 100%;
  max-width: 460px;
}
@media (max-width: 330px) {
  .tribunal-grid { grid-template-columns: 1fr; }
}

.trib-shard {
  position: relative;
  isolation: isolate;             /* keeps the ::before shard behind the content */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 16px 10px 14px;
  border: none;
  background: transparent;
  color: #fff;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* The glow follows the SHARD's outline, not the button's box: a filter on the
     parent draws around whatever the clipped ::before actually painted. */
  filter: drop-shadow(0 0 1px rgba(255, 120, 165, 0.55)) drop-shadow(0 6px 14px rgba(0, 0, 0, 0.5));
  transition: transform 0.16s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.25s ease, filter 0.25s ease;
  /* `backwards`, not `both`: see .mlt-vote-btn — a forwards fill would pin the
     transform and kill :active and .is-accused. */
  animation: tribShardIn 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--trib-i, 0) * 0.045s) backwards;
}
.trib-shard::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    repeating-linear-gradient(115deg, rgba(255, 255, 255, 0.03) 0 2px, transparent 2px 7px),
    linear-gradient(160deg, #3d1124 0%, #1d060f 100%);
  transition: background 0.2s ease;
}
@keyframes tribShardIn {
  from { opacity: 0; transform: translateY(14px) rotate(-3deg) scale(0.9); }
  to   { opacity: 1; transform: none; }
}
.trib-shard:active { transform: scale(0.95); }

.trib-face {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: var(--mlt-color, #6b6d80);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.16);
  transition: box-shadow 0.2s ease;
}
.trib-face img { width: 100%; height: 100%; object-fit: cover; display: block; }

.trib-name {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 1rem;
  overflow-wrap: anywhere;
}
.trib-meta {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.55);
}

/* The name, scratched on. Sits over the face, tilted, like ink on clay. */
.trib-stamp {
  position: absolute;
  top: 42%;
  left: 50%;
  padding: 2px 10px;
  border: 3px solid #ff3b7f;
  border-radius: 6px;
  background: rgba(20, 2, 8, 0.66);
  color: #ff3b7f;
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 0.95rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -50%) rotate(-14deg) scale(2.2);
  transition: opacity 0.12s ease, transform 0.26s cubic-bezier(0.2, 1.6, 0.4, 1);
}

.trib-shard.is-accused {
  transform: scale(1.05);
  filter: drop-shadow(0 0 2px rgba(255, 90, 150, 0.9)) drop-shadow(0 0 16px rgba(255, 59, 127, 0.6));
}
.trib-shard.is-accused::before {
  background:
    repeating-linear-gradient(115deg, rgba(255, 255, 255, 0.05) 0 2px, transparent 2px 7px),
    linear-gradient(160deg, #d62f74 0%, #6f0a30 100%);
}
.trib-shard.is-accused .trib-face { box-shadow: 0 0 0 3px #fff, 0 0 18px rgba(255, 59, 127, 0.8); }
.trib-shard.is-accused .trib-meta { color: rgba(255, 255, 255, 0.82); }
.trib-shard.is-accused .trib-stamp {
  opacity: 1;
  transform: translate(-50%, -50%) rotate(-14deg) scale(1);
  color: #fff;
  border-color: #fff;
  background: rgba(60, 2, 22, 0.72);
}
/* Sent, not yet confirmed by the server. */
.trib-shard.is-pending .trib-stamp { animation: tribPending 0.8s ease-in-out infinite; }
@keyframes tribPending { 50% { opacity: 0.45; } }

.trib-shard.is-other { opacity: 0.62; }
.trib-shard:disabled { cursor: default; }
.trib-shard:disabled:not(.is-accused) { opacity: 0.32; }

.tribunal-foot {
  margin: 2px 0 0;
  max-width: 360px;
  font-size: 0.86rem;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.64);
}

/* Your part in it, once the big screen has named the accused. */
.tribunal-verdict {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
  max-width: 400px;
  margin-top: 10px;
  padding: 26px 22px;
  border-radius: 22px;
  background: rgba(30, 6, 16, 0.86);
  border: 1px solid rgba(255, 92, 154, 0.35);
  box-shadow: 0 0 40px rgba(194, 24, 91, 0.3);
  animation: tribVerdictIn 0.5s cubic-bezier(0.2, 1.4, 0.4, 1) backwards;
}
@keyframes tribVerdictIn {
  from { opacity: 0; transform: translateY(20px) scale(0.92); }
  to   { opacity: 1; transform: none; }
}
/* Once the verdict is in, the header and the card sit together in the middle
   of the phone, rather than the card floating under an empty ballot's space. */
.tribunal-screen--verdict { justify-content: safe center !important; }
.tribunal-screen--verdict .tribunal-sub { display: none; }

.tribunal-verdict-face {
  position: relative;
  width: 108px;
  height: 108px;
  border-radius: 50%;
  background: var(--mlt-color, #6b6d80);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 2.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  box-shadow: 0 0 0 3px rgba(255, 92, 154, 0.7), 0 0 30px rgba(194, 24, 91, 0.55);
}
.tribunal-verdict-face img { width: 100%; height: 100%; object-fit: cover; display: block; }

.tribunal-verdict-title {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: 1.55rem;
  line-height: 1.15;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #ff5c9a;
  text-shadow: 0 0 18px rgba(255, 92, 154, 0.5);
  overflow-wrap: anywhere;
}
.tribunal-verdict-sub {
  font-size: 0.95rem;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.78);
}
.tribunal-verdict-stake {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 0.95rem;
  line-height: 1.3;
  padding: 9px 14px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.14);
}

/* The accused's own phone: the room is looking at them, and it should feel it. */
.tribunal-screen--accused .tribunal-verdict {
  border-color: rgba(255, 59, 127, 0.8);
  animation: tribVerdictIn 0.5s cubic-bezier(0.2, 1.4, 0.4, 1) backwards,
             tribShake 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97) 0.45s,
             tribAccusedGlow 1.3s ease-in-out 1s infinite;
}
.tribunal-screen--accused .tribunal-verdict-title { color: #fff; font-size: 1.9rem; }
.tribunal-screen--accused .tribunal-verdict-stake {
  background: rgba(255, 59, 127, 0.16);
  border-color: rgba(255, 59, 127, 0.55);
}
@keyframes tribShake {
  15%, 55% { transform: translateX(-7px) rotate(-1deg); }
  35%, 75% { transform: translateX(7px) rotate(1deg); }
}
@keyframes tribAccusedGlow {
  50% { box-shadow: 0 0 70px rgba(255, 59, 127, 0.6); }
}

/* The trial question itself, on the accused's phone. */
#answerScreen.on-trial {
  box-shadow: inset 0 0 0 3px rgba(255, 59, 127, 0.55), inset 0 0 80px rgba(194, 24, 91, 0.45);
  animation: onTrialPulse 1.5s ease-in-out infinite;
}
@keyframes onTrialPulse {
  50% { box-shadow: inset 0 0 0 3px rgba(255, 59, 127, 0.8), inset 0 0 110px rgba(194, 24, 91, 0.6); }
}

/* ── The big screen: the ballot box ───────────────────────────────────────
   Where the die would roll. A count and a clock, and a shard dropping into the
   urn for every vote — with no name on it. */
.kaos-ballot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(16px, 2.4vw, 32px);
  margin: 2px auto 16px;
  padding: 14px 28px;
  max-width: 720px;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
  animation: kaosVerdictIn 0.45s cubic-bezier(0.2, 1.4, 0.4, 1) backwards;
}

.kaos-ballot-urn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: clamp(66px, 7vw, 100px);
  height: clamp(66px, 7vw, 100px);
  color: var(--kaos-colour, #c2185b);
  filter: drop-shadow(0 0 16px var(--kaos-glow, rgba(194, 24, 91, 0.6)));
}
/* lcIcon's inline size again — see .kaos-seg-icon for why this needs !important. */
.kaos-ballot-urn svg { width: 100% !important; height: 100% !important; stroke-width: 1.6; }
.kaos-ballot-urn--hit svg { animation: kaosUrnHit 0.3s ease-out; }
@keyframes kaosUrnHit {
  40% { transform: translateY(3px) scale(0.96); }
}

.kaos-ballot-shard {
  position: absolute;
  top: 8%;
  left: calc(50% + var(--shard-x, 0%));
  width: 18px;
  height: 13px;
  background: #ff5c9a;
  box-shadow: 0 0 10px rgba(255, 92, 154, 0.8);
  clip-path: polygon(0 20%, 60% 0, 100% 40%, 80% 100%, 10% 85%);
  animation: kaosShardDrop 0.62s cubic-bezier(0.5, 0, 0.85, 0.45) forwards;
  pointer-events: none;
}
@keyframes kaosShardDrop {
  0%   { transform: translate(-50%, -70px) rotate(var(--shard-r, 0deg)); opacity: 0; }
  20%  { opacity: 1; }
  100% { transform: translate(-50%, 30px) rotate(calc(var(--shard-r, 0deg) * 2)); opacity: 0; }
}

.kaos-ballot-info { text-align: left; }
.kaos-ballot-label {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(0.8rem, 1.4vw, 1.1rem);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.7);
}
.kaos-ballot-count {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(1.8rem, 3.6vw, 3.1rem);
  line-height: 1.05;
  color: #fff;
  font-variant-numeric: tabular-nums;
}
.kaos-ballot-of {
  font-size: 0.42em;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.6);
}
.kaos-ballot-clock {
  width: clamp(200px, 26vw, 380px);
  height: 8px;
  margin-top: 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  overflow: hidden;
}
.kaos-ballot-clock-fill {
  height: 100%;
  width: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #ff5c9a, var(--kaos-colour, #c2185b));
  box-shadow: 0 0 12px var(--kaos-glow, rgba(194, 24, 91, 0.6));
}

/* ── The big screen: the lineup ───────────────────────────────────────────
   The faces stand in a row in the dark; the counts land as shards piling
   under each one; the spotlight stops on the accused. */
.kaos-verdict--lineup { max-width: min(1280px, 94vw); }

.kaos-lineup {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin: 6px 0 16px;
}

.kaos-lineup-row {
  --klineup-size: clamp(60px, calc(min(1180px, 90vw) / (var(--klineup-n, 6) + 0.8) - 22px), 128px);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: clamp(12px, 1.6vw, 24px) clamp(10px, 1.4vw, 22px);
  /* Headroom for the accused, who grows 1.3x when the spotlight lands and
     would otherwise climb over the line above the row. */
  padding: calc(var(--klineup-size) * 0.18 + 10px) 10px 6px;
}

.klineup-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: var(--klineup-size);
  opacity: 0.5;
  filter: grayscale(0.7) brightness(0.8);
  transition: opacity 0.3s ease, filter 0.3s ease, transform 0.4s cubic-bezier(0.2, 1.4, 0.4, 1);
  animation: klineupIn 0.42s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--klineup-i, 0) * 0.05s) backwards;
}
@keyframes klineupIn {
  from { opacity: 0; transform: translateY(18px) scale(0.85); }
}

.klineup-card .lb-avatar {
  width: var(--klineup-size);
  height: var(--klineup-size);
  font-size: calc(var(--klineup-size) * 0.42);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.55), 0 0 0 3px rgba(255, 255, 255, 0.12);
  transition: box-shadow 0.2s ease;
}

.klineup-name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(0.75rem, calc(var(--klineup-size) * 0.16), 1.15rem);
}

.klineup-pile {
  display: flex;
  justify-content: center;
  gap: 3px;
  min-height: 12px;
}
.klineup-shard {
  width: 13px;
  height: 11px;
  background: #ff5c9a;
  box-shadow: 0 0 8px rgba(255, 92, 154, 0.7);
  clip-path: polygon(0 20%, 60% 0, 100% 40%, 80% 100%, 10% 85%);
  animation: klineupShard 0.32s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--s, 0) * 0.06s) backwards;
}
@keyframes klineupShard {
  from { opacity: 0; transform: translateY(-16px) rotate(-40deg); }
}

.klineup-count {
  min-height: 1.1em;
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(1rem, calc(var(--klineup-size) * 0.3), 2.2rem);
  line-height: 1;
  color: #fff;
  font-variant-numeric: tabular-nums;
}

/* Counted: the more votes, the more it comes out of the dark. */
.klineup-card--counted {
  opacity: calc(0.62 + 0.38 * var(--klineup-heat, 0));
  filter: grayscale(calc(0.6 - 0.6 * var(--klineup-heat, 0))) brightness(calc(0.85 + 0.15 * var(--klineup-heat, 0)));
}

/* The roulette's spotlight, flickering between the tied. */
.klineup-card--lit {
  opacity: 1;
  filter: none;
  transform: scale(1.1);
}
.klineup-card--lit .lb-avatar { box-shadow: 0 0 0 4px #fff, 0 0 44px rgba(255, 255, 255, 0.75); }

.klineup-card--accused {
  z-index: 2;
  opacity: 1;
  filter: none;
  transform: scale(1.3);
}
.klineup-card--accused .lb-avatar {
  box-shadow: 0 0 0 5px var(--kaos-colour, #c2185b), 0 0 60px var(--kaos-glow, rgba(194, 24, 91, 0.7));
  animation: klineupAccused 1.3s ease-in-out 0.4s infinite;
}
@keyframes klineupAccused {
  50% { box-shadow: 0 0 0 7px #ff5c9a, 0 0 90px rgba(255, 92, 154, 0.85); }
}
.klineup-card--spared {
  opacity: 0.2;
  filter: grayscale(1) brightness(0.6);
  transform: scale(0.9);
}

.klineup-stamp {
  position: absolute;
  top: calc(var(--klineup-size) * 0.5);
  left: 50%;
  padding: 2px 12px;
  border: 4px solid #ff3b7f;
  border-radius: 8px;
  background: rgba(15, 2, 8, 0.72);
  color: #ff3b7f;
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(0.8rem, calc(var(--klineup-size) * 0.2), 1.5rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  transform: translate(-50%, -50%) rotate(-12deg);
  animation: klineupStamp 0.42s cubic-bezier(0.2, 1.6, 0.4, 1) backwards;
}
@keyframes klineupStamp {
  from { opacity: 0; transform: translate(-50%, -50%) rotate(-12deg) scale(3); }
}

.kaos-lineup-caption {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(1.4rem, 3vw, 2.6rem);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #fff;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.3s ease, transform 0.3s ease;
}
.kaos-lineup-caption.is-shown { opacity: 1; transform: none; }
.kaos-lineup-caption--verdict {
  color: var(--kaos-colour, #c2185b);
  text-shadow: 0 0 26px var(--kaos-glow, rgba(194, 24, 91, 0.6));
}
.kaos-lineup-sub {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(0.9rem, 1.6vw, 1.25rem);
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.72);
  opacity: 0;
  transition: opacity 0.3s ease 0.15s;
}
.kaos-lineup-sub.is-shown { opacity: 1; }

/* Who a face is aimed at (on trial, or the bounty's mark), riding the round chip and the reading card. */
.kaos-target-tag {
  margin-left: 4px;
  padding: 2px 10px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.3);
  font-weight: 900;
  letter-spacing: 0.08em;
}

/* ── The aftermath: the trial ─────────────────────────────────────────────
   One defendant in the dock, a verdict stamped across them, and a jury box of
   face-down shards — turned over only if the accused walks free. */
.ktrial {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.8);
}
.ktrial-dock { display: flex; flex-direction: column; align-items: center; gap: calc(var(--kaft-base) * 0.2); }

.ktrial-face {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.6s cubic-bezier(0.4, 0, 0.3, 1.4), filter 0.6s ease;
}
.ktrial-face .lb-avatar {
  width: calc(var(--kaft-base) * 4.6);
  height: calc(var(--kaft-base) * 4.6);
  font-size: calc(var(--kaft-base) * 2);
  box-shadow: 0 0 0 calc(var(--kaft-base) * 0.2) var(--kaft-colour, #c2185b),
              0 0 calc(var(--kaft-base) * 2) var(--kaft-glow, rgba(194, 24, 91, 0.55));
}

.ktrial-crack {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  fill: none;
  stroke: #fff;
  stroke-width: 2.4;
  stroke-linejoin: round;
  stroke-dasharray: 140;
  stroke-dashoffset: 140;
  filter: drop-shadow(0 0 4px #fff);
}
/* Exiled: cracked through, and cast down. */
.ktrial-face--exiled {
  transform: translateY(calc(var(--kaft-base) * 0.5)) rotate(-9deg) scale(0.88);
  filter: grayscale(1) brightness(0.55);
}
.ktrial-face--exiled .ktrial-crack {
  opacity: 1;
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.45s ease-out;
}

.ktrial-stamp {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  padding: calc(var(--kaft-base) * 0.12) calc(var(--kaft-base) * 0.5);
  border: calc(var(--kaft-base) * 0.17) solid currentColor;
  border-radius: calc(var(--kaft-base) * 0.3);
  background: rgba(12, 3, 8, 0.74);
  font-weight: 900;
  font-size: calc(var(--kaft-base) * 1.05);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0;
  transform: translate(-50%, -50%) rotate(-14deg) scale(2.6);
}
.ktrial-stamp.is-in {
  opacity: 1;
  transform: translate(-50%, -50%) rotate(-14deg) scale(1);
  animation: ktrialStamp 0.45s cubic-bezier(0.2, 1.6, 0.4, 1) backwards;
}
@keyframes ktrialStamp {
  from { opacity: 0; transform: translate(-50%, -50%) rotate(-14deg) scale(2.6); }
}
.ktrial--exiled .ktrial-stamp   { color: #ff3b7f; }
.ktrial--acquitted .ktrial-stamp { color: #3ddc84; }
.ktrial--void .ktrial-stamp      { color: #b8b3c9; }

.ktrial-name {
  margin-top: calc(var(--kaft-base) * 0.45);
  font-weight: 800;
  font-size: calc(var(--kaft-base) * 1.05);
}
.ktrial-amt {
  min-height: calc(var(--kaft-base) * 1.5);
  font-weight: 900;
  font-size: calc(var(--kaft-base) * 1.35);
  font-variant-numeric: tabular-nums;
}
.ktrial-amt--down { color: #ff8095; text-shadow: 0 0 calc(var(--kaft-base) * 0.6) rgba(255, 128, 149, 0.6); }
.ktrial-amt--up   { color: var(--correct, #2fa84f); text-shadow: 0 0 calc(var(--kaft-base) * 0.6) rgba(47, 168, 79, 0.6); }

.ktrial-jury-label {
  font-weight: 800;
  font-size: calc(var(--kaft-base) * 0.62);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.ktrial-jury {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: calc(var(--kaft-base) * 0.36);
  max-width: 92vw;
  perspective: 900px;
}

.ktrial-shard {
  position: relative;
  width: calc(var(--kaft-base) * 3.4);
  height: calc(var(--kaft-base) * 3.7);
  transform-style: preserve-3d;
  transition: transform 0.55s cubic-bezier(0.3, 1.3, 0.5, 1);
  animation: ktrialShardIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) calc(var(--ktrial-i, 0) * 0.04s) backwards;
}
@keyframes ktrialShardIn {
  from { opacity: 0; transform: translateY(calc(var(--kaft-base) * 0.8)) rotate(-8deg); }
}
.ktrial-shard--turned { transform: rotateY(180deg); }

.ktrial-shard-back, .ktrial-shard-front {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(var(--kaft-base) * 0.12);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.ktrial-shard-back {
  background:
    repeating-linear-gradient(115deg, rgba(255, 255, 255, 0.04) 0 2px, transparent 2px 7px),
    linear-gradient(160deg, #4a1628, #1c0710);
  color: rgba(255, 92, 154, 0.6);
  font-weight: 900;
  font-size: calc(var(--kaft-base) * 1.5);
}
.ktrial-shard-front {
  transform: rotateY(180deg);
  padding: calc(var(--kaft-base) * 0.2);
  background: linear-gradient(160deg, #43202f, #1a0a12);
}
.ktrial-shard-front .lb-avatar {
  width: calc(var(--kaft-base) * 1.5);
  height: calc(var(--kaft-base) * 1.5);
  font-size: calc(var(--kaft-base) * 0.7);
}
.ktrial-who {
  max-width: 92%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 700;
  font-size: calc(var(--kaft-base) * 0.5);
}
.ktrial-pay {
  font-weight: 900;
  font-size: calc(var(--kaft-base) * 0.55);
  color: #ff8095;
  font-variant-numeric: tabular-nums;
}
.ktrial-more {
  font-weight: 900;
  font-size: calc(var(--kaft-base) * 0.8);
  color: rgba(255, 255, 255, 0.6);
}

@media (prefers-reduced-motion: reduce) {
  .tribunal-gavel svg, .trib-shard, .trib-shard.is-pending .trib-stamp,
  .tribunal-screen--accused .tribunal-verdict, #answerScreen.on-trial,
  .klineup-card, .klineup-card--accused .lb-avatar, .klineup-shard, .klineup-stamp,
  .kaos-ballot-shard, .ktrial-shard, .ktrial-stamp.is-in { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   KAOS BLACKOUT — results with the lights out
   ---------------------------------------------------------------------------
   How the room voted, and nothing that says who was right. The server strips
   the answer from the payload (GameEngine._blackoutResults); these only draw
   what is left, in the Blackout's own indigo.
   ═══════════════════════════════════════════════════════════════════════════ */
.blackout-results-banner {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  align-self: center;
  margin: 0 auto 14px;
  padding: 8px 18px;
  border-radius: var(--radius-full);
  background: rgba(20, 14, 44, 0.85);
  border: 1.5px solid rgba(107, 91, 214, 0.7);
  box-shadow: 0 0 22px rgba(107, 91, 214, 0.35);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(0.85rem, 1.4vw, 1.1rem);
  color: rgba(255, 255, 255, 0.86);
  animation: fadeIn 0.4s ease;
}
.blackout-results-banner strong { color: #b3abff; letter-spacing: 0.08em; text-transform: uppercase; }
.blackout-results-banner svg { width: 1.2em !important; height: 1.2em !important; color: #b3abff; flex-shrink: 0; }

/* What the room typed: every answer, right and wrong together, unlabelled. */
.blackout-typed-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(0.85rem, 1.4vw, 1.1rem);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #b3abff;
}
.blackout-typed-tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  max-width: 1100px;
}
.blackout-typed-tag {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 10px 18px;
  border-radius: 16px;
  background: rgba(20, 14, 44, 0.85);
  border: 1.5px solid rgba(107, 91, 214, 0.55);
  animation: fadeInUp 0.4s ease var(--delay, 0s) backwards;
}
.blackout-typed-text {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(1.1rem, 2vw, 1.6rem);
  color: #fff;
  overflow-wrap: anywhere;
}
.blackout-typed-count {
  font-family: var(--font-heading);
  font-weight: 900;
  font-size: clamp(0.85rem, 1.3vw, 1.05rem);
  color: #b3abff;
}
.blackout-typed-who {
  font-size: clamp(0.72rem, 1.1vw, 0.9rem);
  color: rgba(255, 255, 255, 0.55);
}
.blackout-typed-empty { color: var(--text-muted, #a8a4b8); }

/* The room's order: ranked by where the room put each item, not by the answer. */
.blackout-order-rank {
  background: rgba(107, 91, 214, 0.85) !important;
  color: #fff !important;
}
.blackout-order-avg {
  margin-left: auto;
  padding-left: 12px;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.85rem;
  color: rgba(179, 171, 255, 0.8);
  white-space: nowrap;
}
