/*
 * SLuSH Softball - public interface.
 *
 * Mobile-first: one centred column, max-width 460px, full-bleed on phones.
 * Every colour is a token; no component hard-codes a hex. Dark is the default
 * ("night game"); light uses the warm paper palette.
 *
 * Team colours arrive as an inline --team custom property from
 * sball_team.color and are only ever used as a 3px accent bar on team
 * surfaces. They never replace a theme token, and they are deliberately kept
 * off game rows, where gold already means "next game".
 */

/*
 * Tokens live on the document root, not just on .sball-app.
 *
 * The app is no longer the only thing wearing this palette: the page shell that
 * the Elementor page template draws, and the account form Ultimate Member
 * renders, both sit OUTSIDE .sball-app and still have to match. The inline
 * script in wp_head stamps data-sball-theme on <html> before first paint, so
 * these are in force from the first frame.
 *
 * The :not() branch is the belt-and-braces case - an .sball-app rendered where
 * that script did not run still gets a full, dark set rather than no tokens.
 */
/* ------------------------------------------------------------ measurements
 *
 * Sizes and spacing, declared for EVERY theme rather than inside one.
 *
 * They used to sit in the dark palette's block, alongside the colours. That
 * block's selectors are html[data-sball-theme="dark"] and friends, so the
 * moment a member chose the light theme not one of them matched and --pad,
 * --shell and --wide were simply undefined. `padding: var(--pad)` with nothing
 * to resolve is invalid at computed-value time, which for padding means ZERO -
 * so the game-day banner rendered edge to edge with square corners while the
 * cards below it, whose values were still hard-coded numbers, looked right.
 * Putting the whole scale on tokens is what made the hole visible; the hole
 * was already there.
 *
 * A palette is a theme's business. A measurement is not, so it lives out here
 * where both themes can reach it.
 *
 * The scale runs in steps of four. Two component tokens do the real work: a
 * CARD holds a block of content and is padded evenly, a ROW is one line in a
 * list and is tighter top-to-bottom than side-to-side so a column of them does
 * not sprawl. Anything that is one of those two should say so with the token
 * rather than inventing its own numbers - which is how forty-one different
 * padding values accumulated in the first place.
 */
html,
.sball-app {
	--shell: 460px;
	--wide: 1100px;
	--pad: 20px;

	--s1: 4px;
	--s2: 8px;
	--s3: 12px;
	--s4: 16px;
	--s5: 20px;
	--s6: 24px;
	--s7: 32px;

	--card-pad: var(--s4);
	--card-radius: 10px;
	--row-pad: var(--s3) var(--s4);
	--row-radius: 8px;
	--stack: var(--s3);
}

/*
 * On a narrow phone the shell and the card were each taking 20px and 16px a
 * side, so 72px of a 360px screen was chrome before any content. Tightening
 * both by one step gives that back without changing the rhythm.
 *
 * Same selectors as above, so this is a straight override with no specificity
 * puzzle to lose.
 */
@media (max-width: 400px) {
	html,
	.sball-app {
		--pad: var(--s4);
		--card-pad: var(--s3);
		--row-pad: var(--s3);
	}
}

html[data-sball-theme="dark"],
html:not([data-sball-theme]) .sball-app,
.sball-app[data-theme="dark"] {
	--bg: #0A1428;
	--surface: #101F3C;
	--nav: #08101F;
	--ink: #F5EFE0;
	--ink2: #9BA9C4;
	--muted: #7E8CA8;
	--line: #1B2842;
	--line2: #23324F;
	--red: #C8102E;
	--onred: #FFFFFF;
	--gold: #F0B429;
	--goldbg: #F0B429;
	--ongold: #0A1428;
	--ph1: #16274A;
	--ph2: #101F3C;
	/* Both team logos are dark navy artwork on a transparent ground, so they
	   need a light plate to read at all in the dark theme. */
	--logoplate: #F6ECD6;

	--tab-active-bg: var(--goldbg);
	--tab-active-ink: var(--ongold);
	--nav-active: var(--gold);
}

html[data-sball-theme="light"],
.sball-app[data-theme="light"] {
	--bg: #F6F1E6;
	--surface: #FFFFFF;
	--nav: #FFFDF8;
	--ink: #0D2249;
	--ink2: #4A453D;
	--muted: #8A8375;
	--line: #EFE9DA;
	--line2: #DED7C7;
	--red: #C8102E;
	--onred: #FFFFFF;
	--gold: #A8791A;
	--goldbg: #E0A81C;
	--ongold: #2A1F00;
	--ph1: #EFE9DA;
	--ph2: #F6F1E6;
	--logoplate: #FFFFFF;

	--tab-active-bg: transparent;
	--tab-active-ink: var(--red);
	--nav-active: var(--red);
}


/* ------------------------------------------------- theme button neutraliser
 *
 * The active theme styles bare button elements - including :hover, :focus and
 * :active - in its own palette. Those selectors outrank a single component
 * class, so without this the app's buttons flip to the theme's teal the moment
 * they are touched or focused. Reset the ground here; the component rules below
 * are scoped under .sball-app so they win cleanly.
 */

.sball-app button,
.sball-app input[type="button"],
.sball-app input[type="reset"],
.sball-app input[type="submit"],
.sball-app button:hover,
.sball-app button:focus,
.sball-app button:active,
.sball-app input[type="submit"]:hover,
.sball-app input[type="submit"]:focus,
.sball-app input[type="submit"]:active {
	background: transparent;
	color: inherit;
	border-radius: 0;
	box-shadow: none;
	text-shadow: none;
	text-transform: none;
	letter-spacing: normal;
}

/* ------------------------------------------------------------------ shell */


.sball-app {
	background: var(--bg);
	color: var(--ink);
	/* tokens are declared above, on <html> and on this element */
	font-family: Barlow, system-ui, -apple-system, "Segoe UI", sans-serif;
	font-size: 15px;
	line-height: 1.45;
	display: flex;
	justify-content: center;
	-webkit-font-smoothing: antialiased;
}

/*
 * min-width: 0 is the whole reason wide content behaves on a phone.
 *
 * A flex item defaults to min-width: auto, which means it refuses to shrink
 * below the widest thing inside it. One roster table 417px wide therefore held
 * this column open at 417px inside a 360px screen, and every ancestor with it -
 * so the PAGE scrolled sideways and the table's own .sball-scroll never had
 * anything to scroll, because it was being stretched to fit too. Letting the
 * column shrink puts the overflow back where it belongs, inside the scroller.
 */
.sball-app > * {
	min-width: 0;
	max-width: 100%;
}

.sball-shell {
	width: 100%;
	max-width: var(--shell);
	/* Tall enough that a short screen still fills the window, short enough that
	   the site footer does not get pushed off into empty space. (This also gave
	   the sticky tab bar somewhere to stick, back when one was rendered.) */
	min-height: 72vh;
	display: flex;
	flex-direction: column;
	background: var(--bg);
	border-left: 1px solid var(--line);
	border-right: 1px solid var(--line);
}

