/*=== SymbolSync ===============================================================
The game's looks: a well oiled typewriter. One monospaced face, hard edges and hard shadows that press flat under a
tap, nothing that glows or blurs, and five colors: ink, steel, sage, paper and tan. Mobile first: a phone in portrait
is the layout everything else grows from */

:root {
	color-scheme: dark;
	--ink: #161614; /* The page */
	--ink-2: #1c1c1a; /* A plate */
	--ink-3: #222220; /* A key */
	--ink-4: #2d2d2b; /* A shutter, a shade lighter than the key behind it */
	--steel: #525255; /* Borders, shadows, what's done with */
	--sage: #98a59e; /* Secondary text */
	--paper: #ecefef; /* Text */
	--tan: #d4ac74; /* The accent: the current round, a right answer, the primary key */
	--font: "IBM Plex Mono", "Courier New", Courier, monospace;
	--shadow: 4px 4px 0 var(--steel);
	--shadow-small: 3px 3px 0 var(--steel);
	--snap: 60ms;
	--safe-top: env(safe-area-inset-top, 0px);
	--safe-right: env(safe-area-inset-right, 0px);
	--safe-bottom: env(safe-area-inset-bottom, 0px);
	--safe-left: env(safe-area-inset-left, 0px);
}

*, *::before, *::after {
	box-sizing: border-box;
}

html {
	background: var(--ink);
	color: var(--paper);
	font: 15px/1.5 var(--font);
	-webkit-text-size-adjust: 100%;
}

body {
	margin: 0;
	min-height: 100dvh;
	overflow-x: hidden;
}

button {
	font: inherit;
	color: inherit;
}

.hidden {
	display: none !important;
}


/*=== The page =================================================================*/

#app {
	min-height: 100dvh;
	display: flex;
	flex-direction: column;
	max-width: 560px;
	margin: 0 auto;
	padding: calc(20px + var(--safe-top)) calc(16px + var(--safe-right)) calc(20px + var(--safe-bottom)) calc(16px + var(--safe-left));
}

.screen {
	display: flex;
	flex-direction: column;
	flex: 1;
	animation: screen_in 90ms steps(3) both;
}

.card {
	background: var(--ink-2);
	border: 2px solid var(--steel);
	padding: 20px;
	box-shadow: var(--shadow);
}


/*=== Keys =====================================================================
A button is a key: a hard border, a hard shadow, and a press that sinks it flat */

.menu {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.button {
	display: block;
	width: 100%;
	text-align: left;
	padding: 12px 16px;
	border: 2px solid var(--steel);
	background: var(--ink-3);
	color: var(--paper);
	font-weight: 700;
	letter-spacing: 1px;
	text-transform: uppercase;
	line-height: 1.2;
	cursor: pointer;
	box-shadow: var(--shadow-small);
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
	transition: transform var(--snap), box-shadow var(--snap), border-color var(--snap);
}

.button .label {
	display: block;
}

.button:active:not(:disabled) {
	transform: translate(3px, 3px);
	box-shadow: none;
}

.button:disabled {
	opacity: 0.35;
	box-shadow: none;
	cursor: default;
}

.button.primary {
	background: var(--tan);
	color: var(--ink);
	border-color: var(--tan);
}

.button.quiet {
	background: transparent;
	box-shadow: none;
}

/* A key held down: a role picked, Ready on */
.button.selected {
	border-color: var(--tan);
	background: rgba(212, 172, 116, 0.14);
	color: var(--tan);
}

.button.primary.selected {
	background: var(--tan);
	color: var(--ink);
}

.button.small {
	width: auto;
	padding: 8px 12px;
	font-size: 12px;
}

.button:focus-visible {
	outline: 2px solid var(--tan);
	outline-offset: 2px;
}

@media (hover: hover) {
	.button:hover:not(:disabled) {
		border-color: var(--sage);
	}

	.button.primary:hover:not(:disabled) {
		border-color: var(--paper);
	}
}


/*=== Home =====================================================================*/

.hero {
	text-align: center;
	padding: 32px 0 24px;
}

.title {
	margin: 0;
	font-size: 40px;
	font-weight: 700;
	line-height: 1;
	letter-spacing: -1px;
}

.title_accent {
	color: var(--tan);
}

/* The carriage's cursor, blinking after the name */
.title::after {
	content: "_";
	color: var(--tan);
	animation: blink 1s steps(1) infinite;
}

.how {
	margin: 0 0 18px;
	color: var(--sage);
	font-size: 14px;
}


/*=== Lobby ====================================================================
The room: the code large enough to read across a table, a field for a friend's, the two seats, the role keys */

.card.lobby {
	margin: auto 0;
}

.lobby_title {
	margin: 0 0 12px;
	font-size: 14px;
	font-weight: 700;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--sage);
}

