/* ==========================================================================
   design-system.css
   Tokens, base styles, primitive components, and utility classes.
   Every other stylesheet imports this file first.
   ========================================================================== */

:root {
	color-scheme: dark;

	/* Surfaces */
	--bg: #07090f;
	--bg-elev: #0c0f17;
	--surface-1: rgba(255, 255, 255, 0.04);
	--surface-2: rgba(255, 255, 255, 0.07);
	--surface-3: rgba(255, 255, 255, 0.1);
	--surface-solid: #0f1320;
	--border: rgba(255, 255, 255, 0.08);
	--border-strong: rgba(255, 255, 255, 0.14);
	--border-accent: rgba(239, 68, 68, 0.4);

	/* Text */
	--text: #f4f5f9;
	--text-2: #a8afc1;
	--text-3: #6b7388;
	--text-on-accent: #07090f;

	/* Accents — aurora palette */
	--violet: #ef4444;
	--indigo: #991b1b;
	--blue: #f87171;
	--cyan: #7f1d1d;
	--pink: #fca5a5;
	--aurora: linear-gradient(
		135deg,
		#ef4444 0%,
		#f87171 35%,
		#7f1d1d 70%,
		#fca5a5 100%
	);
	--aurora-soft: linear-gradient(
		135deg,
		rgba(239, 68, 68, 0.5),
		rgba(248, 113, 113, 0.5),
		rgba(127, 29, 29, 0.5),
		rgba(252, 165, 165, 0.5)
	);
	--aurora-violet: linear-gradient(135deg, #ef4444, #991b1b);
	--aurora-blue: linear-gradient(135deg, #f87171, #7f1d1d);
	--aurora-warm: linear-gradient(135deg, #fca5a5, #f97316);

	/* Semantic */
	--success: #22c55e;
	--warning: #fbbf24;
	--danger: #f87171;
	--score-pos: #22c55e;
	--score-mixed: #fbbf24;
	--score-neg: #f87171;

	/* ponytail: soft-accent derivation for low-alpha focus shadows and tints.
	   Half-strength of --border-accent; same hue, lower alpha. */
	--border-accent-soft: rgba(239, 68, 68, 0.18);

	/* ponytail: warning-color derivations. --warning is the base; --warning-soft
	   is the low-alpha surface tint; --warning-border is the 30% border;
	   --warning-text is a brighter variant for text on dark surfaces. */
	--warning-soft: rgba(251, 191, 36, 0.12);
	--warning-border: rgba(251, 191, 36, 0.3);
	--warning-text: #fde68a;

	/* Typography */
	--font-sans:
		"Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	--font-display: "Inter", system-ui, sans-serif;
	--font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

	--text-xs: 0.75rem;
	--text-sm: 0.8125rem;
	--text-base: 0.875rem;
	--text-md: 1rem;
	--text-lg: 1.125rem;
	--text-xl: 1.25rem;
	--text-2xl: 1.5rem;
	--text-3xl: 2rem;
	--text-4xl: 2.5rem;
	--text-5xl: 3.5rem;

	--leading-tight: 1.15;
	--leading-snug: 1.3;
	--leading-normal: 1.5;
	--leading-relaxed: 1.65;

	--tracking-tight: -0.02em;
	--tracking-snug: -0.01em;
	--tracking-wide: 0.06em;

	/* Spacing (4px base) */
	--s-1: 4px;
	--s-2: 8px;
	--s-3: 12px;
	--s-4: 16px;
	--s-5: 20px;
	--s-6: 24px;
	--s-7: 32px;
	--s-8: 40px;
	--s-9: 48px;
	--s-10: 64px;
	--s-11: 80px;
	--s-12: 96px;

	/* Radius */
	--r-sm: 8px;
	--r-md: 12px;
	--r-lg: 16px;
	--r-xl: 20px;
	--r-2xl: 28px;
	--r-full: 9999px;

	/* Motion */
	--ease: cubic-bezier(0.4, 0, 0.2, 1);
	--ease-out: cubic-bezier(0, 0, 0.2, 1);
	--ease-in: cubic-bezier(0.4, 0, 1, 1);
	--dur-fast: 150ms;
	--dur: 220ms;
	--dur-slow: 400ms;

	/* Effects */
	--glass-blur: saturate(180%) blur(20px);
	--glass-blur-sm: saturate(180%) blur(12px);
	--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.3);
	--shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
	--shadow-lg: 0 20px 60px rgba(0, 0, 0, 0.5);
	--shadow-glow: 0 0 40px rgba(239, 68, 68, 0.3);
	--inner-hi: inset 0 1px 0 rgba(255, 255, 255, 0.06);
	--inner-hi-strong: inset 0 1px 0 rgba(255, 255, 255, 0.1);

	/* Layout */
	--container: 1200px;
	--nav-h: 64px;
}

/* ==========================================================================
   Base reset
   ========================================================================== */

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

html {
	-webkit-text-size-adjust: 100%;
	-moz-text-size-adjust: 100%;
	text-size-adjust: 100%;
	scroll-behavior: smooth;
	-webkit-tap-highlight-color: transparent;
}

body {
	margin: 0;
	min-height: 100vh;
	font-family: var(--font-sans);
	font-size: var(--text-base);
	line-height: var(--leading-normal);
	color: var(--text);
	background: var(--bg);
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-rendering: optimizeLegibility;
	font-feature-settings: "cv11", "ss01", "ss03";
	overflow-x: hidden;
}

h1,
h2,
h3,
h4,
h5,
h6 {
	margin: 0;
	font-family: var(--font-display);
	font-weight: 600;
	line-height: var(--leading-tight);
	letter-spacing: var(--tracking-snug);
	color: var(--text);
}

p {
	margin: 0;
}

a {
	color: var(--pink);
	text-decoration: underline;
	text-decoration-color: rgba(252, 165, 165, 0.45);
	text-decoration-thickness: 1px;
	text-underline-offset: 2px;
	transition:
		color var(--dur-fast) var(--ease),
		text-decoration-color var(--dur-fast) var(--ease);
}

a:hover {
	color: #fff;
	text-decoration-color: rgba(255, 255, 255, 0.7);
}

a:focus-visible {
	outline: 2px solid var(--violet);
	outline-offset: 2px;
	border-radius: 4px;
}

img,
svg,
video {
	display: block;
	max-width: 100%;
}

button,
input,
select,
textarea {
	font: inherit;
	color: inherit;
}

button {
	cursor: pointer;
	background: none;
	border: none;
	padding: 0;
}

::selection {
	background: rgba(239, 68, 68, 0.4);
	color: var(--text);
}

::-webkit-scrollbar {
	width: 10px;
	height: 10px;
}

::-webkit-scrollbar-track {
	background: transparent;
}

::-webkit-scrollbar-thumb {
	background: rgba(255, 255, 255, 0.08);
	border-radius: var(--r-full);
	border: 2px solid var(--bg);
}

::-webkit-scrollbar-thumb:hover {
	background: rgba(255, 255, 255, 0.15);
}

/* ==========================================================================
   Aurora background — fixed, drifts slowly, behind everything
   ========================================================================== */

.aurora-bg {
	position: fixed;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	overflow: hidden;
	background: var(--bg);
}

.aurora-bg::before,
.aurora-bg::after,
.aurora-bg > span {
	content: "";
	position: absolute;
	width: 60vmax;
	height: 60vmax;
	border-radius: 50%;
	filter: blur(120px);
	opacity: 0;
	transition: opacity 1.2s var(--ease-out);
	will-change: transform;
}

.aurora-bg::before {
	top: -20vmax;
	left: -10vmax;
	background: radial-gradient(
		circle,
		rgba(239, 68, 68, 0.55) 0%,
		transparent 70%
	);
	animation: aurora-drift-1 60s var(--ease) infinite;
}

.aurora-bg::after {
	bottom: -20vmax;
	right: -10vmax;
	background: radial-gradient(
		circle,
		rgba(127, 29, 29, 0.4) 0%,
		transparent 70%
	);
	animation: aurora-drift-2 70s var(--ease) infinite;
}

.aurora-bg > span {
	top: 30%;
	left: 50%;
	transform: translate(-50%, -50%);
	background: radial-gradient(
		circle,
		rgba(252, 165, 165, 0.3) 0%,
		transparent 70%
	);
	animation: aurora-drift-3 80s var(--ease) infinite;
}

body.is-loaded .aurora-bg::before,
body.is-loaded .aurora-bg::after,
body.is-loaded .aurora-bg > span {
	opacity: 1;
}

@keyframes aurora-drift-1 {
	0%,
	100% {
		transform: translate(0, 0) scale(1);
	}
	50% {
		transform: translate(20vmax, 10vmax) scale(1.1);
	}
}

@keyframes aurora-drift-2 {
	0%,
	100% {
		transform: translate(0, 0) scale(1);
	}
	50% {
		transform: translate(-15vmax, -10vmax) scale(1.15);
	}
}

@keyframes aurora-drift-3 {
	0%,
	100% {
		transform: translate(-50%, -50%) scale(1);
	}
	50% {
		transform: translate(-40%, -60%) scale(1.2);
	}
}

@media (prefers-reduced-motion: reduce) {
	.aurora-bg::before,
	.aurora-bg::after,
	.aurora-bg > span {
		animation: none;
		opacity: 1;
	}
	*,
	*::before,
	*::after {
		animation-duration: 0.001ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.001ms !important;
		scroll-behavior: auto !important;
	}
}

/* ==========================================================================
   Container
   ========================================================================== */

.container {
	width: 100%;
	max-width: var(--container);
	margin: 0 auto;
	padding-left: var(--s-6);
	padding-right: var(--s-6);
}

@media (max-width: 640px) {
	.container {
		padding-left: var(--s-4);
		padding-right: var(--s-4);
	}
}

/* ==========================================================================
   Glass surfaces
   ========================================================================== */

.glass {
	background: var(--surface-1);
	-webkit-backdrop-filter: var(--glass-blur);
	backdrop-filter: var(--glass-blur);
	border: 1px solid var(--border);
	border-radius: var(--r-lg);
	box-shadow: var(--inner-hi), var(--shadow-sm);
}

.glass--raised {
	background: var(--surface-2);
	border-color: var(--border-strong);
	box-shadow: var(--inner-hi-strong), var(--shadow);
}

.glass--solid {
	background: var(--surface-solid);
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
	border: 1px solid var(--border);
	border-radius: var(--r-lg);
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
	--btn-bg: var(--surface-2);
	--btn-fg: var(--text);
	--btn-border: var(--border);

	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--s-2);
	height: 40px;
	min-height: 44px;
	padding: 0 var(--s-5);
	font-family: var(--font-sans);
	font-size: var(--text-sm);
	font-weight: 600;
	letter-spacing: var(--tracking-snug);
	color: var(--btn-fg);
	background: var(--btn-bg);
	border: 1px solid var(--btn-border);
	border-radius: var(--r-md);
	cursor: pointer;
	text-decoration: none;
	white-space: nowrap;
	user-select: none;
	-webkit-user-select: none;
	transition:
		transform var(--dur-fast) var(--ease),
		background var(--dur) var(--ease),
		border-color var(--dur) var(--ease),
		box-shadow var(--dur) var(--ease),
		color var(--dur) var(--ease);
	box-shadow: var(--inner-hi);
}

.btn:hover {
	background: var(--surface-3);
	border-color: var(--border-strong);
	text-decoration: none;
	color: var(--text);
	transform: translateY(-1px);
}

.btn:active {
	transform: translateY(0);
}

.btn:focus-visible {
	outline: none;
	border-color: var(--border-accent);
	box-shadow:
		0 0 0 3px rgba(239, 68, 68, 0.25),
		var(--inner-hi);
}

.btn--primary {
	--btn-bg: var(--aurora-violet);
	--btn-fg: white;
	--btn-border: transparent;
	background: var(--aurora-violet);
	border: 1px solid transparent;
	position: relative;
	overflow: hidden;
	/* The ::after cross-fade sits at z-index:-1 so the label stays on top.
	   Most .btn--primary labels are bare text nodes, which can't be lifted with
	   z-index the way element children can — hence this own stacking context. */
	isolation: isolate;
}

.btn--primary::after {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--aurora);
	opacity: 0;
	transition: opacity var(--dur) var(--ease);
	pointer-events: none;
	z-index: -1;
}