.sball-main {
	flex: 1 1 auto;
	padding: 22px var(--pad) 28px;
}

.sball-app * {
	box-sizing: border-box;
}

.sball-app img {
	max-width: 100%;
	height: auto;
}

.sball-app a {
	color: inherit;
	text-decoration: none;
}

/* ----------------------------------------------------------------- header */

.sball-header {
	position: sticky;
	top: 0;
	z-index: 20;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 12px var(--pad);
	background: var(--nav);
	border-bottom: 1px solid var(--line);
}

/*
 * The name is SLuSH - capital S L, lowercase u, capital S H. It is an acronym
 * (St. Luke's Softball Hitters), so the lowercase u is meaning, not styling.
 *
 * That makes text-transform: uppercase actively wrong here: it renders SLUSH
 * and destroys the one letter that explains the name. The caps look is kept by
 * writing the word in caps in the markup instead, which no CSS can flatten.
 */
.sball-wordmark {
	font-family: Oswald, "Barlow Condensed", system-ui, sans-serif;
	font-weight: 700;
	font-size: 19px;
	letter-spacing: .06em;
	text-transform: none;
	color: var(--ink);
}

.sball-wordmark span {
	color: var(--gold);
}

.sball-header-left {
	display: flex;
	align-items: center;
	gap: 10px;
	min-width: 0;
}

.sball-season-select {
	font-family: Oswald, sans-serif;
	font-size: 12px;
	font-weight: 600;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--ink2);
	background: transparent;
	border: 1px solid var(--line2);
	border-radius: 999px;
	padding: 4px 10px;
	min-height: 30px;
	cursor: pointer;
}

/* -------------------------------------------------- account page field
 *
 * The appearance picker is embedded in Ultimate Member's account form, so the
 * wrapper carries the tokens but none of the app shell - no centring, no
 * min-height, no background of its own.
 */

.sball-app--inline {
	display: block;
	min-height: 0;
	background: transparent;
}

.sball-account-intro {
	margin: 0 0 14px;
	font-size: 14px;
	color: var(--ink2);
}

.sball-account-status {
	margin: 10px 0 0;
	min-height: 19px;
	font-size: 13px;
	color: var(--muted);
}

.sball-account-status[data-state="saving"] {
	color: var(--gold);
}

.sball-account-status[data-state="error"] {
	color: var(--red);
}

.sball-fieldset {
	border: 0;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.sball-fieldset .sball-section-label {
	margin-bottom: 4px;
	padding: 0;
}

.sball-option {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 12px 14px;
	min-height: 56px;
	border: 1px solid var(--line);
	border-radius: 8px;
	background: var(--bg);
	cursor: pointer;
}

.sball-option:hover {
	border-color: var(--line2);
}

.sball-option input {
	position: absolute;
	opacity: 0;
	pointer-events: none;
}

.sball-option-body {
	flex: 1 1 auto;
	min-width: 0;
}

.sball-option-name {
	display: block;
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 15px;
	letter-spacing: .03em;
	text-transform: uppercase;
	color: var(--ink);
}

.sball-option-hint {
	display: block;
	font-size: 13px;
	color: var(--muted);
	margin-top: 2px;
}

.sball-option-tick {
	flex: 0 0 22px;
	width: 22px;
	height: 22px;
	border-radius: 999px;
	border: 2px solid var(--line2);
	position: relative;
}

.sball-option input:checked ~ .sball-option-tick {
	border-color: var(--goldbg);
	background: var(--goldbg);
}

.sball-option input:checked ~ .sball-option-tick::after {
	content: "";
	position: absolute;
	left: 5px;
	top: 1px;
	width: 6px;
	height: 11px;
	border: solid var(--ongold);
	border-width: 0 2px 2px 0;
	transform: rotate(45deg);
}

.sball-option:has(input:checked) {
	border-color: var(--goldbg);
}

.sball-option input:focus-visible ~ .sball-option-tick {
	outline: 2px solid var(--red);
	outline-offset: 2px;
}


/* -------------------------------------------------------------- tab bar */

.sball-tabbar {
	position: sticky;
	bottom: 0;
	margin-top: auto;
	z-index: 20;
	display: flex;
	background: var(--nav);
	border-top: 1px solid var(--line);
}

.sball-tabbar a {
	flex: 1 1 0;
	text-align: center;
	padding: 10px 2px calc(10px + env(safe-area-inset-bottom, 0px));
	font-family: Oswald, sans-serif;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--muted);
	min-height: 48px;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 3px;
}

.sball-tabbar a.is-active {
	color: var(--nav-active);
}

.sball-tabbar svg {
	width: 18px;
	height: 18px;
	display: block;
	fill: currentColor;
}

/* ------------------------------------------------------------ primitives */

.sball-eyebrow {
	font-family: Barlow, sans-serif;
	font-weight: 500;
	font-size: 11px;
	letter-spacing: .2em;
	text-transform: uppercase;
	color: var(--muted);
}

.sball-section {
	margin-bottom: 26px;
}

.sball-section-label {
	font-family: Barlow, sans-serif;
	font-weight: 500;
	font-size: 11px;
	letter-spacing: .2em;
	text-transform: uppercase;
	color: var(--muted);
	margin-bottom: 12px;
}

.sball-title {
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 26px;
	line-height: 1.1;
	letter-spacing: .05em;
	text-transform: uppercase;
	color: var(--ink);
	margin: 0 0 4px;
}

.sball-h2 {
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 20px;
	letter-spacing: .05em;
	text-transform: uppercase;
	margin: 0 0 10px;
}

.sball-backlink {
	display: inline-block;
	font-family: Oswald, sans-serif;
	font-size: 12px;
	font-weight: 600;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--ink2);
	margin-bottom: 14px;
	min-height: 30px;
	line-height: 30px;
}

.sball-empty {
	border: 1px solid var(--line);
	border-radius: 8px;
	padding: 16px;
	color: var(--muted);
	font-size: 14px;
}

.sball-card {
	background: var(--surface);
	border-radius: var(--card-radius);
	padding: var(--card-pad);
}

.sball-row {
	background: var(--surface);
	border-radius: var(--row-radius);
	padding: var(--row-pad);
	display: flex;
	align-items: center;
	gap: var(--s3);
	margin-bottom: 8px;
	min-height: 52px;
}

.sball-row--outline {
	background: transparent;
	border: 1px solid var(--line);
	color: var(--ink2);
}

.sball-row--accent {
	border-left: 3px solid var(--goldbg);
}

.sball-row--team-accent {
	border-left: 3px solid var(--team, var(--line2));
}

.sball-row--next {
	border: 1px solid var(--goldbg);
}

.sball-row-main {
	flex: 1 1 auto;
	min-width: 0;
}

.sball-row-headline {
	display: block;
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 16px;
	letter-spacing: .03em;
	text-transform: uppercase;
	color: var(--ink);
}

.sball-row--outline .sball-row-headline {
	color: var(--ink2);
}