.code_block {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 10px 12px;
	border: 2px solid var(--steel);
	background: var(--ink);
}

.code_label {
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--sage);
}

.code {
	flex: 1;
	font-size: 34px;
	font-weight: 700;
	line-height: 1;
	letter-spacing: 6px;
	color: var(--tan);
}

.join_block {
	display: flex;
	gap: 10px;
}

.code_input {
	flex: 1;
	min-width: 0;
	padding: 8px 12px;
	border: 2px solid var(--steel);
	background: var(--ink);
	color: var(--paper);
	font: 700 28px/1 var(--font);
	letter-spacing: 6px;
	text-transform: uppercase;
}

.code_input:focus {
	outline: none;
	border-color: var(--tan);
}

.lobby_status {
	min-height: 1.5em;
	margin: 12px 0;
	font-size: 13px;
	color: var(--sage);
}

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

.seats {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 10px;
	margin: 0 0 14px;
}

.seat {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 10px 12px;
	border: 2px solid var(--steel);
}

.seat.empty {
	border-style: dashed;
	opacity: 0.6;
}

.seat.away {
	opacity: 0.5;
}

.seat_label {
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--sage);
}

.seat_role {
	font-size: 15px;
	font-weight: 700;
}

.seat_ready {
	min-height: 1.4em;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: var(--tan);
}

.roles {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 10px;
	margin: 0 0 14px;
}


/*=== Speaker ==================================================================
The ten answers, two to a row, big enough to describe from, in order */

.hud_label {
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--sage);
}

.speaker_note {
	margin: 14px 0 10px;
	min-height: 1.5em;
	font-size: 13px;
	color: var(--sage);
}

.speaker_list {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 14px;
	list-style: none;
	margin: 0 0 18px;
	padding: 8px 4px 4px 8px;
}

.speak_card {
	position: relative;
	overflow: hidden; /* The open shutter's plate waits above the card */
	aspect-ratio: 1.3;
	border: 2px solid var(--steel);
	background: var(--ink-3);
	box-shadow: var(--shadow-small);
	display: grid;
	place-items: center;
	transition: border-color 120ms steps(2), opacity 120ms steps(2);
}

.speak_card img {
	width: 52%;
	height: 70%;
	object-fit: contain;
	opacity: 0.92;
}

/* The round's number, a tab on the corner. Inside the card (its overflow is hidden for the shutter), above the shutter */
.speak_card::before {
	content: attr(data-round);
	position: absolute;
	top: 0;
	left: 0;
	z-index: 1;
	min-width: 28px;
	height: 28px;
	padding: 0 6px;
	background: var(--ink);
	border-right: 2px solid var(--steel);
	border-bottom: 2px solid var(--steel);
	color: var(--sage);
	font-size: 15px;
	font-weight: 700;
	line-height: 28px;
	text-align: center;
}

/* The round the Listener is on */
.speak_card.current {
	border-color: var(--tan);
}

.speak_card.current::before {
	background: var(--tan);
	border-color: var(--tan);
	color: var(--ink);
}

.speak_card.done {
	opacity: 0.4;
}


/*=== Match: the HUD ===========================================================
The bar drains from the right, tan while there's time, paper when it's getting short, and blinking at the end */

.hud {
	padding-top: 2px;
}

.timer_bar {
	height: 10px;
	border: 2px solid var(--steel);
	background: var(--ink-2);
}

.timer_fill {
	height: 100%;
	width: 100%;
	background: var(--tan);
	transform-origin: left center;
	transform: scaleX(var(--t, 1));
}

.timer_fill.warn {
	background: var(--paper);
}

.timer_fill.danger {
	background: var(--paper);
	animation: blink 0.5s steps(1) infinite;
}

.hud_row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	margin-top: 12px;
}

.clock {
	font-size: 30px;
	font-weight: 700;
	line-height: 1;
	font-variant-numeric: tabular-nums;
	min-width: 4.2ch;
	padding: 3px 6px;
	text-align: right;
}