.btn--primary:hover {
	--btn-bg: var(--aurora);
	background: var(--aurora);
	color: white;
	box-shadow: var(--shadow-glow), var(--inner-hi);
	transform: translateY(-1px);
}

.btn--primary:hover::after {
	opacity: 1;
}

.btn--ghost {
	--btn-bg: transparent;
	--btn-border: var(--border);
}

.btn--ghost:hover {
	background: var(--surface-1);
}

.btn--danger {
	--btn-bg: rgba(248, 113, 113, 0.12);
	--btn-fg: var(--danger);
	--btn-border: rgba(248, 113, 113, 0.3);
}

.btn--danger:hover {
	--btn-bg: rgba(248, 113, 113, 0.2);
	--btn-fg: #fda4af;
	--btn-border: rgba(248, 113, 113, 0.5);
	color: #fda4af;
}

.btn--warning {
	--btn-bg: rgba(251, 191, 36, 0.14);
	--btn-fg: var(--warning);
	--btn-border: rgba(251, 191, 36, 0.32);
}

.btn--warning:hover {
	--btn-bg: rgba(251, 191, 36, 0.22);
	--btn-fg: #fcd34d;
	--btn-border: rgba(251, 191, 36, 0.5);
	color: #fcd34d;
}

.btn--success {
	--btn-bg: rgba(34, 197, 94, 0.14);
	--btn-fg: var(--success);
	--btn-border: rgba(34, 197, 94, 0.32);
}