/* ------------------------------------------------------- final game scores
 *
 * A result row reads "KINGS 10 - JUDGES 14". Set in one colour the numbers
 * disappear into the names, and the number is the only part most people are
 * looking for. So the score keeps the full-strength ink and the team name steps
 * back to the secondary tone - the score becomes the thing the eye lands on
 * without introducing a new accent colour.
 *
 * Gold is deliberately not used here: on a game row gold already means "next
 * game", and borrowing it for a score would blur that.
 */
.sball-row-headline .sball-vs-team {
	color: var(--ink2);
	font-weight: 500;
}

.sball-row-headline .sball-vs-score {
	color: var(--ink);
	font-weight: 700;
	/* Tabular figures keep the columns from jittering between 8 and 14. */
	font-variant-numeric: tabular-nums;
}

.sball-row-headline .sball-vs-dash {
	color: var(--muted);
	padding: 0 2px;
}

.sball-row-meta {
	display: block;
	font-size: 13px;
	color: var(--muted);
	margin-top: 2px;
}

.sball-chip {
	display: inline-block;
	font-family: Oswald, sans-serif;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: .1em;
	text-transform: uppercase;
	border-radius: 999px;
	padding: 3px 10px;
	border: 1px solid var(--line2);
	color: var(--ink2);
	background: transparent;
	min-height: 24px;
	line-height: 18px;
}

.sball-chip.is-active {
	background: var(--goldbg);
	border-color: var(--goldbg);
	color: var(--ongold);
}

.sball-chip--sub {
	background: var(--goldbg);
	border-color: var(--goldbg);
	color: var(--ongold);
	letter-spacing: .08em;
	padding: 2px 8px;
}

.sball-chips {
	display: flex;
	gap: 8px;
	overflow-x: auto;
	padding-bottom: 4px;
	margin-bottom: 16px;
	scrollbar-width: none;
}

.sball-chips::-webkit-scrollbar {
	display: none;
}

.sball-chips a {
	flex: 0 0 auto;
	min-height: 32px;
	display: inline-flex;
	align-items: center;
}

.sball-app .sball-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: 44px;
	padding: 0 18px;
	border-radius: 8px;
	border: 1px solid transparent;
	background: var(--red);
	color: var(--onred);
	font-family: Oswald, sans-serif;
	font-size: 13px;
	font-weight: 600;
	letter-spacing: .08em;
	text-transform: uppercase;
	cursor: pointer;
}

.sball-app .sball-btn--outline {
	background: transparent;
	border-color: var(--line2);
	color: var(--ink);
}

.sball-app .sball-btn--done {
	background: transparent;
	border-color: var(--goldbg);
	color: var(--gold);
}

.sball-app .sball-btn[disabled] {
	opacity: .5;
	cursor: default;
}

.sball-btn-row {
	display: flex;
	gap: 10px;
	margin-top: 14px;
}

.sball-btn-row > * {
	flex: 1 1 0;
}

/* --------------------------------------------------------- placeholders */

.sball-ph {
	background: repeating-linear-gradient(135deg, var(--ph1) 0 6px, var(--ph2) 6px 12px);
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--muted);
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 10px;
	border-radius: 6px;
	overflow: hidden;
}

.sball-logo {
	border-radius: 8px;
	object-fit: contain;
	background: var(--logoplate);
	padding: 3px;
}

.sball-logo--initial {
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--surface);
	color: var(--ink);
	font-family: Oswald, sans-serif;
	font-weight: 700;
	border-radius: 8px;
}

/* ------------------------------------------------------------ next game */

.sball-next {
	background: var(--surface);
	border-radius: 10px;
	padding: 18px 16px;
}

.sball-next-teams {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	margin: 14px 0 12px;
}

.sball-next-team {
	flex: 1 1 0;
	text-align: center;
	min-width: 0;
}

.sball-next-team img,
.sball-next-team .sball-logo--initial {
	width: 78px;
	height: 78px;
	margin: 0 auto 8px;
	font-size: 30px;
}

.sball-next-team-name {
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 16px;
	letter-spacing: .04em;
	text-transform: uppercase;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.sball-next-team-record {
	font-size: 13px;
	color: var(--muted);
}

.sball-vs {
	flex: 0 0 auto;
	font-family: Oswald, sans-serif;
	font-weight: 700;
	font-size: 15px;
	letter-spacing: .1em;
	color: var(--muted);
}

.sball-next-when {
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 25px;
	letter-spacing: .02em;
	color: var(--ink);
}

.sball-next-where {
	font-size: 14px;
	color: var(--ink2);
	margin-top: 2px;
}

/* -------------------------------------------------------------- tables */

.sball-scroll {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
}

.sball-table {
	width: 100%;
	min-width: 320px;
	border-collapse: collapse;
	font-size: 14px;
}

.sball-table th {
	font-family: Barlow, sans-serif;
	font-weight: 500;
	font-size: 11px;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--muted);
	text-align: right;
	padding: 6px 8px;
	white-space: nowrap;
}

.sball-table th:first-child,
.sball-table td:first-child {
	text-align: left;
	padding-left: 0;
}

.sball-table td {
	padding: 9px 8px;
	text-align: right;
	border-top: 1px solid var(--line);
	white-space: nowrap;
	color: var(--ink);
}

.sball-table tbody tr:first-child td {
	border-top: 1px solid var(--line);
}

.sball-table .is-leader {
	color: var(--gold);
}

/* -------------------------------------------------------- standings rows */

.sball-standings-head {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 0 14px 8px;
	font-family: Barlow, sans-serif;
	font-size: 11px;
	font-weight: 500;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--muted);
}

.sball-standings-head span:first-child,
.sball-standing-name {
	flex: 1 1 auto;
	min-width: 0;
}

.sball-standings-head span:not(:first-child),
.sball-standing-val {
	flex: 0 0 34px;
	text-align: right;
}

.sball-standing-val--pct {
	flex-basis: 48px;
}

.sball-standing-name {
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 16px;
	letter-spacing: .03em;
	text-transform: uppercase;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* ------------------------------------------------------------ game hero */

.sball-hero {
	background: var(--surface);
	border-radius: 10px 10px 0 0;
	padding: 18px 16px 16px;
	border-bottom: 4px solid var(--red);
	text-align: center;
}

.sball-hero-teams {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 8px;
}

.sball-hero-team {
	flex: 1 1 0;
	min-width: 0;
}

.sball-hero-team img,
.sball-hero-team .sball-logo--initial {
	width: 58px;
	height: 58px;
	margin: 0 auto 6px;
	font-size: 22px;
}

.sball-hero-abbr {
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 14px;
	letter-spacing: .1em;
	color: var(--ink2);
}

.sball-hero-score {
	font-family: Oswald, sans-serif;
	font-weight: 700;
	font-size: 62px;
	line-height: .9;
	color: var(--ink);
}

.sball-hero-score.is-loser {
	color: var(--muted);
}

.sball-hero-dash {
	flex: 0 0 auto;
	align-self: center;
	color: var(--muted);
	font-family: Oswald, sans-serif;
	font-size: 20px;
}

.sball-hero-label {
	font-family: Barlow, sans-serif;
	font-size: 12px;
	font-weight: 500;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--muted);
	margin-top: 10px;
}

