/*
 * Leaderboard widget styling.
 *
 * The widget is embedded in the game through an Iframe or HTML Element object, so
 * it has to sit on top of whatever the game is drawing: `?bg=transparent` removes
 * the panel background, and nothing here assumes a particular viewport size.
 *
 * ---------------------------------------------------------------------------
 * TO MATCH THE GAME'S LOOK, EDIT public/theme.css - NOT THIS FILE.
 * ---------------------------------------------------------------------------
 *
 * Every colour, font and radius below is a custom property, and theme.css loads
 * after this file so it can override any of them. Keeping your values there means
 * pulling a new version of the widget never clobbers your styling.
 *
 * The tokens, and what each one paints:
 *
 *   --font          whole widget; set this to the game's UI font
 *   --font-light    low-emphasis text: status line, run count, breakdown labels
 *   --font-numeric  scores and stat columns, if the game uses a separate face
 *   --bg            page behind the panel (invisible when bg=transparent)
 *   --panel         the panel itself
 *   --panel-alt     row hover, submit panel, breakdown block
 *   --border        every rule and outline
 *   --text          primary text
 *   --text-dim      column headers, stat figures, secondary text
 *   --accent        score preview, focus rings, primary button
 *   --accent-soft   the player's own row, at rest
 *   --accent-flash  the player's own row, mid-flash after submitting
 *   --gold/--silver/--bronze   ranks 1, 2 and 3
 *   --gold-soft/--silver-soft/--bronze-soft   podium row tints
 *   --danger        error text
 *   --radius        corner rounding
 *   --pad           panel padding
 */

:root {
	--font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	/* Both default to --font, so a theme that sets only --font still works. */
	--font-light: var(--font);
	--font-numeric: var(--font);
	--bg: #0e1116;
	--panel: #161b22;
	--panel-alt: #1c222c;
	--border: #2a323d;
	--text: #e6edf3;
	--text-dim: #8b98a8;
	--accent: #4da3ff;
	--accent-soft: rgba(77, 163, 255, 0.14);
	--accent-flash: rgba(77, 163, 255, 0.42);
	--gold: #e3b341;
	--silver: #b6c2cf;
	--bronze: #cd7f47;
	/* Podium row tints. Deliberately fainter than --accent-soft so the player's
	   own row still reads as the strongest mark on the table. */
	--gold-soft: rgba(227, 179, 65, 0.11);
	--silver-soft: rgba(182, 194, 207, 0.08);
	--bronze-soft: rgba(205, 127, 71, 0.10);
	--danger: #ff6b6b;
	--radius: 10px;
	--pad: 14px;
	color-scheme: dark;
}

:root[data-theme="light"] {
	--bg: #f4f6f9;
	--panel: #ffffff;
	--panel-alt: #eef2f7;
	--border: #d5dce5;
	--text: #16202b;
	--text-dim: #5d6b7c;
	--accent: #1667c8;
	--accent-soft: rgba(22, 103, 200, 0.10);
	--accent-flash: rgba(22, 103, 200, 0.28);
	--gold: #b8860b;
	--silver: #7c8794;
	--bronze: #a2653a;
	--gold-soft: rgba(184, 134, 11, 0.12);
	--silver-soft: rgba(124, 135, 148, 0.10);
	--bronze-soft: rgba(162, 101, 58, 0.11);
	--danger: #c62828;
	color-scheme: light;
}

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

/* Author `display` rules outrank the user agent's `[hidden] { display: none }`
   whatever their specificity, so hiding has to be restated here. */
[hidden] { display: none !important; }

html, body {
	margin: 0;
	padding: 0;
	min-height: 100%;
}

body {
	background: var(--bg);
	color: var(--text);
	font: 15px/1.45 var(--font);
	/* Stops a drag inside the widget from scrolling the page behind the game. */
	overscroll-behavior: contain;
}

body[data-bg="transparent"] { background: transparent; }
body[data-bg="transparent"] .board { background: rgba(14, 17, 22, 0.86); backdrop-filter: blur(6px); }
body[data-bg="transparent"]:where([data-theme="light"]) .board { background: rgba(255, 255, 255, 0.9); }

.visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.board {
	max-width: 900px;
	margin: 0 auto;
	padding: var(--pad);
	background: var(--panel);
	border-radius: var(--radius);
	display: flex;
	flex-direction: column;
	gap: 12px;
	min-height: 100vh;
}

@media (min-width: 640px) {
	.board {
		min-height: 0;
		margin: 12px auto;
		border: 1px solid var(--border);
	}
}

.board__head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: 8px;
}

.board__title {
	margin: 0;
	font-size: 19px;
	font-weight: 650;
	letter-spacing: 0.01em;
}

.board__meta {
	font-family: var(--font-light);
	margin: 0;
	color: var(--text-dim);
	font-size: 13px;
}

/* The table is wider than a phone. It scrolls inside this box so the page itself
   never scrolls sideways under the game. */
.board__scroll {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
	margin: 0 calc(var(--pad) * -1);
	padding: 0 var(--pad);
}

.table {
	width: 100%;
	border-collapse: collapse;
	font-family: var(--font-numeric);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.table th {
	text-align: right;
	font-size: 11px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--text-dim);
	/* 6px rather than 8: the sort caret costs each header a few pixels, and this
	   buys it back across the row instead of dropping a column a size early. */
	padding: 6px 6px;
	border-bottom: 1px solid var(--border);
}

.table th.col-rank,
.table th.col-name { text-align: left; }

.table td {
	padding: 7px 6px;
	border-bottom: 1px solid var(--border);
	text-align: right;
}

/* Every entry appends a row plus a hidden detail row, so `tr:last-child` is the
   detail and the last visible row would keep its rule - doubling up against the
   footer's. The class is applied in renderTable instead. */
.table tbody tr.row--last td { border-bottom: none; }

/* Qualified with `.table` so these beat the right-aligned `.table td` default. */
.table .col-rank, .table .cell-rank { width: 3.2em; text-align: left; }
.table .col-name, .table .cell-name { text-align: left; }
.table .col-score, .table .cell-score { font-weight: 650; }

/*
 * Podium. The class is applied only on the score ordering - sorted by kills,
 * third place means third-most kills and a medal would be a lie - which is the
 * same condition that colours the rank digit.
 *
 * Deliberately quieter than .row--highlight: the player's own row has to stay
 * the loudest thing on the table even when it is also on the podium, and it
 * wins because it is defined after these.
 */
.row--medal-1 { background: var(--gold-soft); box-shadow: inset 3px 0 0 var(--gold); }
.row--medal-2 { background: var(--silver-soft); box-shadow: inset 3px 0 0 var(--silver); }
.row--medal-3 { background: var(--bronze-soft); box-shadow: inset 3px 0 0 var(--bronze); }

/* Podium stats lose the dimmed treatment - the tint eats some of the contrast
   the dim tone was relying on. */
.row--medal-1 .stat,
.row--medal-2 .stat,
.row--medal-3 .stat { color: var(--text); }

.row { cursor: pointer; }
.row:hover { background: var(--panel-alt); }
.row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.row--highlight {
	background: var(--accent-soft);
	box-shadow: inset 3px 0 0 var(--accent);
}

/* The player's own row is the one they are reading, so its stat cells drop the
   dimmed treatment - dim text over the highlight tint is hard to read. */
.row--highlight .stat,
.row--highlight .rank { color: var(--text); }

.row--context-head td {
	padding-top: 14px;
	color: var(--text-dim);
	font-size: 12px;
	text-align: center;
	letter-spacing: 0.08em;
}

.rank { font-weight: 650; color: var(--text-dim); }
.rank--1 { color: var(--gold); }
.rank--2 { color: var(--silver); }
.rank--3 { color: var(--bronze); }

.name {
	display: inline-block;
	max-width: 16ch;
	overflow: hidden;
	text-overflow: ellipsis;
	vertical-align: bottom;
}

.side {
	display: inline-block;
	margin-left: 6px;
	padding: 0 5px;
	border: 1px solid var(--border);
	border-radius: 4px;
	font-size: 10px;
	line-height: 16px;
	color: var(--text-dim);
	vertical-align: middle;
}

.stat { color: var(--text-dim); }

/* Expanded score breakdown */
.detail td {
	padding: 0 8px 12px;
	text-align: left;
	white-space: normal;
}

