:root {
	color-scheme: light dark;
	--bg: #ffffff;
	--fg: #1a1a1a;
	--muted: #6b7280;
	--border: #d1d5db;
	--accent: #2563eb;
	--accent-fg: #ffffff;
	--error: #b91c1c;
	--panel: #f3f4f6;
	--overlay: rgba(255, 255, 255, 0.92);
	--chat-bg: rgba(243, 244, 246, 0.72);
}

@media (prefers-color-scheme: dark) {
	:root {
		--bg: #111827;
		--fg: #f3f4f6;
		--muted: #9ca3af;
		--border: #374151;
		--accent: #3b82f6;
		--accent-fg: #0b1220;
		--error: #f87171;
		--panel: #1f2937;
		--overlay: rgba(17, 24, 39, 0.92);
		--chat-bg: rgba(17, 24, 39, 0.72);
	}
}

* {
	box-sizing: border-box;
}

/* Без этого любой элемент, у которого собственный класс задаёт `display` (например,
   `display: flex` у .board-overlay), игнорирует атрибут/свойство `hidden` целиком:
   авторское правило побеждает правило [hidden] из UA-таблицы стилей при равной
   специфичности. Переключение `.hidden` из JS должно всегда реально прятать элемент. */
[hidden] {
	display: none !important;
}

html, body {
	height: 100%;
}

body {
	margin: 0;
	font-family: system-ui, sans-serif;
	background: var(--bg);
	color: var(--fg);
}

h1 {
	margin-top: 0;
}

.centered {
	max-width: 420px;
	margin: 4rem auto;
	padding: 0 1rem;
}

form {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

label {
	font-size: 0.9rem;
	color: var(--muted);
}

input {
	font: inherit;
	padding: 0.5rem 0.75rem;
	border: 1px solid var(--border);
	border-radius: 6px;
	background: var(--bg);
	color: var(--fg);
}

button {
	font: inherit;
	padding: 0.5rem 1rem;
	border: none;
	border-radius: 6px;
	background: var(--accent);
	color: var(--accent-fg);
	cursor: pointer;
}

button:hover {
	filter: brightness(1.1);
}

.actions {
	display: flex;
	flex-direction: column;
	gap: 1rem;
}

.join {
	display: flex;
	gap: 0.5rem;
}

.join input {
	flex: 1;
}

.error {
	color: var(--error);
}

.muted {
	color: var(--muted);
}

.lobby-layout {
	display: flex;
	min-height: 100vh;
}

.lobby-own,
.lobby-open {
	flex: 1 1 0;
	min-width: 0;
	box-sizing: border-box;
	padding: 2rem;
}

.lobby-own {
	display: flex;
	flex-direction: column;
	justify-content: center;
	border-right: 1px solid var(--border);
}

.lobby-own > * {
	max-width: 320px;
	width: 100%;
	margin-left: auto;
	margin-right: auto;
}

.public-toggle {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

.open-games {
	width: 100%;
	border-collapse: collapse;
	font-size: 0.95rem;
}

.open-games th,
.open-games td {
	padding: 0.5rem 0.6rem;
	text-align: left;
	border-bottom: 1px solid var(--border);
	white-space: nowrap;
}

.open-games th {
	color: var(--muted);
	font-weight: 600;
}

@media (max-width: 760px) {
	.lobby-layout {
		flex-direction: column;
	}

	.lobby-own {
		border-right: none;
		border-bottom: 1px solid var(--border);
	}
}

.game-layout {
	display: flex;
	height: 100vh;
	/* Страховка: даже с исправлениями min-height:0 ниже это гарантирует, что сама
	   страница игры никогда не покажет полосу прокрутки. */
	overflow: hidden;
}

.board {
	flex: 1;
	display: flex;
	flex-direction: column;
	min-width: 0;
	/* Без этого flex-элемент не может сжаться меньше своего естественного размера:
	   собственный размер canvas по умолчанию 300x150 (или перенесённая .board-info)
	   иначе выталкивал бы .game-layout за 100vh. */
	min-height: 0;
}

.board-info {
	display: flex;
	align-items: center;
	gap: 1rem;
	padding: 0.5rem 1rem;
	border-bottom: 1px solid var(--border);
	font-size: 0.9rem;
	flex-wrap: wrap;
	/* .players-panel/.chat — это оверлеи `position: fixed` у самого верха/правого
	   низа окна (z-index 10). Без этого они перекрывали бы содержимое этой полосы
	   (включая end-turn-btn) в правом верхнем углу, хотя по порядку разметки идут позже. */
	position: relative;
	z-index: 15;
}

#turn-indicator {
	font-weight: 600;
}

.board-canvas-wrap {
	position: relative;
	flex: 1;
	min-height: 0;
}

#board-canvas {
	width: 100%;
	height: 100%;
	display: block;
	/* Нативный курсор grab/grabbing в Chrome на Windows почти не виден на белой доске —
	   его заменяет собственная рука с высоким контрастом (белая заливка + чёрный контур,
	   чтобы читалась и на белой доске, и на чёрных карточках). Ключевое слово после
	   запятой — запасной вариант для браузера, который не принимает data URI. */
	cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cg fill='white' stroke='black' stroke-width='2' stroke-linejoin='round'%3E%3Crect x='9' y='17' width='15' height='11' rx='5'/%3E%3Crect x='9.5' y='9' width='4' height='10' rx='2'/%3E%3Crect x='14' y='6' width='4' height='13' rx='2'/%3E%3Crect x='18.5' y='7' width='4' height='12' rx='2'/%3E%3Crect x='23' y='9' width='4' height='10' rx='2'/%3E%3Crect x='5' y='17' width='4' height='9' rx='2' transform='rotate(-35 7 21)'/%3E%3C/g%3E%3C/svg%3E") 16 21, grab;
	touch-action: none;
}