.sball-linescore th,
.sball-linescore td {
	text-align: center;
	padding-left: 6px;
	padding-right: 6px;
}

.sball-linescore th:first-child,
.sball-linescore td:first-child {
	text-align: left;
	min-width: 92px;
}

.sball-embed {
	position: relative;
	border-radius: 8px;
	overflow: hidden;
	background: var(--surface);
}

.sball-embed iframe {
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	height: auto;
	border: 0;
}

.sball-hero-status {
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 13px;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--gold);
	margin-top: 12px;
}

.sball-hero-meta {
	font-size: 14px;
	color: var(--ink2);
	margin-top: 6px;
}

/*
 * A game that was called off.
 *
 * The hero normally puts two big numbers on the screen; with no game there are
 * no numbers, and a pair of dashes reads as a score somebody forgot to type in.
 * So the dashes go and the status line has to carry the whole message on its
 * own - which means it stops being the small gold caption under a result and
 * becomes a plain-language statement, reason included.
 */
.sball-hero-status--off {
	display: inline-block;
	font-size: 14px;
	letter-spacing: .08em;
	color: var(--ink);
	background: var(--surface);
	border: 1px solid var(--line2);
	border-radius: 999px;
	padding: 7px 16px;
	/* The reason is free text, so it must be allowed to wrap on a phone. */
	white-space: normal;
	max-width: 100%;
}

/* The placeholder dash on a game that simply has not been played yet. */
.sball-hero-score--empty {
	color: var(--muted);
}

/* ---------------------------------------------------------------- tabs */

.sball-tabs {
	display: flex;
	background: var(--surface);
	border-radius: 0 0 10px 10px;
	overflow: hidden;
	margin-bottom: 20px;
}

.sball-tabs a {
	flex: 1 1 0;
	text-align: center;
	padding: 11px 4px;
	font-family: Oswald, sans-serif;
	font-size: 12px;
	font-weight: 600;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--muted);
	min-height: 44px;
	display: flex;
	align-items: center;
	justify-content: center;
}

.sball-tabs a[aria-selected="true"] {
	background: var(--tab-active-bg);
	color: var(--tab-active-ink);
}

/* -------------------------------------------------------- schedule rows */

.sball-game-row {
	display: flex;
	align-items: stretch;
	gap: 12px;
	border-radius: 8px;
	padding: 12px 14px;
	margin-bottom: 8px;
	background: var(--surface);
	min-height: 62px;
}

.sball-game-row--upcoming {
	background: transparent;
	border: 1px solid var(--line);
}

.sball-game-row--next {
	border: 1px solid var(--goldbg);
}

.sball-game-date {
	flex: 0 0 46px;
	text-align: center;
	border-right: 1px solid var(--line);
	padding-right: 10px;
	display: flex;
	flex-direction: column;
	justify-content: center;
}

.sball-game-date-m {
	font-family: Barlow, sans-serif;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--muted);
}

.sball-game-date-d {
	font-family: Oswald, sans-serif;
	font-weight: 700;
	font-size: 21px;
	line-height: 1;
	color: var(--ink);
}

.sball-flag {
	display: block;
	font-family: Oswald, sans-serif;
	font-size: 10px;
	font-weight: 600;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--gold);
	margin-top: 4px;
}

.sball-month {
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 15px;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--muted);
	margin: 20px 0 10px;
}

.sball-month:first-child {
	margin-top: 0;
}

/* ------------------------------------------------------------ stat cards */

.sball-statgrid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 8px;
}

.sball-statcard {
	background: var(--surface);
	border-radius: var(--row-radius);
	padding: var(--row-pad);
	text-align: center;
}

.sball-statcard-label {
	font-family: Barlow, sans-serif;
	font-size: 11px;
	font-weight: 500;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--muted);
}

.sball-statcard-value {
	font-family: Oswald, sans-serif;
	font-weight: 700;
	font-size: 26px;
	line-height: 1.15;
	color: var(--ink);
}

/* ------------------------------------------------------------ ranked rows */

.sball-ranked {
	display: flex;
	align-items: center;
	gap: 12px;
	background: var(--surface);
	border-radius: 8px;
	padding: 11px 14px;
	margin-bottom: 8px;
	min-height: 52px;
}

.sball-ranked.is-first {
	border-left: 3px solid var(--goldbg);
}

.sball-rank {
	flex: 0 0 24px;
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 15px;
	color: var(--muted);
}

.sball-ranked-value {
	font-family: Oswald, sans-serif;
	font-weight: 700;
	font-size: 19px;
	color: var(--ink);
}

/* ---------------------------------------------------------------- photos */

.sball-photogrid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 6px;
}

.sball-photogrid a,
.sball-photogrid span {
	position: relative;
	display: block;
	aspect-ratio: 1 / 1;
	border-radius: 6px;
	overflow: hidden;
}

.sball-photogrid img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.sball-photo-more {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background: rgba(10, 20, 40, .72);
	color: #F5EFE0;
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 17px;
}

/* -------------------------------------------------------------- rosters */

.sball-roster-group {
	margin-bottom: 20px;
}

.sball-roster-team {
	display: flex;
	align-items: center;
	gap: 8px;
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 16px;
	letter-spacing: .06em;
	text-transform: uppercase;
	padding-bottom: 8px;
	border-bottom: 2px solid var(--team, var(--line2));
	margin-bottom: 10px;
}

.sball-roster-row {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 9px 0;
	border-bottom: 1px solid var(--line);
	min-height: 44px;
}

.sball-roster-row:last-child {
	border-bottom: 0;
}

.sball-jersey {
	flex: 0 0 30px;
	font-family: Oswald, sans-serif;
	font-weight: 600;
	color: var(--gold);
}

.sball-roster-name {
	flex: 1 1 auto;
	min-width: 0;
	font-family: Oswald, sans-serif;
	font-weight: 600;
	font-size: 15px;
	letter-spacing: .02em;
	text-transform: uppercase;
}

.sball-roster-pos {
	flex: 0 0 auto;
	font-size: 13px;
	color: var(--muted);
}

.sball-footnote {
	font-size: 13px;
	color: var(--muted);
	margin-top: 10px;
}

/* -------------------------------------------------------------- player */

.sball-player-head {
	display: flex;
	align-items: center;
	gap: 14px;
	margin-bottom: 18px;
}

.sball-player-photo,
.sball-player-head .sball-ph {
	width: 92px;
	height: 92px;
	flex: 0 0 92px;
	border-radius: 8px;
	object-fit: cover;
}

.sball-player-name {
	font-family: Oswald, sans-serif;
	font-weight: 700;
	font-size: 30px;
	line-height: 1.05;
	letter-spacing: .02em;
	text-transform: uppercase;
}

.sball-player-meta {
	font-size: 14px;
	color: var(--ink2);
	margin-top: 4px;
}

/* -------------------------------------------------------- notices, forms */