.clock.warn {
	color: var(--tan);
}

/* The last seconds: the clock inverts, like a ribbon switched to red, and blinks */
.clock.danger {
	background: var(--paper);
	color: var(--ink);
	animation: blink 0.5s steps(1) infinite;
}


/*=== Pips: a square per round =================================================
Hollow to come, blinking now, tan when right, steel when wrong, dashed when time ran out first */

.pips {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	list-style: none;
	margin: 0;
	padding: 0;
}

.pip {
	width: 11px;
	height: 11px;
	border: 2px solid var(--steel);
}

.pip.current {
	border-color: var(--tan);
	animation: blink 1s steps(1) infinite;
}

.pip.correct {
	background: var(--tan);
	border-color: var(--tan);
	animation: stamp 120ms steps(2) both;
}

.pip.wrong {
	background: var(--steel);
	animation: stamp 120ms steps(2) both;
}

.pip.missed {
	border-style: dashed;
	opacity: 0.6;
}


/*=== The grid: nine tiles =====================================================
Three by three, as wide as the screen allows and never taller than the room between the clock and the strip. A tile
is a key behind a shutter: a tap sinks it and the shutter shuts, the symbol changes behind it, and the shutter opens
on the next round (see listener.js) */

.grid {
	flex: 1;
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 12px;
	align-content: center;
	width: 100%;
	max-width: min(520px, calc(100dvh - 270px));
	margin: 18px auto;
	padding-right: 4px;
	padding-bottom: 4px;
}

.tile {
	position: relative;
	overflow: hidden; /* The open shutter's blades wait outside the tile */
	aspect-ratio: 1;
	padding: 0;
	border: 2px solid var(--steel);
	background: var(--ink-3);
	box-shadow: var(--shadow);
	display: grid;
	place-items: center;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
	user-select: none;
	-webkit-user-select: none;
	transition: transform var(--snap), box-shadow var(--snap), border-color var(--snap);
}

.tile img {
	width: 56%;
	height: 56%;
	object-fit: contain;
	opacity: 0.92;
	pointer-events: none;
}

.tile img:not([src]),
.speak_card img:not([src]) {
	visibility: hidden;
}

/* The count, 3, 2, 1, shown behind one shutter before the match: the Listener's centre tile, the Speaker's first card */
.tile_count {
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
	font-size: clamp(36px, 10vw, 60px);
	font-weight: 700;
	line-height: 1;
	color: var(--paper);
	pointer-events: none;
}

/* The shutter: one plate, a shade lighter than the key, with a steel edge along its bottom. Open, it waits above the
tile; shut, it covers it. It drops in two hard steps, near instant, and lifts in three */
.blade {
	position: absolute;
	inset: 0;
	background: var(--ink-4);
	border-bottom: 3px solid var(--steel);
	pointer-events: none;
	transform: translateY(-100%);
	transition: transform 70ms steps(3);
}

.closed > .blade {
	transform: translateY(0);
	transition: transform 45ms steps(2);
}

/* A box rattles as its shutter moves: a small rotation, back and forth and dying away, that runs on past the
shutter's travel. --j is the first swing, set afresh each time (see shutters.js). On the rotate property, so it
composes with the press and hover transforms */
.jitter {
	animation: jitter 220ms linear both;
}

.tile:active {
	transform: translate(4px, 4px);
	box-shadow: none;
}

@media (hover: hover) {
	.tile:hover {
		border-color: var(--sage);
	}
}

.tile:focus-visible {
	outline: 2px solid var(--tan);
	outline-offset: 2px;
}

.grid.locked .tile {
	pointer-events: none;
}


/*=== The strip: the Speaker's view, in practice ===============================
The ten answers in a row along the bottom, the current round lit and scrolled into view, the finished ones dimmed
with their result stamped on. In M2 the Speaker's own screen is this, grown up */

.strip {
	border-top: 2px solid var(--steel);
	padding-top: 10px;
}

.strip_label {
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--sage);
	margin-bottom: 10px;
}

.answers {
	display: flex;
	gap: 12px;
	list-style: none;
	margin: 0;
	padding: 12px 12px 12px 12px; /* Room for the tabs and stamps, which a scrolling row would otherwise clip */
	overflow-x: auto;
	scrollbar-width: none;
}

