/*
 * Widget chrome: buttons, cards, labels, layout.
 *
 * Warm paper rather than the usual minesweeper grey. The cell size is the one
 * measurement script owns -- it is published as --cell and everything on the
 * board, corners and digits included, derives from it.
 */

:root {
	--paper: #f7efe3;
	--cream: #fff6ea;

	--ink: #4c3b31; /* headings and the cell outline */
	--ink-soft: #8b7767; /* secondary labels */
	--ink-faint: #bcb0a2; /* disabled button text */

	--accent: #ef8f5a;
	--accent-deep: #d4703c;
	--go: #28a745; /* the way on out of a card */
	--go-deep: #1e7e34;

	--edge: #e2cfb4; /* button and card borders */
	--edge-hover: #d8bf9d;
	--disabled: #f2ece2;
	--disabled-edge: #e8ddcc;

	--tile: #e8d7bd; /* a cell with nothing known about it */
	--tile-edge: #d9c2a0;
	--open: #fbf4e9; /* a cell that has been cleared */
	--boom: #c2504b; /* the mine that went off */

	--radius-control: 12px;
	--radius-card: 20px;

	/* Board metrics, both published by the board's own measurement. */
	--cell: 32px;
	--gap: 2px;
	--columns: 9;

	color-scheme: light;
}

* {
	box-sizing: border-box;
}

html,
body {
	/* The dynamic viewport, with the percentage left in for browsers that do not
	   know dvh: a toolbar that collapses and reappears must not leave the bottom
	   controls under the fold. */
	height: 100%;
	height: 100dvh;
	margin: 0;
	/* The app is always one screen, never a document. Nothing scrolls; whatever
	   cannot fit gives up space instead. */
	overflow: hidden;
}

body {
	background: var(--paper);
	color: var(--ink);
	font-family: ui-rounded, "SF Pro Rounded", "Segoe UI Variable Display", system-ui,
		-apple-system, "Helvetica Neue", sans-serif;
	font-size: clamp(0.95rem, 3.4vw, 1.1rem);
	line-height: 1.35;
	overscroll-behavior: none;
	touch-action: manipulation;
	-webkit-text-size-adjust: 100%;
	/* A hold on a cell means flag. Without this the browser answers it with a
	   text selection and a callout menu. */
	-webkit-user-select: none;
	user-select: none;
	-webkit-touch-callout: none;
}

#app {
	position: relative;
	width: 100%;
	max-width: 32rem;
	height: 100%;
	margin-inline: auto;
	/* A phone's design width on a desktop screen. Wider than this and the board
	   grows past any reasonable reach without getting easier to read. */
}

.screen {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	padding: max(0.875rem, env(safe-area-inset-top)) 0.875rem
		max(0.875rem, env(safe-area-inset-bottom));
	overflow: hidden;
}

.screen[hidden] {
	display: none;
}

/* --- Controls ------------------------------------------------------------- */

button {
	min-height: 3.25rem;
	padding: 0.5rem 0.875rem;
	border: 2px solid var(--edge);
	border-radius: var(--radius-control);
	background: var(--cream);
	color: var(--ink);
	font: inherit;
	font-weight: 600;
	cursor: pointer;
	-webkit-user-select: none;
	user-select: none;
	transition: background-color 90ms ease-out, border-color 90ms ease-out;
}

button.primary {
	min-height: 3.75rem;
	font-size: 1.1em;
}

/* Cells are buttons too, and they are not this kind of button: their own hover
   and press rules are further down. Excluded here rather than undone there,
   because :not(:disabled) outranks a plain class and the undoing loses. */
@media (hover: hover) {
	button:not(.cell):hover:not(:disabled) {
		background: #fff0dd;
		border-color: var(--edge-hover);
		color: #3b2d25;
	}
}

button:not(.cell):active:not(:disabled) {
	background: var(--accent);
	border-color: var(--accent-deep);
	color: var(--cream);
}

button:focus-visible {
	outline: none;
	border-color: var(--accent-deep);
	box-shadow: 0 0 0 3px rgb(212 112 60 / 25%);
}

button:disabled {
	background: var(--disabled);
	border-color: var(--disabled-edge);
	color: var(--ink-faint);
	cursor: default;
}

/* Flag mode is a state, not a press, so it stays lit while it is on. */
button[aria-pressed="true"] {
	background: var(--accent);
	border-color: var(--accent-deep);
	color: var(--cream);
}

/* --- Menu ---------------------------------------------------------------- */

.menu {
	/* Centred, but not past the top edge: a stack too tall for the screen would
	   otherwise hang off both ends and take the buttons with it. Browsers
	   without `safe` keep the line above. */
	justify-content: center;
	justify-content: safe center;
	gap: 0.625rem;
	text-align: center;
	padding-inline: 1.625rem;
}