.sball-notice {
	border-radius: 8px;
	padding: 11px 14px;
	font-size: 14px;
	margin-bottom: 16px;
	border: 1px solid var(--line2);
	color: var(--ink);
}

.sball-notice--good {
	border-color: var(--goldbg);
	color: var(--gold);
}

.sball-notice--bad {
	border-color: var(--red);
	color: var(--red);
}

.sball-avail {
	display: flex;
	gap: 6px;
	flex-wrap: wrap;
}

.sball-app .sball-avail button {
	flex: 1 1 auto;
	min-height: 40px;
	min-width: 72px;
	border-radius: 8px;
	border: 1px solid var(--line2);
	background: transparent;
	color: var(--ink2);
	font-family: Oswald, sans-serif;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: .08em;
	text-transform: uppercase;
	cursor: pointer;
}

.sball-app .sball-avail button.is-on {
	background: var(--goldbg);
	border-color: var(--goldbg);
	color: var(--ongold);
}

/* --------------------------------------------------------- 461 to 899px */

@media (min-width: 461px) {
	.sball-shell {
		box-shadow: 0 0 0 1px var(--line);
	}
}

/* ------------------------------------------------------- 900px and above */

@media (min-width: 900px) {
	.sball-shell {
		max-width: var(--wide);
		border: 0;
	}

	.sball-main {
		padding: 26px 32px 40px;
	}

	/* The five-item tab bar is promoted to a top nav row, same items, same
	   order. */
	.sball-tabbar {
		position: static;
		margin-top: 0;
		border-top: 0;
		border-bottom: 1px solid var(--line);
		justify-content: flex-start;
		padding: 0 32px;
		gap: 4px;
	}

	.sball-tabbar a {
		flex: 0 0 auto;
		flex-direction: row;
		gap: 8px;
		padding: 14px 16px;
		font-size: 12px;
	}

	.sball-header {
		padding: 14px 32px;
	}

	.sball-home-grid {
		display: grid;
		grid-template-columns: 1.15fr 1fr;
		gap: 26px;
		align-items: start;
	}

	.sball-statgrid--6 {
		grid-template-columns: repeat(6, 1fr);
	}

	.sball-photogrid {
		grid-template-columns: repeat(6, 1fr);
	}
}

/* Order matters: the tab bar must render above the main column on wide
   screens, and below it on phones. */
@media (min-width: 900px) {
	.sball-shell {
		display: grid;
		grid-template-rows: auto auto 1fr;
	}

	.sball-header { grid-row: 1; }
	.sball-tabbar { grid-row: 2; }
	.sball-main   { grid-row: 3; }
}

/* ------------------------------------------------------------- game day
 *
 * The one thing on the home screen that is allowed to shout. It only exists on
 * a day there is actually a game, so it never becomes wallpaper - which is the
 * usual fate of a banner that is always there.
 *
 * Gold fill rather than gold text: everywhere else on this screen gold means
 * "the next game", and this needs to outrank that without introducing a colour
 * the palette does not have.
 */
.sball-app .sball-gameday,
.sball-gameday {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s3);
	margin: 0 0 16px;
	padding: var(--card-pad);
	border-radius: var(--card-radius);
	background: var(--goldbg);
	color: var(--ongold);
}

/* The text half, which is the link to the game. */
.sball-app .sball-gameday-body,
.sball-gameday-body {
	display: flex;
	flex: 1 1 220px;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 4px 12px;
	min-width: 0;
	color: inherit;
	text-decoration: none;
}

/* --- the three things you actually do on a game day --- */

.sball-gameday-actions {
	display: flex;
	align-items: center;
	gap: 8px;
	flex: 0 0 auto;
}

.sball-app .sball-gameday-action,
.sball-gameday-action {
	display: inline-flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 2px;
	/* 44px minimum, because this is tapped outdoors with one hand. */
	min-width: 52px;
	min-height: 48px;
	padding: 4px 6px;
	border-radius: 8px;
	border: 1px solid currentColor;
	color: inherit;
	text-decoration: none;
	/* The border is the affordance; the fill would fight the gold. */
	background: transparent;
}

.sball-app .sball-gameday-action:hover,
.sball-app .sball-gameday-action:focus-visible,
.sball-gameday-action:hover,
.sball-gameday-action:focus-visible {
	background: rgba(0, 0, 0, .12);
}

.sball-gameday--final .sball-gameday-action:hover,
.sball-gameday--final .sball-gameday-action:focus-visible {
	background: var(--line2);
}

.sball-gameday-action-text {
	font-family: Oswald, sans-serif;
	font-size: 9px;
	font-weight: 600;
	letter-spacing: .08em;
	text-transform: uppercase;
	white-space: nowrap;
}

.sball-icon {
	display: block;
}

.sball-gameday-flag {
	font-family: Oswald, sans-serif;
	font-weight: 700;
	font-size: 12px;
	letter-spacing: .14em;
	/* Already uppercased in PHP - see sball_upper() and the &MIDDOT; trap. */
	white-space: nowrap;
	opacity: .8;
	width: 100%;
}

@media (min-width: 700px) {
	.sball-gameday-body {
		align-items: center;
	}
}

.sball-gameday-main {
	font-family: Oswald, sans-serif;
	font-weight: 700;
	font-size: 19px;
	letter-spacing: .02em;
	line-height: 1.15;
}

.sball-gameday-detail {
	font-family: Barlow, sans-serif;
	font-size: 13px;
	font-weight: 600;
	opacity: .82;
}

/* A game that is on right now earns a pulse; nothing else moves on the page. */
.sball-gameday--live .sball-gameday-flag::before {
	content: "";
	display: inline-block;
	width: 8px;
	height: 8px;
	margin-right: 7px;
	border-radius: 50%;
	background: var(--red);
	animation: sball-pulse 1.6s ease-in-out infinite;
}

@keyframes sball-pulse {
	0%, 100% { opacity: 1; }
	50% { opacity: .25; }
}

@media (prefers-reduced-motion: reduce) {
	.sball-gameday--live .sball-gameday-flag::before {
		animation: none;
	}
}

/* A result is news, not an invitation, so it steps back to a quieter surface. */
.sball-app .sball-gameday--final,
.sball-app .sball-gameday--off,
.sball-gameday--final,
.sball-gameday--off {
	background: var(--surface);
	color: var(--ink);
	border: 1px solid var(--line2);
}

.sball-gameday--off {
	opacity: .8;
}

/* "CANCELLED - RAINED OUT" is longer than any other flag and must be allowed
   to wrap rather than run off the side of a phone. */
.sball-gameday--off .sball-gameday-flag {
	white-space: normal;
	opacity: 1;
	color: var(--ink);
}

@media (min-width: 700px) {
	.sball-gameday-flag {
		width: auto;
	}
}

/* ------------------------------------------------------------------ uploads */