.btn--success:hover {
	--btn-bg: rgba(34, 197, 94, 0.22);
	--btn-fg: #4ade80;
	--btn-border: rgba(34, 197, 94, 0.5);
	color: #4ade80;
}

.btn--icon {
	width: 44px;
	min-width: 44px;
	min-height: 44px;
	padding: 0;
}

/* ponytail: .btn--sm is explicitly below the 44px touch target. Desktop-only.
   Future work should remove the sm variant entirely; until then, every
   usage is a known small-target exemption. */
.btn--sm {
	height: 32px;
	min-height: 0;
	padding: 0 var(--s-4);
	font-size: var(--text-xs);
}

.btn--lg {
	height: 48px;
	padding: 0 var(--s-6);
	font-size: var(--text-md);
}

.btn--block {
	display: flex;
	width: 100%;
}

.btn[disabled],
.btn:disabled {
	opacity: 0.5;
	cursor: not-allowed;
	transform: none !important;
}

/* ==========================================================================
   Forms
   ========================================================================== */

.field {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}

.field__label {
	display: block;
	font-size: var(--text-xs);
	font-weight: 600;
	color: var(--text-2);
	text-transform: uppercase;
	letter-spacing: var(--tracking-wide);
}

.field__hint {
	font-size: var(--text-xs);
	color: var(--text-3);
	margin-top: var(--s-1);
}