#board-canvas:active {
	cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cg fill='white' stroke='black' stroke-width='2' stroke-linejoin='round'%3E%3Crect x='9' y='14' width='15' height='14' rx='5'/%3E%3Crect x='10' y='10' width='4' height='6' rx='2'/%3E%3Crect x='14.5' y='9' width='4' height='7' rx='2'/%3E%3Crect x='19' y='9' width='4' height='7' rx='2'/%3E%3Crect x='23.5' y='10' width='4' height='6' rx='2'/%3E%3Crect x='5' y='16' width='5' height='8' rx='2' transform='rotate(-20 7.5 20)'/%3E%3C/g%3E%3C/svg%3E") 16 21, grabbing;
}

.board-overlay {
	position: absolute;
	inset: 0;
	/* .chat/.players-panel — оверлеи `position: fixed` (z-index 10), которые могут
	   накладываться на часть области canvas. Без этого их углы перекрыли бы кнопки
	   самого оверлея. */
	z-index: 11;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 1.25rem;
	padding: 1rem;
	text-align: center;
	background: var(--overlay);
}

.board-overlay p {
	margin: 0;
	font-size: 1.2rem;
	font-weight: 600;
}

.owner-actions {
	display: flex;
	gap: 1rem;
}

.board-overlay button {
	font-size: 1.1rem;
	padding: 0.9rem 2rem;
}

/* Всплывающая подсказка вне потока flex у .board-info. Она появляется на каждую
   рассылку хода, а не только при ошибках. Внутри строки flex она перестраивала бы
   строку и сдвигала остальное содержимое. Плавающий элемент ничего не сдвигает. */
#board-message {
	position: absolute;
	top: 100%;
	right: 1rem;
	margin-top: 0.35rem;
	max-width: 320px;
	padding: 0.35rem 0.75rem;
	background: var(--panel);
	border-radius: 6px;
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
}

/* .hand-actions — настоящий flex-элемент .hand-tray, стоящий там, где лежала бы следующая карта руки
   («как будто там ещё одна карточка»). Перекрыть что-либо он не может, потому что
   участвует в той же раскладке, что и соседние карты, а не в отдельной схеме
   позиционирования. */