/*
 * FooGallery ships the upload trigger as a large dashed drop zone with a line
 * of limits inside it. On this site it should be a button like every other
 * button, with the limits underneath - so the box becomes a button, its own
 * limits line is hidden, and ours (rendered outside the form, in
 * sball_upload_limits()) takes its place.
 *
 * The long selectors are not decoration. FooGallery's own stylesheet carries
 * .fg-feu-form .fg-feu-drop-zone at (0,3,0) - and a .fg-feu-style2 variant that
 * also sits at (0,3,0) and resets text-transform and border-radius. Matching
 * that specificity would only tie, and a tie is decided by load order, which is
 * not ours to rely on. Naming .fg-feu-form as well puts these at (0,4,0) and
 * settles it outright, style2 included, whether or not that style is in play
 * today.
 */
.sball-app .sball-upload .fg-feu-form .fg-feu-drop-zone {
	position: relative;
	display: inline-flex;
	flex-direction: row;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: 44px;
	padding: 0 18px;
	border: 1px solid transparent;
	border-radius: 8px;
	background: var(--red);
	background-image: none;
	color: var(--onred);
	font-family: Oswald, sans-serif;
	font-size: 13px;
	font-weight: 600;
	letter-spacing: .08em;
	text-transform: uppercase;
	text-align: center;
	cursor: pointer;
	width: auto;
	min-width: 0;
	/* FooGallery sets a tall min-height for the dashed box. */
	height: auto;
}

.sball-app .sball-upload .fg-feu-form .fg-feu-drop-zone > span {
	display: inline;
	font: inherit;
	color: inherit;
}

/* FooGallery's combined "Max Size ~ Max Uploads ~ Allowed Types" line. */
.sball-app .sball-upload .fg-feu-form .fg-feu-drop-zone small {
	display: none;
}

/*
 * The file input has to stay in the DOM and stay focusable - FooGallery binds
 * to it, and the label only opens a picker because the input is inside it - so
 * it is hidden visually rather than removed.
 *
 * display:block is doing real work here. FooGallery sets display:none on this
 * input, which does still let a CLICK on the wrapping label open the picker,
 * but takes the control out of the tab order entirely - a keyboard user can
 * never reach the button at all. Restoring it to a rendered box and hiding it
 * by clipping puts it back in the tab order, which is what makes the
 * :focus-within ring below reachable rather than decorative.
 */
.sball-app .sball-upload .fg-feu-form .fg-feu-drop-zone input[type="file"] {
	display: block;
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* Keyboard users get the focus ring the hidden input can no longer show. */
.sball-app .sball-upload .fg-feu-form .fg-feu-drop-zone:focus-within {
	outline: 2px solid var(--gold);
	outline-offset: 2px;
}

.sball-upload-meta {
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin: 10px 0 0;
	font-size: 13px;
	line-height: 1.45;
	color: var(--muted);
}

/*
 * Touch devices lose the file-type line. The photo picker on a phone only
 * offers what the input accepts, so the list tells the person nothing they can
 * act on - and there is nothing to drag from either.
 *
 * (pointer: coarse) asks whether the PRIMARY pointer is a finger, so a
 * touchscreen laptop correctly keeps the line. Same test app.js uses to choose
 * between "Tap" and "Click".
 */
@media (pointer: coarse) {
	.sball-upload-types {
		display: none;
	}
}

/* -------------------------------------------------------------- claiming */

/*
 * The nudge on the home page. Sized and coloured to read as a task rather than
 * an announcement - it sits directly under the game-day strip on a game day,
 * and must not compete with it.
 */
.sball-claimbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--s3);
	margin: 0 0 18px;
	padding: var(--card-pad);
	border: 1px solid var(--line2);
	border-radius: var(--card-radius);
	background: var(--surface);
}

.sball-claimbar-body {
	display: flex;
	flex-direction: column;
	gap: 3px;
	min-width: 0;
	flex: 1 1 240px;
}

.sball-claimbar-flag {
	font-family: Oswald, sans-serif;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--gold);
}

.sball-claimbar-text {
	font-size: 14px;
	line-height: 1.45;
	color: var(--ink);
}

/*
 * Two answers, side by side. They wrap to their own line together rather than
 * splitting, so "I'm a fan" never ends up orphaned under the primary button
 * looking like an afterthought.
 */
.sball-claimbar-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	flex: 0 0 auto;
}

.sball-claimbar-optout {
	margin: 0;
}

/* The acknowledgement that replaces the bar once someone says they are a fan. */
.sball-claimbar--done {
	border-style: dashed;
	background: transparent;
}

.sball-claimbar--done .sball-claimbar-text {
	color: var(--muted);
}

.sball-claimbar--done a {
	color: var(--gold);
}

.sball-claim-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 10px 0;
	border-bottom: 1px solid var(--line);
}

.sball-claim-row:last-child {
	border-bottom: 0;
}

.sball-claim-who {
	display: flex;
	align-items: center;
	gap: 12px;
	min-width: 0;
}

.sball-claim-name {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.sball-claim-title {
	font-family: Oswald, sans-serif;
	font-size: 15px;
	font-weight: 600;
	color: var(--ink);
}

.sball-claim-meta {
	font-size: 12px;
	color: var(--muted);
}

.sball-claim-row .sball-btn {
	flex: 0 0 auto;
}

/* The escape hatch on a profile the member picked themselves. */
.sball-claim-note {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	margin: 0 0 16px;
	padding: 10px 14px;
	border: 1px dashed var(--line2);
	border-radius: 8px;
	font-size: 13px;
	color: var(--muted);
}

.sball-claim-optout {
	margin: 18px 0 0;
}

/*
 * "I don't play" and "This isn't me" are real actions, but quiet ones - a
 * button would give them the same weight as claiming, which is the thing we
 * actually want people to do.
 */
.sball-linkbtn {
	padding: 0;
	border: 0;
	background: none;
	color: var(--muted);
	font-family: inherit;
	font-size: 13px;
	text-decoration: underline;
	text-underline-offset: 3px;
	cursor: pointer;
}

.sball-linkbtn:hover {
	color: var(--ink);
}

.sball-lede {
	margin: 0 0 18px;
	font-size: 14px;
	line-height: 1.5;
	color: var(--muted);
}

@media (max-width: 420px) {
	.sball-claim-row {
		align-items: flex-start;
	}

	.sball-claim-row .sball-btn {
		align-self: center;
	}
}

/*
 * I'M IN, on the game-day strip. A button among links, so it needs the button
 * reset the anchors do not - and a filled state, because "have I already said
 * yes?" is the question people open the page to answer.
 */
.sball-gameday-action--toggle {
	font: inherit;
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
}

.sball-gameday-action--toggle.is-in {
	background: currentColor;
	border-color: currentColor;
}

/* Punch the icon and words out of the filled pill. */
.sball-gameday-action--toggle.is-in .sball-icon {
	stroke: var(--bg);
}

.sball-gameday-action--toggle.is-in .sball-gameday-action-text {
	color: var(--bg);
}

.sball-gameday-action--toggle[disabled] {
	opacity: .55;
	cursor: default;
}

/* --------------------------------------------------------- scorer line-ups */

.sball-lineup-team {
	margin: 0 0 22px;
}

.sball-lineup-team-name {
	margin: 0 0 8px;
	font-family: Oswald, sans-serif;
	font-size: 15px;
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--gold);
}

