/*
Theme Name:  Repulse
Theme URI:   https://repulse.com
Description: Child theme of Fastest for Repulse Game Studios.
Author:      Repulse Game Studios
Template:    fastest
Version:     1.0.0
Text Domain: repulse
*/

/* ============================================================
   Layer 1 — Chrome: header, navigation, anchor targets
   Fastest is a classic theme with no CSS custom properties of
   its own; these come from the child theme.json.
   ============================================================ */

:root {
	--repulse-header-height: 72px;
	--repulse-border: 1px solid rgba(255, 255, 255, 0.08);
	--repulse-radius: 10px;
}

body {
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--md);
	line-height: 1.65;
}

h1, h2, h3, h4, h5, h6 {
	font-family: var(--wp--preset--font-family--heading);
	color: var(--wp--preset--color--contrast);
	line-height: 1.2;
}

h1 { font-size: var(--wp--preset--font-size--hero); }
h2 { font-size: var(--wp--preset--font-size--xxl); }
h3 { font-size: var(--wp--preset--font-size--xl); }
h4 { font-size: var(--wp--preset--font-size--lg); }

a { color: var(--wp--preset--color--primary); }
a:hover { color: var(--wp--preset--color--secondary); }

.site-header {
	background: var(--wp--preset--color--deep);
	border-bottom: var(--repulse-border);
}

.site-branding .site-title,
.site-branding .site-title a {
	font-family: var(--wp--preset--font-family--heading);
	font-weight: 900;
	letter-spacing: 0.04em;
	color: var(--wp--preset--color--contrast);
	text-transform: uppercase;
}

.site-branding .site-description {
	color: var(--wp--preset--color--muted);
	font-size: var(--wp--preset--font-size--sm);
}

.main-navigation a {
	font-family: var(--wp--preset--font-family--heading);
	font-size: 0.8125rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--wp--preset--color--contrast);
	border-bottom: 2px solid transparent;
}

.main-navigation a:hover,
.main-navigation .current-menu-item > a,
.main-navigation .current_page_item > a {
	color: var(--wp--preset--color--primary);
	border-bottom-color: var(--wp--preset--color--primary);
}

.menu-toggle {
	background: transparent;
	color: var(--wp--preset--color--contrast);
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
}

/* Fastest paints the content region white (its style.css: body .site-content)
   and constrains it to a 1280px max-width. Our text is white, so without the
   background override the hero and any unpainted section render white-on-white.
   No sidebar is registered on this site (is_active_sidebar('sidebar-1') is
   false), so the 67% content-area float leaves a third of every page empty
   and prevents the section bands from spanning full-bleed; the max-width
   removal lets .entry-content > * > * actually reach its own 1200px cap. */
body .site-content {
	background: var(--wp--preset--color--base);
	max-width: none;
}

/* Fastest pads 104px to clear its fixed 70px header. The hero starts 27px
   below that padding box, so 44px lands its top edge flush with the header and
   the band runs straight out of it — the hero carries its own generous top
   padding, so nothing is covered. Only the front page has a hero, though; every
   other page leads with its title, which needs the full 104px of clearance. */
body.home .site-content {
	padding-top: 44px;
}

/* Everywhere else the page-header band takes that job, so pad only enough to
   clear the header itself and let the band start flush beneath it. */
body:not(.home) .site-content {
	padding-top: 71px;
}

.content-area {
	width: 100%;
	float: none;
}

/* The header is sticky — Fastest ships .topmost/.nav-down — so anchored
   sections must offset or their heading hides beneath it. Two of the
   main-nav links point at these. */
#games,
#team {
	scroll-margin-top: calc(var(--repulse-header-height) + 1rem);
}

a:focus-visible,
button:focus-visible,
.wp-block-button__link:focus-visible {
	outline: 3px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
}

@media (prefers-reduced-motion: no-preference) {
	a,
	.wp-block-button__link,
	.repulse-team__member {
		transition: color 0.15s ease, background-color 0.15s ease,
			border-color 0.15s ease, transform 0.15s ease;
	}
}

/* Below 768px .site-header is fixed with no backdrop of its own, and our
   skin changed the nav links from dark to white, so an opened mobile menu
   would otherwise be white text floating over the page content. */
@media (max-width: 767px) {
	.main-navigation ul {
		background: var(--wp--preset--color--deep);
	}

	/* 2rem inline padding is too generous on a phone. */
	.entry-content > * {
		padding-inline: var(--wp--preset--spacing--20);
	}
}

/* ============================================================
   Layer 2 — Sections: full-bleed alternating rhythm
   Replaces WPBakery's per-row background hacks, which were
   discarded during conversion.
   ============================================================ */

/* Alternate on position among ALL top-level blocks. NOT :nth-of-type --
   that counts by tag name, so a page beginning with columns shifts the
   parity and two sections silently render the same colour. */
.entry-content > * {
	padding-block: var(--wp--preset--spacing--50);
	padding-inline: var(--wp--preset--spacing--30);
}

/* Two of the converted top-level blocks are empty spacers; without this
   they contribute 8rem of dead space and a painted empty band. */
.entry-content > *:empty {
	padding: 0;
	background: none;
}

.entry-content > *:nth-child(even) {
	background: var(--wp--preset--color--surface);
}

.entry-content > * > * {
	max-width: 1200px;
	margin-inline: auto;
}

/* ============================================================
   Layer 3 — Components
   ============================================================ */

/* Fastest draws a light grey rule above every entry-content heading via a
   ::before pseudo-element with generated content (.entry-content h2:before
   … h6:before sets border-top and content: ''), not on the heading itself.
   Suppressing the generated content removes its box. */
.entry-content h2::before,
.entry-content h3::before,
.entry-content h4::before,
.entry-content h5::before,
.entry-content h6::before {
	content: none;
}

.wp-block-columns {
	gap: var(--wp--preset--spacing--40);
	align-items: flex-start;
}

@media (max-width: 782px) {
	.wp-block-columns {
		flex-wrap: wrap;
	}
	.wp-block-column {
		flex-basis: 100% !important;
	}
}

.wp-block-button__link {
	background: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--contrast);
	font-family: var(--wp--preset--font-family--heading);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	border-radius: var(--repulse-radius);
	padding: 0.85em 1.9em;
}

.wp-block-button__link:hover {
	background: var(--wp--preset--color--secondary);
	color: var(--wp--preset--color--contrast);
}

.wp-block-image img {
	border-radius: var(--repulse-radius);
	border: var(--repulse-border);
	height: auto;
}

.wp-block-separator {
	border: 0;
	border-top: 2px solid var(--wp--preset--color--primary);
	opacity: 0.5;
	max-width: 120px;
	margin-inline: auto;
}

.wp-block-details {
	background: var(--wp--preset--color--surface);
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
	padding: 1rem 1.25rem;
}

.wp-block-details summary {
	font-family: var(--wp--preset--font-family--heading);
	font-weight: 700;
	cursor: pointer;
}

/* Embeds decoded out of vc_raw_html: keep them responsive. */
.wp-block-html iframe,
.wp-block-html video {
	max-width: 100%;
	border-radius: var(--repulse-radius);
}

/* ============================================================
   Layer 4 — Site-specific: team cards, Road Map timeline
   ============================================================ */

.repulse-team {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
	gap: var(--wp--preset--spacing--30);
	list-style: none;
	padding: 0;
	margin: 0;
}

/* #team is an even section (surface band). The card must read against both
   base and surface, so it uses deep rather than matching the band. */
.repulse-team__member {
	background: var(--wp--preset--color--deep);
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
	padding: 1rem 1rem 1.25rem;
	text-align: center;
}

.repulse-team__member:hover {
	border-color: var(--wp--preset--color--primary);
	transform: translateY(-2px);
}

.repulse-team__photo img {
	width: 100%;
	height: auto;
	display: block;
	border-radius: var(--repulse-radius);
	border: 0;
}

.repulse-team__name {
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--md);
	font-weight: 700;
	margin: 0.85rem 0 0.25rem;
}

/* Muted, never pink: pink on the base navy is 4.59:1, which is marginal
   for small text. */
.repulse-team__role {
	color: var(--wp--preset--color--muted);
	font-size: 0.9rem;
	margin: 0;
}


/* Footer -------------------------------------------------- */

.repulse-footer {
	background: var(--wp--preset--color--deep);
	border-top: var(--repulse-border);
	padding-block: var(--wp--preset--spacing--40);
	margin-top: var(--wp--preset--spacing--50);
}

.repulse-footer .wrap {
	display: grid;
	grid-template-columns: 2fr 1fr 1fr;
	gap: var(--wp--preset--spacing--30);
	align-items: start;
	max-width: 1200px;
	margin-inline: auto;
}