.input,
.textarea,
.select {
	width: 100%;
	height: 44px;
	min-height: 44px;
	padding: 0 var(--s-4);
	font-family: var(--font-sans);
	font-size: var(--text-md);
	color: var(--text);
	background: var(--surface-1);
	border: 1px solid var(--border);
	border-radius: var(--r-md);
	box-shadow: var(--inner-hi);
	transition:
		border-color var(--dur) var(--ease),
		box-shadow var(--dur) var(--ease),
		background var(--dur) var(--ease);
}

.textarea {
	height: auto;
	min-height: 100px;
	padding: var(--s-3) var(--s-4);
	resize: vertical;
	line-height: var(--leading-normal);
}

.select {
	appearance: none;
	-webkit-appearance: none;
	background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23a8afc1' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
	background-repeat: no-repeat;
	background-position: right 16px center;
	padding-right: 40px;
}

/* An open dropdown is painted by the UA, not by the page. `color` still
   inherits from .select (--text, near-white) but the option list falls back to
   the UA's light surface, so every unselected option renders near-white on
   white — invisible, while the highlighted row stays readable and makes it
   look like a rendering glitch. `color-scheme: dark` on :root is not enough
   here, so set the surface explicitly.

   --surface-solid rather than --surface-1: the translucent value is unreliable
   once the popup composites outside the page. Same fix as .form-select
   (admin.css) and .uas-form__select (profile.css); on the primitive so every
   class="select" in the app inherits it. */
.select option {
	color: var(--text);
	background-color: var(--surface-solid);
	padding: var(--s-2) var(--s-3);
}

/* Where the customizable <select> spec is implemented the popup is a real
   element, so it needs its own surface and border. Browsers without support
   drop this rule and fall back to the option styling above. */
.select::picker(select) {
	background: var(--surface-solid);
	color: var(--text);
	border: 1px solid var(--border);
	border-radius: var(--r-md);
}

.input::placeholder,
.textarea::placeholder {
	color: var(--text-3);
}

.input:hover,
.textarea:hover,
.select:hover {
	border-color: var(--border-strong);
}

.input:focus,
.textarea:focus,
.select:focus {
	outline: none;
	border-color: var(--border-accent);
	background: var(--surface-2);
	box-shadow:
		0 0 0 4px rgba(239, 68, 68, 0.18),
		var(--inner-hi);
}

.input:focus::placeholder,
.textarea:focus::placeholder {
	color: var(--text-2);
}

/* Checkbox */
.checkbox {
	display: flex;
	align-items: flex-start;
	gap: var(--s-3);
	cursor: pointer;
	padding: var(--s-3) 0;
}

.checkbox__input {
	appearance: none;
	-webkit-appearance: none;
	flex-shrink: 0;
	margin-top: 2px;
	width: 20px;
	height: 20px;
	background: var(--surface-1);
	border: 1px solid var(--border-strong);
	border-radius: 6px;
	cursor: pointer;
	position: relative;
	transition:
		background var(--dur) var(--ease),
		border-color var(--dur) var(--ease);
}