.sball-lineup-list {
	margin: 0;
	padding: 0;
	list-style: none;
	border: 1px solid var(--line);
	border-radius: 10px;
	overflow: hidden;
}

.sball-lineup-row {
	display: flex;
	align-items: center;
	gap: var(--s3);
	padding: var(--row-pad);
	border-bottom: 1px solid var(--line);
	background: var(--surface);
}

.sball-lineup-row:last-child {
	border-bottom: 0;
}

/* Somebody taken out of today's order is still listed, but plainly not batting. */
.sball-lineup-row.is-out {
	opacity: .5;
}

.sball-lineup-row.is-out .sball-lineup-name {
	text-decoration: line-through;
}

/* A stated "I can't make it" that nobody has acted on yet. */
.sball-lineup-row.is-unavailable:not(.is-out) {
	box-shadow: inset 3px 0 0 var(--red);
}

.sball-lineup-order {
	flex: 0 0 52px;
	width: 52px;
	min-height: 40px;
	text-align: center;
}

.sball-lineup-who {
	display: flex;
	flex-direction: column;
	gap: 3px;
	min-width: 0;
	flex: 1 1 auto;
}

.sball-lineup-name {
	font-size: 14px;
	color: var(--ink);
}

.sball-lineup-marks {
	display: flex;
	flex-wrap: wrap;
	gap: 5px;
}

/* The out switch is a tap target on a phone at the side of a field. */
.sball-lineup-out {
	display: flex;
	align-items: center;
	gap: 6px;
	flex: 0 0 auto;
	min-height: 44px;
	padding: 0 2px;
	font-size: 12px;
	color: var(--muted);
	cursor: pointer;
}

.sball-lineup-out input {
	width: 20px;
	height: 20px;
}

.sball-lineup-loans {
	margin: 22px 0 0;
	display: grid;
	gap: 16px;
}

.sball-lineup-loan-block h3 {
	margin: 0 0 8px;
	font-family: Oswald, sans-serif;
	font-size: 13px;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--ink2);
}

.sball-lineup-loan-form {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: 10px;
	padding: 12px;
	border: 1px solid var(--line);
	border-radius: 10px;
	background: var(--surface);
}

.sball-lineup-loan-pick {
	flex: 1 1 180px;
	min-width: 0;
}

.sball-lineup-loan-slot {
	flex: 0 0 92px;
}

.sball-lineup-loan-pick > span,
.sball-lineup-loan-slot > span {
	display: block;
	margin: 0 0 4px;
	font-size: 12px;
	color: var(--muted);
}

.sball-lineup-return {
	margin: 8px 0 0;
}

.sball-badge--sub {
	background: var(--gold);
	color: var(--ongold);
}

.sball-badge--good {
	background: #1E7A46;
	color: #FFFFFF;
}

.sball-badge--bad {
	background: var(--red);
	color: var(--onred);
}

/* ------------------------------------------------------------ text alerts */

.sball-alert-purposes {
	margin: 0 0 12px;
	padding: 0 0 0 20px;
	font-size: 14px;
	line-height: 1.6;
	color: var(--ink);
}

.sball-alert-number,
.sball-invite-number {
	display: block;
	margin: 0 0 12px;
}

.sball-alert-optin {
	margin: 0 0 8px;
}

/*
 * The invitation, shown once.
 *
 * The [hidden] rule has to be restated. display: flex outranks the browser's
 * own [hidden] rule and the theme reset's (which loads earlier), so without it
 * the card stayed on screen after app.js closed it - Count me in and No thanks
 * both did their work and then appeared to do nothing, because closing only
 * ever set an attribute nothing was honouring.
 */
.sball-invite {
	position: fixed;
	inset: 0;
	z-index: 9999;
	display: flex;
	justify-content: center;
	padding: var(--s4);
	/* Keep the card clear of the admin bar managers see. The property is only
	   defined while the bar is showing, so everyone else gets plain padding. */
	padding-top: calc(var(--s4) + var(--wp-admin--admin-bar--height, 0px));
	background: rgba(4, 9, 20, .72);
	overflow-y: auto;
	overscroll-behavior: contain;
}

.sball-invite[hidden] {
	display: none;
}

/*
 * Placed with auto margins, not align-items. Aligning pushed a card taller
 * than the window off the TOP of the overlay, where scrolling cannot reach -
 * on a phone with the keyboard up, the title and the explanation were simply
 * gone. An auto margin collapses to zero instead, so a tall card starts at the
 * top and scrolls. On a phone it sits at the bottom like a sheet; wider, it
 * centres.
 */
.sball-invite-card {
	width: 100%;
	max-width: 440px;
	margin: auto 0 0;
	/* A dialog earns one step more room than a card in the flow. */
	padding: var(--s6);
	border: 1px solid var(--line2);
	border-radius: 14px;
	background: var(--surface);
	box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
}

@media (min-width: 560px) {
	.sball-invite-card {
		margin: auto;
	}
}

.sball-invite-title {
	margin: 0 0 10px;
	font-family: Oswald, sans-serif;
	font-size: 20px;
	font-weight: 600;
	letter-spacing: .02em;
	color: var(--gold);
}

.sball-invite-body p {
	margin: 0 0 10px;
	font-size: 14px;
	line-height: 1.55;
	color: var(--ink);
}

.sball-invite-body .sball-hint {
	color: var(--muted);
}

/*
 * The form controls. The manager screens style fields and tick boxes in
 * manager.css, which is not loaded on member screens, so here the number field
 * fell back to the theme reset - a white box with its label jammed against the
 * top border - and the tick box to a bare 13px browser default with its
 * wording wrapping underneath it.
 */
.sball-invite-number {
	font-size: 13px;
	font-weight: 500;
	line-height: 1.4;
	color: var(--ink2);
}

.sball-invite .sball-invite-number input {
	display: block;
	width: 100%;
	margin-top: var(--s2);
	min-height: 46px;
	padding: 10px 12px;
	border: 1px solid var(--line2);
	border-radius: 8px;
	background: var(--bg);
	color: var(--ink);
	font-family: inherit;
	font-size: 16px; /* 16px stops iOS zooming the page on focus */
	line-height: 1.3;
	-webkit-appearance: none;
	appearance: none;
}

.sball-invite .sball-invite-number input::placeholder {
	color: var(--muted);
	opacity: 1;
}

.sball-invite .sball-invite-number input:focus {
	border-color: var(--gold);
	outline: 2px solid var(--gold);
	outline-offset: 2px;
}

.sball-invite .sball-check {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	margin: var(--s4) 0 0;
	font-size: 14px;
	line-height: 1.45;
	color: var(--ink);
	cursor: pointer;
}

.sball-invite .sball-check input {
	flex: 0 0 auto;
	width: 20px;
	height: 20px;
	margin: 0;
	accent-color: var(--red);
}

.sball-invite-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s3) var(--s5);
	margin: var(--s5) 0 0;
}

.sball-invite-status {
	margin: var(--s3) 0 0;
	font-size: 13px;
	color: var(--muted);
}