@media (max-width: 782px) {
	.repulse-footer .wrap {
		grid-template-columns: 1fr;
	}
}

.repulse-footer__name {
	font-family: var(--wp--preset--font-family--heading);
	font-weight: 900;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	margin: 0;
}

.repulse-footer__tagline,
.repulse-footer__legal {
	color: var(--wp--preset--color--muted);
	font-size: 0.9rem;
}

.repulse-footer__nav ul,
.repulse-footer__social {
	list-style: none;
	margin: 0;
	padding: 0;
}

.repulse-footer__nav a,
.repulse-footer__social a {
	color: var(--wp--preset--color--muted);
	text-decoration: none;
	line-height: 2;
}

.repulse-footer__nav a:hover,
.repulse-footer__social a:hover {
	color: var(--wp--preset--color--primary);
}

.repulse-footer__legal {
	grid-column: 1 / -1;
	border-top: var(--repulse-border);
	padding-top: 1rem;
	margin: 0;
}

/* ============================================================
   Layer 5 — Composition
   Banding is removed. With two zero-height spacers sitting
   between the real sections, `:nth-child(even)` collapsed Games,
   Road Map and Team into one 3,600px slab of identical colour.
   Rhythm now comes from spacing, section headers and the accent
   rule instead of alternating backgrounds.
   ============================================================ */

.entry-content > *:nth-child(even) {
	background: none;
}

.entry-content > * {
	padding-block: clamp(3rem, 6vw, 5.5rem);
}

/* Hero — the studio's thesis, set as the largest thing on the page.
   The pink reads as light rather than a flat accent: a soft wash
   behind the headline, which suits a fantasy-RPG studio. */
.home .entry-content > *:first-child {
	position: relative;
	padding-block: clamp(4rem, 10vw, 8.5rem);
	isolation: isolate;
}

.home .entry-content > *:first-child::before {
	content: "";
	position: absolute;
	inset: -15% -25% auto -25%;
	height: 90%;
	z-index: -1;
	pointer-events: none;
	background: radial-gradient(
		55% 55% at 32% 45%,
		color-mix(in srgb, var(--wp--preset--color--primary) 20%, transparent),
		transparent 72%
	);
}

.home .entry-content > *:first-child h3 {
	font-family: var(--wp--preset--font-family--heading);
	font-size: clamp(2rem, 4.6vw, 3.75rem);
	font-weight: 900;
	line-height: 1.04;
	letter-spacing: -0.015em;
	text-wrap: balance;
}

/* Section headers. Excludes team names, which are also h2 — without
   the :not() the 42px section rule would win over the card name. */
.entry-content h2:not(.repulse-team__name) {
	font-size: clamp(1.625rem, 2.8vw, 2.25rem);
	letter-spacing: 0.01em;
	margin-bottom: 0.5rem;
}

/* The converted pages already emit a separator under each section
   heading; styling that rather than inventing a pseudo-element. */
.entry-content .wp-block-separator {
	border-top: 2px solid var(--wp--preset--color--primary);
	opacity: 1;
	max-width: 56px;
	margin: 1rem auto 2.75rem;
}

/* Long-form copy needs a readable measure; the full 1129px column
   runs to roughly 150 characters a line. */
.entry-content p {
	max-width: 68ch;
}

.entry-content [style*="text-align: center"] p,
.entry-content p[style*="text-align: center"] {
	margin-inline: auto;
}

/* Team grid — square crops so 17 mixed-ratio photos align. */
.repulse-team {
	grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
	gap: 1.25rem;
}

.repulse-team__photo img {
	aspect-ratio: 1;
	object-fit: cover;
}

.repulse-team__member {
	padding: 0.85rem 0.85rem 1.1rem;
}

/* Header corrections ---------------------------------------- */

/* Fastest paints the masthead its own blue at a specificity a bare
   .site-header selector cannot reach. */
body .site-header,
body .site-header.topmost,
body .site-header.nav-down {
	background: var(--wp--preset--color--deep);
}

/* The tagline is a full sentence with 16px/24px margins inside a fixed
   70px bar: it overflowed and collided with the wordmark. It still
   appears in the footer, where it has room. */
.site-header .site-description {
	display: none;
}

.site-branding {
	display: flex;
	align-items: center;
	height: 100%;
}

.site-branding .site-title {
	font-size: 1.5rem;
	line-height: 1;
	margin: 0;
}

/* ============================================================
   Layer 6 — Header composition and hero bleed
   ============================================================ */

/* Hero goes full-bleed. As a pseudo-element with negative insets the
   pink wash was clipped by the content column; as the section's own
   background it reaches the viewport edges and cannot be cut off. */
.home .entry-content > *:first-child {
	width: 100vw;
	margin-left: calc(50% - 50vw);
	padding-inline: max(var(--wp--preset--spacing--30), calc(50vw - 620px));
	background-image: radial-gradient(
		68% 78% at 30% 46%,
		color-mix(in srgb, var(--wp--preset--color--primary) 20%, transparent),
		transparent 68%
	);
}

.home .entry-content > *:first-child::before {
	content: none;
}

/* The key art is transparent cutout work, so a hairline reads as a
   mistake rather than a frame. */
.wp-block-image img {
	border: 0;
}

/* Header: wordmark left, call to action centred, navigation right. */
.site-header .wrap {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
}

/* Fastest emits a bare <img> for the logo, not the_custom_logo()'s
   .custom-logo markup, so target the element rather than the class. */
.site-branding img {
	display: block;
	height: 34px;
	width: auto;
}

/* Only hide the text title once the image is actually rendering. */
.site-branding:has(img) .site-title {
	display: none;
}

/* The legacy menu already marks this item as the call to action. */
.main-navigation .repulse-cta {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
}

.main-navigation .repulse-cta > a {
	background: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--contrast);
	border-radius: 999px;
	border-bottom: 0;
	padding: 0.6em 1.5em;
	white-space: nowrap;
}

.main-navigation .repulse-cta > a:hover,
.main-navigation .repulse-cta.current-menu-item > a {
	background: var(--wp--preset--color--secondary);
	color: var(--wp--preset--color--contrast);
	border-bottom: 0;
}

/* Below this width the centred button would collide with the menu. */
@media (max-width: 1100px) {
	.main-navigation .repulse-cta {
		position: static;
		transform: none;
	}

	.site-header .wrap {
		flex-wrap: wrap;
	}
}

/* ============================================================
   Layer 7 — Hero proportions and a ghost call to action
   ============================================================ */

/* The hero deliberately keeps the same 620px half-column as every other
   section. Layer 7 once widened it to 700px so each sentence fit on one
   line; the copy now wraps to three lines in a 720px column, so that no
   longer applies. Diverging by 80px put the hero's copy off the page grid
   at any viewport above ~1400px, where the two formulas stop clamping to
   the same minimum gutter. */

.home .entry-content > *:first-child .wp-block-column:first-child {
	flex-basis: 52% !important;
}

.home .entry-content > *:first-child .wp-block-column:last-child {
	flex-basis: 48% !important;
}

/* Smaller than the previous 54px: the copy is three full sentences, not a
   three-word slogan, and it should read as three lines. */
.home .entry-content > *:first-child h3 {
	/* Measured: three full sentences fit three lines at roughly 18-20px in
	   this column. At 24px they ran to six. */
	font-size: clamp(0.95rem, 1.55vw, 1.375rem);
	line-height: 1.3;
	letter-spacing: 0;
}

/* Let the key art fill its column rather than sit at its intrinsic size. */
.home .entry-content > *:first-child figure,
.home .entry-content > *:first-child img {
	width: 100%;
	max-width: none;
	height: auto;
}

/* Bigger wordmark. */
.site-branding img {
	height: 52px;
}

/* Ghost call to action: outline until hover, then it fills. */
.main-navigation .repulse-cta > a {
	background: transparent;
	color: var(--wp--preset--color--primary);
	border: 2px solid var(--wp--preset--color--primary);
	padding: 0.55em 1.4em;
}

.main-navigation .repulse-cta > a:hover,
.main-navigation .repulse-cta > a:focus-visible {
	background: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--contrast);
	border-color: var(--wp--preset--color--primary);
}

/* Headline and key art read as one unit when they share a centre line. */
.home .entry-content > *:first-child {
	align-items: center;
}

/* ============================================================
   Layer 8 — Hero restructured
   The copy is 131 characters. In a half-width column it needed
   18px to stay on three lines, which is too small to lead a page.
   Stacking gives the statement full measure and the key art real
   size, which suits a studio whose product is the art anyway.
   ============================================================ */