.checkbox__input:hover {
	border-color: var(--violet);
}

.checkbox__input:checked {
	background: var(--aurora-violet);
	border-color: transparent;
}

.checkbox__input:checked::after {
	content: "";
	position: absolute;
	top: 4px;
	left: 7px;
	width: 5px;
	height: 9px;
	border: solid white;
	border-width: 0 2px 2px 0;
	transform: rotate(45deg);
}

.checkbox__input:focus-visible {
	outline: none;
	box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.3);
}

.checkbox__label {
	flex: 1;
	cursor: pointer;
}

.checkbox__title {
	display: block;
	font-size: var(--text-md);
	font-weight: 500;
	color: var(--text);
}

.checkbox__description {
	display: block;
	font-size: var(--text-sm);
	color: var(--text-3);
	margin-top: 2px;
}

/* ==========================================================================
   Pills, badges, status
   ========================================================================== */

.pill {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	height: 26px;
	padding: 0 var(--s-3);
	font-size: var(--text-xs);
	font-weight: 500;
	color: var(--text-2);
	background: var(--surface-1);
	border: 1px solid var(--border);
	border-radius: var(--r-full);
	white-space: nowrap;
	transition:
		background var(--dur) var(--ease),
		border-color var(--dur) var(--ease),
		color var(--dur) var(--ease);
}

.pill:hover {
	background: var(--surface-2);
	border-color: var(--border-strong);
	color: var(--text);
}

.pill--accent {
	color: var(--violet);
	border-color: rgba(239, 68, 68, 0.3);
	background: rgba(239, 68, 68, 0.1);
}

.badge {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	padding: 3px 8px;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.02em;
	text-transform: uppercase;
	border-radius: var(--r-sm);
	white-space: nowrap;
	line-height: 1.3;
}

.badge--want {
	color: #fca5a5;
	background: rgba(248, 113, 113, 0.16);
	border: 1px solid rgba(248, 113, 113, 0.3);
}

.badge--playing {
	color: #6ee7b7;
	background: rgba(16, 185, 129, 0.16);
	border: 1px solid rgba(16, 185, 129, 0.3);
}

.badge--played {
	color: #fecaca;
	background: rgba(239, 68, 68, 0.16);
	border: 1px solid rgba(239, 68, 68, 0.3);
}

.badge--dropped {
	color: #fdba74;
	background: rgba(249, 115, 22, 0.16);
	border: 1px solid rgba(249, 115, 22, 0.3);
}

.badge--discount {
	color: #fca5a5;
	background: rgba(239, 68, 68, 0.18);
	border: 1px solid rgba(239, 68, 68, 0.36);
}

.badge--free {
	color: #86efac;
	background: rgba(34, 197, 94, 0.16);
	border: 1px solid rgba(34, 197, 94, 0.3);
}

.badge--hidden-gem {
	color: #fef3c7;
	background: rgba(245, 158, 11, 0.16);
	border: 1px solid rgba(245, 158, 11, 0.3);
}

.badge--year,
.badge--weekly {
	color: #fecaca;
	background: rgba(239, 68, 68, 0.16);
	border: 1px solid rgba(239, 68, 68, 0.3);
}

.badge--weekly {
	background: rgba(127, 29, 29, 0.16);
	border-color: rgba(127, 29, 29, 0.3);
	color: #fca5a5;
}

.badge--featured {
	color: #fde68a;
	background: var(--aurora-warm);
	border: none;
	background-clip: padding-box;
}

/* ponytail: app-show surface badge variants. Promote from inline
   style="background: var(--aurora-blue)" on app/views/apps/show.html.erb. */
.badge--coming-soon {
	background: var(--aurora-blue);
	color: white;
	border: none;
}

.badge--released {
	background: var(--surface-2);
	color: var(--text);
	border: 1px solid var(--border);
}

.badge--metacritic {
	background: var(--score-pos);
	color: var(--text-on-accent);
	border: none;
}

.badge--warning {
	background: color-mix(in srgb, var(--violet) 18%, transparent);
	color: var(--violet);
	border: 1px solid color-mix(in srgb, var(--violet) 35%, transparent);
}

.badge--muted {
	background: var(--surface-2);
	color: var(--text-3);
	border: 1px solid var(--border);
}

/* ponytail: AI-slop flag.

   Two densities, because the surfaces differ. In dense rows and cards a
   pill stole title width and read as loud as a price chip, so the inline
   mark is just "AI" in muted mono — chrome-free, ~16px, tooltip carries
   the meaning. The hero badge row and admin panel have room and use the
   full pill, since a status badge is the established pattern there.

   Deliberately neutral rather than --warning (amber, reserved for "Patch
   Required") so a flagged game never reads as a patching problem. */