/* Takes no room until there is something to say, so the tick box and the
   buttons are not held apart by an empty line. */
.sball-invite-status:empty {
	margin: 0;
}

.sball-invite-status[data-state="error"] {
	color: var(--red);
}

/* Stop the page behind a modal from scrolling under it. */
body.sball-modal-open {
	overflow: hidden;
}

/* ------------------------------------------------------- post-game photos */

/*
 * The same shape as the claim bar, one notch quieter: it comes back after every
 * game, so it must never feel like an alarm.
 */
.sball-nudge {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--s3);
	margin: 0 0 18px;
	padding: var(--card-pad);
	border: 1px dashed var(--line2);
	border-radius: var(--card-radius);
	background: transparent;
}

.sball-nudge-body {
	display: flex;
	flex-direction: column;
	gap: 3px;
	min-width: 0;
	flex: 1 1 220px;
}

.sball-nudge-flag {
	font-family: Oswald, sans-serif;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--ink2);
}

.sball-nudge-text {
	font-size: 14px;
	line-height: 1.45;
	color: var(--ink);
}

.sball-nudge-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px;
	flex: 0 0 auto;
}

/* ------------------------------------------------------ how it works */

.sball-help-section {
	margin: 0 0 var(--s6);
}

.sball-help-title {
	margin: 0 0 var(--s1);
	font-family: Oswald, sans-serif;
	font-size: 15px;
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--gold);
}

.sball-help-lede {
	margin: 0 0 var(--s3);
	font-size: 13px;
	color: var(--muted);
}

.sball-help-list {
	margin: 0;
	padding: 0;
	list-style: none;
	border: 1px solid var(--line);
	border-radius: var(--card-radius);
	overflow: hidden;
}

.sball-help-item {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--s3);
	padding: var(--card-pad);
	border-bottom: 1px solid var(--line);
	background: var(--surface);
}

.sball-help-item:last-child {
	border-bottom: 0;
}

.sball-help-item-body {
	display: flex;
	flex-direction: column;
	gap: var(--s1);
	min-width: 0;
	flex: 1 1 200px;
}

.sball-help-item-title {
	font-family: Oswald, sans-serif;
	font-size: 15px;
	font-weight: 600;
	color: var(--ink);
}

.sball-help-item-text {
	font-size: 13px;
	line-height: 1.5;
	color: var(--muted);
}

.sball-help-item .sball-btn {
	flex: 0 0 auto;
}

/* ------------------------------------------------------------- spacing utilities
 *
 * A small, closed set of nudges, all on the scale.
 *
 * These exist because fifty-four inline style="margin:..." attributes had grown
 * across the templates - thirty different values, several of them the same idea
 * written three ways. An inline style cannot be themed, cannot be overridden
 * without !important, and does not show up when you grep the stylesheet for how
 * something is spaced, so a drifting value is invisible until somebody
 * screenshots it.
 *
 * Deliberately few. This is not a utility framework; it is the handful of
 * adjustments the templates actually needed, named after what they are FOR.
 * Anything beyond a nudge belongs in a component rule.
 */
.sball-flush { margin: 0; }
.sball-flush-top { margin-top: 0; }

.sball-gap-sm { margin-top: var(--s2); }
.sball-gap { margin-top: var(--s3); }
.sball-gap-lg { margin-top: var(--s5); }
.sball-gap-xl { margin-top: var(--s7); }

.sball-mb-sm { margin-bottom: var(--s2); }
.sball-mb { margin-bottom: var(--s3); }
.sball-mb-lg { margin-bottom: var(--s4); }

.sball-below-sm { margin: 0 0 var(--s2); }
.sball-below { margin: 0 0 var(--s3); }
.sball-below-lg { margin: 0 0 var(--s4); }

/*
 * A line that belongs to the heading above it - a date under a title, a hint
 * under a label. It is pulled up so it reads as part of that block rather than
 * as the next one. This was written four different ways inline (-6px, -8px,
 * with three different bottoms); it is one idea and now has one name.
 */
.sball-subline {
	margin: calc(var(--s1) * -1) 0 var(--s3);
}

.sball-plain-list {
	margin: 0;
	padding: 0;
	list-style: none;
}

.sball-rule {
	border: 0;
	border-top: 1px solid var(--line);
	margin: var(--s5) 0;
}

/* A panel whose child provides its own padding - a table, usually. */
.sball-panel--flush {
	padding: 0;
}

/* A card holding a list of rows: the rows carry the rhythm, so the card only
   needs to keep them off its own edges. */
.sball-card--rows {
	padding: var(--s2) var(--s4);
}

/* The photo-tab headline is a link that has to occupy its own line. */
.sball-row-headline--block {
	display: block;
	margin-bottom: var(--s3);
}

/* ------------------------------------------------------------ privacy */

/*
 * The privacy notice at the foot of the account page's Privacy tab.
 *
 * Collapsed by default. The tab above it is a working form with a Save button,
 * and a thousand words wedged between the controls and the button would make
 * the form harder to use to no one's benefit - the notice is one click away
 * and its summary says what it is.
 */
.sball-privacy {
	border: 1px solid var(--line2);
	border-radius: var(--card-radius);
	background: var(--surface);
	padding: var(--card-pad);
	margin-top: var(--s4);
}

.sball-privacy > summary {
	cursor: pointer;
	display: flex;
	flex-direction: column;
	gap: 2px;
	/* The default triangle sits oddly against a two-line summary, so the whole
	   block is the target instead. */
	list-style: none;
}

.sball-privacy > summary::-webkit-details-marker {
	display: none;
}

.sball-privacy > summary::after {
	content: "Read this";
	font-size: 12px;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--gold);
	margin-top: 6px;
}

.sball-privacy[open] > summary::after {
	content: "Hide";
}

.sball-privacy-body {
	margin-top: var(--s4);
	padding-top: var(--s4);
	border-top: 1px solid var(--line);
	/* Long-form reading, unlike the rest of the app, which is scannable
	   fragments - so this is the one place with a measure and looser leading. */
	max-width: 62ch;
	line-height: 1.6;
}

.sball-privacy-body h4 {
	margin: var(--s5) 0 var(--s2);
	font-size: 15px;
	color: var(--ink);
}

.sball-privacy-body h4:first-child {
	margin-top: 0;
}

.sball-privacy-body p {
	margin: 0 0 var(--s3);
	color: var(--ink2);
}

.sball-privacy-list {
	margin: 0 0 var(--s3);
	padding-left: 1.2em;
	display: flex;
	flex-direction: column;
	gap: var(--s2);
	color: var(--ink2);
}

.sball-privacy-list li {
	/* Overriding the app's usual list reset, which strips markers. */
	list-style: disc;
}

.sball-app .sball-privacy-body a[href] {
	color: var(--gold);
	text-decoration: underline;
	text-underline-offset: 2px;
}

.sball-app .sball-privacy-body a[href]:hover,
.sball-app .sball-privacy-body a[href]:focus {
	color: var(--ink);
}