.hand-tray {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	display: flex;
	justify-content: center;
	align-items: flex-end;
	gap: 0.75rem;
	padding: 0.75rem;
	/* Карты лежат поверх доски, но промежутки между ними и вокруг них должны пропускать
	   клики (и перетаскивание) на доску под ними. */
	pointer-events: none;
}

.hand-cards {
	display: flex;
	gap: 0.75rem;
}

.hand-card {
	width: 110px;
	height: 110px;
	flex-shrink: 0;
	cursor: pointer;
	pointer-events: auto;
	border-radius: 8px;
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}

.hand-card.selected {
	outline: 3px solid var(--accent);
	outline-offset: 2px;
}

.hand-card.exchange-selected {
	outline: 3px solid #f59e0b;
	outline-offset: 2px;
	box-shadow: 0 0 0 6px rgba(245, 158, 11, 0.35);
}

/*: приглушает карту руки, которой сейчас некуда легально лечь, чтобы
   выделялись те, по которым можно ходить. Неактивная карта не выбирается по клику. */
.hand-card.inactive {
	opacity: 0.35;
	cursor: not-allowed;
}

/* Размером с карту (110x110, как .hand-card) и визуально пустой — без собственного
   фона и рамки, поэтому воспринимается как лишний невидимый слот карты с кнопками.
   Всегда присутствует, чтобы центровка ряда карт не сдвигалась, когда кнопки внутри
   него по отдельности показываются и прячутся. */
.hand-actions {
	width: 110px;
	min-height: 110px;
	flex-shrink: 0;
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 0.4rem;
	pointer-events: auto;
}

.hand-actions button {
	font-size: 0.75rem;
	padding: 0.4rem 0.3rem;
	line-height: 1.15;
}

/* Позицию задаёт JS динамически (left/top при каждом клике, прямо над картой джокера,
   которая его открыла); здесь только внешний вид. */
.joker-config-popup {
	position: fixed;
	z-index: 22;
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
	padding: 0.5rem;
	border: 1px solid var(--border);
	border-radius: 8px;
	background: var(--panel);
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
}

.joker-config-row {
	display: flex;
	gap: 0.3rem;
}

.joker-config-row button {
	width: 2rem;
	height: 2rem;
	padding: 0;
	font-size: 1rem;
	line-height: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--bg);
	color: var(--fg);
	border: 2px solid var(--border);
}

.joker-config-row button.selected {
	border-color: var(--accent);
	outline: 2px solid var(--accent);
	outline-offset: 1px;
}

#overlay-subtext {
	color: var(--error);
	font-size: 1rem !important;
	font-weight: 600;
}

/* Итоговая таблица — показывается вместо простого «Конец» и сразу,
   когда игра завершилась, и при позднем открытии по прямой ссылке (оверлей и
   результаты не зависят от того, *как* пришли к `finished`). */
.results-table {
	border-collapse: collapse;
	background: var(--bg);
	border-radius: 0.5rem;
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
	overflow: hidden;
}

.results-row td {
	padding: 0.5rem 1.25rem;
	text-align: left;
	font-size: 1.05rem;
	border-bottom: 1px solid var(--border);
}

.results-row:last-child td {
	border-bottom: none;
}

.results-trophy {
	width: 1.4rem;
}

.results-trophy svg {
	display: block;
	width: 1.4rem;
	height: 1.4rem;
}

.results-score {
	text-align: right;
	font-variant-numeric: tabular-nums;
	font-weight: 600;
}

/* Трофей, имя и счёт берут цвет медали через currentColor и каскад, а не повторяют
   hex-значение в каждом элементе. */
.results-row.medal-gold {
	color: #b8860b;
}

.results-row.medal-silver {
	color: #71717a;
}

.results-row.medal-bronze {
	color: #a0522d;
}

.board-overlay button.danger {
	background: var(--error);
	color: #ffffff;
}