.badge--slop {
	color: var(--text-2);
	background: var(--surface-3);
	border: 1px dashed var(--border-strong);
}

.slop-mark {
	flex-shrink: 0;
	font-family: var(--font-mono);
	font-size: 9px;
	font-weight: 700;
	letter-spacing: 0.04em;
	line-height: 1;
	text-transform: uppercase;
	color: var(--text-3);
	cursor: help;
}

/* Card variant: the artwork has room, so anchor the mark to its top-left
   corner instead of putting it in the title flow. Out of flow entirely —
   it costs the card no horizontal space. Needs a scrim of its own because
   the image-wrap gradient runs dark only at the very top of the frame. */
.game-card .slop-mark,
.home-hero__featured-wrap .slop-mark,
.home-hero__tile-wrap .slop-mark {
	position: absolute;
	top: var(--s-3);
	left: var(--s-3);
	z-index: 3;
	padding: 3px 5px;
	border-radius: var(--r-sm);
	color: var(--text-2);
	background: rgba(7, 9, 15, 0.72);
	backdrop-filter: blur(4px);
	border: 1px solid var(--border-strong);
}

/* ponytail: hero badges row — replaces inline display:flex + gap on
   app/views/apps/show.html.erb. Used by both home and app show hero badges. */
.hero-badges {
	display: flex;
	gap: var(--s-2);
	flex-wrap: wrap;
}

/* ponytail: inline-flex row of stars in the star-rating component.
   Promoted from inline style="display: flex; gap: 2px;" on app show. */
.stars--inline {
	display: flex;
	gap: 2px;
}

/* ponytail: price variant for the discount value. Promoted from
   inline style="color: var(--score-pos);" on the discount card. */
.price--discount {
	color: var(--score-pos);
}

.status-pill {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 4px 10px;
	font-size: 11px;
	font-weight: 600;
	border-radius: var(--r-full);
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.status-pill::before {
	content: "";
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: currentColor;
	box-shadow: 0 0 8px currentColor;
}

.status-pill--pending {
	color: var(--warning);
	background: rgba(251, 191, 36, 0.12);
	border: 1px solid rgba(251, 191, 36, 0.28);
}

.status-pill--syncing {
	color: var(--blue);
	background: rgba(248, 113, 113, 0.12);
	border: 1px solid rgba(248, 113, 113, 0.28);
}

.status-pill--synced {
	color: var(--success);
	background: rgba(34, 197, 94, 0.12);
	border: 1px solid rgba(34, 197, 94, 0.28);
}

.status-pill--failed {
	color: var(--danger);
	background: rgba(248, 113, 113, 0.12);
	border: 1px solid rgba(248, 113, 113, 0.28);
}

.score--positive {
	color: var(--score-pos);
}
.score--mixed {
	color: var(--score-mixed);
}
.score--negative {
	color: var(--score-neg);
}

/* ==========================================================================
   Headings & section titles
   ========================================================================== */

.eyebrow {
	display: inline-flex;
	align-items: center;
	gap: var(--s-2);
	font-size: var(--text-xs);
	font-weight: 600;
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	color: var(--text-3);
}

.eyebrow::before {
	content: "";
	width: 24px;
	height: 1px;
	background: var(--aurora);
}

.section-title {
	font-family: var(--font-display);
	font-size: var(--text-2xl);
	font-weight: 600;
	letter-spacing: var(--tracking-tight);
	color: var(--text);
	margin: 0;
}

.section-title--lg {
	font-size: var(--text-3xl);
}

.section-title--xl {
	font-size: var(--text-4xl);
	letter-spacing: -0.03em;
}

.section-subtitle {
	font-size: var(--text-sm);
	font-weight: 400;
	color: var(--text-3);
	margin-left: var(--s-2);
}

.section-header {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--s-3);
	margin-bottom: var(--s-5);
	/* A heading plus a row of controls is a pair that fits on one line right up
	   until it does not, and the controls are the part that cannot be
	   compressed -- a select at a squeezed width clips its own text. Wrapping
	   puts the controls on their own line instead, which is the only place they
	   still have room. Nothing changes at any width where the two already fit,
	   so this costs the wide layouts nothing. */
	flex-wrap: wrap;
}

/* A heading that used to sit directly in .section carries `margin: 0 0
   var(--s-4)`, and as a flex item that margin does not collapse with the
   container's own -- it would open a double-width gap. The container's
   margin-bottom is the gap; the heading does not add one. */
.section-header > h2 {
	margin-bottom: 0;
}