.mascot {
	display: block;
	width: 100%;
	height: auto;
	max-height: 28vh;
	object-fit: contain;
	/* Without this an image is never squeezed below its own height. The art is
	   the one thing on either screen that can afford to give. */
	min-height: 0;
	-webkit-user-drag: none;
	pointer-events: none;
}

.menu h1 {
	margin: 0;
	font-size: clamp(2rem, 9vw, 2.6rem);
	font-weight: 700;
	letter-spacing: -0.01em;
}

.subtitle {
	margin: 0;
	color: var(--ink-soft);
	text-wrap: balance;
}

.difficulties {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	margin-top: 0.5rem;
}

/* The board size is the answer; the mine count is the detail under it. */
.difficulties small {
	display: block;
	font-weight: 400;
	font-size: 0.8em;
	opacity: 0.75;
}

.stats {
	color: var(--ink-soft);
	margin-top: 0.5rem;
}

.stats h2 {
	margin: 0 0 0.25rem;
	font-size: 1em;
	font-weight: 700;
	color: var(--ink);
}

.stats p {
	margin: 0.15rem 0;
}

/*
 * A landscape phone, or a short desktop window. The art has shrunk to nothing by
 * this height anyway, so it goes, and the title stops taking a tenth of the
 * screen. Standalone installs are locked to portrait, so this is a browser tab.
 */
@media (max-height: 34rem) {
	.menu .mascot,
	.menu .subtitle,
	.stats h2 {
		display: none;
	}

	.menu {
		gap: 0.375rem;
	}

	.menu h1 {
		font-size: 1.75rem;
	}

	.difficulties button {
		min-height: 2.75rem;
	}

	.stats {
		margin-top: 0;
	}
}

/* --- Game screen --------------------------------------------------------- */

.game {
	/* Room for the anchored Menu button, so the play area can never ride up
	   underneath it on a short screen. */
	padding-top: calc(max(0.875rem, env(safe-area-inset-top)) + 3.5rem);
}

/* Anchored rather than stacked: a way out belongs at the top of the screen, not
   travelling up and down with the play area. */
.corner {
	position: absolute;
	top: max(0.875rem, env(safe-area-inset-top));
	left: 0.875rem;
	min-height: 2.75rem;
	z-index: 2;
}

.play-area {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: 0.625rem;
	/* The frame below is what gives, so the header and the toolbar keep their
	   size on a short screen. */
	min-height: 0;
}

.header-row {
	display: flex;
	align-items: center;
	gap: 0.625rem;
}

.tier {
	flex: 1;
	font-weight: 600;
}

.time {
	color: var(--ink-soft);
	font-variant-numeric: tabular-nums;
}

.score {
	display: flex;
	align-items: baseline;
	gap: 0.4rem;
	font-variant-numeric: tabular-nums;
}

.score b {
	font-size: 1.05rem;
	font-weight: 700;
}

/* Over-flagged: more flags down than there are mines. */
.score.is-over b {
	color: var(--boom);
}

/* --- The board ----------------------------------------------------------- */

/* The space the board is measured against. It is what shrinks when the screen
   is short, and it centres whatever size the board settles on. */
.board-frame {
	display: flex;
	flex: 1;
	align-items: center;
	justify-content: center;
	min-height: 0;
	overflow: hidden;
}

.board {
	display: grid;
	grid-template-columns: repeat(var(--columns), var(--cell));
	gap: var(--gap);
	/* The gesture owns the pointer outright. Without this the browser claims a
	   hold as a scroll and the flag never lands. */
	touch-action: none;
	-webkit-tap-highlight-color: transparent;
	-webkit-touch-callout: none;
}

/* Buttons, so the keyboard reaches every cell and the screen reader announces
   one. Everything the stylesheet says about a button has to be undone here. */
.cell {
	width: var(--cell);
	height: var(--cell);
	min-height: 0;
	padding: 0;
	border: 1px solid var(--tile-edge);
	border-radius: max(3px, calc(var(--cell) * 0.16));
	background: var(--tile);
	font-size: calc(var(--cell) * 0.58);
	font-weight: 700;
	line-height: 1;
	text-align: center;
	transition: none;
}

/*
 * A covered cell on a board still in play answers the pointer; nothing else on
 * the board does. A cleared cell is not a control, and once the game is over
 * neither is a covered one.
 *
 * The press is a shade of the tile rather than the accent: a hold on a cell
 * means flag, and a cell that lights up under the finger reads as the wrong
 * move landing.
 */