.home .entry-content > *:first-child {
	flex-direction: column;
	align-items: center;
	gap: clamp(1.5rem, 3.5vw, 3rem);
	text-align: center;
	/* Softer and wider than before: the previous wash was a small,
	   high-contrast ellipse that read as a stain rather than light. */
	background-image:
		radial-gradient(
			110% 70% at 50% 108%,
			color-mix(in srgb, var(--wp--preset--color--primary) 13%, transparent),
			transparent 62%
		),
		radial-gradient(
			80% 55% at 50% -10%,
			color-mix(in srgb, var(--wp--preset--color--secondary) 10%, transparent),
			transparent 65%
		);
}

.home .entry-content > *:first-child .wp-block-column {
	flex-basis: auto !important;
	width: 100%;
	max-width: 940px;
}

.home .entry-content > *:first-child .wp-block-column:last-child {
	max-width: 780px;
}

.home .entry-content > *:first-child h3 {
	font-size: clamp(1.05rem, 1.85vw, 1.5rem);
	margin-inline: auto;
	text-wrap: balance;
}

/* ============================================================
   Layer 9 — Hero side by side
   Measured: at a 660px column the three sentences still hold three
   lines at 22px. The earlier 52% column (550px) was simply too
   narrow, which is what forced 18px, not the copy length itself.
   Layer 10 sets the column widths, since the art moved out of flow.
   ============================================================ */

.home .entry-content > *:first-child {
	flex-direction: row;
	align-items: center;
	text-align: left;
	gap: clamp(1.5rem, 3vw, 3rem);
}

.home .entry-content > *:first-child h3 {
	font-size: clamp(1.05rem, 1.9vw, 1.4rem);
	margin-inline: 0;
}

/* ============================================================
   Layer 10 — Key art as background
   As a block the art competed with the copy for width: every pixel
   it gained cost the headline a line. As a background it sits in
   its own plane, so the text can take the measure it needs and the
   art can be as large as the section allows.
   ============================================================ */

.home .entry-content > *:first-child {
	/* Relative to this stylesheet, so it survives a domain change. */
	background-image:
		/* .webp: 34_webp.php converts the installed crop and removes the .png. */
		url("../../uploads/2026/09/WebsiteMain_NoChair-crop.webp"),
		radial-gradient(
			110% 70% at 50% 108%,
			color-mix(in srgb, var(--wp--preset--color--primary) 13%, transparent),
			transparent 62%
		),
		radial-gradient(
			80% 55% at 50% -10%,
			color-mix(in srgb, var(--wp--preset--color--secondary) 10%, transparent),
			transparent 65%
		);
	background-repeat: no-repeat;
	/* The section is full-bleed, so 'right' against the default padding box
	   means the browser edge and the art gets sliced by it. Origin
	   content-box makes 'right' the content column the copy is aligned to,
	   at every viewport, so the gutter is respected. The washes stay on
	   border-box or they would leave unpainted strips in the gutters. */
	background-origin: content-box, border-box, border-box;
	background-position: right center, center, center;
	/* The crop is square (508x503), so height is the binding dimension.
	   Absolute rather than a percentage: content-box origin would resolve
	   a percentage against the content box, which is much shorter than the
	   band and collapsed the art. Clip stays border-box, so this may spill
	   into the vertical padding without being cut. */
	background-size: auto clamp(260px, 32vw, 440px), cover, cover;
	min-height: clamp(380px, 36vw, 540px);
	align-items: center;
}

/* The figure is now decorative and lives in the background layer. */
.home .entry-content > *:first-child .wp-block-column:last-child {
	display: none;
}

.home .entry-content > *:first-child .wp-block-column:first-child {
	flex-basis: 100% !important;
	max-width: min(62%, 720px);
}

/* Below the column breakpoint the art would sit under the copy, so it
   moves behind it at low opacity rather than fighting for space. */
@media (max-width: 782px) {
	.home .entry-content > *:first-child {
		background-size: min(72%, 330px) auto, cover, cover;
		background-position: center bottom, center, center;
		padding-bottom: min(72vw, 330px);
	}

	.home .entry-content > *:first-child .wp-block-column:first-child {
		max-width: none;
	}
}

/* ============================================================
   Layer 11 — Let full-bleed sections actually bleed
   ============================================================ */

/* Fastest sets overflow:hidden on the post article, a clearfix habit from
   its floated layout. That clips descendants to the 1240px content column,
   so a section using the negative-margin full-bleed trick still computes a
   100vw box but only ever paints inside the column: the hero's wash stopped
   dead at the column edge, and copy positioned outside it was sheared.
   Verified by painting the hero opaque and observing it render 257..1497
   inside a border box measured at 0..1753. */
.content-area article {
	overflow: visible;
}

/* ============================================================
   Layer 12 — Section layout repairs
   ============================================================ */

/* Games: the block markup is one flex row of four columns —
   [heading + separator][game][game][game]. Only the three game columns
   carry an inline flex-basis, so the heading column collapsed to
   min-content: "Our Games" rendered one letter per line and its
   separator was invisible. Wrapping and giving the heading its own full
   row puts it above the games where it belongs. */
#games {
	/* Core's block-library CSS ships
	   @media (min-width: 782px) { .wp-block-columns { flex-wrap: nowrap !important } }
	   so even an ID selector loses without !important here. */
	flex-wrap: wrap !important;
	align-items: flex-start;
}

#games > .wp-block-column:first-child {
	flex: 0 0 100% !important;
	text-align: center;
}

/* Equal columns whatever the count, so three or four games both work.
   The basis must be a real length, not 0: with a zero basis an item's
   hypothetical size always "fits" the current line, so these never wrapped
   past the full-width heading — they stayed on its line at zero width.
   !important because the inline flex-basis would otherwise win. */
#games > .wp-block-column:not(:first-child) {
	flex: 1 1 220px !important;
	min-width: 0;
}

#games figure.wp-block-image {
	margin: 0;
}

/* The sources are 256px squares. Capped so they stay crisp rather than
   upscaling to fill a ~380px column, and centred so unequal caps still
   read as a row. */
#games figure img {
	display: block;
	width: 100%;
	max-width: 300px;
	margin-inline: auto;
	aspect-ratio: 1;
	object-fit: cover;
	border-radius: var(--repulse-radius);
}

/* Titles were rendering in the default link blue. */
#games h4 {
	margin: 0.9rem 0 0;
}

#games h4 a {
	color: inherit;
	text-decoration: none;
}

#games h4 a:hover,
#games h4 a:focus-visible {
	color: var(--wp--preset--color--primary);
}


/* Team: 178px columns made these markedly smaller than the game tiles.
   200px lands five across at 1240 and matches their weight. */
.repulse-team {
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

/* Footer: auto-placement was dropping the brand into column two and the
   social list into column one despite source order. Placing the cells
   explicitly is deterministic. */
.repulse-footer__brand {
	grid-column: 1;
	grid-row: 1;
}

.repulse-footer__nav {
	grid-column: 2;
	grid-row: 1;
}

.repulse-footer__social {
	grid-column: 3;
	grid-row: 1;
}

.repulse-footer__legal {
	grid-row: 2;
}

@media (max-width: 782px) {
	.repulse-footer__brand,
	.repulse-footer__nav,
	.repulse-footer__social,
	.repulse-footer__legal {
		grid-column: 1;
		grid-row: auto;
	}
}

/* ============================================================
   Layer 13 — Match the live section styling
   ============================================================ */

/* Ornament under each section heading: rule, small node, larger hub,
   small node, rule — symmetric about the centre. That is five discrete
   pieces and an <hr> has only two pseudo-elements, so each piece is a
   background layer instead. Earlier layers paint on top, so the dots are
   listed first and sit over the rules.

   background-position resolves a mixed calc() as
   P% * (box - image) + length, so `calc(50% - var(--sep-offset))` centres
   the node and then shifts it left by the offset — which is what lets the
   pair be positioned from the centre rather than from either edge. */
.entry-content .wp-block-separator {
	--sep-hub: 20px;
	--sep-node: 10px;
	--sep-rule: 2px;
	--sep-gap: 14px;
	/* Centre of the box to centre of each node. */
	--sep-offset: calc(var(--sep-hub) / 2 + var(--sep-gap) + var(--sep-node) / 2);
	/* Each rule runs from its edge to the outside of the near node. */
	--sep-run: calc(50% - var(--sep-hub) / 2 - var(--sep-gap) - var(--sep-node));

	width: 100%;
	max-width: 220px;
	height: var(--sep-hub);
	border: 0;
	/* The rule now sits at the middle of a 20px box rather than the top of
	   a 2px one, so both margins lose that 9px to hold the old rhythm. */
	margin: 0.45rem auto 2.2rem;
	background-color: transparent;
	background-repeat: no-repeat;
	background-image:
		radial-gradient(circle closest-side, var(--wp--preset--color--primary) 99%, transparent 100%),
		radial-gradient(circle closest-side, var(--wp--preset--color--primary) 99%, transparent 100%),
		radial-gradient(circle closest-side, var(--wp--preset--color--primary) 99%, transparent 100%),
		linear-gradient(var(--wp--preset--color--primary), var(--wp--preset--color--primary)),
		linear-gradient(var(--wp--preset--color--primary), var(--wp--preset--color--primary));
	background-size:
		var(--sep-hub) var(--sep-hub),
		var(--sep-node) var(--sep-node),
		var(--sep-node) var(--sep-node),
		var(--sep-run) var(--sep-rule),
		var(--sep-run) var(--sep-rule);
	background-position:
		center center,
		calc(50% - var(--sep-offset)) center,
		calc(50% + var(--sep-offset)) center,
		left center,
		right center;
}

/* Live sets the game titles in brand pink, not white. */
#games h4 a {
	color: var(--wp--preset--color--primary);
}