.breakdown {
	list-style: none;
	margin: 0;
	padding: 10px 12px;
	background: var(--panel-alt);
	border-radius: 8px;
	display: grid;
	grid-template-columns: 1fr auto auto;
	gap: 4px 12px;
	font-size: 13px;
}

.breakdown__label { color: var(--text-dim); font-family: var(--font-light); }
.breakdown__factor { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.breakdown__points { text-align: right; font-variant-numeric: tabular-nums; }

.breakdown__total {
	grid-column: 1 / -1;
	display: grid;
	grid-template-columns: subgrid;
	padding-top: 6px;
	margin-top: 2px;
	border-top: 1px solid var(--border);
	font-weight: 650;
}

/* Submit panel */
.submit {
	background: var(--panel-alt);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	padding: 14px;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.submit__title {
	margin: 0;
	font-size: 13px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--text-dim);
}

.submit__score { margin: 0; display: flex; align-items: baseline; gap: 8px; }

.submit__score-value {
	font-size: 34px;
	font-weight: 700;
	line-height: 1;
	font-variant-numeric: tabular-nums;
	color: var(--accent);
}

.submit__score-label { color: var(--text-dim); font-size: 13px; }

.submit .breakdown { background: transparent; padding: 0; }

.submit__form { display: flex; flex-direction: column; gap: 6px; }

.submit__label {
	font-family: var(--font-light);
	font-size: 12px;
	color: var(--text-dim);
}

.submit__row { display: flex; gap: 8px; flex-wrap: wrap; }

.submit__input {
	flex: 1 1 12ch;
	min-width: 0;
	padding: 9px 10px;
	font: inherit;
	color: var(--text);
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: 8px;
}

.submit__input:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 1px;
}

.submit__error { margin: 0; color: var(--danger); font-size: 13px; }

.button {
	padding: 9px 16px;
	font: inherit;
	font-weight: 600;
	color: var(--text);
	background: var(--panel-alt);
	border: 1px solid var(--border);
	border-radius: 8px;
	cursor: pointer;
}

.button:hover:not(:disabled) { border-color: var(--accent); }
.button:disabled { opacity: 0.55; cursor: default; }
.button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.button--primary {
	background: var(--accent);
	border-color: var(--accent);
	color: #fff;
}

:root[data-theme="light"] .button--primary { color: #fff; }

.status { margin: 0; color: var(--text-dim); font-size: 13px; font-family: var(--font-light); }
.status--error { color: var(--danger); }
.status[hidden] { display: none; }

.board__foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	flex-wrap: wrap;
}

.board__total { color: var(--text-dim); font-size: 13px; font-family: var(--font-light); }

/* Compact mode drops the per-stat columns for narrow overlays. */
body[data-compact="1"] .col-stat,
body[data-compact="1"] .cell-stat { display: none; }

/* ------------------------------------------------------------------ filters */

/* Centred under the centred panel title. The admin page sets --start, where the
   row sits below a left-aligned toolbar and centring would read as a mistake. */
.filters {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: flex-end;
	gap: 8px 12px;
}

.filters--start { justify-content: flex-start; }

.filters__field { display: flex; flex-direction: column; gap: 3px; flex: 0 1 11rem; min-width: 0; }

.filters__label {
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--text-dim);
	font-family: var(--font-light);
	text-align: center;
}

/* Structurally the same control as the name field, so the theme can style the
   two together and they cannot drift apart. */
.filters__select {
	font: inherit;
	font-size: 13px;
	color: var(--text);
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	padding: 6px 10px;
	min-width: 0;
	max-width: 100%;
	/* text-align centres the closed control in Firefox; text-align-last is what
	   Chromium reads. The open list is drawn by the platform either way. */
	text-align: center;
	text-align-last: center;
}

.filters__select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Marks the select that is actually narrowing the board. A class rather than
   [value=""]: a select has no value *attribute*, so an attribute selector
   matches every one of them and lights up filters that are set to Any. */
.filters__select.is-set { border-color: var(--accent); color: var(--accent); }
.filters__clear { align-self: flex-end; }

/* The match badge sits beside the side badge and reads as the same kind of mark,
   one step quieter so the side stays the more prominent of the two. */
.side--match { opacity: 0.75; letter-spacing: 0.04em; }

/* Map and mode are words, not figures: they keep the body face and wrap rather
   than forcing the table wider. */