.settings-gear {
	position: fixed;
	left: 1rem;
	top: 1rem;
	z-index: 21;
	width: 3rem;
	height: 3rem;
	padding: 0;
	border-radius: 50%;
	background: var(--muted);
	color: #ffffff;
	font-size: 1.5rem;
	line-height: 1;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* Прибиты к краю экрана (fixed), а не к .board-canvas-wrap, по тому же принципу,
   что и .settings-gear — не должны сдвигаться при пересчёте раскладки canvas.
   Выключены по умолчанию (см. zoom-buttons-checkbox): лишние элементы над полем
   нужны не всем. */
.zoom-controls {
	position: fixed;
	left: 1rem;
	top: 50%;
	transform: translateY(-50%);
	z-index: 12;
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

.zoom-controls button {
	width: 3.5rem;
	height: 3.5rem;
	padding: 0;
	border-radius: 50%;
	background: var(--muted);
	color: #ffffff;
	opacity: 0.5;
	font-size: 1.8rem;
	line-height: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: opacity 0.15s ease;
}

.zoom-controls button:hover,
.zoom-controls button:active {
	opacity: 0.85;
}

/* Остаётся в DOM (а не через [hidden]), чтобы max-height/opacity могли плавно
   меняться: display:none не анимируется. Когда закрыто, pointer-events выключены,
   чтобы свёрнутый, но всё ещё занимающий место элемент не перехватывал клики по доске. */
.settings-menu {
	position: fixed;
	left: 1rem;
	top: 4.5rem;
	z-index: 20;
	width: 240px;
	max-height: 0;
	overflow: hidden;
	opacity: 0;
	transform: translateY(-8px);
	pointer-events: none;
	transition: max-height 0.25s ease, opacity 0.2s ease, transform 0.2s ease;
	border: 1px solid var(--border);
	border-radius: 10px;
	background: var(--chat-bg);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
}

.settings-menu.open {
	max-height: 220px;
	opacity: 1;
	transform: translateY(0);
	pointer-events: auto;
}

.settings-row {
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
	padding: 0.6rem 0.9rem;
	font-size: 0.85rem;
}

.settings-row label {
	display: flex;
	align-items: center;
	gap: 0.4rem;
	color: var(--fg);
}

.settings-row input[type='range'] {
	width: 100%;
}

.settings-row input[type='range']:disabled {
	opacity: 0.4;
}

/* Тот же плавающий оверлей, что и .chat, стоящий прямо над ним (его верхний край —
   верхний край .chat с `height: 50vh`), а не прибитый к верху окна. Так он опущен
   к собственным картам руки и не сталкивается с содержимым .board-info наверху. */
.players-panel {
	position: fixed;
	right: 0;
	bottom: 50vh;
	z-index: 10;
	min-width: 180px;
	max-width: 260px;
	border: 1px solid var(--border);
	border-right: none;
	border-radius: 10px 0 0 10px;
	background: var(--chat-bg);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
}

.players-rows {
	display: flex;
	flex-direction: column;
	padding: 0.4rem 0;
}

.player-row {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.3rem 0.75rem;
	font-size: 0.9rem;
}

/* Треугольник, указывающий вправо, острием к имени. Место зарезервировано даже в
   скрытом состоянии, чтобы строки не сдвигались при смене хода. */
.player-turn-icon {
	width: 0;
	height: 0;
	flex-shrink: 0;
	border-top: 5px solid transparent;
	border-bottom: 5px solid transparent;
	border-left: 7px solid var(--accent);
	visibility: hidden;
}

.player-row.current-turn .player-turn-icon {
	visibility: visible;
}

/* Светофор соединения игрока: зелёный, пока heartbeat свежий, жёлтый
   после минуты тишины, красный, когда соединение считается потерянным. */
.conn-light {
	width: 0.65rem;
	height: 0.65rem;
	border-radius: 50%;
	flex-shrink: 0;
	box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
}

.conn-light.conn-ok {
	background: #22c55e;
}

.conn-light.conn-stale {
	background: #eab308;
}

.conn-light.conn-lost {
	background: #ef4444;
}

.player-name {
	flex: 1;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.player-row.current-turn .player-name {
	font-weight: 600;
}

.player-score {
	color: var(--muted);
	font-variant-numeric: tabular-nums;
}

/* Плавающий оверлей в правом нижнем углу (как чат в Dota/WoW), а не боковая панель:
   .board может занимать всю ширину под ним. Высота — половина окна, полупрозрачный,
   чтобы доска оставалась видна сквозь него. */
.chat {
	position: fixed;
	right: 0;
	bottom: 0;
	width: 260px;
	height: 50vh;
	z-index: 10;
	display: flex;
	flex-direction: column;
	min-height: 0;
	border: 1px solid var(--border);
	border-right: none;
	border-bottom: none;
	border-top-left-radius: 10px;
	background: var(--chat-bg);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
}

.chat-log {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	padding: 0.75rem;
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
	font-size: 0.85rem;
}

.chat-message {
	overflow-wrap: anywhere;
}

.chat-author {
	font-weight: 600;
}

.chat-form {
	flex-direction: row;
	padding: 0.5rem;
	border-top: 1px solid var(--border);
}

.chat-form input {
	flex: 1;
}

.app-version {
	position: fixed;
	left: 6px;
	bottom: 4px;
	z-index: 5;
	font-size: 10px;
	color: rgba(0, 0, 0, 0.3);
	pointer-events: none;
	user-select: none;
}

.lobby-page {
	--bg: #efeef3;
	--fg: #1c1c1c;
	--muted: #55525e;
	--border: #ccc9d4;
	--accent: #1e6fd9;
	--accent-fg: #ffffff;
	--panel: #e3e1e8;
	--error: #b91c1c;
	background: #6a6775;
	color: var(--fg);
}

.lobby-bg {
	position: fixed;
	inset: 0;
	overflow: hidden;
	pointer-events: none;
}

.lobby-bg-grid {
	position: absolute;
	left: 50%;
	top: 50%;
	width: 3200px;
	height: 3200px;
	transform: translate(-50%, -50%) rotate(-30deg);
	display: grid;
	grid-template-columns: repeat(36, 80px);
	grid-auto-rows: 80px;
	gap: 8px;
	justify-content: center;
	align-content: center;
	opacity: 0.7;
}

.lobby-bg-cell {
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 10px;
	box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.08);
}

.lobby-bg-card {
	background: #3e3c47;
	box-shadow: 0 2px 0 rgba(30, 28, 40, 0.35), inset 0 0 0 1px rgba(255, 255, 255, 0.1);
}

.lobby-bg-face {
	position: relative;
	width: 52px;
	height: 52px;
}

.lobby-bg-shape {
	position: absolute;
	inset: 0;
	background: var(--c);
	border-radius: var(--r);
	clip-path: var(--clip);
}

.lobby-bg-num {
	position: absolute;
	left: 0;
	right: 0;
	top: var(--num-top);
	text-align: center;
	font-size: 22px;
	font-weight: 800;
	line-height: 1;
	color: var(--ink);
}

.lobby-page .lobby-layout {
	position: relative;
	z-index: 1;
	gap: 32px;
	padding: 48px 56px;
	align-items: flex-start;
}

.lobby-page .lobby-own,
.lobby-page .lobby-open {
	flex: 1 1 0;
	background: var(--panel);
	border: none;
	border-radius: 16px;
	box-shadow: 0 4px 16px rgba(30, 28, 40, 0.18);
}

.lobby-page .lobby-own {
	justify-content: flex-start;
}

.lobby-page .lobby-own > h1 {
	font-size: 36px;
}

.lobby-page .lobby-open > h2 {
	margin-top: 0;
}

.lobby-page .lobby-own > * {
	max-width: 360px;
}

@media (max-width: 760px) {
	.lobby-page .lobby-layout {
		padding: 24px 16px;
	}

	.lobby-page .lobby-own {
		border-bottom: none;
	}
}