#games h4 a:hover,
#games h4 a:focus-visible {
	color: var(--wp--preset--color--text, #fff);
}

/* The cover is wrapped in an anchor, which is inline-block and so shrinks
   to the image's intrinsic width. width:100% on the image then resolves
   against a box that is itself sizing to the image, so it collapsed back
   to the native 256px and the tiles came out 256/256/300. Sizing the
   anchor instead gives the image a real basis to fill. */
#games figure a {
	display: block;
	width: 100%;
	max-width: 300px;
	margin-inline: auto;
}

#games figure img {
	max-width: none;
}

/* ============================================================
   Layer 14 — Team cards, matched to live
   ============================================================ */


/* Team: live runs three across with large square portraits, the name, an
   uppercase role and a small rule — no card chrome. */
.repulse-team {
	grid-template-columns: repeat(3, 1fr);
	gap: clamp(1.5rem, 3vw, 2.5rem);
}

.repulse-team__member {
	background: none;
	border: 0;
	padding: 0;
}

.repulse-team__member:hover {
	border-color: transparent;
}

.repulse-team__role {
	text-transform: uppercase;
	letter-spacing: 0.08em;
	font-size: 0.78rem;
}

/* The small rule live sets under each role. */
.repulse-team__member::after {
	content: "";
	display: block;
	width: 44px;
	height: 2px;
	margin: 0.9rem auto 0;
	background: color-mix(in srgb, var(--wp--preset--color--primary) 55%, transparent);
}

@media (max-width: 900px) {
	.repulse-team { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
	.repulse-team { grid-template-columns: 1fr; }
}

/* ============================================================
   Layer 15 — Footer, matched to live
   ============================================================ */

/* Live stacks the footer centred: a row of social links, a row of menu
   links, then the copyright. The three-column grid was our invention.
   Switching to flex also retires the explicit grid placement in Layer 12,
   which grid-column/grid-row no longer apply to. */
.repulse-footer .wrap {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 1.25rem;
	max-width: 900px;
}

.repulse-footer__brand {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.25rem;
}

.repulse-footer__tagline {
	max-width: 46ch;
	margin: 0;
}

/* Both link rows run horizontally under the brand. */
.repulse-footer__social,
.repulse-footer__nav ul {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.5rem 1.5rem;
}

.repulse-footer__nav a,
.repulse-footer__social a {
	text-transform: uppercase;
	letter-spacing: 0.08em;
	font-size: 0.8rem;
	line-height: 1.6;
}

.repulse-footer__legal {
	border-top: var(--repulse-border);
	padding-top: 1.25rem;
	width: 100%;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	font-size: 0.75rem;
}

/* ============================================================
   Layer 16 — Game tiles as one group, Road Map cards
   ============================================================ */

/* The cover and the title are two separate anchors, so hovering one left
   the other cold. Making the column the hover root ties them together. */
#games > .wp-block-column:not(:first-child) figure a,
#games > .wp-block-column:not(:first-child) figure img,
#games > .wp-block-column:not(:first-child) h4 a {
	transition: transform 180ms ease, box-shadow 180ms ease, color 180ms ease;
}

#games > .wp-block-column:not(:first-child):hover figure img,
#games > .wp-block-column:not(:first-child):focus-within figure img {
	transform: translateY(-5px);
	box-shadow: 0 14px 32px rgb(0 0 0 / 0.45);
}

#games > .wp-block-column:not(:first-child):hover h4 a,
#games > .wp-block-column:not(:first-child):focus-within h4 a {
	color: var(--wp--preset--color--text, #fff);
}

/* ============================================================
   Layer 17 — Team cards
   ============================================================ */

.repulse-team__member {
	text-align: left;
	transition: transform 200ms ease;
}

/* 360 is the largest source WordPress holds for these portraits, so the
   cap is where the image stops gaining detail rather than an arbitrary
   number. The column is a little wider than that at desktop. */
.repulse-team__photo img {
	max-width: 360px;
	transition: box-shadow 220ms ease;
}

/* Scoped past Fastest's `.content-area h2`, which is one class plus one
   type and would otherwise set the size. */
.repulse-team .repulse-team__name {
	font-size: 1.4rem;
	line-height: 1.25;
	margin-top: 1.4rem;
	margin-bottom: 0.35rem;
	transition: color 180ms ease;
}

/* Fastest sizes body copy with `.content-area p`, one class plus one type,
   which outranks a lone class here. The role is a <p>, so it was rendering
   at the 17px body size instead. */
.repulse-team .repulse-team__role {
	font-size: 0.72rem;
	line-height: 1.5;
}

/* A rule that fades out rather than stopping dead. */
/* A quiet hairline. The earlier lead dot read as a stray artefact at this
   size and sat too close under the role. */
.repulse-team__member::after {
	width: 56px;
	height: 1px;
	margin: 1.1rem 0 0;
	background: color-mix(in srgb, var(--wp--preset--color--primary) 60%, transparent);
	background-image: none;
	transition: width 220ms ease, background-color 220ms ease;
}

.repulse-team__member:hover::after,
.repulse-team__member:focus-within::after {
	width: 88px;
	background: var(--wp--preset--color--primary);
}

/* Hover lights the whole card: a glow behind the portrait and the name in
   brand pink. */
.repulse-team__member:hover .repulse-team__photo img,
.repulse-team__member:focus-within .repulse-team__photo img {
	box-shadow:
		0 0 0 1px color-mix(in srgb, var(--wp--preset--color--primary) 45%, transparent),
		0 8px 46px color-mix(in srgb, var(--wp--preset--color--primary) 42%, transparent);
}

.repulse-team__member:hover .repulse-team__name,
.repulse-team__member:focus-within .repulse-team__name {
	color: var(--wp--preset--color--primary);
}

/* ============================================================
   Layer 18 — Inner pages
   The skin was built against the front page. These are the rules
   every other template needs.
   ============================================================ */

/* The page title is a full-bleed band: breadcrumbs, then the title, closed
   by a hairline. The band is what makes it read as a header rather than a
   heading that happens to be first. It runs flush out of the fixed site
   header, so .site-content only pads enough to clear that. */
.content-area .entry-header {
	position: relative;
	width: 100vw;
	margin-left: calc(50% - 50vw);
	padding-block: clamp(1.75rem, 3.5vw, 2.75rem);
	padding-inline: var(--wp--preset--spacing--30);
	background: var(--wp--preset--color--surface);
	border-bottom: 1px solid color-mix(in srgb, var(--wp--preset--color--primary) 22%, transparent);
}

.content-area .entry-header__inner {
	max-width: 1200px;
	margin-inline: auto;
}

/* Fastest sets the title in its dark ink (#212b35), which on our background
   is very nearly invisible, and hangs a 1240px light-grey rule off it. The
   band's own bottom edge is that rule now. */