.answers::-webkit-scrollbar {
	display: none;
}

.answer {
	position: relative;
	flex: 0 0 auto;
	width: 52px;
	aspect-ratio: 1;
	border: 2px solid var(--steel);
	background: var(--ink-2);
	display: grid;
	place-items: center;
	opacity: 0.5;
}

.answer img {
	width: 64%;
	height: 64%;
	object-fit: contain;
	opacity: 0.92;
}

/* The round's number, a tab on the corner */
.answer::before {
	content: attr(data-round);
	position: absolute;
	top: -10px;
	left: -10px;
	min-width: 22px;
	height: 22px;
	padding: 0 4px;
	background: var(--ink);
	border: 2px solid var(--steel);
	color: var(--sage);
	font-size: 13px;
	font-weight: 700;
	line-height: 18px;
	text-align: center;
}

.answer.current {
	opacity: 1;
	border-color: var(--tan);
}

.answer.current::before {
	background: var(--tan);
	border-color: var(--tan);
	color: var(--ink);
}

.answer.done {
	opacity: 0.35;
}

/* The result, stamped on the other corner */
.answer.done::after {
	position: absolute;
	right: -10px;
	bottom: -10px;
	width: 22px;
	height: 22px;
	font-size: 14px;
	font-weight: 700;
	line-height: 22px;
	text-align: center;
}

.answer.done.correct::after {
	content: "\2713";
	background: var(--tan);
	color: var(--ink);
}

.answer.done.wrong::after {
	content: "\2715";
	background: var(--steel);
	color: var(--paper);
}


/*=== Results ==================================================================
A short show, top to bottom (see results.js): the title typed, the rounds revealed one by one, the scoring lines
tallied into the score, a flash, then the keys. Every part is in place from the start and only comes into view, so
the screen is the same height whatever the result */

.results {
	margin: auto 0;
	text-align: center;
}

.results_title {
	margin: 0 0 16px;
	min-height: 1.2em;
	font-size: 26px;
	font-weight: 700;
	line-height: 1.2;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--tan);
}

/* Typed out, with the cursor after it until it's done */
.results_title.typing::after {
	content: "_";
}

/* A unit per round, five to a row: one frame, two slots sharing an edge, the symbol submitted on top and the right
one under it. The empty frames are there from the start and fill in three steps: .shown (the submitted symbol),
.answered (the right one), .stamped (the verdict: the whole unit lit tan with a tick when right, the submission
greyed with a cross when wrong). A round never reached goes dashed, an empty top over its answer */

.rounds {
	display: grid;
	grid-template-columns: repeat(5, 1fr);
	gap: 26px 12px; /* Rows far enough apart that a stamp below one unit and the number above the next can't meet */
	list-style: none;
	margin: 0 0 18px;
	padding: 8px 0 0 4px;
}

.round_card {
	position: relative;
	display: flex;
	flex-direction: column;
	border: 2px solid var(--steel);
	background: var(--ink-2);
	transition: border-color 120ms steps(2), background-color 120ms steps(2), opacity 120ms steps(2);
}

/* The round's number, a tab on the corner */
.round_card::before {
	content: attr(data-round);
	position: absolute;
	top: -11px;
	left: -11px;
	z-index: 1;
	min-width: 22px;
	height: 22px;
	padding: 0 4px;
	background: var(--ink);
	border: 2px solid var(--steel);
	color: var(--sage);
	font-size: 13px;
	font-weight: 700;
	line-height: 18px;
	text-align: center;
}

.round_symbol {
	position: relative;
	width: 100%; /* Said outright: in a flex column, aspect-ratio alone sizes the box from its picture */
	aspect-ratio: 1;
	display: grid;
	place-items: center;
}

/* The shared edge */
.slot_tapped {
	border-bottom: 2px solid var(--steel);
}

.round_symbol img {
	width: 64%;
	height: 64%;
	object-fit: contain;
	opacity: 0.92;
}

/* Out of view until its step */
.round_card:not(.shown) .slot_tapped img,
.round_card:not(.answered) .slot_answer img {
	opacity: 0;
}

.round_card.shown .slot_tapped img,
.round_card.answered .slot_answer img {
	animation: strike 90ms steps(3) backwards; /* backwards, not both: once in, the verdict's own opacity must apply */
}