.section-header__actions {
	display: inline-flex;
	align-items: center;
	gap: var(--s-3);
}

.divider {
	height: 1px;
	background: linear-gradient(
		90deg,
		transparent,
		var(--border-strong),
		transparent
	);
	border: 0;
	margin: var(--s-7) 0;
}

/* ==========================================================================
   Empty state
   ========================================================================== */

.empty-state {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--s-3);
	padding: var(--s-9) var(--s-6);
	text-align: center;
	color: var(--text-3);
	font-size: var(--text-md);
	background: var(--surface-1);
	border: 1px dashed var(--border);
	border-radius: var(--r-lg);
}

.empty-state__icon {
	width: 48px;
	height: 48px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--surface-2);
	border: 1px solid var(--border);
	border-radius: 50%;
	color: var(--text-2);
}

.empty-state__action {
	margin-top: var(--s-2);
	gap: 6px;
}

.empty-state__action > span:last-child {
	transition: transform var(--dur) var(--ease);
}

.empty-state__action:hover > span:last-child {
	transform: translateX(2px);
}

@media (prefers-reduced-motion: reduce) {
	.empty-state__action > span:last-child,
	.empty-state__action:hover > span:last-child {
		transition: none;
		transform: none;
	}
}

/* ==========================================================================
   Modal
   ========================================================================== */

.modal-overlay {
	position: fixed;
	inset: 0;
	z-index: 100;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--s-6);
	background: rgba(7, 9, 15, 0.78);
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
}

.modal-card {
	position: relative;
	width: 100%;
	max-width: 440px;
	padding: var(--s-7);
	background: var(--surface-2);
	-webkit-backdrop-filter: var(--glass-blur);
	backdrop-filter: var(--glass-blur);
	border: 1px solid var(--border-strong);
	border-radius: var(--r-2xl);
	box-shadow: var(--shadow-lg), var(--inner-hi-strong);
	color: var(--text);
}

.modal-card::before {
	content: "";
	position: absolute;
	inset: -1px;
	border-radius: inherit;
	padding: 1px;
	background: var(--aurora-soft);
	-webkit-mask:
		linear-gradient(white, white) content-box,
		linear-gradient(white, white);
	-webkit-mask-composite: xor;
	mask-composite: exclude;
	opacity: 0.5;
	pointer-events: none;
}

/* ==========================================================================
   Skeleton
   ========================================================================== */

.skeleton {
	background: linear-gradient(
		90deg,
		rgba(255, 255, 255, 0.04) 25%,
		rgba(255, 255, 255, 0.1) 50%,
		rgba(255, 255, 255, 0.04) 75%
	);
	background-size: 200% 100%;
	animation: shimmer 1.5s infinite;
	border-radius: var(--r-sm);
}

@keyframes shimmer {
	0% {
		background-position: -200% 0;
	}
	100% {
		background-position: 200% 0;
	}
}

/* ==========================================================================
   Kbd
   ========================================================================== */

kbd {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 22px;
	height: 22px;
	padding: 0 6px;
	font-family: var(--font-mono);
	font-size: 11px;
	font-weight: 600;
	color: var(--text-2);
	background: var(--surface-2);
	border: 1px solid var(--border-strong);
	border-bottom-width: 2px;
	border-radius: 6px;
	line-height: 1;
}

/* ==========================================================================
   Utilities
   ========================================================================== */

.stack {
	display: flex;
	flex-direction: column;
}
.stack-1 {
	display: flex;
	flex-direction: column;
	gap: var(--s-1);
}
.stack-2 {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}
.stack-3 {
	display: flex;
	flex-direction: column;
	gap: var(--s-3);
}
.stack-4 {
	display: flex;
	flex-direction: column;
	gap: var(--s-4);
}
.stack-5 {
	display: flex;
	flex-direction: column;
	gap: var(--s-5);
}
.stack-6 {
	display: flex;
	flex-direction: column;
	gap: var(--s-6);
}

.row {
	display: flex;
	align-items: center;
}
.row-1 {
	display: flex;
	align-items: center;
	gap: var(--s-1);
}
.row-2 {
	display: flex;
	align-items: center;
	gap: var(--s-2);
}
.row-3 {
	display: flex;
	align-items: center;
	gap: var(--s-3);
}
.row-4 {
	display: flex;
	align-items: center;
	gap: var(--s-4);
}
.row-5 {
	display: flex;
	align-items: center;
	gap: var(--s-5);
}
.row-6 {
	display: flex;
	align-items: center;
	gap: var(--s-6);
}

.cluster {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s-3);
}