.content-area .entry-title {
	color: var(--wp--preset--color--text, #fff);
	margin: 0;
}

.content-area .entry-title::after {
	content: none;
}

.repulse-crumbs {
	margin-bottom: 0.6rem;
	font-family: var(--wp--preset--font-family--heading, inherit);
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.09em;
	text-transform: uppercase;
}

/* Fastest sets an explicit font-size on li, which breaks inheritance from the
   nav — without this the crumbs render at 17px whatever the nav asks for. */
.content-area .repulse-crumbs li {
	font-size: inherit;
}

.repulse-crumbs ol {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.repulse-crumbs li + li::before {
	content: "/";
	margin-right: 0.5rem;
	color: color-mix(in srgb, var(--wp--preset--color--primary) 55%, transparent);
}

.repulse-crumbs a {
	color: color-mix(in srgb, var(--wp--preset--color--text, #fff) 62%, transparent);
	text-decoration: none;
}

.repulse-crumbs a:hover,
.repulse-crumbs a:focus-visible {
	color: var(--wp--preset--color--primary);
}

.repulse-crumbs [aria-current] {
	color: var(--wp--preset--color--primary);
}

/* Every top-level block carries the full section band padding. On the front
   page each band IS a section, but elsewhere the pattern is
   [heading group][the content it introduces] — two blocks, so the pair was
   splitting across ~176px of dead space. Close the seam between them. */
.entry-content > .wp-block-group:has(> .wp-block-separator) {
	padding-bottom: 0;
}

.entry-content > .wp-block-group:has(> .wp-block-separator) + * {
	padding-top: 0;
}

/* Same seam, without a separator to bridge it: a top-level group holding only
   a heading is a section header, not a section, so it should sit on the
   content it introduces rather than a full band away from it. Groups that do
   carry a separator are excluded — the rule above already zeroes those, and
   the separator's own bottom margin supplies their spacing. */
.entry-content > .wp-block-group:has(:is(h1, h2, h3, h4)):not(:has(p, figure, ul, ol, dl, .wp-block-columns, .wp-block-buttons, .wp-block-separator)) {
	padding-bottom: 0;
}

.entry-content > .wp-block-group:has(:is(h1, h2, h3, h4)):not(:has(p, figure, ul, ol, dl, .wp-block-columns, .wp-block-buttons, .wp-block-separator)) + * {
	padding-top: 2.25rem;
}

/* Game entries on /games/: the conversion emitted a 50/50 split, stranding a
   256px cover in the middle of a 564px column with the spec list marooned on
   the far side. Size the media column to the art and give the list the rest.
   The block writes flex-basis:50% inline, so only !important reaches it. */
body:not(.home) .wp-block-columns:has(> .wp-block-column > figure.wp-block-image:only-child) {
	align-items: center;
	/* The spec list never fills the 1240px column, so the pair read as
	   left-shifted under its own centred heading. */
	max-width: 860px;
	margin-inline: auto;
}

body:not(.home) .wp-block-column:has(> figure.wp-block-image:only-child) {
	flex: 0 0 auto !important;
	flex-basis: 300px !important;
}

body:not(.home) .wp-block-column:has(> figure.wp-block-image:only-child) ~ .wp-block-column {
	flex: 1 1 auto !important;
	flex-basis: auto !important;
}

body:not(.home) .wp-block-column > figure.wp-block-image:only-child img {
	display: block;
	width: 100%;
	border-radius: var(--repulse-radius);
}

/* Spec list: label and value share a row, values aligned in their own
   column. 16_game_specs.php builds the <dl>; before that this content was a
   run of loose <strong>/<p>/<br> blocks that stacked each value under its
   own label with a full paragraph lead between them. */
.repulse-spec {
	display: grid;
	grid-template-columns: max-content 1fr;
	/* Values that wrap would otherwise leave their label floating against the
	   middle of the row. */
	align-items: baseline;
	column-gap: 0.75rem;
	row-gap: 0.55rem;
	margin: 0;
	font-size: 0.95rem;
	line-height: 1.5;
}

.repulse-spec dt {
	font-weight: 700;
	color: color-mix(in srgb, var(--wp--preset--color--text, #fff) 70%, transparent);
}

.repulse-spec dt::after {
	content: ":";
}

.repulse-spec dd {
	margin: 0;
}

.entry-content .repulse-spec + p {
	margin-top: 1.5rem;
}

/* Body links fall through to the browser's dark blue, which is close to
   unreadable on the navy ground. */
.entry-content a:not(.wp-block-button__link):not([class*="repulse"]) {
	color: var(--wp--preset--color--primary);
	text-decoration-color: color-mix(in srgb, var(--wp--preset--color--primary) 45%, transparent);
	text-underline-offset: 0.2em;
}

.entry-content a:not(.wp-block-button__link):not([class*="repulse"]):hover,
.entry-content a:not(.wp-block-button__link):not([class*="repulse"]):focus-visible {
	text-decoration-color: currentColor;
}

/* ============================================================
   Layer 19 — /about/
   ============================================================ */

/* The conversion nested every section in a constrained group, so its children
   were capped at theme.json's 760px contentSize inside a 1240px column. Let
   them take the same 1200px the top-level blocks already get. */
body:not(.home) .entry-content .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull):not(.wp-block-separator)) {
	max-width: 1200px;
}

/* The 68ch measure keeps long-form copy readable, but it left this page's
   prose at 652px in a 1240px column. */
body:not(.home) .entry-content .is-layout-constrained p {
	max-width: none;
}

/* An image narrower than its figure sat against the left edge: the figure is
   a block at the full column width and the img is inline inside it. */
body:not(.home) .entry-content figure.wp-block-image img {
	display: block;
	margin-inline: auto;
}

/* The placeholder SVG this replaced was a 150px glyph stretched to 760px; the
   hero art is 508px and should sit at its own size. */
.repulse-about-art img {
	display: block;
	width: auto;
	max-width: min(100%, 508px);
	margin-inline: auto;
}

/* Our Process: icon leading, three across so the five points break 3-2. Six
   tracks with each point spanning two gives the same three columns while
   leaving half-track offsets available, which is what lets the trailing row
   of two centre instead of sitting left. */
.repulse-process {
	display: grid;
	grid-template-columns: repeat(6, 1fr);
	gap: 1.5rem;
	margin: 2.5rem 0 0;
	padding: 0;
	list-style: none;
}

/* Carded in the same vocabulary as the Road Map events: deep ground, the
   shared hairline border and radius. */
.repulse-process__step {
	grid-column: span 2;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1.15rem;
	padding: 2rem 1.5rem;
	background: var(--wp--preset--color--deep);
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
	font-size: 0.95rem;
	line-height: 1.55;
	text-align: center;
}

/* Start the fourth point one track in, so points 4 and 5 land on tracks 2-3
   and 4-5 with an empty half-track either side. */
.repulse-process__step:nth-child(4) {
	grid-column: 2 / span 2;
}

/* The icon sits in a chip rather than floating as a stray glyph, picking up
   the circle-and-accent language of the section ornament. */
.repulse-process__icon {
	box-sizing: border-box;
	flex: none;
	width: 58px;
	height: 58px;
	padding: 15px;
	color: var(--wp--preset--color--primary);
	background: color-mix(in srgb, var(--wp--preset--color--primary) 12%, transparent);
	border: 1px solid color-mix(in srgb, var(--wp--preset--color--primary) 38%, transparent);
	border-radius: 50%;
}

/* Discord: a full-bleed banner. The 100vw trick needs no clipping ancestor,
   which Layer 11 already guaranteed by unsetting article's overflow. */
.entry-content > .repulse-banner {
	width: 100vw;
	margin-left: calc(50% - 50vw);
	padding-inline: var(--wp--preset--spacing--30);
	background:
		radial-gradient(120% 140% at 15% 0%, color-mix(in srgb, var(--wp--preset--color--primary) 22%, transparent), transparent 60%),
		var(--wp--preset--color--surface);
	border-block: 1px solid color-mix(in srgb, var(--wp--preset--color--primary) 22%, transparent);
}

.entry-content > .repulse-banner .wp-block-columns {
	max-width: 1200px;
	margin-inline: auto;
	align-items: center;
}

.entry-content > .repulse-banner h3 {
	margin: 0;
}

.entry-content > .repulse-banner .wp-block-buttons {
	justify-content: flex-end;
}

/* Addresses: the source built these from <br> blocks, which put a full
   paragraph lead between every line. */
/* Two short addresses in half of a 1176px column left a lot of air to the
   right of each and read as stranded. Centred, and the pair pulled together
   so they sit as one block under their heading. */
body:not(.home) .entry-content .wp-block-columns:has(.repulse-address) {
	max-width: 760px;
	margin-inline: auto;
}

.entry-content .wp-block-group:has(+ .wp-block-columns .repulse-address) h3 {
	text-align: center;
}

.repulse-address {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.1rem;
	font-style: normal;
	line-height: 1.55;
	text-align: center;
}

.repulse-address__label {
	margin-bottom: 0.5rem;
	font-family: var(--wp--preset--font-family--heading, inherit);
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: var(--wp--preset--color--primary);
}

.repulse-address__name {
	font-weight: 700;
}

/* Two across: the odd point out is the fifth, so that is the one to centre. */
@media (max-width: 900px) {
	.repulse-process {
		grid-template-columns: repeat(4, 1fr);
	}

	.repulse-process__step:nth-child(4) {
		grid-column: auto / span 2;
	}

	.repulse-process__step:nth-child(5) {
		grid-column: 2 / span 2;
	}
}

@media (max-width: 560px) {
	.repulse-process {
		grid-template-columns: 1fr;
	}

	.repulse-process__step,
	.repulse-process__step:nth-child(4),
	.repulse-process__step:nth-child(5) {
		grid-column: 1 / -1;
	}
}

/* Fastest colours links inside the content area with a selector that beats
   .wp-block-button__link, so the button label came through as its blue
   (#26519e) on our pink. Dark ink on the pink also clears AA where white
   does not: 4.8:1 against 3.6:1. The hover state darkens the background to
   secondary, where white is the better pairing, and already sets it. */
.entry-content .wp-block-button__link,
.entry-content .wp-block-button__link:visited {
	color: var(--wp--preset--color--deep);
}

.entry-content .wp-block-button__link:hover,
.entry-content .wp-block-button__link:focus-visible {
	color: var(--wp--preset--color--contrast);
}

/* The banner's tint is primary at 22%, so a pink heading on it barely
   separated. White carries the line; the accent stays in the underline. */
.entry-content > .wp-block-group.repulse-banner h3 a {
	color: var(--wp--preset--color--contrast);
	text-decoration-color: color-mix(in srgb, var(--wp--preset--color--primary) 70%, transparent);
}

.entry-content > .wp-block-group.repulse-banner h3 a:hover,
.entry-content > .wp-block-group.repulse-banner h3 a:focus-visible {
	text-decoration-color: currentColor;
}

/* The desktop dropdown was the last piece of Fastest's own palette showing
   through: a white panel with its blue (#26519e) filling each link. Scoped by
   the nav's id so it clears the parent's `.main-navigation ul ul a` chain
   without !important. */
#site-navigation .sub-menu {
	background: var(--wp--preset--color--deep);
	border: var(--repulse-border);
	/* The panel hangs off the header, so a rounded top edge reads as a gap
	   between the two. Only the free bottom corners are rounded. */
	border-top: 0;
	border-radius: 0 0 var(--repulse-radius) var(--repulse-radius);
	overflow: hidden;
	box-shadow: 0 18px 40px rgba(0, 0, 0, 0.4);
}

/* Fastest borders each item all round, so adjacent items stacked two
   hairlines between them. One divider per seam, none on the last. */
#site-navigation .sub-menu a {
	background: transparent;
	color: var(--wp--preset--color--text, #fff);
	border-width: 0 0 1px;
	border-color: rgba(255, 255, 255, 0.08);
}

#site-navigation .sub-menu li:last-child a {
	border-bottom-width: 0;
}

#site-navigation .sub-menu a:hover,
#site-navigation .sub-menu a:focus-visible {
	background: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--primary);
}