.cell-label {
	font-variant-numeric: normal;
	white-space: normal;
	color: var(--text-dim);
	font-size: 12px;
}

/*
 * Docked down one side of the screen the board is tall and narrow, so the stat
 * columns are dropped from the right as space runs out rather than left to
 * scroll off. Rank, player and score always survive - they are the leaderboard;
 * the rest is detail, and the expanded row still shows every figure.
 *
 * These are iframe viewport widths: the widget sees the panel's own width, not
 * the game's.
 */
@media (max-width: 780px) {
	.col-bullets, .cell-bullets { display: none; }
}

@media (max-width: 890px) {
	.col-mode, .cell-mode { display: none; }
}

@media (max-width: 660px) {
	.col-deaths, .cell-deaths { display: none; }
}

/* Map outlives mode: on a board where every run is the same mode it is the map
   that distinguishes them, and it is the shorter word of the two. */
@media (max-width: 800px) {
	.col-map, .cell-map { display: none; }
}

@media (max-width: 540px) {
	.col-creations, .cell-creations { display: none; }
}

@media (max-width: 470px) {
	.col-strikes, .cell-strikes { display: none; }
}

@media (max-width: 390px) {
	.col-time, .cell-time { display: none; }
}

/*
 * The breakdown is label / factor / points. Below this the label column gets so
 * narrow that "Monetary costs (8,420,000)" wraps to three lines while the two
 * numeric columns sit on the first, leaving the count stranded from the figure
 * it belongs to. Dropping the multiplier - the least load-bearing of the three,
 * and fixed anyway - gives the label room to wrap cleanly.
 */
@media (max-width: 430px) {
	.breakdown { grid-template-columns: 1fr auto; }
	.breakdown__factor { display: none; }
	/* The total row inherits the parent's column count through subgrid, so its
	   spacer has to go too - otherwise the figure is pushed onto its own line. */
	.breakdown__spacer { display: none; }
}

/* Coarse pointers get taller rows. A finger is not a mouse, and expanding a row
   is the one gesture the whole table depends on. */
@media (pointer: coarse) {
	.table td { padding-top: 11px; padding-bottom: 11px; }
	.table th button.sort { min-height: 32px; }
}

@media (prefers-reduced-motion: no-preference) {
	.row--highlight { animation: flash 1.4s ease-out 1; }
	/* Tint only. Animating the text colour as well left the stat cells briefly
	   unreadable against a saturated fill. */
	@keyframes flash {
		0%, 30% { background: var(--accent-flash); }
		100% { background: var(--accent-soft); }
	}
}

/* ------------------------------------------------------------ sortable head */

/*
 * The header cell keeps its own padding and alignment; the button is a
 * transparent hit target inside it, so the table layout is unchanged whether a
 * column is sortable or not.
 */
/*
 * The header text is 11px, which as a tap target is far too small on a phone.
 * The button grows into the cell's own padding with a matching negative margin,
 * so the hit area clears the 24px minimum without moving anything.
 */
.table th button.sort {
	all: unset;
	display: inline-flex;
	align-items: center;
	min-height: 24px;
	padding: 6px;
	margin: -6px;
	cursor: pointer;
	font: inherit;
	letter-spacing: inherit;
	text-transform: inherit;
	color: inherit;
	white-space: nowrap;
}

.table th button.sort:hover { color: var(--text); }

.table th button.sort:focus-visible {
	outline: 1px solid var(--text);
	outline-offset: 2px;
}

.table th.is-sorted button.sort { color: var(--accent); }

/* Direction marker. A caret rather than a glyph from the font, because NORD has
   no arrow and a fallback glyph would break the line. */
.table th.is-sorted button.sort::after {
	content: "";
	display: inline-block;
	margin-left: 5px;
	border-left: 3.5px solid transparent;
	border-right: 3.5px solid transparent;
	border-top: 4px solid currentColor;
	vertical-align: middle;
}

.table th.is-sorted.is-sorted-asc button.sort::after {
	border-top: none;
	border-bottom: 4px solid currentColor;
}

/* Shown instead of the name field when the game supplied the name. */
.submit__as {
	margin: 0;
	color: var(--text-dim);
	font-size: 13px;
	font-family: var(--font-light);
}