.between {
	justify-content: space-between;
}
.around {
	justify-content: space-around;
}
.center {
	justify-content: center;
}
.start {
	justify-content: flex-start;
}
.end {
	justify-content: flex-end;
}

.middle {
	align-items: center;
}
.baseline {
	align-items: baseline;
}
.stretch {
	align-items: stretch;
}

.grow {
	flex: 1;
}
.shrink-0 {
	flex-shrink: 0;
}

.text-2 {
	color: var(--text-2);
}
.text-3 {
	color: var(--text-3);
}
.text-accent {
	color: var(--violet);
}
.text-pos {
	color: var(--score-pos);
}
.text-mix {
	color: var(--score-mixed);
}
.text-neg {
	color: var(--score-neg);
}
.text-mono {
	font-family: var(--font-mono);
}
.text-xs {
	font-size: var(--text-xs);
}
.text-sm {
	font-size: var(--text-sm);
}
.text-md {
	font-size: var(--text-md);
}
.text-lg {
	font-size: var(--text-lg);
}
.text-xl {
	font-size: var(--text-xl);
}

.fw-500 {
	font-weight: 500;
}
.fw-600 {
	font-weight: 600;
}
.fw-700 {
	font-weight: 700;
}

.truncate {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	min-width: 0;
}

.clamp-2 {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.clamp-3 {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.hidden {
	display: none !important;
}
.sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* ==========================================================================
   Entity follow (developer / publisher / tag pages)
   ========================================================================== */

.entity-follow {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	margin: var(--s-4) 0 var(--s-5);
}

/* Those block margins are for a follow row sitting in normal flow under a
   heading. In a section header the row is a flex item, and the margins would
   push it off the row's centre line rather than space anything. */
.section-header__actions .entity-follow {
	margin: 0;
}

.entity-follow__count {
	font-size: var(--text-sm);
	color: var(--text-3);
	/* "1 follower" breaking across lines reads as two facts. The count sits
	   beside the follow button wherever that button lands, so it holds
	   together in all of those places, not just the list rows. */
	white-space: nowrap;
}

.tag-description {
	max-width: 65ch;
	margin: var(--s-3) 0 0;
	color: var(--text-2);
	font-size: var(--text-sm);
	line-height: 1.6;
}

.entity-follow__hint {
	max-width: 60ch;
	margin: calc(var(--s-4) * -1 + var(--s-2)) 0 var(--s-5);
	font-size: var(--text-sm);
	color: var(--text-3);
	line-height: 1.5;
}

.entity-follow__hint a {
	color: var(--text-2);
	text-decoration: underline;
	text-underline-offset: 2px;
}

.entity-follow__hint a:hover {
	color: var(--text);
}

/* ==========================================================================
   Search — studio (developer / publisher) and tag results
   ========================================================================== */

.search-studios {
	margin-bottom: var(--s-6);
}

.search-studios__title {
	font-size: var(--text-sm);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--text-3);
	margin-bottom: var(--s-3);
}

.search-entity-list {
	list-style: none;
	padding: 0;
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}

.search-entity {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-3) var(--s-4);
	background: var(--surface-1);
	border: 1px solid var(--border);
	border-radius: var(--r-md);
	transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

/* The row is not itself a link — it holds a nested <form> for the follow
   button, so the hover affordance lives on the name. */
.search-entity:hover {
	background: var(--surface-2);
	border-color: var(--border-strong);
}

.search-entity__badge {
	flex-shrink: 0;
	font-size: var(--text-xs);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--text-3);
	background: var(--surface-3);
	border: 1px solid var(--border);
	border-radius: var(--r-sm);
	padding: 2px var(--s-2);
}

.search-entity__name {
	font-size: var(--text-sm);
	font-weight: 500;
	color: var(--text);
	text-decoration: none;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.search-entity__name:hover {
	color: var(--text);
	text-decoration: underline;
	text-underline-offset: 2px;
}

.search-entity__name:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
	border-radius: 2px;
}

.search-entity__count {
	flex-shrink: 0;
	font-size: var(--text-sm);
	color: var(--text-3);
	white-space: nowrap;
}

/* Pushes the follow button to the right edge, after the count. */
.search-entity .entity-follow {
	margin: 0 0 0 auto;
}

.entity-follow--compact {
	gap: var(--s-2);
}

.entity-follow--compact .entity-follow__form {
	display: inline;
}

.search-follow-hint {
	margin-top: var(--s-5);
	font-size: var(--text-sm);
	color: var(--text-3);
}

.search-follow-hint a {
	color: var(--text-2);
	text-underline-offset: 2px;
}

.search-follow-hint a:hover {
	color: var(--text);
}