/* The verdict */
.round_card.stamped.correct {
	border-color: var(--tan);
	background: rgba(212, 172, 116, 0.1);
}

.round_card.stamped.correct .slot_tapped {
	border-bottom-color: var(--tan);
}

.round_card.stamped.correct::before {
	background: var(--tan);
	border-color: var(--tan);
	color: var(--ink);
}

.round_card.stamped.wrong .slot_tapped img {
	opacity: 0.3;
}

.round_card.stamped.missed {
	border-style: dashed;
	opacity: 0.45;
}

.round_card.stamped.missed .slot_tapped {
	border-bottom-style: dashed;
}

/* The verdict's stamp, on the bottom corner as on the Speaker's strip: the top corners hold the numbers, and a
stamp up there would meet the next unit's number in the gap */
.round_card.stamped.correct::after,
.round_card.stamped.wrong::after {
	position: absolute;
	bottom: -11px;
	right: -11px;
	z-index: 1;
	width: 22px;
	height: 22px;
	font-size: 14px;
	font-weight: 700;
	line-height: 22px;
	text-align: center;
	animation: stamp 120ms steps(2) both;
}

.round_card.stamped.correct::after {
	content: "\2713";
	background: var(--tan);
	color: var(--ink);
}

.round_card.stamped.wrong::after {
	content: "\2715";
	background: var(--steel);
	color: var(--paper);
}

/* The scoring lines, tallied one after another into the score */

.breakdown {
	margin: 0 0 14px;
	text-align: left;
	border-top: 2px solid var(--steel);
}

.row {
	display: grid;
	grid-template-columns: auto 1fr auto;
	gap: 12px;
	align-items: baseline;
	padding: 8px 2px;
	border-bottom: 1px solid var(--steel);
}

.row_label {
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: var(--sage);
}

.row_detail {
	font-size: 13px;
	color: var(--sage);
}

.row_points {
	min-height: 18px;
	font-size: 18px;
	font-weight: 700;
	line-height: 1;
	font-variant-numeric: tabular-nums;
}

.row.tallying .row_points {
	color: var(--tan);
}

.row_points.zero {
	color: var(--steel);
}

/* The score, and the flash as it lands */

.score_block {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	margin: 0 0 18px;
	padding: 10px 12px;
	border: 2px solid var(--steel);
}

.score_label {
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--sage);
}

.score {
	font-size: 44px;
	font-weight: 700;
	line-height: 1;
	font-variant-numeric: tabular-nums;
}

.score_block.flash {
	border-color: var(--tan);
	animation: flash 320ms steps(1) both;
}

/* The keys are in place, unseen, until the show is over */
.results_menu {
	visibility: hidden;
}

.results_menu.ready {
	visibility: visible;
	animation: strike 90ms steps(3) both;
}

/* Play again, in a room: two keys on one line, roles kept or swapped. Yours is held down once pressed */
.rematch_row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 10px;
}

.button .sub {
	display: block;
	margin-top: 3px;
	font-size: 11px;
	font-weight: 400;
	letter-spacing: 1px;
	opacity: 0.8;
}

.rematch_status {
	min-height: 1.5em;
	margin: -2px 0 0;
	font-size: 12px;
	text-align: left;
	color: var(--sage);
}


/*=== Motion ===================================================================
Short and stepped, never eased: things strike into place */

@keyframes screen_in {
	from { opacity: 0; }
	to { opacity: 1; }
}

@keyframes strike {
	from { opacity: 0; transform: translateY(-4px); }
	to { opacity: 1; transform: none; }
}

@keyframes stamp {
	from { transform: scale(1.6); }
	to { transform: scale(1); }
}

@keyframes blink {
	0%, 100% { opacity: 1; }
	50% { opacity: 0.2; }
}

@keyframes jitter {
	0% { rotate: 0deg; }
	15% { rotate: var(--j, 1deg); }
	35% { rotate: calc(var(--j, 1deg) * -0.7); }
	55% { rotate: calc(var(--j, 1deg) * 0.45); }
	75% { rotate: calc(var(--j, 1deg) * -0.2); }
	100% { rotate: 0deg; }
}

/* Two hard flashes, inverted, then back */
@keyframes flash {
	0%, 40% { background: var(--paper); color: var(--ink); border-color: var(--paper); }
	20%, 60%, 100% { background: transparent; color: var(--paper); border-color: var(--tan); }
}

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