/* Roboto, a part of the game (from Google Fonts), so it is the same without the internet too: the weights
   400 / 500 / 700, each split by alphabet; a browser loads only the files it needs */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/roboto-400-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/roboto-400-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/roboto-400-latin-ext.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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/roboto-400-latin.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;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/roboto-500-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/roboto-500-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/roboto-500-latin-ext.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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/roboto-500-latin.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;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('assets/fonts/roboto-700-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('assets/fonts/roboto-700-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('assets/fonts/roboto-700-latin-ext.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-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('assets/fonts/roboto-700-latin.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;
}

/* All sizes are design units = pixels of the reference screenshots (1582 px wide).
   #stage is scaled to the viewport by JS (see layout() in app.js). */
:root {
  --yellow: #ffe650;
  --yellow-text: #c0660f;
  --red: #cc2e14;
  --blue: #3e73a0;
  --cloth: #93aec8;
  --white-soft: rgba(255, 255, 255, 0.45);
  --font: 'Roboto', system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
  /* the default gap between buttons / fields, one under another or side by side (as in the context
     menu): 20px between their 4px frames */
  --gap: 28px;
  /* the default height of buttons and fields (as the language button in the context menu) */
  --control-h: 64px;
}

*, *::before, *::after { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background-color: var(--cloth);
  font-family: var(--font);
  color: #fff;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
}

#stage {
  position: fixed;
  left: 0;
  top: 0;
  transform-origin: 0 0;
  overflow: hidden;
}

/* Vignette over the whole screen (the stage always fills it), stretched whatever its proportions.
   Multiply: its white middle leaves the picture as is, the grey-blue edges darken it. Clicks and taps
   go through it. Only the corner icon buttons are above it (z-index 1001). */
#vignette {
  position: absolute;
  inset: 0;
  z-index: 1000;
  pointer-events: none;
  background: url('assets/vignette.png') center / 100% 100% no-repeat;
  mix-blend-mode: multiply;
}

.spr {
  background-image: url('assets/atlas.webp');
  background-repeat: no-repeat;
  flex: none;
}
/* 2 atlas px of the rim are clipped away: that is where neighbouring chips bleed in when scaled.
   clip-path also clips shadows, so rings around chips are drawn by their containers. */
.chip { border-radius: 50%; clip-path: circle(49.3% at 50% 50%); } /* keep CHIP_VISIBLE in sprites.js in sync */