/* Fastest opens the panel at `left: 0.5em`, which slides it 8px right of the
   item it belongs to. Align it to the item's own edge. */
#site-navigation li:hover > .sub-menu,
#site-navigation li.focus > .sub-menu {
	left: 0;
}

/* Its item hover is a light-theme leftover: an #ececec fill that flashes
   around the link, and a 10px text-indent that shunts the label sideways. */
#site-navigation .sub-menu li:hover,
#site-navigation .sub-menu li.focus {
	background: transparent;
	text-indent: 0;
}

/* Footer social: brand marks in a chip, matching the process icons. Scoped
   through #colophon because the Customizer's inline `footer#colophon a` rule
   carries an id and would otherwise keep the hover label white on pink. */
.repulse-footer__social {
	gap: 0.6rem;
}

#colophon .repulse-footer__social a {
	display: grid;
	place-items: center;
	width: 40px;
	height: 40px;
	padding: 0;
	color: color-mix(in srgb, var(--wp--preset--color--text, #fff) 72%, transparent);
	background: color-mix(in srgb, var(--wp--preset--color--text, #fff) 5%, transparent);
	border: var(--repulse-border);
	border-radius: 50%;
	transition: color 180ms ease, background-color 180ms ease, border-color 180ms ease;
}

#colophon .repulse-footer__social a svg {
	width: 17px;
	height: 17px;
	display: block;
}

#colophon .repulse-footer__social a:hover,
#colophon .repulse-footer__social a:focus-visible {
	color: var(--wp--preset--color--deep);
	background: var(--wp--preset--color--primary);
	border-color: var(--wp--preset--color--primary);
}

/* The legal block is two lines now, and the copy is mixed case, so the
   uppercase transform the single line carried has to go. */
.repulse-footer__legal {
	text-transform: none;
	letter-spacing: 0.02em;
}

.repulse-footer__legal p {
	margin: 0;
	line-height: 1.6;
}

.repulse-footer__entity {
	color: color-mix(in srgb, var(--wp--preset--color--text, #fff) 55%, transparent);
}

/* The footer brand carries the same mark as the header, a step smaller. */
.repulse-footer__logo {
	display: block;
	width: auto;
	height: 40px;
	margin: 0 auto 0.85rem;
}

/* Fastest's float clearfix puts a `content: ""` ::before on .wrap. The header
   wrap is display:flex, so that pseudo-element counts as a flex item: with
   justify-content:space-between it took a share of the free space and pushed
   the logo 75px right of the content edge. Floats are not in play here. */
#masthead .wrap::before,
#masthead .wrap::after {
	content: none;
}

/* ============================================================
   Layer 20 — Games
   The /games/ index is a card grid rendered from post meta, and every
   page beneath it uses templates/template-game.php: header band, then a
   hero pairing the cover with the spec list, then the page's own blocks.
   The hero sits outside .entry-content, so it carries its own width.
   ============================================================ */

.repulse-game__hero {
	display: grid;
	align-items: start;
	gap: clamp(1.75rem, 4vw, 3rem);
	/* The header band caps its inner at 1200 inside its own padding, so the
	   cap here has to include the padding for the two to share a left edge. */
	max-width: calc(1200px + 2 * var(--wp--preset--spacing--30));
	margin-inline: auto;
	padding-block: clamp(2rem, 4vw, 3rem);
	padding-inline: var(--wp--preset--spacing--30);
}

/* A trailer needs room to read as video; a cover only needs to be legible.
   With neither, the specs take the full measure rather than sit in a column. */
.repulse-game__hero--trailer {
	grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
}

.repulse-game__hero--cover {
	grid-template-columns: 260px minmax(0, 1fr);
}

.repulse-game__hero--bare {
	grid-template-columns: minmax(0, 1fr);
}

.repulse-game__art img {
	display: block;
	width: 100%;
	height: auto;
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
}

/* The spec list is the same kind of thing as the requirements table further
   down Experiment Gone Rogue's page — a label and a value per row — so it is
   set the same way: one bordered box, tinted label column, hairline rows. */
.repulse-game__specs {
	grid-template-columns: minmax(7rem, 40%) 1fr;
	align-items: stretch;
	column-gap: 0;
	row-gap: 0;
	overflow: hidden;
	font-size: 0.95rem;
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
}

.repulse-game__specs dt,
.repulse-game__specs dd {
	margin: 0;
	padding: 0.7rem 1.1rem;
	border-top: var(--repulse-border);
}

.repulse-game__specs dt:first-of-type,
.repulse-game__specs dd:first-of-type {
	border-top: 0;
}

.repulse-game__specs dt {
	font-weight: 600;
	background: color-mix(in srgb, var(--wp--preset--color--text, #fff) 3%, transparent);
}

/* The cell edge separates label from value now. */
.repulse-game__specs dt::after {
	content: none;
}

/* A rule closes the block of media, facts and buy box before the prose
   starts. It rides on whichever of those comes last — on the hero when the
   game has no Steam listing, on the buy box when it does — rather than on
   .entry-content, which is wider and would overhang. */
.repulse-game__hero:not(:has(+ .repulse-game__store)),
.repulse-game__store {
	border-bottom: 1px solid color-mix(in srgb, var(--wp--preset--color--primary) 18%, transparent);
}

.repulse-game__hero + .entry-content,
.repulse-game__store + .entry-content {
	padding-top: clamp(1.75rem, 3.5vw, 2.5rem);
}

/* ---- the /games/ index ---- */

.repulse-games {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 1.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Grid stretch alone leaves a card sized by its cover when the art is
   narrower than the column, so the widths are declared outright. */
.repulse-games__item {
	display: flex;
	width: 100%;
}

.entry-content .repulse-games__link {
	display: flex;
	flex: 1;
	width: 100%;
	flex-direction: column;
	align-items: center;
	gap: 0.35rem;
	padding: 1.5rem 1.25rem 1.75rem;
	color: inherit;
	text-align: center;
	text-decoration: none;
	background: var(--wp--preset--color--deep);
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
	transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

.entry-content .repulse-games__link:hover,
.entry-content .repulse-games__link:focus-visible {
	transform: translateY(-4px);
	border-color: color-mix(in srgb, var(--wp--preset--color--primary) 55%, transparent);
	box-shadow: 0 16px 34px rgba(0, 0, 0, 0.38);
}

/* Covers come from two eras: square art for the older games, Steam's 2:3
   library capsule for the newer ones. Cropping to a square keeps the grid
   from going ragged. */
.entry-content .repulse-games__cover {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 1;
	margin-bottom: 1.1rem;
	object-fit: cover;
	border-radius: calc(var(--repulse-radius) - 2px);
}

.repulse-games__title {
	font-family: var(--wp--preset--font-family--heading, inherit);
	font-size: 1.15rem;
	font-weight: 700;
	line-height: 1.3;
}

.repulse-games__link:hover .repulse-games__title,
.repulse-games__link:focus-visible .repulse-games__title {
	color: var(--wp--preset--color--primary);
}

.repulse-games__genre {
	font-size: 0.85rem;
	line-height: 1.4;
	color: color-mix(in srgb, var(--wp--preset--color--text, #fff) 62%, transparent);
}

@media (max-width: 900px) {
	.repulse-games {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 900px) {
	.repulse-game__hero--trailer {
		grid-template-columns: minmax(0, 1fr);
	}
}

@media (max-width: 700px) {
	.repulse-game__hero--cover {
		grid-template-columns: minmax(0, 1fr);
		justify-items: center;
	}

	.repulse-game__hero--cover .repulse-game__art {
		max-width: 260px;
	}
}

@media (max-width: 560px) {
	.repulse-games {
		grid-template-columns: minmax(0, 1fr);
	}
}


/* ============================================================
   Layer 21 — Game media
   Steam carries the studio's own trailer and screenshots for every game
   but HeartBaked, so the page opens on the trailer, closes on the store
   widget, and puts the gallery between the prose and the buy box.
   ============================================================ */

.repulse-game__video {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 16 / 9;
	background: #000;
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
}

/* Sections below the content share the header band's measure so the page
   keeps one left edge from the title all the way down. */
.repulse-game__gallery,
.repulse-game__store {
	max-width: calc(1200px + 2 * var(--wp--preset--spacing--30));
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--30);
}

.repulse-game__gallery {
	padding-block: clamp(2rem, 4vw, 3rem);
}

/* Every heading on a game page gets the same treatment, whether it comes
   from the template or from the post's own blocks: left-aligned, with a
   left-aligned rule under it rather than the centred ornament the front
   page's sections use. */
.repulse-game__section,
.repulse-game .entry-content h2:not(.repulse-team__name) {
	margin: 0 0 1.5rem;
	font-family: var(--wp--preset--font-family--heading, inherit);
	font-size: clamp(1.5rem, 2.4vw, 1.9rem);
	line-height: 1.2;
}

.repulse-game__section::after,
.repulse-game .entry-content h2:not(.repulse-team__name)::after {
	content: "";
	display: block;
	width: 56px;
	margin-top: 0.75rem;
	border-top: 2px solid var(--wp--preset--color--primary);
}

/* Headings after the first open a new section and need air above them; the
   first one already has the block's own padding. */
.repulse-game .entry-content h2:not(:first-child) {
	margin-top: clamp(2.5rem, 4vw, 3.5rem);
}

/* Same reasoning for separators inside the prose: a game page reads as a
   left-aligned document, so a centred ornament under a left-aligned heading
   looks like a mistake. */
.repulse-game .entry-content .wp-block-separator {
	margin-inline: 0;
}

/* The front page gives every top-level block up to 5.5rem of padding so its
   sections read as bands. A game page is one continuous document, so that
   padding lands as a chasm between the hero and the first heading. */
.repulse-game .entry-content > * {
	padding-block: 0;
}

.repulse-game .entry-content > * + * {
	margin-top: clamp(2.5rem, 4vw, 3.5rem);
}

.repulse-shots {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: 1rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.repulse-shots__item {
	display: flex;
}

.repulse-shots__link {
	display: block;
	width: 100%;
	overflow: hidden;
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
	transition: transform 0.2s ease, border-color 0.2s ease;
}

.repulse-shots__link:hover,
.repulse-shots__link:focus-visible {
	transform: translateY(-3px);
	border-color: color-mix(in srgb, var(--wp--preset--color--primary) 55%, transparent);
}

.repulse-shots img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 16 / 9;
	object-fit: cover;
}

.repulse-game__widget {
	display: block;
	width: 100%;
	max-width: 646px;
	height: 190px;
	margin-inline: auto;
	border: 0;
	color-scheme: normal;
}

/* The buy box is its own centred band. The hero's own padding sits above it,
   so it only needs room underneath before the closing rule. */
.repulse-game__store {
	padding-top: 0;
	padding-bottom: clamp(2.25rem, 4.5vw, 3.25rem);
}

/* The spec list sits outside .entry-content, so the content link rule never
   reached it and no :visited colour was declared anywhere — a followed link
   fell back to the browser's own near-black, unreadable on the dark panel. */
.repulse-spec a,
.repulse-spec a:visited {
	color: var(--wp--preset--color--primary);
	text-underline-offset: 0.18em;
	text-decoration-color: color-mix(in srgb, var(--wp--preset--color--primary) 45%, transparent);
	transition: color 0.2s ease, text-decoration-color 0.2s ease;
}

.repulse-spec a:hover,
.repulse-spec a:focus-visible {
	color: color-mix(in srgb, var(--wp--preset--color--primary) 55%, #fff);
	text-decoration-color: currentColor;
}

/* Same guarantee for prose links, which had a colour but no :visited. */
.entry-content a:not(.wp-block-button__link):not([class*="repulse"]):visited {
	color: var(--wp--preset--color--primary);
}

/* ---- screenshot lightbox ---- */

/* The dialog fills the viewport and pads itself away from its own controls,
   so the picture area is a fixed box: an image that is portrait, tiny, or
   still loading is centred in the same space and can never slide under a
   button. `display` only applies when open — a bare `display: grid` would
   show the dialog while it is closed. */
.repulse-lightbox[open] {
	display: grid;
	place-items: center;
}

.repulse-lightbox {
	box-sizing: border-box;
	inset: 0;
	width: 100vw;
	max-width: 100vw;
	height: 100vh;
	height: 100dvh;
	max-height: 100dvh;
	margin: 0;
	padding: 5rem 5.75rem 2rem;
	color: var(--wp--preset--color--text, #fff);
	background: transparent;
	border: 0;
	overflow: hidden;
}

.repulse-lightbox::backdrop {
	background: rgba(9, 10, 18, 0.9);
}

.repulse-lightbox__frame {
	display: grid;
	grid-template-rows: minmax(0, 1fr) auto;
	grid-template-columns: minmax(0, 1fr);
	gap: 0.85rem;
	place-items: center;
	width: 100%;
	height: 100%;
	margin: 0;
}

/* The stage is the picture's share of the frame; the counter has its own
   row. Image and spinner share the stage's single cell, so the spinner
   centres on the photo rather than on the figure. */
.repulse-lightbox__stage {
	display: grid;
	grid-area: 1 / 1;
	place-items: center;
	width: 100%;
	height: 100%;
}

.repulse-lightbox__image,
.repulse-lightbox__spinner {
	grid-area: 1 / 1;
}

.repulse-lightbox__count {
	grid-area: 2 / 1;
}

/* JS sets width and height in pixels from the thumbnail's dimensions before
   the full image arrives, so the border and fill below are already the exact
   box the photo will occupy. */
.repulse-lightbox__image {
	max-width: 100%;
	max-height: 100%;
	object-fit: contain;
	background: color-mix(in srgb, var(--wp--preset--color--text, #fff) 5%, transparent);
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
	transition: opacity 0.25s ease;
}

/* Transparent only while loading, so the resting state is opaque. Driving it
   the other way round — transparent by default, revealed by a class — leaves
   the photo invisible for good if the transition never runs, which is what
   happens in a background tab. */
.repulse-lightbox.is-loading .repulse-lightbox__image {
	opacity: 0;
}

.repulse-lightbox__spinner {
	width: 44px;
	height: 44px;
	border: 3px solid color-mix(in srgb, var(--wp--preset--color--text, #fff) 16%, transparent);
	border-top-color: var(--wp--preset--color--primary);
	border-radius: 50%;
	opacity: 0;
	visibility: hidden;
	transition: opacity 0.2s ease;
}

.repulse-lightbox.is-loading .repulse-lightbox__spinner {
	opacity: 1;
	visibility: visible;
	animation: repulse-spin 0.8s linear infinite;
}

@keyframes repulse-spin {
	to {
		transform: rotate(1turn);
	}
}

.repulse-lightbox__count {
	margin: 0;
	font-family: var(--wp--preset--font-family--heading, inherit);
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.09em;
	text-align: center;
	text-transform: uppercase;
	color: color-mix(in srgb, var(--wp--preset--color--text, #fff) 62%, transparent);
}

/* Fixed, not absolute: the controls belong to the viewport corners, not to
   whatever size the current image happens to be. */
.repulse-lightbox__close,
.repulse-lightbox__nav {
	position: fixed;
	display: grid;
	place-items: center;
	padding: 0;
	color: var(--wp--preset--color--text, #fff);
	background: rgba(25, 27, 42, 0.82);
	border: var(--repulse-border);
	border-radius: 50%;
	cursor: pointer;
	transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}

.repulse-lightbox__close:hover,
.repulse-lightbox__nav:hover,
.repulse-lightbox__close:focus-visible,
.repulse-lightbox__nav:focus-visible {
	color: var(--wp--preset--color--deep);
	background: var(--wp--preset--color--primary);
	border-color: var(--wp--preset--color--primary);
}

/* A drawn icon centres on the box; a text arrow centres on its baseline,
   which is why the chevrons sat high in their circles. */
.repulse-lightbox__icon {
	display: block;
	width: 20px;
	height: 20px;
}

.repulse-lightbox__close {
	top: 1.5rem;
	right: 1.5rem;
	width: 44px;
	height: 44px;
}

.repulse-lightbox__nav {
	top: 50%;
	width: 54px;
	height: 54px;
	transform: translateY(-50%);
}

.repulse-lightbox__nav .repulse-lightbox__icon {
	width: 26px;
	height: 26px;
}

.repulse-lightbox__nav--prev {
	left: 1.25rem;
}

.repulse-lightbox__nav--next {
	right: 1.25rem;
}

@media (max-width: 700px) {
	.repulse-lightbox {
		padding: 4.5rem 1rem 4.5rem;
	}

	/* No room for side columns on a phone, so the arrows move to the foot
	   where they cannot cover the picture. */
	.repulse-lightbox__nav {
		top: auto;
		bottom: 1.25rem;
		transform: none;
	}

	.repulse-lightbox__nav--prev {
		left: calc(50% - 4.25rem);
	}

	.repulse-lightbox__nav--next {
		right: calc(50% - 4.25rem);
	}
}

@media (prefers-reduced-motion: reduce) {
	.repulse-lightbox__image {
		transition: none;
	}

	/* Still a signal that something is happening, without the rotation. */
	.repulse-lightbox.is-loading .repulse-lightbox__spinner {
		animation: repulse-pulse 1.4s ease-in-out infinite;
	}

	@keyframes repulse-pulse {
		50% {
			opacity: 0.35;
		}
	}
}

@media (max-width: 900px) {
	.repulse-shots {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

@media (max-width: 560px) {
	.repulse-shots {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/* ============================================================
   Layer 22 — Game reference material
   The requirements table and the changelog that follow the screenshots on
   Experiment Gone Rogue. Both arrived as paragraph soup inside an HTML
   block; they are real tables and details now, so they only need type.
   ============================================================ */

.repulse-game__appendix {
	padding-top: clamp(2rem, 4vw, 3rem);
}

/* Layer 19's constrained-width override is (0,3,1) and caps every block at
   1200px; matching the layout class outranks it without !important. */
.repulse-game .entry-content .is-layout-constrained > .repulse-reqs {
	max-width: 640px;
	/* Core's constrained layout centres every child with `margin-inline: auto
	   !important`, which would leave this table floating under a left-aligned
	   heading. !important is the only thing that outranks !important. */
	margin-block: 0;
	margin-inline: 0 !important;
	overflow: hidden;
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
}

/* Fastest gives tables a bottom margin. Inside a bordered figure that reads
   as dead space under the last row rather than separation from what follows. */
.repulse-game .entry-content .repulse-reqs table {
	width: 100%;
	margin: 0;
	border-collapse: collapse;
	font-size: 0.95rem;
}

.repulse-game .entry-content .repulse-reqs td {
	padding: 0.7rem 1.1rem;
	border: 0;
	border-top: var(--repulse-border);
}

.repulse-game .entry-content .repulse-reqs tr:first-child td {
	border-top: 0;
}

/* The label column names the requirement; the value carries the weight. */
.repulse-game .entry-content .repulse-reqs td:first-child {
	width: 42%;
	color: color-mix(in srgb, var(--wp--preset--color--text, #fff) 68%, transparent);
	background: color-mix(in srgb, var(--wp--preset--color--text, #fff) 3%, transparent);
}

/* ---- changelog ---- */

.repulse-game .entry-content .wp-block-details {
	margin: 0;
	padding: 0 1.25rem;
	background: var(--wp--preset--color--deep);
	border: var(--repulse-border);
	border-radius: var(--repulse-radius);
}

.repulse-game .entry-content .wp-block-details + .wp-block-details {
	margin-top: 0.6rem;
}

.repulse-game .entry-content .wp-block-details summary {
	position: relative;
	padding: 0.9rem 2rem 0.9rem 0;
	font-family: var(--wp--preset--font-family--heading, inherit);
	font-size: 0.95rem;
	font-weight: 700;
	list-style: none;
	cursor: pointer;
	transition: color 0.2s ease;
}

.repulse-game .entry-content .wp-block-details summary::-webkit-details-marker {
	display: none;
}

/* A chevron drawn from two borders, so there is no icon to load. */
.repulse-game .entry-content .wp-block-details summary::after {
	content: "";
	position: absolute;
	top: 50%;
	right: 0.25rem;
	width: 8px;
	height: 8px;
	border-right: 2px solid var(--wp--preset--color--primary);
	border-bottom: 2px solid var(--wp--preset--color--primary);
	transform: translateY(-70%) rotate(45deg);
	transition: transform 0.2s ease;
}

.repulse-game .entry-content .wp-block-details[open] summary::after {
	transform: translateY(-30%) rotate(225deg);
}

.repulse-game .entry-content .wp-block-details summary:hover,
.repulse-game .entry-content .wp-block-details[open] summary {
	color: var(--wp--preset--color--primary);
}

.repulse-game .entry-content .wp-block-details[open] summary {
	border-bottom: var(--repulse-border);
}

.repulse-game .entry-content .wp-block-details > :not(summary) {
	margin-block: 1rem;
	font-size: 0.95rem;
}

.repulse-game .entry-content .wp-block-details ul {
	padding-left: 1.15rem;
}

.repulse-game .entry-content .wp-block-details li {
	margin-bottom: 0.45rem;
	line-height: 1.55;
}

/* A paragraph between two lists is a sub-heading inside the entry. */
.repulse-game .entry-content .wp-block-details p {
	max-width: none;
	font-weight: 700;
	color: color-mix(in srgb, var(--wp--preset--color--text, #fff) 78%, transparent);
}

@media (prefers-reduced-motion: reduce) {
	.repulse-game .entry-content .wp-block-details summary::after {
		transition: none;
	}
}

/* ============================================================
   Layer 23 — Credits
   A credits roll, so it reads down the middle: one name per line, centred
   under a centred department title. The attribution list is the exception —
   165 reference entries in a single centred column would be a page of
   scrolling, so it keeps flowing in columns.
   ============================================================ */

.entry-content .repulse-credits-page {
	text-align: center;
}

/* Air after the logo and between departments, plus the site's accent rule
   under each title. */
.entry-content .repulse-credits-page h2 {
	margin-top: clamp(3.25rem, 6vw, 5rem);
	margin-bottom: 0.35rem;
	text-align: center;
}

.entry-content .repulse-credits-page h2::after {
	content: "";
	display: block;
	width: 56px;
	margin: 0.85rem auto 0;
	border-top: 2px solid var(--wp--preset--color--primary);
}

/* The note introduces the list under it; it is an aside, not a heading. */
.entry-content .repulse-credits-page h2 + p {
	max-width: none;
	margin: 0 auto 1.5rem;
	font-size: 0.85rem;
	font-weight: 400;
	color: color-mix(in srgb, var(--wp--preset--color--text, #fff) 58%, transparent);
}

.entry-content .repulse-credits-page p {
	max-width: 68ch;
	margin-inline: auto;
}

.entry-content .repulse-credits {
	display: block;
	margin: 0;
	padding: 0;
	list-style: none;
}

.entry-content .repulse-credits li {
	margin-bottom: 0.55rem;
	font-size: 1.1rem;
	line-height: 1.5;
}

/* Attributions carry no order to preserve and there are 165 of them, so they
   flow in columns and read from their own left edge. */
.entry-content .repulse-credits--assets {
	columns: 25rem;
	column-gap: 2.5rem;
	text-align: left;
}

.entry-content .repulse-credits--assets li {
	margin-bottom: 0.6rem;
	font-size: 0.9rem;
	color: color-mix(in srgb, var(--wp--preset--color--text, #fff) 78%, transparent);
	break-inside: avoid;
}

/* The disclaimer closes the page; it is prose, not a credit. */
.entry-content .repulse-credits--assets ~ p {
	font-size: 0.9rem;
	text-align: left;
}