@media (hover: hover) {
	.board:not(.is-done) .cell:not(.is-open):hover {
		background: #f0e2cc;
		border-color: var(--edge-hover);
	}
}

.board:not(.is-done) .cell:not(.is-open):active {
	background: #dfcba9;
	border-color: var(--edge-hover);
}

.cell.is-open,
.board.is-done .cell {
	cursor: default;
}

.cell.is-open {
	border-color: transparent;
	background: var(--open);
}

.cell.is-flagged,
.cell.is-mine,
.cell.is-wrong {
	/* Emoji carry their own colour, and a font size tuned for digits leaves them
	   overflowing the cell. */
	font-size: calc(var(--cell) * 0.5);
}

/* The one that went off, as against the ones uncovered with it at the end. */
.cell.is-boom {
	background: var(--boom);
	border-color: var(--boom);
}

/* A flag left on a cell that had no mine under it. */
.cell.is-wrong {
	background: #f3ddd8;
	border-color: #e3bdb6;
}

/* The classic digit colours, warmed to sit on paper. */
.cell.n1 { color: #2f6fb0; }
.cell.n2 { color: #2f7d4f; }
.cell.n3 { color: #c2504b; }
.cell.n4 { color: #4a3f8f; }
.cell.n5 { color: #8a4b1f; }
.cell.n6 { color: #1f7f86; }
.cell.n7 { color: #3f3a36; }
.cell.n8 { color: #8b7767; }

/* --- Bottom bar ---------------------------------------------------------- */

.bottom-bar {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.status {
	min-height: 2.5em;
	margin: 0;
	text-align: center;
	text-wrap: pretty;
	color: var(--accent-deep);
	font-size: 0.85em;
}

/* The end of a lost board is read from here rather than from a card, so it
   carries the weight a card would have. */
.status.is-loss {
	color: var(--boom);
	font-weight: 600;
}

.toolbar {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 0.375rem;
}

.toolbar button {
	padding-inline: 0.25rem;
}

/* The way on from a finished board. Green, so the eye finds it without the
   button moving or the toolbar changing size. */
.toolbar button.is-next,
.toolbar button.is-next:active {
	border-color: var(--go-deep);
	background: var(--go);
	color: var(--cream);
}

@media (hover: hover) {
	.toolbar button.is-next:hover {
		border-color: var(--go-deep);
		background: var(--go-deep);
		color: var(--cream);
	}
}

/* --- Overlays ------------------------------------------------------------ */

.overlay {
	position: absolute;
	inset: 0;
	z-index: 3;
	/* Flex rather than grid so the card's max-height has a definite height to
	   measure against: a grid track sized by its own content cannot answer it. */
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 1.25rem;
	background: rgb(247 239 227 / 55%);
}

.overlay[hidden] {
	display: none;
}

.overlay.solid {
	background: rgb(247 239 227 / 94%);
}

.overlay p {
	margin: 0;
}

/* Two lines stacked, not sat side by side: the overlay centres a row by default. */
#boot-panel {
	flex-direction: column;
}

/* The reason the boot panel is still up. Long, and often a file name and a line
   number, so it wraps anywhere rather than pushing the panel sideways. */
.boot-detail {
	max-width: 22rem;
	margin-top: 0.75rem;
	color: var(--boom);
	font-size: 0.85em;
	line-height: 1.4;
	text-align: center;
	overflow-wrap: anywhere;
}

.card {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	width: 100%;
	max-height: 100%;
	padding: 2rem 2.25rem;
	border: 2px solid var(--edge);
	border-radius: var(--radius-card);
	background: var(--cream);
	text-align: center;
}

.card h2 {
	margin: 0;
	font-size: clamp(1.75rem, 8vw, 2.4rem);
	font-weight: 700;
}

#result-detail {
	color: var(--ink-soft);
	white-space: pre-wrap;
}

/*
 * The card asks a question with two answers, so its buttons are the same size
 * and carry the same ink. The fill is what separates them, and it needs its own
 * hover and press states, because the ones every other button gets would paint
 * over it.
 */
.card button.primary {
	min-height: 3.25rem;
	border-color: var(--go-deep);
	background: var(--go);
	color: var(--cream);
	font-size: inherit;
}

@media (hover: hover) {
	.card button.primary:hover {
		border-color: var(--go-deep);
		background: var(--go-deep);
		color: var(--cream);
	}
}

.card button.primary:active {
	border-color: color-mix(in srgb, black 12%, var(--go-deep));
	background: color-mix(in srgb, black 12%, var(--go-deep));
	color: var(--cream);
}

@media (prefers-reduced-motion: reduce) {
	button {
		transition: none;
	}
}