.screen {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.center-col {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* always on its own layer: a layer only for the time of a fade (or slide) is drawn with fractional
     positions, and when it is merged back the text snaps by a pixel. (The corner buttons are not in it.) */
  will-change: opacity;
}

/* ---------- buttons ---------- */
button {
  font-family: inherit;
  cursor: pointer;
  border: 0;
  padding: 0;
  background: none;
  color: inherit;
}
button:disabled { cursor: default; opacity: 0.55; }

.btn {
  width: 360px; /* every menu button (the main menu, the context menu, the dark layer screens) */
  height: var(--control-h);
  border-radius: 8px;
  font-size: 30px;
  text-transform: uppercase;
}
.btn:not(:disabled).pressing { transform: scale(0.97); }
/* hover / keyboard focus: the translucent frame around a button (or table card) turns solid white */
.btn, .card { transition: transform 0.08s, box-shadow 0.15s, border-color 0.15s; }
.btn-yellow:not(:disabled):focus-visible, .btn-red:not(:disabled):focus-visible, .card:not(:disabled):focus-visible { box-shadow: 0 0 0 4px #fff; outline: none; }
.btn-outline:not(:disabled):focus-visible { box-shadow: 0 0 0 4px #fff; outline: none; }
@media (hover: hover) {
  /* .hovered: the element under the mouse right after the screen was rebuilt (keepHover() in app.js) */
  .btn-yellow:not(:disabled):is(:hover, .hovered), .btn-red:not(:disabled):is(:hover, .hovered), .card:not(:disabled):is(:hover, .hovered) { box-shadow: 0 0 0 4px #fff; }
  .btn-outline:not(:disabled):is(:hover, .hovered) { box-shadow: 0 0 0 4px #fff; }
}

.btn-yellow {
  font-weight: 500; /* medium, like all the yellow and the white (outline) buttons */
  background: var(--yellow);
  color: var(--yellow-text);
  box-shadow: 0 0 0 4px rgba(255, 236, 120, 0.3);
}
.btn-red {
  height: 90px;
  background: var(--red); /* (under its atlas face, .btn-face) */
  font-size: 50px;
  font-weight: 700;
  box-shadow: 0 0 0 4px rgba(150, 90, 100, 0.45);
}
/* a glint over "Play" every 8 s: a soft slanted band blended in the overlay mode, so it lightens the red
   (like light gliding over lacquer) instead of whitening it; under the text, clipped inside the button's
   rim (.btn-shine-clip: 4 px in from its edge, so the rim of the face stays as it is) */
.btn-shine { position: relative; overflow: hidden; isolation: isolate; }
.btn-shine-clip { position: absolute; inset: 4px; border-radius: 4px; overflow: hidden; pointer-events: none; }
.btn-shine-band, .ribbon-shine-run::before {
  content: '';
  position: absolute;
  inset: -20% auto -20% 0;
  width: 55%;
  z-index: -1;
  /* soft on both sides; the band itself is slanted (skew), so it has no straight edge */
  background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, var(--shine-peak, 0.55)) 50%, rgba(255, 255, 255, 0) 100%);
  mix-blend-mode: overlay;
  pointer-events: none;
}
/* "Play"'s face: the textured red of the atlas, under the glint and the word (playButton() in app.js) */
.btn-face { position: absolute; left: 0; top: 0; z-index: -2; pointer-events: none; }
/* on "Play" the band changes on its way: it comes in upright and half as wide, slants to 20deg, widens
   and brightens by the middle, and goes out upright and narrow again (slanted "\", its top to the left). The run takes ~0.9 s of the 8 s:
   the movement eases in and out over the whole run, the shape over each half. The run starts and ends
   with the narrow band just outside the button's edge, so its upright ends are seen. */
.btn-shine-band {
  /* brightest at 70%: the light is in the band's front (it runs to the right) */
  /* (the whole gradient at 70%; 110% of the button wide) */
  width: 110%;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.7) 70%, rgba(255, 255, 255, 0) 100%);
  translate: -75% 0;
  transform: skewX(0deg) scaleX(0.5);
  opacity: 0.79; /* dimmer at the ends */
  animation: shine-move 8s var(--shine-delay, 1.5s) infinite, shine-shape 8s var(--shine-delay, 1.5s) infinite;
}
@keyframes shine-move {
  0% { translate: -75% 0; animation-timing-function: ease-in-out; }
  11.25%, 100% { translate: 66% 0; } /* (its narrow band just past the right edge) */
}
@keyframes shine-shape {
  0% { transform: skewX(0deg) scaleX(0.5); opacity: 0.79; animation-timing-function: ease-in-out; }
  5.625% { transform: skewX(20deg) scaleX(1); opacity: 1; animation-timing-function: ease-in-out; }
  11.25%, 100% { transform: skewX(0deg) scaleX(0.5); opacity: 0.79; }
}

/* ---------- menu over the current screen ---------- */
.menu-overlay {
  position: absolute;
  inset: 0;
  z-index: 1002; /* above the vignette and everything else */
  background: rgba(0, 0, 0, 0.3);
  /* what is under it is blurred by depth() in app.js (MENU_BLUR) */
  display: flex;
  align-items: center;
  justify-content: center;
}
.menu-overlay.closing { pointer-events: none; }
/* the tablecloth layer (see app.js): sized and placed in layout(), shrinks with the screen under the menu */
#cloth { position: absolute; pointer-events: none; }
/* the penalty tablecloth over it (a ban for leaving a game early): fades in and back out */
.cloth-ban { position: absolute; inset: 0; opacity: 0; } /* (its fade: banCloth() in app.js) */
/* the rules sliding in or out (slid() in app.js): no corner buttons, no clicks */
#stage.sliding > .screen { pointer-events: none; overflow: visible; } /* the board's edge beyond the screen isn't cut off */
#stage.sliding > .screen .icon-btn { visibility: hidden; }
/* the screen shrinks under the menu (depth() in app.js): what is beyond its edges must not be cut off */
#stage.menu-open > .screen { overflow: visible; }
.menu-grid {
  display: grid;
  /* the button column stays at the centre of the screen whatever the label widths; labels to its left */
  width: 100%;
  grid-template-columns: 1fr 360px 1fr;
  column-gap: 24px;
  row-gap: var(--gap);
  align-items: center;
}
.menu-grid > :nth-child(odd) { grid-column: 1; }
.menu-grid > :nth-child(even) { grid-column: 2; }
.menu-grid label { justify-self: end; text-align: right; font-size: 28px; line-height: 32px; white-space: pre-line; }
/* the white (transparent) button: its 4px frame lies outside its box, as the yellow buttons' glow does,
   so both are the same size on the screen (64 + the frame) */
.btn-outline {
  font-weight: 500;
  box-shadow: 0 0 0 4px var(--white-soft);
  color: #fff;
}

.icon-btn {
  position: absolute;
  top: 38px;
  width: 64px;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1001; /* above the vignette */
  will-change: opacity; /* it fades in: always on its own layer, so it doesn't snap by a pixel after (see .center-col) */
}
/* the icon buttons (cross, menu, back arrow) and the big arrows grow a little under the mouse, and
   are back to their size while pressed */
.icon-btn > .spr, .arrow > .spr { transition: scale 0.15s ease-out; }
@media (hover: hover) {
  .icon-btn:is(:hover, .hovered) > .spr, .arrow:not(:disabled):is(:hover, .hovered) > .spr { scale: 1.15; }
}
.icon-btn.pressing > .spr, .arrow:not(:disabled).pressing > .spr { scale: 1; }
.icon-btn.left { left: 70px; }
.icon-btn.right { right: 58px; }
/* all the sound off / on (muteLayer() in app.js): the bottom left corner, as far from the edges as the menu
   button over it; not under the menu, a white dialog, the rules sliding */
.icon-btn.mute-btn { top: auto; bottom: 40.5px; } /* (the speaker's bottom as far from the edge as the menu icon's top: 50.5) */
.icon-btn.mute-btn { transition: opacity 0.15s ease-out; }
.icon-btn.mute-btn.covered { opacity: 0; pointer-events: none; } /* (under an achievement's star: coverMute() in app.js) */
#stage.menu-open > .mute-btn, #stage.sliding > .mute-btn, #stage:has(> .modal-back) > .mute-btn { visibility: hidden; }

/* ---------- start / menu ---------- */
/* (the logo grew from 102.6 to 107.2 tall, 360 wide: it reaches up by the difference, so nothing under it moves) */
.logo { margin-top: -4.6px; margin-bottom: 44px; }
.stack { display: flex; flex-direction: column; align-items: center; gap: var(--gap); }
.stack .btn-red { margin-bottom: 0; }
/* the round donation button (132 x 132, the frame of the white (outline) buttons, solid white under the
   mouse): in a place of its own on the main screen, where it first stood — one of its sizes (132) to the right
   of the menu, its middle level with the third of three buttons (Play, Rules, Log in): 378 px right of and 194 px
   below the screen's middle; it doesn't move with the menu, whatever the menu shows (the user, 2026-10-25) */
.donate-btn {
  position: absolute;
  left: calc(50% + 378px - 66px);
  top: calc(50% + 194px - 66px);
  z-index: 2; will-change: transform; /* (on its own layer, as the menu's column: no 1 px snaps) */
  width: 132px;
  height: 132px;
  border-radius: 50%;
  border: 4px solid var(--white-soft);
  background: none;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.08s, border-color 0.15s;
}
.donate-btn.pressing { transform: scale(0.97); }
.donate-btn:focus-visible { border-color: #fff; outline: none; }
@media (hover: hover) { .donate-btn:is(:hover, .hovered) { border-color: #fff; } }
/* under the button, always: what it is, white, in two lines */
.donate-label {
  position: absolute; left: 50%; top: calc(100% + 14px); translate: -50% 0;
  font-size: 28px; line-height: 34px; font-weight: 400; color: #fff; text-align: center; white-space: nowrap;
  pointer-events: none;
}
.me-line { margin-top: 36px; font-size: 24px; opacity: 0.85; display: flex; align-items: baseline; } /* the icon stands on the baseline of the name */
/* the account icon's 38 px cell (iconAccount): the person's feet stand on the name's baseline (15 of the
   64 atlas px under them = 8.9 px), and it is two spaces from the name (its cell is 9 px wider than it) */
/* the cockroach (cockroach() in ui.js): its whiskers under the body, turning about their bases */
.roach { position: relative; flex: none; }
.roach > * { position: absolute; left: 0; top: 0; }
.roach-whisker { user-select: none; pointer-events: none; }
.roach-shadow { border-radius: 50%; background: rgba(0, 0, 0, 0.1); } /* (as .shape-shadow: black at 10%) */
/* a cockroach running about the screen (runningRoach() in ui.js): not in the way of clicks */
.roach-run { position: absolute; left: 0; top: 0; pointer-events: none; }
/* the splat a squashed cockroach leaves (showSplats() in ui.js): under it, where it was */
.roach-splat { position: absolute; left: 0; top: 0; pointer-events: none; }
.roach-layer { position: absolute; inset: 0; overflow: hidden; pointer-events: none; } /* on every screen but the main one */
/* on the game screen: over the board (5), the ribbon (4), the mitt (2), under the interface (30) */
.game-roach { z-index: 10; inset: auto; }
.game-root :is(.logo-slot, .names, .tooltip, .countdown, .mitt-text, .mitt-note, .mitt-timer, .back-label, .end-reason, .end-actions, .rematch-bubble) { z-index: 30; }
.me-icon { position: relative; top: 8.9px; margin: -16px 3px 0 0; } /* (-16: the line stays as tall as the name alone) */
/* the main menu's spacing, as in its design */
.screen.start .center-col { padding-top: 20px; }
.screen.start .logo { margin-bottom: 55px; }
.screen.start .stack { gap: var(--gap); }
.screen.start .me-line { margin-top: 47px; }
/* behind the main menu (startBackground() in app.js): blurred as much as under the menu (MENU_BLUR),
   like the tablecloth under it (clothBlur()) */
.start-bg { position: absolute; inset: 0; filter: blur(10px); pointer-events: none; }
.start-bg > * { position: absolute; }
.screen.start.leaving { pointer-events: none; } /* "Play" was pressed: the screen is coming apart */
.screen.fading, .screen.fading * { pointer-events: none !important; } /* a screen fading out over the main one */
.start-board { animation-name: start-spin; animation-timing-function: linear; animation-iteration-count: infinite; }
.start-board > .spr { position: absolute; left: 0; top: 0; }
.start-board > .start-chip { left: auto; top: auto; }
/* the mitt on the main screen (startMitt() in app.js): upside down (= mirrored and turned by 180°), a
   chip coming out of its cuff under it */
.start-mitt { transform: scaleY(-1); }
.start-mitt > * { position: absolute; left: 0; top: 0; }
@keyframes start-spin { to { rotate: 360deg; } }

/* ---------- logging in / new account: over the main screen, like the menu (authOverlay() in app.js) ---------- */
.auth { width: 100%; display: flex; flex-direction: column; align-items: center; }
.auth-title { font-size: 30px; font-weight: 700; text-transform: uppercase; margin-bottom: 44px; }
/* the field and the buttons in the middle of the screen; the label to their left, a message to their right */
.auth-grid { width: 100%; display: grid; grid-template-columns: 1fr 360px 1fr; column-gap: 24px; row-gap: var(--gap); align-items: center; }
.auth-grid label { justify-self: end; text-align: right; font-size: 28px; line-height: 32px; white-space: pre-line; } /* two lines = the field's height */
.auth-grid .btn { width: 360px; }
.auth-input {
  width: 360px;
  height: var(--control-h);
  box-sizing: border-box;
  padding: 0 18px;
  border: 3px solid #fff;
  border-radius: 8px;
  background: #fff;
  color: #111;
  font: 28px var(--font);
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.3);
  outline: none;
}
.auth-input.error { box-shadow: 0 0 0 4px #e8130c; } /* as thick as the buttons' frame */
/* "Write a review" (feedbackOverlay() in app.js): the field 780 wide, 6 lines tall (more text goes up under its
   top edge; the wheel scrolls it, no scrollbar shown), "Send" under it */
.feedback-input {
  display: block; width: 780px; height: 234px; box-sizing: border-box; padding: 14px 18px;
  border: 3px solid #fff; border-radius: 8px; background: #fff; color: #111;
  font: 28px/33px var(--font); box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.3); outline: none;
  resize: none; overflow-y: auto; scrollbar-width: none;
}
.feedback-input::-webkit-scrollbar { display: none; }
.feedback-input.error { box-shadow: 0 0 0 4px #e8130c; }
.auth.feedback .btn { width: 360px; margin-top: var(--gap); }
/* "Send" pressed: the thanks in its place, as tall as it */
.feedback-thanks { height: var(--control-h); margin-top: var(--gap); display: flex; align-items: center; font-size: 28px; }
/* "Developer support" (donate.js): the Ko-fi page in a frame over the layer's empty slot, on Ko-fi's own light grey,
   rounded (its scroll bar is beyond the right edge); the frame is the stage's last child, over the dark layer (1002),
   under a dialog over it (1003) */
.donate { display: flex; flex-direction: column; align-items: center; }
.donate-host { position: absolute; z-index: 1002; overflow: hidden; border-radius: 32px; background: #f9f9f9; }
.donate-host.closing { pointer-events: none; }
.donate-host iframe { display: block; border: none; transform-origin: 0 0; }
.donate-note { margin-top: 16px; font-size: 24px; line-height: 30px; color: #fff; text-align: center; } /* (24: the game's smallest text) */
.donate-link { margin-top: 6px; font-size: 24px; line-height: 30px; color: #fff; text-decoration: none; }
.donate-link u { text-underline-offset: 4px; text-decoration-thickness: 2px; }
/* the menu's "Write a review" for a minute after sending: struck through (yellow, not dimmed), the time left and
   "Spam protection" to its right */
.spam-cell { position: relative; }
.btn-yellow.btn-struck:disabled { color: var(--yellow-text); }
.spam-note { position: absolute; left: calc(100% + 24px); top: 0; height: var(--control-h); display: flex; align-items: center; gap: 0.5em; font-size: 26px; white-space: nowrap; } /* (the gap: two spaces) */
.spam-note b { font-weight: 700; }
.modal.feedback-modal { width: 572px; padding: 90px 60px 84px; } /* "too many messages": as the design */
.auth-error, .auth-hint { justify-self: start; font-size: 26px; line-height: 30px; white-space: pre-line; }
.auth-error { color: #ff2414; }
.auth-hint { color: rgba(255, 255, 255, 0.55); }
/* a new password (passField() in account.js): the offered one in a field framed as the white buttons, which the
   player may change; the white button that copies it to its right (the two as wide as a field) */
.auth-pass-row { display: flex; gap: var(--gap); }
.auth-pass {
  width: calc(360px - var(--control-h) - var(--gap)); /* with the copy button: as wide as a field */
  height: var(--control-h);
  box-sizing: border-box; padding: 0 12px; border: none; outline: none;
  box-shadow: 0 0 0 4px var(--white-soft); /* (outside, as the white buttons' frame) */
  border-radius: 8px; background: transparent;
  font: 700 28px var(--font); color: #fff; text-align: center;
  caret-color: #fff; transition: box-shadow 0.15s;
}
.auth-pass:focus { box-shadow: 0 0 0 4px #fff; } /* being typed in: the white frame */
@media (hover: hover) { .auth-pass:hover { box-shadow: 0 0 0 4px #fff; } }
.auth-pass.error { box-shadow: 0 0 0 4px #e8130c; }
.auth-pass::selection { background: rgba(255, 255, 255, 0.35); }
.auth-grid .auth-copy { width: var(--control-h); height: var(--control-h); padding: 0; display: flex; align-items: center; justify-content: center; }
/* "Account": its buttons in a column, as wide as the fields */
.account-menu { display: flex; flex-direction: column; gap: var(--gap); }
.account-menu .btn { width: 360px; }
/* "Password copied": a white speech bubble, its tail pointing left at the password */
.auth-copied {
  justify-self: start; position: relative; margin-left: 12px;
  padding: 10px 18px; background: #fff; color: #111; border-radius: 8px;
  font-size: 24px; line-height: 30px; white-space: nowrap;
}
.auth-copied::before { content: ''; position: absolute; left: -11px; top: calc(50% - 12px); border: 12px solid transparent; border-left: 0; border-right-color: #fff; } /* (1 px into the bubble: no hairline gap when scaled) */

/* ---------- statistics: in the dark layer (statsOverlay() in app.js) ---------- */
/* the switcher (tabs: switcher() in app.js): one frame as the white buttons' (outside its box), equal items,
   a translucent white fill under the chosen one (2 px in from the frame), sliding over to another; an item
   under the mouse gets a white frame */
.switcher { position: relative; display: grid; height: var(--control-h); border-radius: 8px; box-shadow: 0 0 0 4px var(--white-soft); }
.stats > .switcher { margin-bottom: 61px; } /* (the statistics' title: as far from the content as before) */
.switcher-fill { position: absolute; left: 2px; top: 2px; bottom: 2px; border-radius: 6px; background: rgba(255, 255, 255, 0.3); pointer-events: none; }
.switcher-item {
  position: relative; height: 100%; padding: 0 30px; border-radius: 8px;
  font-size: 30px; font-weight: 500; text-transform: uppercase; color: #fff; white-space: nowrap;
  transition: box-shadow 0.15s;
}
.switcher-item.selected { cursor: default; }
@media (hover: hover) { .switcher-item:not(.selected):is(:hover, .hovered) { box-shadow: 0 0 0 4px #fff; } }
.switcher-item:not(.selected):focus-visible { box-shadow: 0 0 0 4px #fff; outline: none; }
/* the on / off toggle (toggleSwitch() in app.js): as wide as the buttons, the fill over 3/4 of it */
.toggle-switch { position: relative; width: 360px; height: var(--control-h); border-radius: 8px; box-shadow: 0 0 0 4px var(--white-soft); transition: box-shadow 0.15s; }
@media (hover: hover) { .toggle-switch:is(:hover, .hovered) { box-shadow: 0 0 0 4px #fff; } }
.toggle-switch:focus-visible { box-shadow: 0 0 0 4px #fff; outline: none; }
/* a setting that does nothing (yet): no white frame under the mouse, its word struck through */
.toggle-switch:disabled { box-shadow: 0 0 0 4px var(--white-soft) !important; opacity: 1; } /* (not dimmed as other disabled buttons: the strike says it) */
.toggle-switch:disabled .toggle-word { text-decoration: line-through; text-decoration-thickness: 3px; }
.toggle-icon { position: absolute; top: 50%; translate: -50% -50%; pointer-events: none; } /* (its middle at its left: toggleSwitch()) */
.toggle-fill { position: absolute; left: 2px; top: 2px; bottom: 2px; width: 270px; border-radius: 6px; background: rgba(255, 255, 255, 0.3); pointer-events: none; } /* (360 - 270 - 2 * 2 = 86 to slide) */
.toggle-word { position: absolute; left: 2px; top: 2px; bottom: 2px; width: 270px; display: flex; pointer-events: none; align-items: center; justify-content: center; font-size: 30px; font-weight: 500; text-transform: uppercase; color: #fff; white-space: nowrap; }
.stats-content { display: flex; flex-direction: column; align-items: center; }
.stats-title { margin-bottom: 61px; font-size: 30px; line-height: 36px; font-weight: 700; text-transform: uppercase; } /* as far from the content as the tables screen title */
/* the same frame on both tabs, a little below the middle (as in the design): the content, then the pager */
.stats { display: flex; flex-direction: column; align-items: center; }
.stats-body { position: relative; height: 360px; display: flex; justify-content: center; }
/* the pager (a switcher of number items) where a button would be: every item at least 84 wide, so its
   fill (2 px in from each side) is at least 80 */
.switcher.pager { margin-top: 55px; }
/* the sounds' volume (volumeControl() in app.js): as wide as the buttons, an icon in the middle of each item */
.switcher.volume { width: 360px; }
.volume .switcher-item { padding: 0; display: flex; align-items: center; justify-content: center; }
.pager .switcher-item { min-width: 84px; padding: 0 16px; }
/* the language (langSwitcher() in app.js): as wide as the buttons, the chosen code bold, the language's name to its right */
.lang-cell { position: relative; }
.switcher.lang { width: 360px; }
.lang .switcher-item { padding: 0; }
.lang .switcher-item.selected { font-weight: 700; }
.lang-name { position: absolute; left: calc(100% + 24px); top: 0; height: var(--control-h); display: flex; align-items: center; font-size: 28px; white-space: nowrap; }
.stat-value { font-size: 50px; line-height: 56px; font-weight: 700; }
.stat-value small { font-size: 26px; font-weight: 700; line-height: 0; }
.stat-tiles { display: flex; }
.stat-tile { width: 280px; display: flex; flex-direction: column; align-items: center; text-align: center; }
.stat-tile .stat-value { margin-top: 13px; }
.stat-label { font-size: 28px; line-height: 35px; white-space: pre-line; }
/* an achievement tile: the icon, its name, what to do (in the tile with some room at the sides) */
.ach-tile { padding: 0 14px; }
.ach-tile.locked > .spr { filter: brightness(0); opacity: 0.2; } /* not earned yet: the icon black, at 20% */
.ach-title { margin-top: 13px; font-size: 26px; line-height: 32px; font-weight: 700; }
.ach-text { margin-top: 6px; font-size: 20px; line-height: 25px; }
/* a page of tiles; while it is turned, the old and the new page side by side slide over in it */
.stat-page { display: flex; touch-action: none; } /* dragged by a finger too (pageRow()), not scrolled or zoomed by the browser */
.stat-page-row { display: flex; flex: none; }
/* the tiles with the arrows at their sides: level with the big icons (198 tall), each arrow in the
   middle of where the next tile would be (half a tile 140 - half the 90 px button) */
.stat-carousel { display: flex; align-items: flex-start; }
.stat-arrow { margin-top: 29px; position: relative; z-index: 1; } /* over the tiles dragged or sliding under it */
.stat-carousel .stat-arrow.left { margin-right: 95px; }
.stat-carousel .stat-arrow.right { margin-left: 95px; }

/* ---------- forms ---------- */
.form-title { font-size: 30px; font-weight: 700; margin: 30px 0 42px; text-transform: uppercase; }
.form-grid {
  display: grid;
  grid-template-columns: 1fr 360px 1fr;
  column-gap: 24px;
  row-gap: 30px;
  align-items: center;
  width: 1100px;
}
.form-grid label { justify-self: end; font-size: 28px; }
.form-grid .hint { font-size: 28px; color: rgba(255, 255, 255, 0.45); line-height: 1.25; }
.form-grid .btn { width: 360px; grid-column: 2; }
.input {
  width: 360px;
  height: 64px;
  border-radius: 8px;
  border: 0;
  font: 28px var(--font);
  padding: 0 18px;
  color: #333;
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.35);
  outline: none;
  user-select: text;
  -webkit-user-select: text;
}
.form-error {
  grid-column: 2 / 4;
  justify-self: start;
  font-size: 24px;
  background: rgba(204, 46, 20, 0.92);
  padding: 10px 16px;
  border-radius: 8px;
  max-width: 560px;
}

/* ---------- lobby ---------- */
.lobby-title { font-size: 28px; margin: 18px 0 26px; }
/* over the tables (the user's mock-up, 2026-10-27): "Players on the server: 25    Open tables: 10", the numbers bold */
.lobby-counts { display: flex; gap: 54px; font-size: 30px; margin: 35px 0 62px; white-space: nowrap; }
.lobby-counts b { font-weight: 700; }
.carousel { position: relative; display: flex; align-items: center; }
.cards { display: flex; gap: var(--gap); }
.card {
  width: 260px;
  height: 260px;
  border-radius: 8px;
  background: var(--yellow);
  color: var(--yellow-text);
  box-shadow: 0 0 0 4px rgba(255, 236, 120, 0.3);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  padding-bottom: 12px;
}
.card.pressing { transform: scale(0.97); }
.card .letter { font-size: 200px; font-weight: 700; line-height: 1; margin-bottom: 8px; }
.card .name { font-size: 28px; white-space: nowrap; max-width: 240px; overflow: hidden; text-overflow: ellipsis; }
.arrow {
  width: 90px;
  height: 140px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.arrow.left .spr { transform: scaleX(-1); } /* the atlas has only the right-pointing arrow */
/* the arrow stands in the middle of where the next card would be: gap 28 + half a card 130 - half
   the 90 px button */
.arrow.left { margin-right: 113px; }
.arrow.right { margin-left: 113px; }
.arrow:disabled { visibility: hidden; } /* no way further: the arrow is gone (its place kept, nothing moves) */
.lobby-empty {
  height: 260px;
  display: flex;
  align-items: center;
  font-size: 30px;
  opacity: 0.9;
}
.lobby-title.bold { font-weight: 400; margin: 47px 0 61px; } /* ("no tables waiting": regular, not bold — the user, 2026-10-25; the class kept for its place) */
/* nobody waiting: the own empty table and the bot side by side, each above its button */
.lobby-choice { display: grid; grid-template-columns: 405px 405px; column-gap: var(--gap); row-gap: 74px; justify-items: center; align-items: center; }
.lobby-choice .btn { width: 405px; }
/* the own empty table looks like a secondary button: clear, with a translucent white frame */
.card.card-own { width: 266px; height: 266px; background: none; color: #fff; box-shadow: none; border: 4px solid var(--white-soft); }
.card.card-own .letter { margin-bottom: -8px; }
.card.card-own .name { font-size: 30px; opacity: 0.85; }
@media (hover: hover) { .card.card-own:not(:disabled):is(:hover, .hovered) { box-shadow: none; border-color: #fff; } }
.card.card-own:not(:disabled):focus-visible { box-shadow: none; border-color: #fff; }
/* the bot (botFace() in ui.js): parts placed in its box, each turning about its own centre */
.bot-slot { display: flex; align-items: center; justify-content: center; height: 266px; }
.bot-face { position: relative; width: 244px; height: 170px; margin-top: -8px; }
.bot-face > div { position: absolute; }
.bot-face .spr { position: absolute; left: 0; top: 0; }
.bot-face .bot-horn { transform-origin: 50% 100%; } /* grows out of the head */
/* the bot (a button: clicking it changes its level) and its speech bubble to the right of its eyes, the tail pointing at them */
/* its hit area is an invisible square like the table card on the left (266 x 266, in the same row):
   the face (244 x 170) sits 4px above the middle of the 266px slot, so the square starts 11px left and 44px up */
.bot-btn { display: block; position: relative; }
.bot-btn::before { content: ''; position: absolute; left: -11px; top: -44px; width: 266px; height: 266px; border-radius: 8px; }
.bot-btn:focus-visible { outline: none; }
.bot-btn:focus-visible::before { box-shadow: 0 0 0 4px #fff; }
.bot-wrap { position: relative; margin-top: -8px; }
.bot-wrap .bot-face { margin-top: 0; }
.bot-bubble {
  position: absolute;
  left: 253px;
  top: 86px;
  width: 272px;
  padding: 14px 12px;
  background: #fff;
  color: #111;
  border-radius: 8px;
  font-size: 22px;
  line-height: 28px;
  text-align: center;
  translate: 0 -50%;
}
.bot-bubble::before {
  content: '';
  position: absolute;
  left: -11px; /* (1 px into the bubble: no hairline gap when scaled) */
  top: calc(50% - 12px); /* centered on the bubble's left side */
  border: 12px solid transparent;
  border-left: 0;
  border-right-color: #fff;
}
/* not logged in: a lock with why on the left, the bot with its bubble under it on the right */
.lobby-choice.guest { align-items: end; row-gap: 54px; margin-top: -4px; }
.guest-lock, .guest-bot { display: flex; flex-direction: column; align-items: center; }
.guest-note { margin-top: 24px; font-size: 28px; line-height: 35px; text-align: center; white-space: pre-line; opacity: 0.9; }
.bot-bubble.below { position: relative; left: auto; top: auto; translate: none; width: 307px; margin: 31px auto 0; padding: 8px 4px; font-size: 24px; line-height: 28px; }
.bot-bubble.below::before { left: calc(50% - 12px); top: -11px; border: 12px solid transparent; border-top: 0; border-bottom-color: #fff; }
.guest-bot .bot-wrap { margin-top: 0; }
.guest-bot .bot-btn { width: 244px; margin: 0 auto; } /* the face centred over its bubble */
/* not available: the text crossed out, and it doesn't react to the mouse */
.btn-struck { text-decoration: line-through; }
.btn-struck:disabled { opacity: 1; color: rgba(255, 255, 255, 0.85); }
/* "Play again" already pressed: still yellow, the waiting dots turning on it in its text colour */
/* the rules drawn unseen the first time (warmRules() in app.js): on the stage, all but transparent, no clicks */
.rules-warm { position: absolute; inset: 0; opacity: 0.001; pointer-events: none; z-index: -1; }
.btn-waiting:disabled { opacity: 1; color: var(--yellow-text); display: flex; align-items: center; justify-content: center; }
/* an atlas icon in its element's text colour (tintedSprite() in sprites.js): the sprite is the mask */
.spr.tinted {
  background: currentColor;
  mask-image: url('assets/atlas.webp'); mask-repeat: no-repeat;
  -webkit-mask-image: url('assets/atlas.webp'); -webkit-mask-repeat: no-repeat;
}
.lobby-actions { margin-top: 74px; display: flex; gap: var(--gap); }
.lobby-actions .btn { width: 405px; }
/* banned: the time left, in the row of the buttons, where "Create your game" was */
.lobby-choice .ban-time { height: var(--control-h); display: flex; align-items: center; font-size: 48px; font-weight: 700; }

/* ---------- white panel screens (rules) ---------- */
.scroll { position: absolute; inset: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.panel {
  margin: 57px auto 60px;
  width: 730px;
  background: #fff;
  color: #111;
  border-radius: 40px;
  padding: 44px 32px 50px;
  font-size: 24px;
  line-height: 1.15;
  user-select: text;
  -webkit-user-select: text;
}
.panel .logo { margin: 0 auto 30px; } /* (in the rules it keeps its room under the panel's top: the text below moves down instead) */
.panel h2 { font-size: 40px; text-transform: uppercase; margin: 48px 0 12px; }
.panel ol { padding-left: 35px; margin: 0; }
.panel p { margin: 0 0 30px; }
.panel .attention { color: #e01010; font-weight: 700; }
.panel ol.spaced li { margin: 0 0 21px; }
.panel .author { margin: 40px 0 0; color: #999; text-align: center; }
/* the illustrations (rules.js scheme()): as wide as the text, their parts placed from its middle */
.scheme { position: relative; width: 666px; }
.scheme > * { position: absolute; left: 0; top: 0; }
.scheme-svg { overflow: visible; font-family: inherit; }
.scheme-mark { width: 40px; height: 40px; border-radius: 50%; box-shadow: 0 0 0 5px #fff; }
.scheme-mark > .spr { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); }
.scheme-mark.no { background: var(--red); }
.scheme-mark.ok { background: #00c006; }

/* ---------- plain message screens ---------- */
.msg-title { font-size: 30px; font-weight: 700; text-transform: uppercase; margin-top: 40px; }
.msg-text { font-size: 28px; margin-top: 30px; white-space: pre-line; text-align: center; } /* (the lines break as written) */
.dev-line { font-size: 30px; margin-top: 44px; white-space: nowrap; }

/* ---------- modal ---------- */
.modal-back {
  position: absolute;
  inset: 0;
  background: rgba(40, 60, 90, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
}
.modal {
  position: relative;
  width: 940px;
  background: #fff;
  color: #111;
  border-radius: 40px;
  padding: 90px 110px 84px;
  text-align: center;
  font-size: 44px;
  line-height: 1.28;
  box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.06);
}
/* the ban notice: on top of everything, the text red and bold */
.modal-back.ban-back, .modal-back.over-menu { z-index: 1003; } /* (over the dark layer) */
.ban-modal { width: 760px; padding: 90px 90px 84px; }
.ban-text { color: #ff1a00; font-weight: 700; font-size: 40px; line-height: 1.3; }
.modal .x { position: absolute; top: 38px; right: 44px; width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; }
.modal .x > .spr { filter: brightness(0); } /* the white cross of the atlas, black on the white dialog */
.modal-actions { display: flex; justify-content: center; gap: var(--gap); margin-top: 60px; }
.modal-text { max-width: 640px; margin: 0 auto; } /* (the lines break as in the design) */
.modal-text.danger { color: #ff1010; } /* (deleting the account: the warning in red) */
.modal-actions .btn { width: 180px; font-size: 28px; }
/* on the white dialog: a white button with a light grey frame (outside its box, as the yellow buttons'
   glow, so both are the same size), the frame darker under the mouse */
.btn-grey { box-shadow: 0 0 0 4px #ddd; color: #111; font-weight: 500; }
@media (hover: hover) { .btn-grey:not(:disabled):is(:hover, .hovered) { box-shadow: 0 0 0 4px #aaa; } }

/* ---------- game ---------- */
.game-root { position: absolute; will-change: opacity; } /* own layer, as .center-col: no 1px snap after the assembly */
.board { position: absolute; z-index: 5; } /* above the result ribbon, which tucks under its edge */
.board-shadow { left: 175px; top: 0; z-index: 0; }
/* shadows of atlas shapes: the shape itself turned black at 10% opacity */
.shape-shadow { filter: brightness(0) opacity(0.1); pointer-events: none; }
.shape-shadow.ready { filter: none; } /* its own black silhouette image by now (shadowSprite() in sprites.js) */
.mitt.mitt-shadow { z-index: 0; }
.holes { position: absolute; inset: 0; }
.hole {
  position: absolute;
  width: 87px;
  height: 87px;
  margin: -43.5px 0 0 -43.5px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.hole.can { cursor: pointer; }
@media (hover: hover) {
  .hole.can:hover::after {
    content: '';
    position: absolute;
    /* hole radius is 43.3 design px; the ring's inner edge is ~1px outside it */
    inset: -5px;
    border: 4px solid #fff;
    border-radius: 50%;
  }
}
.hole .chip { position: absolute; z-index: 1; } /* above the 3 s ring, which slides out from behind it */
.hole.last::before {
  content: '';
  position: absolute;
  width: 87.3px; /* = hole and chip diameter (HOLE_D in sprites.js) */
  height: 87.3px;
  border-radius: 50%;
  box-shadow: 0 0 0 5px rgba(120, 85, 40, 0.18);
}
.hole.pick { cursor: pointer; }
.ring {
  position: absolute;
  width: 110px;
  height: 110px;
  left: -11.5px;
  top: -11.5px;
  pointer-events: none;
  overflow: visible;
}
.ring circle { fill: none; }
.ring .track { stroke: rgba(89, 0, 0, 0.2); } /* #590000 at 20%: the not-yet-filled part of the 3 s ring */
.ring .prog { stroke: #fff; transform: rotate(-90deg); transform-origin: 50% 50%; }
/* dashed rings from the atlas (108 design px) centered on an 87px hole */
.ring-spr { position: absolute; left: -10.6px; top: -10.6px; pointer-events: none; }

.panel-r { position: absolute; width: 515px; height: 900px; }
.panel-r > * { position: absolute; }
/* a part of the table flying in or out (part() in game.js): its elements keep their places in it */
.asm { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; }
.asm > * { position: absolute; pointer-events: auto; }
.asm.over-ribbon { z-index: 5; } /* the board stays above the ribbon tucked under its edge */
.asm.ribbon-layer { z-index: 4; }
.end-confetti { z-index: 8; pointer-events: none; } /* over the board, the mitt and the ribbon; under the cockroach and the interface */
.end-confetti > .confetti { position: relative; }
.end-confetti .confetto { position: absolute; border-radius: 50%; will-change: transform; } /* each circle turns and scales about its own middle */
/* while the table is assembled or taken apart nothing on it (nor the menu button) can be clicked */
.screen.game.busy, .screen.game.busy * { pointer-events: none !important; }
/* logo + names/score block (and the "waiting" tooltip under it) sit 30px left of the panel origin */
.names { left: -30px; top: 116px; width: 515px; height: 110px; }
.names .divider { position: absolute; left: 237px; top: 4px; width: 2px; height: 96px; background: rgba(255, 255, 255, 0.35); }
.pname { position: absolute; top: 0; font-size: 28px; white-space: nowrap; line-height: 36px; }
.pname.l { right: 300px; text-align: right; }
.pname.r { left: 262px; }
/* score badges: a circle (or a pill for 3 digits) in the chip colour, the number on it (badge() in game.js) */
.badge {
  position: absolute;
  top: 42px;
  height: 60.5px;
  border-radius: 30.25px;
  font-size: 30px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
.badge.l { right: 299px; color: var(--yellow-text); background: var(--yellow); }
.badge.r { left: 260px; color: #fff; background: var(--red); }
/* whose move it is: an arrow at the outer side of that score, level with its middle (turnArrow() in game.js) */
.turn-arrow { position: absolute; top: calc(42px + 60.5px / 2); translate: 0 -50%; }
.turn-arrow.l { transform: scaleX(-1); } /* (the atlas arrow points left: mirrored to point right, at the left score) */
/* waiting icon (iconProgress): its 64 px cell turns about its middle; that middle level with the score
   badge's on the left (badge: top 42, height 60.5) and where the old icon's was (x 280.3) */
/* the opponent reconnecting (awayDots() in game.js): the turning dots beyond their turn arrow's place */
.away-dots { position: absolute; top: calc(42px + 60.5px / 2 - 64px / 2); }
.names .dots { position: absolute; left: calc(280.3px - 64px / 2); top: calc(42px + 60.5px / 2 - 64px / 2); }
/* jumps by 360/5 = 72°: the dots stay in place and only their shades move, like a progress indicator */
.dots-spin { animation: spin 1.5s steps(5) infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

.tooltip {
  left: 166px;
  top: 237px;
  width: 170px;
  padding: 10px 8px;
  background: #fff;
  color: #111;
  border-radius: 8px;
  font-size: 22px;
  line-height: 28px;
  text-align: center;
}
.tooltip::before {
  content: '';
  position: absolute;
  left: calc(50% - 12px); /* centered on the bubble */
  top: -11px; /* (1 px into the bubble) */
  border: 12px solid transparent;
  border-top: 0;
  border-bottom-color: #fff;
}
.countdown { left: 126.2px; width: 290px; } /* left edge = left edge of the score badge above */
.countdown b { display: block; font-size: 44px; line-height: 50px; }
.countdown span { display: block; font-size: 22px; line-height: 28px; }

.mitt { left: 47px; top: 385px; z-index: 2; }
/* the drawn chip peeks out of the mitt: its lower edge is slightly hidden under the cuff */
/* shown at the native atlas size: 132 atlas px = 132 design px, 130.2 visible after the clip */
.drawn { --chip-d: 130.2px; left: 90px; top: 337px; z-index: 1; display: flex; align-items: center; justify-content: center; }
.drawn > .chip { position: relative; } /* stays above the shadow */
/* the drawn chip on its way from the mitt to the board (positioned by its animation) */
.flying { position: absolute; left: 0; top: 0; z-index: 20; pointer-events: none; transform-origin: 50% 50%; }
.spin { animation: spin-cw 20s linear infinite; } /* SPIN_MS in game.js */
.spin-ramp { position: absolute; z-index: 1; } /* winner's chip speeding up (see game.js) */
@keyframes spin-cw { to { transform: rotate(360deg); } }/* soft shadow under the chip, shifted down-left */
.drawn::before {
  content: '';
  position: absolute;
  width: var(--chip-d);
  height: var(--chip-d);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.1);
  transform: translate(-7px, 7px);
}
/* the shadow of the chip together with its white ring */
.drawn.mine::before { width: calc(var(--chip-d) + 10px); height: calc(var(--chip-d) + 10px); }
.drawn.mine::after {
  content: '';
  position: absolute;
  /* 5px white ring around the visible chip circle; it overlaps the chip edge by 1.5px
     so no background or shadow shows through the anti-aliased clip edge */
  width: calc(var(--chip-d) + 7px);
  height: calc(var(--chip-d) + 7px);
  border-radius: 50%;
  border: 5px solid #fff;
}
.back-hint { left: 195px; top: 300px; border-radius: 50%; z-index: 1; } /* atlas circle with the dots of the other side; under the drawn chip (same z, put before it) */
.back-label { left: 291px; top: 316px; font-size: 22px; line-height: 25px; opacity: 0.9; }
/* Texts on the mitt: narrow enough to stay on it, and every next line steps to the right
   along the slant of the mitt ("staircase"). */
.mitt-text, .mitt-note, .mitt-timer, .end-reason { pointer-events: none; }
/* an invisible slanted float at the start of the text (see stairs() in game.js) pushes every
   next line to the right, 0.6px per 1px down; the right edge stays put, so lines get shorter */
.stair { float: left; width: 180px; height: 300px; shape-outside: polygon(0 0, 100% 100%, 0 100%); }
.mitt-text { left: 150px; top: 510px; width: 255px; font-size: 44px; line-height: 50px; z-index: 3; }
.mitt-text.bold { font-weight: 700; left: 140px; } /* ("Пора ходить!": 10 px left of the others, the user 2026-10-25) */
/* "Opponent’s turn..." — the dots light up one after another */
.think i { font-style: normal; } /* blinking: thinkingDots() in game.js */
/* "Opponent’s turn…": small (as the notes on the mitt), lower and to the right, near the middle of the
   mitt (as in the design: its "Х" 104 px right of / 200 px below the drawn chip's edge); its lines step
   less (7 px a line: the stair is 0.25 px per 1 px down) */
.mitt-text.opp { left: 189px; top: 532px; width: 220px; font-size: 22px; line-height: 28px; }
.mitt-text.opp .stair { width: 75px; }
/* while the menu is open, only its own back arrow shows in the corner */
#stage.menu-open > :not(.menu-overlay) .icon-btn { visibility: hidden; }
.mitt-note { left: 153px; top: 522px; width: 200px; font-size: 22px; line-height: 28px; z-index: 3; }
.mitt-timer { left: 128px; top: 495px; width: 240px; z-index: 3; }
.mitt-timer b { display: block; font-size: 44px; line-height: 50px; }
.mitt-timer span { display: block; font-size: 22px; line-height: 28px; }

/* atlas ribbon at native size (523 x 198); its left edge goes ~30px under the board */
.ribbon { left: -30px; top: 235px; width: 523px; height: 198px; z-index: 4; }
.ribbon-body { position: absolute; inset: 0; transform-origin: left center; } /* stretches without the text */
.ribbon-body > .spr { position: absolute; left: 0; top: 0; }
/* the shadow is cut to the front band (atlas: x 0..290 at the top, 0..327 at its bottom y 108 of 138),
   so the folded tail casts none */
.ribbon-shadow { clip-path: polygon(0 0, 79% 0, 89.1% 78.3%, 0 78.3%); }
/* the same glint as on "Play", over the ribbon: cut to the band's shape (the sprite is its mask) and
   blended as one layer; it runs over the front band up to its fold (same cut as .ribbon-shadow), not
   onto the tail, slanted 15deg the other way, parallel to the fold */
.ribbon-shine {
  position: absolute; left: 0; top: 0;
  mix-blend-mode: overlay;
  mask-image: url('assets/atlas.webp'); mask-repeat: no-repeat;
  -webkit-mask-image: url('assets/atlas.webp'); -webkit-mask-repeat: no-repeat;
  pointer-events: none;
}
.ribbon-shine-run {
  position: absolute; inset: 0;
  overflow: hidden;
  clip-path: polygon(0 0, 79% 0, 89.1% 78.3%, 0 78.3%);
}
.ribbon-shine-run::before {
  z-index: auto; mix-blend-mode: normal;
  width: 27.5%; /* as wide as the glint on "Play" was over half the ribbon */
  transform: translateX(-130%) skewX(15deg);
  animation: ribbon-shine 8s var(--shine-delay, 1.5s) infinite;
}
@keyframes ribbon-shine {
  0% { transform: translateX(-130%) skewX(15deg); animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1); }
  16.875%, 100% { transform: translateX(370%) skewX(15deg); } /* fully past the fold */
}
.ribbon .txt {
  position: absolute;
  left: 40px; /* over the part of the band not hidden by the board, nudged 10px right */
  top: 0;
  width: 383px;
  height: 154px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-size: 48px;
  line-height: 58px;
  font-weight: 700;
  text-transform: uppercase;
}
/* its two lines start together, the second 35 px further right (0.6 px per 1 px down, as on the mitt) */
.ribbon .txt-lines { text-align: left; }
.ribbon .txt-lines > div + div { padding-left: 35px; }
.ribbon.ribbonYellow .txt { color: var(--yellow-text); } /* dark text on the yellow ribbon */
/* "Technical" (a win or a defeat): under the ribbon, centered on its text (ribbon text area: 10..393) */
.end-reason { left: 51.5px; top: 405px; width: 300px; font-size: 24px; line-height: 30px; text-align: center; z-index: 3; }
.end-actions { left: -12px; top: 607px; display: flex; gap: var(--gap); z-index: 5; }
.end-actions .btn { width: 216px; font-size: 28px; }
/* "<nick> offers": a white speech bubble over "Play again" (its centre: -12 + 216 / 2), the tail pointing at it */
.rematch-bubble {
  left: 96px; top: 585px; translate: -50% -100%;
  padding: 8px 18px; /* no max width: the longest nickname (NICK_MAX = 14 letters, even 14 wide "Щ") fits whole */
  background: #fff; color: #111; border-radius: 8px;
  font-size: 24px; line-height: 30px; text-align: center; white-space: nowrap;
  z-index: 5; pointer-events: none;
}
.rematch-bubble::before { content: ''; position: absolute; left: calc(50% - 12px); bottom: -11px; border: 12px solid transparent; border-bottom: 0; border-top-color: #fff; } /* (1 px into the bubble) */

.logo-slot { left: 65px; top: -4.6px; } /* (its bottom where the smaller logo's was: see .logo) */

/* captured chip on the board: grows, flips over to its other side, shrinks back (see captureFlip() in game.js) */
.capture-flip { position: absolute; z-index: 2; transform-style: preserve-3d; }
.capture-flip .face { position: absolute; left: 0; top: 0; backface-visibility: hidden; -webkit-backface-visibility: hidden; }
/* the chip's edge: discs of the sides' colours between the two faces (see captureFlip) */
.capture-flip .rim { position: absolute; inset: 0.7%; border-radius: 50%; }
.capture-flip .rim0 { background: #f5d63d; } /* cheese side, a touch darker than its face */
.capture-flip .rim1 { background: #b8290f; } /* sausage side */
.drop-in { animation: drop 0.25s ease-out; }
@keyframes drop { 0% { transform: scale(1.25); opacity: 0.4; } 100% { transform: scale(1); opacity: 1; } }

.toast {
  position: absolute;
  left: 50%;
  top: 30px;
  transform: translateX(-50%);
  background: rgba(30, 40, 60, 0.85);
  padding: 14px 24px;
  border-radius: 10px;
  font-size: 24px;
  z-index: 60;
  pointer-events: none;
}

/* ---------- an achievement just earned (award.js): at the bottom left of the game screen ---------- */
.award-layer { position: absolute; inset: 0; z-index: 1001; pointer-events: none; } /* (above the vignette, under the menu: blurred with the screen, see depth() in app.js) */
.award-layer.hidden-under, .award-layer.hidden-under * { pointer-events: none !important; } /* (the rules over them: awardsFade() in app.js) */
.award-star { position: absolute; padding: 0; border: 0; background: none; cursor: pointer; pointer-events: auto; z-index: 2; }
.award-star:disabled { opacity: 1; cursor: default; pointer-events: none; } /* (whole: not a button, award.js setFace()) */
.award-star > .spr { position: absolute; left: 0; top: 0; }
/* the ones waiting behind the first (award.js backStar()): plain stars, each a layer lower, further right */
.award-back { position: absolute; pointer-events: none; }
.award-back > .spr { position: absolute; left: 0; top: 0; }
/* the glint over the star: a soft slanted bluish band, blended in the overlay mode, in the star's shape */
.award-shine {
  position: absolute; left: 0; top: 0; overflow: hidden; pointer-events: none;
  mix-blend-mode: overlay;
  mask-image: url('assets/atlas.webp'); mask-repeat: no-repeat;
  -webkit-mask-image: url('assets/atlas.webp'); -webkit-mask-repeat: no-repeat;
}
.award-shine-run { position: absolute; inset: 0; overflow: hidden; }
.award-shine-run::before {
  content: ''; position: absolute; inset: -20% auto -20% 0; width: 80%;
  background: linear-gradient(90deg, rgba(150, 215, 255, 0) 0%, rgba(150, 215, 255, 0.85) 50%, rgba(150, 215, 255, 0) 100%);
  transform: translateX(-150%) skewX(15deg);
  animation: award-shine 4s 0.4s infinite; /* (a run of ~1 s every 4 s, the first soon after it shows) */
}
@keyframes award-shine {
  0% { transform: translateX(-150%) skewX(15deg); animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1); }
  25%, 100% { transform: translateX(150%) skewX(15deg); } /* (from just off its left edge to just off its right one) */
}
.award-face { position: absolute; left: 50%; translate: -50% -50%; display: flex; pointer-events: none; }
.award-face.arrow { rotate: 90deg; } /* (it points left in the atlas; its glyph is centred in the sprite) */
/* the arrow grows a little under the mouse, as the corner icon buttons */
.award-face.arrow > .spr { transition: scale 0.15s ease-out; }
@media (hover: hover) { .award-star.peeking:hover .award-face.arrow > .spr { scale: 1.15; } }
.award-star.peeking.pressing .award-face.arrow > .spr { scale: 1; }
/* "Achievement earned!": a white bubble over the peeking star, its tail pointing down at it */
.award-tease {
  position: absolute; transform: translate(-50%, 0);
  padding: 10px 18px; background: #fff; color: #111; border-radius: 8px;
  font-size: 24px; line-height: 30px; text-align: center; white-space: nowrap; font-weight: 500;
}
.award-tease::before { content: ''; position: absolute; left: calc(50% - 12px); bottom: -11px; border: 12px solid transparent; border-bottom: 0; border-top-color: #fff; } /* (1 px into the bubble) */
.award-close { position: absolute; width: 64px; height: 64px; padding: 0; border: 0; background: none; display: flex; align-items: center; justify-content: center; cursor: pointer; pointer-events: auto; z-index: 3; }
.award-close > .spr { transition: scale 0.15s ease-out; }
@media (hover: hover) { .award-close:hover > .spr { scale: 1.15; } }
/* the achievement's name and text: a white bubble coming out from under the star (its left part under it) */
.award-info {
  position: absolute; transform: translate(0, 50%); z-index: 1; width: max-content; /* (as wide as its text, up to its max-width: award.js) */
  padding: 20px 26px 22px; background: #fff; color: #111; border-radius: 10px; text-align: left;
}
.award-title { font-size: 32px; line-height: 38px; font-weight: 700; }
.award-text { margin-top: 4px; font-size: 26px; line-height: 32px; font-weight: 400; }

/* ---------- the admin's table of players (admin.js; the user's mock-up, 2026-10-31) ---------- */
/* 922 wide in the middle; its rows and titles in four columns: the name from the left edge, the language centred at
   295, the status from 387, the time to the right edge; it sits as in the mock-up on a 728 high screen (titles at 128) */
.pl-table { position: absolute; left: 50%; top: calc(50% - 254px); width: 922px; translate: -50% 0; }
.pl-heads, .pl-row { position: relative; height: 36px; }
.pl-row { height: 62px; line-height: 62px; }
.pl-heads > *, .pl-row > * { position: absolute; top: 0; font-size: 30px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pl-heads > * { line-height: 36px; font-weight: 700; }
.pl-name { left: 0; width: 250px; text-align: left; }
.pl-lang { left: 295px; width: 120px; translate: -50% 0; text-align: center; }
.pl-status { left: 387px; width: 370px; text-align: left; }
.pl-time { right: 0; width: 160px; text-align: right; }
.pl-head { background: none; border: none; padding: 0; color: #fff; font-family: var(--font); cursor: pointer; }
/* under the titles: the sort arrow under the sorted column's, "•" under the others, joined by thin lines */
.pl-marks { position: relative; height: 36px; margin-top: 14px; }
.pl-mark { position: absolute; top: 0; width: 64px; height: 36px; margin-left: -32px; padding: 0; border: none; background: none; color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.pl-dot { font-size: 40px; line-height: 36px; } /* (as big as in the mock-up: ~8 px) */
.pl-arrow.down { rotate: -90deg; }
.pl-arrow.up { rotate: 90deg; }
.pl-line { position: absolute; top: 17px; height: 2px; background: rgba(255, 255, 255, 0.3); }
.pl-rows { height: 310px; margin-top: 39px; } /* (5 rows of 62) */
.pl-empty { font-size: 30px; line-height: 62px; text-align: center; opacity: 0.6; }
.pl-table > .switcher.pager, .pl-pager-slot { margin: 66px auto 0; height: var(--control-h); }
.pl-table > .switcher.pager { width: max-content; }
