/* =========================================================================
   Doncaster Rovers — design system
   Green & gold. Bold condensed headings, generous rhythm, card systems.
   ========================================================================= */

:root {
	--green:      #0B5C34;
	--green-dark: #08331D;
	--green-deep: #052517;
	--gold:       #F5C518;
	--gold-dark:  #D9A800;
	/* The gold button hover. Sampling the crest gave #E8CF03, which is
	   green-shifted and reads muddy next to the resting --gold; this is a cleaner,
	   lighter yellow that lifts on hover instead of dulling. Dark ink on it
	   measures 14.7:1, against 11.0:1 on the resting colour. */
	--gold-bright: #FFEB62;
	/* The colour accent *text* uses — eyebrows, tags, roles, small labels.
	   Gold is a fill: --gold and --gold-dark are for buttons, borders and large
	   display only, because as small text they measure ~2.2:1 on white and fail
	   AA. A darkened gold clears the gate but reads muddy, so accent text is the
	   brand green instead — 7.9:1 on white, 7.2:1 on cream, and it sits clearly
	   apart from the near-black --green-dark used for headings. */
	--accent-ink: #0B5C34;
	/* Links carry their own green, lighter than --accent-ink. With no resting
	   underline, WCAG 1.4.1 asks a link to clear 3:1 against the text around
	   it, and --accent-ink measures only 2.21:1 on body copy. Same hue and
	   chroma, lightness raised until it clears — the OKLCH move, so the colour
	   stays in the family. #2F774D. Measured: 3.29:1 on body ink, 5.43:1 on
	   white, 4.93:1 on cream. The window is narrow (L 0.490–0.533): below it
	   the link stops separating from body text, above it fails AA on cream. */
	--link:       oklch(0.511 0.0985 155.1);
	--ink:        #0E1A13;
	--slate:      #4A5750;
	--cream:      #F6F4EC;
	--line:       #E4E1D6;
	--white:      #FFFFFF;

	--radius-sm: 6px;
	--radius:    14px;
	--radius-lg: 22px;
	--shadow:    0 18px 40px -24px rgba(8, 51, 29, .45);
	--shadow-sm: 0 8px 20px -14px rgba(8, 51, 29, .4);

	--wrap: 1200px;
	--gap: clamp(1.5rem, 4vw, 4rem);

	--font-head: "Barlow Condensed", "Arial Narrow", system-ui, sans-serif;
	--font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
}

/* ---- reset-ish ---- */
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
/* A phone should never scroll sideways. This is a guard, not a fix: anything
   that overflows is still a bug and still worth finding — but a stray wide
   element should degrade to being clipped, not to a page that slides under the
   reader's thumb while they try to scroll down.
   `clip` rather than `hidden`: hidden makes the element a scroll container,
   which silently breaks position: sticky on descendants. clip does not. */
html { overflow-x: clip; }
img, svg, video { max-width: 100%; height: auto; display: block; }
/* No resting underline anywhere; the cue appears on hover and on keyboard
   focus, so a keyboard user is never worse off than a mouse user. */
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }

body {
	font-family: var(--font-body);
	color: var(--ink);
	background: var(--white);
	font-size: 1.0625rem;
	line-height: 1.65;
	-webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
	font-family: var(--font-head);
	font-weight: 600;
	line-height: 1.03;
	text-transform: uppercase;
	letter-spacing: .01em;
	color: var(--green-dark);
	margin: 0 0 .5em;
}
h1 { font-size: clamp(2.5rem, 6vw, 4rem); }
h2 { font-size: clamp(1.9rem, 4vw, 2.9rem); }
h3 { font-size: clamp(1.35rem, 2.4vw, 1.75rem); }
p { margin: 0 0 1rem; }

/* Balance headings and kill orphans in ledes. Condensed uppercase at these
   sizes breaks badly at mid widths without it. */
h1, h2, h3, h4 { text-wrap: balance; }
.section__lede, .hero__body, .page-header__lede { text-wrap: pretty; }

/* A section heading is an <h1> when it's first on a page with no hero, and an
   <h2> otherwise (see dr_heading_tag). The class holds the size steady so the
   document outline can change without the design moving. */
.section__title { font-size: clamp(1.9rem, 4vw, 2.9rem); }

.screen-reader-text {
	position: absolute !important; width: 1px; height: 1px; overflow: hidden;
	clip: rect(1px,1px,1px,1px); white-space: nowrap;
}
.skip-link { position: absolute; left: -999px; }
.skip-link:focus { left: 1rem; top: 1rem; background: var(--gold); color: var(--ink); padding: .6rem 1rem; border-radius: var(--radius-sm); z-index: 100; }

/* ---- layout ---- */
.wrap { width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; }
.section { padding-block: clamp(3rem, 7vw, 6rem); }
/* The header is sticky, so an anchored section must stop clear of it or its
   heading lands underneath. */
.section[id] { scroll-margin-top: 90px; }
.section--center { text-align: center; }
.section.is-green { background: var(--green); color: #EAF3EC; }
.section.is-green h1, .section.is-green h2, .section.is-green h3 { color: var(--white); }
.section.is-green-dark { background: var(--green-dark); color: #EAF3EC; }
.section.is-cream { background: var(--cream); }
/* Gold band. Measured, not guessed: --ink on --gold is 10.96:1 and the green
   eyebrow on it is 4.96:1, so both clear AA. Headings take --green-dark, which
   is higher again. Note the eyebrow cannot stay gold here as it does on the
   green bands — it would vanish. */
.section.is-gold { background: var(--gold); color: var(--ink); }
.section.is-gold h1, .section.is-gold h2, .section.is-gold h3 { color: var(--green-dark); }
.is-gold .section__eyebrow { color: var(--green); }
.is-gold .section__lede { color: var(--ink); }
.section__head { max-width: 46rem; margin-bottom: clamp(1.5rem, 3vw, 2.75rem); }
/* --accent-ink (green), not --gold-dark: this is small text on a light ground
   and --gold-dark measures 2.19:1 there. --gold stays on the green bands below,
   where it has the contrast to work. */
.section__eyebrow { font-family: var(--font-head); text-transform: uppercase; letter-spacing: .12em; font-weight: 600; color: var(--accent-ink); font-size: .95rem; margin: 0 0 .5rem; }
.is-green .section__eyebrow { color: var(--gold); }
.section__lede { font-size: 1.15rem; color: var(--slate); }
.is-green .section__lede { color: #CFE3D5; }

/* ---- buttons ---- */
.btn {
	display: inline-flex; align-items: center; gap: .5rem;
	font-family: var(--font-head); text-transform: uppercase; letter-spacing: .04em;
	font-weight: 600; font-size: 1.05rem; line-height: 1;
	/* Optical centring, not mathematical: Barlow Condensed's caps sit low in the
	   line box, so an even padding-block leaves the label looking dropped. */
	padding: .8rem 1.6rem .92rem; border-radius: 999px; text-decoration: none;
	border: 1px solid transparent; transition: transform .15s ease, background .15s ease, color .15s ease, border-color .15s ease;
}
.btn:hover { transform: translateY(-2px); }
.btn--gold { background: var(--gold); color: var(--ink); }
.btn--gold:hover { background: var(--gold-bright); }
.btn--green { background: var(--green); color: var(--white); }
.btn--green:hover { background: var(--green-dark); }
/* Outline buttons stay unfilled on hover — the outline and the label go gold.
   On the dark bands that is 8.6:1 on --green-dark and 5.0:1 on --green.
   Gold on a light ground is only ~2.2:1, so the two ghost buttons that sit on
   white (the news teaser and "Back to all news") take the link green instead.
   Same gesture, legible in both places. */
.btn--ghost { border-color: currentColor; color: inherit; }
.btn--ghost:hover { background: transparent; border-color: var(--accent-ink); color: var(--accent-ink); }
.hero .btn--ghost:hover,
.cta-band .btn--ghost:hover,
.page-header .btn--ghost:hover,
.section.is-green .btn--ghost:hover,
.section.is-green-dark .btn--ghost:hover { background: transparent; border-color: var(--gold); color: var(--gold); }
.section.is-green .btn--ghost, .page-header .btn--ghost { color: var(--white); }
.cta-row { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.5rem; }

/* ---- utility bar ---- */
.utility-bar { background: var(--green-deep); color: #BFD8C6; font-size: .85rem; }
.utility-bar__inner { display: flex; justify-content: space-between; align-items: center; min-height: 40px; gap: 1rem; }
.utility-bar__tag { margin: 0; letter-spacing: .02em; }
.utility-nav { display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap; }
.utility-nav__list { list-style: none; display: flex; gap: 1.25rem; margin: 0; padding: 0; }
.utility-nav__list a { text-decoration: none; }
.utility-nav__list a:hover { color: var(--gold); }
/* Rendered outside the menu (it comes from Rovers Settings, not a menu item) but
   styled to match it exactly — it should read as one more link, not an ad. */
.utility-nav__shop { text-decoration: none; }
.utility-nav__shop:hover { color: var(--gold); }
@media (max-width: 760px) { .utility-bar__tag { display: none; } .utility-bar__inner { justify-content: center; } .utility-nav { justify-content: center; } }

/* ---- header / nav ---- */
.site-header { position: sticky; top: 0; z-index: 50; background: var(--white); border-bottom: 1px solid var(--line); }
.site-header__inner { display: flex; align-items: center; gap: 1.5rem; min-height: 76px; }
.site-brand { display: flex; align-items: center; gap: .75rem; text-decoration: none; margin-right: auto; }
.site-brand__crest { width: 52px; height: 52px; border-radius: 50%; flex: none; }
.site-brand__crest--fallback {
	display: grid; place-items: center; background: var(--green); color: var(--gold);
	font-family: var(--font-head); font-weight: 700; font-size: 1.4rem; letter-spacing: .02em;
	border: 3px solid var(--gold);
}
.site-brand__text { display: flex; flex-direction: column; line-height: 1; }
.site-brand__name { font-family: var(--font-head); font-weight: 700; text-transform: uppercase; font-size: 1.5rem; color: var(--green-dark); letter-spacing: .01em; }
.site-brand__sub { font-family: var(--font-head); text-transform: uppercase; letter-spacing: .28em; font-size: .72rem; color: var(--accent-ink); }

/*
 * Full-lockup header logo (Rovers Settings → Brand → Header logo).
 *
 * Sized by height with width:auto, because the lockup and the crest it swaps to
 * on a phone have very different proportions — 3.46:1 against 1:1 — and height
 * is the only dimension common to both.
 *
 * --brand-logo-h is the one number to change if the club wants it bigger, and
 * it is a real trade-off rather than a default worth defending. The supplied
 * lockup is 1760x508, so at 64px the "Soccer Club" line inside it lands around
 * 7px tall. Matching the 11.5px of the live type it replaces would take roughly
 * 102px of logo, which is most of the header height over again.
 */
.site-brand--lockup { gap: 0; }
.site-brand__logo { display: block; height: var(--brand-logo-h, 64px); width: auto; }

@media (max-width: 640px) {
	/*
	 * The lockup, not a crest, so no rounding — and a touch shorter than the
	 * desktop height rather than the 44px a round badge wanted, because the
	 * wordmark has to stay readable. Nothing shares the row but the menu
	 * button, so there is room for it.
	 */
	.site-brand__logo { height: 52px; }
}

.primary-nav { display: flex; align-items: center; gap: 1.25rem; }
.primary-nav__list { list-style: none; display: flex; gap: 1.1rem; margin: 0; padding: 0; align-items: center; }
.primary-nav__list a { text-decoration: none; font-family: var(--font-head); text-transform: uppercase; font-weight: 600; font-size: 1.05rem; letter-spacing: .02em; color: var(--green-dark); padding: .4rem 0; }
.primary-nav__list a:hover { color: var(--accent-ink); }
/* Current page. A real underline rather than a pseudo-element, because it has to
   follow the text: the dropdown's anchors are full-width blocks, so a positioned
   bar spans the whole item while the text may be half of it. text-decoration
   tracks the glyphs, so the top-level items and the dropdown match by
   construction. The nav is uppercase throughout, so there are no descenders for
   the line to collide with. */
.primary-nav__list .current-menu-item > a,
.primary-nav__list .current_page_item > a,
.primary-nav__list .current-menu-ancestor > a,
.primary-nav__list .current-menu-parent > a,
.primary-nav__list [aria-current="page"] {
	color: var(--green-dark);
	text-decoration: underline;
	text-decoration-color: var(--gold);
	text-decoration-thickness: 3px;
	text-underline-offset: .5rem;
}
/* Club and About Rovers are both underlined when you are on About Rovers, and
   that is intended: the top-level marks the section you are in, the dropdown
   item marks the page. */
.primary-nav__list .menu-item-has-children { position: relative; }
.primary-nav__list .sub-menu {
	list-style: none; margin: 0; padding: .5rem; position: absolute; top: 100%; left: -1rem;
	background: var(--white); min-width: 230px; border: 1px solid var(--line); border-radius: var(--radius);
	box-shadow: var(--shadow); opacity: 0; visibility: hidden; transform: translateY(6px);
	transition: opacity .15s ease, transform .15s ease, visibility .15s ease; z-index: 60;
}
.primary-nav__list .menu-item-has-children:hover > .sub-menu,
.primary-nav__list .menu-item-has-children:focus-within > .sub-menu { opacity: 1; visibility: visible; transform: none; }
.primary-nav__list .sub-menu a { display: block; padding: .55rem .75rem; border-radius: var(--radius-sm); font-size: .98rem; }
.primary-nav__list .sub-menu a:hover { background: var(--cream); }

.nav-toggle { display: none; background: none; border: 0; padding: .5rem; }
.nav-toggle__bars, .nav-toggle__bars span { display: block; }
.nav-toggle__bars { width: 26px; }
.nav-toggle__bars span { height: 3px; background: var(--green-dark); border-radius: 2px; margin: 5px 0; transition: transform .2s ease, opacity .2s ease; }

@media (max-width: 1040px) {
	.nav-toggle { display: block; }
	.primary-nav {
		position: fixed; inset: 116px 0 0 auto; width: min(88vw, 360px); background: var(--white);
		flex-direction: column; align-items: stretch; gap: 0; padding: 1.25rem; overflow-y: auto;
		box-shadow: var(--shadow); transform: translateX(105%); z-index: 55;
		/* transform alone only moves the panel off-screen — its 22 links stay
		   focusable and in the accessibility tree. visibility takes them out of
		   both, and is delayed on close so the slide-out still animates. */
		visibility: hidden;
		overscroll-behavior: contain;
		transition: transform .25s ease, visibility 0s linear .25s;
	}
	.primary-nav.is-open { transform: none; visibility: visible; transition: transform .25s ease, visibility 0s; }
	.primary-nav__list { flex-direction: column; align-items: stretch; gap: 0; }
	.primary-nav__list > li { border-bottom: 1px solid var(--line); }
	.primary-nav__list a { padding: .85rem .25rem; display: block; }
	.primary-nav__list .sub-menu { position: static; opacity: 1; visibility: visible; transform: none; box-shadow: none; border: 0; padding: 0 0 .5rem 1rem; }
	.primary-nav__cta { margin-top: 1rem; justify-content: center; }
	body.nav-open { overflow: hidden; }

	/*
	 * Scrim behind the open panel.
	 *
	 * Two jobs. It dims the page so the panel reads as the thing in front,
	 * rather than as a white block that happens to overlap; and it is the
	 * visible cue that tapping away is a thing you can do. The closing itself
	 * is bound in main.js on the document, so the menu still closes on a tap
	 * even where this pseudo-element is not what receives it.
	 *
	 * A pseudo-element rather than a real div: no markup to keep in sync, and
	 * nothing focusable added between the toggle and the panel.
	 */
	/*
	 * 49, and it has to be below 50.
	 *
	 * .site-header is position:sticky with z-index 50, which makes it a
	 * stacking context — so the panel's own z-index:55 is scoped inside the
	 * header and the whole thing still resolves to 50 against the page. A scrim
	 * at 54 therefore painted *over* the open menu: greyed out, every tap
	 * swallowed. Below the header it dims the page and leaves the menu on top,
	 * and a tap still lands on body, which is what closes it.
	 */
	body.nav-open::before {
		content: ''; position: fixed; inset: 0; z-index: 49;
		background: rgb(0 0 0 / .45);
	}

	@media (prefers-reduced-motion: no-preference) {
		body.nav-open::before { animation: dr-scrim-in .25s ease both; }
	}
}

@keyframes dr-scrim-in { from { opacity: 0; } to { opacity: 1; } }

/* ---- page header ---- */
.page-header { background: var(--green); color: #EAF3EC; padding-block: clamp(3rem, 6vw, 5rem); }
.page-header__inner > * { max-width: 52rem; }
.page-header__eyebrow { font-family: var(--font-head); text-transform: uppercase; letter-spacing: .14em; color: var(--gold); font-weight: 600; margin: 0 0 .4rem; }
.page-header__title { color: var(--white); margin: 0; }
.page-header__lede { font-size: 1.2rem; color: #CFE3D5; margin-top: 1rem; }

/* ---- prose (styles native Gutenberg core-block output) ---- */
.prose { max-width: 46rem; }
.prose > * { margin-bottom: 1.15rem; }
.prose h2 { margin-top: 2.5rem; }
.prose h3 { margin-top: 2rem; }
.prose a { color: var(--link); text-underline-offset: 3px;
	text-decoration-thickness: from-font; }
/* Darken on hover: --accent-ink is now the same green as the resting state, so
   the hover has to move the other way to stay visible. */
.prose a:hover, .prose a:focus-visible { color: var(--green-dark); text-decoration: underline; }
.prose ul, .prose ol { padding-left: 1.25rem; }
.prose li { margin-bottom: .4rem; }
.prose img, .prose .wp-block-image { border-radius: var(--radius); }
.prose figure { margin: 2rem 0; }
.prose figcaption { font-size: .9rem; color: var(--slate); text-align: center; margin-top: .5rem; }

/* ---- club song ----
   The song sits on the club's gold. Eyebrow and title centre over it; the
   lyrics stay left-aligned but sit as a centred block, because a verse centred
   line by line reads as a poem and is measurably harder to follow — the ragged
   left edge is what your eye returns to on every line.

   Hooked on the id the heading already generates (dr_section_id_attr), so this
   needs no new field and no content change — it ships with the theme. The
   trade: rename the heading away from "Club song" and the treatment lifts. */
#club-song {
	/* A pool of the bright gold under the words, over the artwork. It settles
	   the busiest part of the pattern where the lyrics sit, and it earns its
	   place on contrast alone: --ink measures 9.60:1 on the darkest gold in the
	   flow and 13.83:1 through the middle of this overlay. Three stops rather
	   than two so the falloff has no edge to catch the eye. */
	background-image:
		radial-gradient(ellipse 62% 72% at 50% 50%,
			rgba(255, 235, 98, .92) 0%,
			rgba(255, 235, 98, .72) 45%,
			rgba(255, 235, 98, 0) 78%),
		url('../img/club-song-flow.webp');
	background-size: auto, cover;
	background-position: center, center;
	background-repeat: no-repeat, no-repeat;
}
#club-song .section__eyebrow,
#club-song .section__title { text-align: center; }
/* On this gold the usual --accent-ink measures 4.96:1 against the darkest part
   of the pattern — over the line, but only just. --green-dark takes it to
   8.58:1 for the cost of nothing. Body copy is already --ink at 10.96:1. */
#club-song .section__eyebrow { color: var(--green-dark); }
/* Left-aligned text, centred block. fit-content is the point: centring .prose
   alone centres a 46rem box, and since the longest lyric is about half that the
   verse still sits visibly left of centre. Shrink the box to the words first,
   then centre it, and the song lands under the title. */
#club-song .prose { width: fit-content; max-width: 100%; margin-inline: auto; }
.prose blockquote, .prose .wp-block-quote {
	border-left: 4px solid var(--gold); padding: .5rem 0 .5rem 1.5rem; margin: 2rem 0;
	font-size: 1.25rem; color: var(--green-dark); font-style: italic;
}
.prose .wp-block-pullquote { border-top: 3px solid var(--gold); border-bottom: 3px solid var(--gold); padding: 1.5rem 0; text-align: center; }
.prose .wp-block-button__link { background: var(--gold); color: var(--ink); border-radius: 999px; font-family: var(--font-head); text-transform: uppercase; }
.prose .alignwide { width: min(100vw - 2.5rem, 1000px); margin-inline: calc(50% - min(50vw - 1.25rem, 500px)); max-width: none; }
.prose .wp-block-gallery { border-radius: var(--radius); overflow: hidden; }

/* ---- news grid / cards ---- */
.news-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.75rem; container-type: inline-size; }
/* Two columns start at 2x300 + 28 gap and give way to three at 3x300 + 56.
   In that band a third post sits alone on its own row, so the odd one out is
   dropped. Written as "odd and last" rather than "the third", so it still holds
   if the club raises the post count: four stay four, five show four. */
@container (min-width: 628px) and (max-width: 955px) {
	.news-grid > :nth-child(odd):last-child { display: none; }
}
.news-card { position: relative; background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; display: flex; flex-direction: column; transition: transform .15s, box-shadow .15s; }
.news-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.news-card__media { aspect-ratio: 16 / 9; overflow: hidden; display: block; }
.news-card__img { width: 100%; height: 100%; object-fit: cover; object-position: center; }
.news-card__img--placeholder { display: grid; place-items: center; background: var(--green); color: var(--gold); font-family: var(--font-head); text-transform: uppercase; letter-spacing: .1em; }
.news-card__body { padding: 1.25rem 1.35rem 1.5rem; display: flex; flex-direction: column; gap: .5rem; }
.news-card__meta { display: flex; gap: .75rem; align-items: center; font-size: .82rem; color: var(--slate); text-transform: uppercase; letter-spacing: .06em; margin: 0; }
.news-card__cat { background: var(--cream); color: var(--green-dark); padding: .15rem .55rem; border-radius: 999px; font-weight: 600; white-space: nowrap; }
/* One date, two spellings — the long one by default. */
.news-card__date--short { display: none; }
@media (max-width: 720px) {
	/* "October 20, 2025" wraps to two lines in a card that is 85% of a phone,
	   and drags the category chip onto two lines with it. "Oct 20, 2025" holds
	   one line and the meta row stays a row. */
	.news-card__date--long { display: none; }
	.news-card__date--short { display: inline; }
	.news-card__meta { gap: .5rem; white-space: nowrap; }
}
/* Below about 380px even the short date and the chip together are too wide, and
   the chip is the one that can go: the date says something the card does not
   repeat, while the category is already implied by the news section it sits in
   and is on the post itself. */
@media (max-width: 380px) {
	.news-card__cat { display: none; }
}
.news-card__title { margin: 0; font-size: 1.35rem; }
.news-card__title a { text-decoration: none; }
.news-card__title a:hover { color: var(--accent-ink); }
/*
 * The title's link is stretched over the whole card, so "Read more" — a
 * decorative span, not an anchor — is clickable along with everything else.
 * Before this it looked exactly like a text link and did nothing when clicked.
 *
 * Stretching the existing link rather than adding a second one keeps one link
 * per card in the accessibility tree, and the focus ring still draws around the
 * title text because ::after does not change the anchor's own box.
 */
.news-card__title a::after { content: ""; position: absolute; inset: 0; }
/* The excerpt stays selectable above the overlay. */
.news-card__excerpt { position: relative; }
/* Hovering anywhere on the card moves the affordance, not just the words. */
.news-card:hover .news-card__more { text-decoration: underline; }
@media (prefers-reduced-motion: no-preference) {
	.news-card:hover .news-card__more::after { transform: translateX(3px); }
}
/* Three lines, then an ellipsis. WordPress already trims the excerpt by word
   count, but a word count is not a line count: one story ran to seven lines on
   a phone while another ran to two, so a row of cards had no common baseline —
   and a swipe row takes its height from the worst of them. Clamping by line
   gives every card the same shape at every width.

   The whole excerpt stays in the markup. This hides lines rather than cutting
   words, so search engines and screen readers still get all of it. */
.news-card__excerpt {
	color: var(--slate);
	margin: 0;
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	line-clamp: 3;
	overflow: hidden;
}
/* Typography comes from the shared text-link rule; this only holds it to the card foot. */
.news-card__more { margin-top: auto; }

.pagination { margin-top: 2.5rem; }
.pagination .nav-links { display: flex; gap: .5rem; flex-wrap: wrap; justify-content: center; }
.pagination a, .pagination span { padding: .5rem .9rem; border-radius: var(--radius-sm); border: 1px solid var(--line); text-decoration: none; }
.pagination .current { background: var(--green); color: var(--white); border-color: var(--green); }

/* ---- single news ---- */
.single-news__header { background: var(--green); color: #EAF3EC; padding-block: clamp(2.5rem, 5vw, 4rem); }
.single-news__header-inner > * { max-width: 52rem; }
.single-news__meta { display: flex; gap: .75rem; text-transform: uppercase; letter-spacing: .06em; font-size: .85rem; color: var(--gold); margin: 0 0 .75rem; }
.single-news__title { color: var(--white); margin: 0; }
.single-news__feature { margin: 0; }
.single-news__feature .wrap { margin-top: -2.5rem; }

/*
 * The image lifting up into the green header is a desktop flourish, and it only
 * works while the header has padding to spare. On a phone that padding sits at
 * the bottom of its clamp — 2.5rem — so a -2.5rem lift cancels it exactly and
 * the image lands hard against the title. A shallower lift keeps the overlap
 * and gives the headline somewhere to end.
 */
@media (max-width: 720px) {
	.single-news__feature .wrap { margin-top: -1rem; }
}
/* The featured image keeps its own proportions. The news library runs from 0.43
   to 3.01, so one fixed crop could only ever suit a third of it — a 16:8 window
   beheaded the portraits and cut the ends off the banners.

   Height is what gets bounded, not width. Stretched to the full 1200px column,
   38 of the 85 posts would stand over 900px tall and one over 2600px — three
   screens of photograph before a word of the story.

   No upscaling: an image narrower than the column sits centred at its own size
   rather than being blown up soft. */
.single-news__feature-img {
	width: auto;
	max-width: 100%;
	/* 50rem is 800px, which against the 1200px column is a ratio of 1.5 — the
	   median of the library. So the typical landscape shot fills the column
	   exactly, wider ones fill it and come up short of the cap, and only the
	   portraits are held back by height. At 40rem the cap bit at 1.875 and left
	   three quarters of the posts sitting inset. The vh term keeps a hero off
	   the whole screen on a laptop. */
	max-height: min(80vh, 50rem);
	margin-inline: auto;
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow);
}
.single-news__footer { padding-bottom: 4rem; }

/* ---- sponsor footer strip ---- */
.sponsor-footer { background: var(--cream); padding-block: 2.5rem; border-top: 1px solid var(--line); }
.sponsor-footer__eyebrow { text-align: center; font-family: var(--font-head); text-transform: uppercase; letter-spacing: .14em; color: var(--slate); font-size: .85rem; margin: 0 0 1.25rem; }
/* Four across, and whatever is left centred underneath. A plain wrap packed as
   many as would fit per row, which with seven sponsors left a single orphan
   stranded on its own line. Fixing the tiles to a quarter of the row makes the
   break deliberate — 4 + 3 — and justify-content centres the short row. */
.sponsor-footer__grid { list-style: none; margin: 0 auto 1.25rem; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; align-items: stretch; gap: 1rem; max-width: 58rem; }
.sponsor-footer__item { flex: 0 0 calc(25% - .75rem); background: #fff; border-radius: 10px; padding: .7rem 1.25rem; display: grid; place-items: center; min-height: 66px; }
/* Step down rather than squeeze: 3 up on tablet, 2 up on a phone. The basis
   subtracts the gaps the row carries — (n-1)/n of the 1rem gap. */
@media (max-width: 820px) { .sponsor-footer__item { flex-basis: calc(33.333% - .667rem); } }
@media (max-width: 560px) { .sponsor-footer__item { flex-basis: calc(50% - .5rem); } }
.sponsor-footer__link { display: grid; place-items: center; width: 100%; height: 100%; }
.sponsor-footer__logo { max-height: 46px; max-width: 150px; width: auto; object-fit: contain; }
.sponsor-footer__name { font-family: var(--font-head); text-transform: uppercase; font-weight: 600; color: var(--green-dark); }
.sponsor-footer__cta { display: flex; justify-content: center; gap: .75rem; flex-wrap: wrap; }

/* ---- footer ---- */
.site-footer { background: var(--green-deep); color: #AEC8B6; }
.site-footer__grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1.2fr; gap: 2.5rem; padding-block: clamp(2.5rem, 5vw, 4rem); }
.site-footer__crest { width: 72px; height: 72px; object-fit: contain; }
.site-footer__club { font-family: var(--font-head); text-transform: uppercase; font-size: 1.25rem; color: var(--white); margin: 1rem 0 .5rem; }
.site-footer__addr { font-size: .95rem; line-height: 1.6; }
.site-footer__heading { font-size: 1.05rem; color: var(--gold); letter-spacing: .08em; margin-bottom: 1rem; }
.site-footer__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.site-footer__list a { text-decoration: none; font-size: .96rem; }
.site-footer__list a:hover { color: var(--gold); }
.site-footer__social { display: flex; gap: .6rem; margin-top: 1rem; }
.social-icon { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; background: rgba(255,255,255,.1); color: #fff; transition: background .2s, color .2s; }
.social-icon:hover { background: var(--gold); color: var(--ink); }
.social-icon svg { width: 20px; height: 20px; display: block; }
.site-footer__bar { border-top: 1px solid rgba(255,255,255,.1); }
.site-footer__bar-inner { display: flex; justify-content: space-between; gap: 1rem; padding-block: 1.25rem; font-size: .85rem; }
.site-footer__bar-inner p { margin: 0; }
.site-footer__credit a { color: var(--gold); text-decoration: none; }
@media (max-width: 860px) { .site-footer__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .site-footer__grid { grid-template-columns: 1fr; } .site-footer__bar-inner { flex-direction: column; } }

/* Footer columns collapse on a phone.
   The footer costs 1.4 screens on every page — 40% of a short page like /play/,
   and it is chrome rather than content. Below 720px the three link columns
   become disclosures. The club name and address stay open: that is the part
   people scroll to the footer to find.

   <summary> is allowed to hold heading content, so the h2 stays inside it and
   the document outline is unchanged. main.js drives `open` off the viewport,
   which means with JS off the footer stays fully open — exactly what it does
   today. */
.site-footer__col > summary { list-style: none; }
.site-footer__col > summary::-webkit-details-marker { display: none; }
@media (min-width: 721px) {
	/* Forced open above the breakpoint, so a stray click collapsing a column
	   that then could not be reopened would just be a puzzle. */
	.site-footer__col > summary { pointer-events: none; }
}
@media (max-width: 720px) {
	.site-footer__grid { gap: 0; }
	.site-footer__brand { margin-bottom: 1.5rem; }
	.site-footer__col > summary {
		cursor: pointer;
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 1rem;
		/* Padding rather than a height, so the target grows with the text. */
		padding-block: .9rem;
		border-bottom: 1px solid rgba(255,255,255,.14);
	}
	.site-footer__col > summary .site-footer__heading { margin: 0; }
	/* Chevron drawn from borders — no icon to ship, and it takes the gold the
	   heading already uses. */
	.site-footer__col > summary::after {
		content: "";
		flex: none;
		width: .55rem;
		height: .55rem;
		border-inline-end: 2px solid var(--gold);
		border-block-end: 2px solid var(--gold);
		transform: translateY(-.15rem) rotate(45deg);
		transition: transform .15s ease;
	}
	.site-footer__col[open] > summary::after { transform: translateY(.1rem) rotate(-135deg); }
	.site-footer__col[open] .site-footer__list,
	.site-footer__col[open] .site-footer__social { padding-block: .9rem .4rem; }
}
@media (prefers-reduced-motion: reduce) {
	.site-footer__col > summary::after { transition: none; }
}

/* ---- SECTIONS ---- */

/* hero */
.hero { position: relative; background: var(--green-dark); color: var(--white); overflow: hidden; display: flex; align-items: center; min-height: 60vh; }
.hero__bg { position: absolute; inset: 0; }
.hero__bg img { width: 100%; height: 100%; object-fit: cover; object-position: center 25%; opacity: .4; }
.hero::after { content: ""; position: absolute; inset: 0; background: linear-gradient(120deg, var(--green-deep) 20%, rgba(5,37,23,.65) 65%, rgba(5,37,23,.35)); }
.hero__inner { position: relative; z-index: 2; padding-block: clamp(2.5rem, 6vw, 4rem); }
.hero__inner > * { max-width: 44rem; }
.hero__eyebrow { font-family: var(--font-head); text-transform: uppercase; letter-spacing: .16em; color: var(--gold); font-weight: 600; margin: 0 0 1rem; }
.hero__title { color: var(--white); font-size: clamp(2.75rem, 7vw, 5rem); margin: 0 0 1.25rem; }
.hero__body { font-size: 1.25rem; color: #DCEBE0; max-width: 38rem; }
.hero .cta-row { margin-top: 2rem; }

/* cards grid */
.cards__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1.5rem; }
.card { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.75rem; display: flex; flex-direction: column; gap: .6rem; transition: transform .15s, box-shadow .15s; }
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.is-green .card { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.14); color: #EAF3EC; }
.card__tag { font-family: var(--font-head); text-transform: uppercase; letter-spacing: .08em; font-size: .8rem; color: var(--accent-ink); font-weight: 600; }
.is-green .card__tag { color: var(--gold); }
.card__title { margin: 0; font-size: 1.7rem; line-height: 1.1; }
.is-green .card__title { color: var(--white); }
.card__text { color: var(--slate); margin: 0; flex: 1; }
.is-green .card__text { color: #CFE3D5; }
.card__link { margin-top: .5rem; }
.is-green .card__link { color: var(--gold); }
.card--icon .card__icon { width: 56px; height: 56px; border-radius: 50%; background: var(--cream); display: grid; place-items: center; margin-bottom: .5rem; }

/* checklist */
/* Default: heading beside the list. */
.checklist__grid { display: grid; grid-template-columns: 1fr 1.2fr; gap: var(--gap); align-items: start; }
.checklist__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .85rem; }

/* --wide: heading centred above, list in two columns beneath. Opt-in per
   section, because it only suits a long list of short items — the hall's
   thirteen facilities. Beside a heading the list has roughly half the measure,
   too narrow to hold "Commercial kitchen with servery" on one line, and a
   wrapped item breaks the run of ticks. Above it, the list has the whole
   measure to divide, capped so it doesn't sprawl.

   Column-count, not a grid, so items flow down one column and back up the next
   and stay in their given order. The gap is fixed rather than --gap because
   --gap climbs to 4rem, which would take back the width each column needs. */
@media (min-width: 900px) {
	.checklist__grid--wide { display: block; }
	.checklist__grid--wide .checklist__intro { text-align: center; max-width: 42rem; margin: 0 auto 2.5rem; }
	.checklist__grid--wide .checklist__list { max-width: 48rem; margin-inline: auto; display: block; columns: 2; column-gap: 3rem; }
	.checklist__grid--wide .checklist__list li { break-inside: avoid; margin-bottom: .85rem; }
}
/* Tick and text sit as flex siblings rather than text with an absolutely
   positioned tick. In the two-column layout above, an absolutely positioned
   pseudo-element resolves its containing block against the column
   fragmentainer instead of its own list item, so badges clipped at the column
   edge and the leftover fragment painted as a stray gold bar. Nothing in
   normal flow fragments that way. */
.checklist__list li { display: flex; align-items: flex-start; gap: .65rem; }
.checklist__list li::before { content: "✓"; flex: none; width: 1.6rem; height: 1.6rem; margin-top: .05em; background: var(--gold); color: var(--ink); border-radius: 50%; display: grid; place-items: center; font-weight: 700; font-size: .9rem; }
.is-green .checklist__list li::before { background: var(--gold); }
@media (max-width: 800px) { .checklist__grid { grid-template-columns: 1fr; } }

/* feature / two-column */
.feature__grid { display: grid; grid-template-columns: 1.1fr 1fr; gap: var(--gap); align-items: center; }
.feature--flip .feature__media { order: -1; }
.feature__media img { border-radius: var(--radius-lg); box-shadow: var(--shadow); width: 100%; aspect-ratio: 4/3; object-fit: cover; object-position: center 30%; }
.feature__media--placeholder { aspect-ratio: 4/3; border-radius: var(--radius-lg); background: linear-gradient(135deg, var(--green), var(--green-dark)); display: grid; place-items: center; color: var(--gold); font-family: var(--font-head); text-transform: uppercase; letter-spacing: .1em; }
@media (max-width: 820px) { .feature__grid { grid-template-columns: 1fr; } .feature--flip .feature__media { order: 0; } }

/* pathway stages */
.stages__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.25rem; counter-reset: stage; }
.stage { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem; position: relative; }
.is-green .stage { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.14); }
.stage__num { counter-increment: stage; font-family: var(--font-head); font-size: 2.5rem; color: var(--gold); line-height: 1; }
/* Leading zero: 01 reads as a step in a sequence, 1 reads as a stray digit. */
.stage__num::before { content: counter(stage, decimal-leading-zero); }
.stage__title { margin: .35rem 0 .4rem; font-size: 1.7rem; line-height: 1.1; }
.is-green .stage__title { color: var(--white); }
.stage__text { color: var(--slate); margin: 0; font-size: .98rem; }
.is-green .stage__text { color: #CFE3D5; }

/* ---- card density on phones ----
   The desktop padding and heading sizes were set for a four-across grid with
   room to breathe. At one card filling the screen they read as loose, and every
   spare centimetre is one more centimetre of scrolling — now also the height of
   a whole swipe row, since a flex row is as tall as its tallest card. Titles
   step down but body copy does not: the text still has to be read. */
@media (max-width: 560px) {
	.card { padding: 1.35rem; gap: .5rem; }
	.card__title { font-size: 1.45rem; }
	.stage { padding: 1.25rem; }
	.stage__num { font-size: 2.1rem; }
	.stage__title { font-size: 1.45rem; }
	.news-card__body { padding: 1rem 1.1rem 1.25rem; }
	.news-card__title { font-size: 1.2rem; }
}
@media (max-width: 820px) { .stages__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 460px) { .stages__grid { grid-template-columns: 1fr; } }

/* document groups */
.docs__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1.5rem; }
.doc-group { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem 1.75rem; }
/* Matched to .card__title — same heading level doing the same job (naming a
   card), so it takes the same size rather than a quieter one of its own. */
.doc-group__title { font-size: 1.7rem; line-height: 1.1; margin: 0 0 1.15rem; }
.doc-group__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
/* Was --green-dark, which is near-black and did not read as a link at all. */
.doc-group__list a { display: flex; gap: .5rem; align-items: baseline; }
/* File-type badge — says a link leaves the page for a download before it's clicked. */
/* The chip carries both facts: what the file is, and that it opens elsewhere.
   That is why these links have no trailing arrow — an arrow reads as "go to a
   page", and the icon says "opens in a new tab" more precisely. */
.doc-group__type { display: inline-flex; align-items: center; gap: .3em; font-size: .6875rem; font-weight: 600; letter-spacing: .06em; color: var(--slate); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: .1em .45em; white-space: nowrap; }
.doc-group__ext { width: 1em; height: 1em; flex: none; }
/* A document the club lists but hasn't supplied — stated plainly, never linked to '#'. */
.doc-group__missing { display: flex; gap: .5rem; align-items: baseline; color: var(--slate); }
.doc-group__missing::before { content: "–"; color: var(--line); }
.doc-group__note { font-size: .8125rem; font-style: italic; }

/* record tables — honour board, life members, committee */
/* Narrow two-column tables tile; a three-column table takes the full row. */
.records__stack { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); gap: 1.5rem; align-items: start; }
.record--wide { grid-column: 1 / -1; }
.record__title { font-size: 1.25rem; margin: 0 0 .75rem; }
.record__table { width: 100%; border-collapse: collapse; background: var(--white); font-size: .9375rem; }
.record__table th, .record__table td { text-align: start; padding: .55rem .9rem; border-bottom: 1px solid var(--line); }
/* Filled green header: white on --green measures 8.1:1, clearing AA and AAA for
   this 14px uppercase label. The bottom rule takes the fill colour so the header
   reads as one solid band rather than a band with a hairline under it. */
.record__table thead th { background: var(--green); color: var(--white); font-family: var(--font-head); font-size: 1.0625rem; letter-spacing: .04em; text-transform: uppercase; border-bottom-color: var(--green); }
/* Years and periods read as data, so they align on the digit rather than drifting. */
.record__table td:first-child { font-variant-numeric: tabular-nums; color: var(--slate); white-space: nowrap; }
.record--wide .record__table td:first-child { white-space: normal; color: var(--ink); font-variant-numeric: normal; }
.record__table tbody tr:last-child th, .record__table tbody tr:last-child td { border-bottom: 0; }
/* A 5% tint is enough to guide the eye across a long row without dimming the
   email links: --link holds 5.09:1 on it, against 5.45:1 on plain white. */
.record__table tbody tr:nth-child(even) { background: rgba(11, 92, 52, .05); }
.record__table a { color: var(--link); }
.record__table a:hover { color: var(--accent-ink); }
/* The scroll region only earns a border when it can actually scroll. */
.record .fx-tablewrap { border-radius: var(--radius-sm); }
@media (max-width: 560px) { .record__table { font-size: .875rem; } .record__table th, .record__table td { padding: .5rem .65rem; } }
/* The address is the column that will not fit: a three-column record table needs
   719px of content on a 350px phone, and swapping the address for an envelope
   brings that to 336px — so the table fits whole, with its semantics and header
   associations intact, rather than being restructured into blocks or left to
   scroll sideways. */
.record__mail { display: inline-flex; align-items: center; gap: .4rem; }
.record__mail-icon { display: none; width: 22px; height: 22px; flex: none; }
@media (max-width: 720px) {
	.record--wide .record__mail-icon { display: block; }
	/* Hidden from view, not from the accessible name — the link still announces
	   the address it sends to. */
	.record--wide .record__mail-text {
		position: absolute; width: 1px; height: 1px; overflow: hidden;
		clip: rect(1px,1px,1px,1px); white-space: nowrap;
	}
	/* A tap target rather than a line of text. */
	.record--wide .record__mail { min-width: 44px; min-height: 44px; justify-content: center; }
	.record--wide .record__table td:last-child { padding-inline: .35rem; }
}

/* Chips — a long list of short pairs (life members) laid out as a wrapped run
   rather than a tall two-column table with empty space beside it. */
.record--chips { grid-column: 1 / -1; }
.chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .6rem; }
.chip { display: inline-flex; align-items: baseline; gap: .5rem; background: var(--white); border: 1px solid var(--line); border-radius: 999px; padding: .45rem 1rem; }
/* The year leads and stays on the digit, so a scan down the run still lines up. */
.chip__lead { font-family: var(--font-head); font-size: 1rem; font-weight: 600; letter-spacing: .02em; font-variant-numeric: tabular-nums; color: var(--accent-ink); }
.chip__label { font-size: .9375rem; color: var(--ink); }
/* Chips centre: a wrapped run of pills has no left edge to align to, so a
   left-aligned heading above a centred-looking run reads as a mistake. Applies
   to the display mode, not to one page, so any chip list behaves the same. */
.records--chips .section__head { max-width: 46rem; margin-inline: auto; text-align: center; }
.records--chips .record__title { text-align: center; }
.records--chips .chips { justify-content: center; }

/* timeline — the club history chronology */
/* Logical properties throughout so the rail sits on the leading edge in any direction. */
.timeline__list { list-style: none; margin: 0; padding: 0; position: relative; max-width: 60ch; }
.timeline__item { position: relative; padding-inline-start: 2.25rem; padding-block-end: 2rem; }
/* The rail is one continuous line drawn per item, stopping at the last marker. */
.timeline__item::before { content: ""; position: absolute; inset-block: .55rem 0; inset-inline-start: .3125rem; width: 2px; background: var(--line); }
.timeline__item:last-child { padding-block-end: 0; }
.timeline__item:last-child::before { display: none; }
/* Marker: decorative — the date beside it carries the meaning. */
.timeline__item::after { content: ""; position: absolute; inset-block-start: .3rem; inset-inline-start: 0; width: 12px; height: 12px; border-radius: 50%; background: var(--gold); border: 2px solid var(--white); box-shadow: 0 0 0 2px var(--line); }
.timeline__date { font-family: var(--font-head); font-size: .9375rem; letter-spacing: .04em; text-transform: uppercase; color: var(--accent-ink); margin: 0 0 .15rem; font-variant-numeric: tabular-nums; }
.timeline__title { font-size: 1.25rem; margin: 0 0 .35rem; }
.timeline__text { margin: 0; color: var(--slate); }
/* On green, the date takes --gold (the band colour this theme already verifies)
   and the body text inherits .section.is-green's #EAF3EC rather than inventing a
   second muted-on-dark. */
.timeline.is-green .timeline__date { color: var(--gold); }
.timeline.is-green .timeline__text { color: inherit; }
.timeline.is-green .timeline__item::before { background: rgba(255,255,255,.22); }
.timeline.is-green .timeline__item::after { border-color: var(--green-deep); box-shadow: 0 0 0 2px rgba(255,255,255,.22); }

/* sponsor strip section — primary logos on white tiles.
   Four across, and whatever is left centred beneath. A plain grid strands the
   remainder against the left edge, which reads as a mistake when the row is
   short — the club has seven primary sponsors, so that is every page. Flex with
   a 25% basis fills the row and centres the tail. */
.sponsor-tiles { list-style: none; margin: 0 auto 2rem; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; align-items: stretch; gap: 1.5rem; max-width: 1080px; }
.sponsor-tile { flex: 0 0 calc(25% - 1.125rem); background: #fff; border-radius: var(--radius); padding: 2rem; display: grid; place-items: center; min-height: 150px; box-shadow: var(--shadow-sm); }
.sponsor-tile__link { display: grid; place-items: center; width: 100%; height: 100%; }
.sponsor-tile__logo { max-height: 94px; max-width: 100%; width: auto; object-fit: contain; }
/* Step down rather than squeeze. The basis subtracts the gaps the row carries —
   (n-1)/n of the 1.5rem gap. */
@media (max-width: 900px) { .sponsor-tile { flex-basis: calc(33.333% - 1rem); } }
@media (max-width: 720px) { .sponsor-tile { flex-basis: calc(50% - .75rem); } }
@media (max-width: 440px) { .sponsor-tile { flex-basis: 100%; } }
.sponsor-tile__name { font-family: var(--font-head); text-transform: uppercase; font-weight: 600; color: var(--green-dark); text-align: center; }
.sponsor-strip__cta { justify-content: center; }

.sponsor-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.5rem; }
/* Three across for the major sponsors — room for the logo and a sentence. */
@media (min-width: 1000px) { .sponsor-cards--3 { grid-template-columns: repeat(3, 1fr); } }
/* Four across for everyone else. A narrower card, so it steps down with it:
   less inset, a shorter logo well and a smaller name. Four cards at the wider
   card's proportions would crowd the logos and wrap every name. */
@media (min-width: 1000px) { .sponsor-cards--4 { grid-template-columns: repeat(4, 1fr); gap: 1.25rem; } }
.sponsor-cards--4 .sponsor-card { --card-pad: 1.35rem; padding-block: 1.35rem 1.75rem; gap: .75rem; }
.sponsor-cards--4 .sponsor-card__logo-wrap { min-height: 84px; padding-bottom: 1.15rem; }
.sponsor-cards--4 .sponsor-card__logo { max-height: 60px; }
.sponsor-cards--4 .sponsor-card__name { font-size: 1.2rem; }
.sponsor-cards--4 .sponsor-card__blurb { font-size: .9rem; }
/* --card-pad drives both the card's inset and the logo well's full-bleed
   negative margin. One value, so the divider cannot drift out of line with the
   card edge when the padding is next adjusted. */
.sponsor-card { --card-pad: 1.9rem; background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.75rem var(--card-pad) 2.25rem; text-align: center; display: flex; flex-direction: column; gap: 1rem; align-items: center; }
/* A hairline under the logo separates the mark from the club's own words about
   the sponsor. Full-bleed to the card's padding so it reads as a rule, not a
   short underline. */
.sponsor-card__logo-wrap { width: calc(100% + var(--card-pad) * 2); margin-inline: calc(var(--card-pad) * -1); margin-bottom: .4rem; min-height: 104px; display: grid; place-items: center; padding: .5rem var(--card-pad) 1.5rem; border-bottom: 1px solid var(--line); }
.sponsor-card__logo { max-height: 78px; max-width: 100%; width: auto; object-fit: contain; }
.sponsor-card__name { font-family: var(--font-head); text-transform: uppercase; font-size: 1.5rem; line-height: 1.1; margin: 0; color: var(--green-dark); }
/* Partnership tier as a chip: it is a category, not a sentence, and the pill
   stops it reading as a subheading under the sponsor's name. */
.sponsor-card__type { display: inline-block; font-size: .75rem; text-transform: uppercase; letter-spacing: .06em; color: var(--accent-ink); font-weight: 600; background: var(--cream); border: 1px solid var(--line); border-radius: 999px; padding: .25rem .8rem; }
/* Top tier in gold. Ink on --gold is 11:1, so the chip stays legible at 12px. */
.sponsor-card__type--major { background: var(--gold); border-color: var(--gold); color: var(--ink); }
.sponsor-card__blurb { color: var(--slate); font-size: .95rem; margin: 0; }
/* Body-font text link, matching .fx-map on the fixtures page — see .text-link. */
.sponsor-card__link { margin-top: auto; }

/* Sponsor cards on small screens ----------------------------------------
   Twenty-nine sponsors at one card per row is nine screens of scrolling on the
   Current sponsors page — comfortably the longest thing on the site. Two across
   halves it, and a logo survives that halving in a way a paragraph would not,
   so the whole card steps down with it: less inset, a shorter logo well, and a
   smaller name and blurb.

   660px is where the auto-fill grid would otherwise drop to one column — two
   300px tracks plus the gap need a 624px wrap.

   These replace an earlier set of rules that *grew* the logo on a phone. That
   was right when the card was full width and the mark looked lost in it; at two
   across the opposite is true. */
@media (max-width: 660px) {
	/* minmax(0, 1fr), not 1fr. A grid track defaults to min-width: auto, so it
	   refuses to shrink below its content — and a long one-word sponsor name
	   ("DREAMSTREET") set that floor above the track, which pushed the page
	   sideways rather than wrapping. Measured at 320px: a 161px card in a
	   134px track, and 23px of horizontal page scroll. */
	.sponsor-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .75rem; }
	.sponsor-card { min-width: 0; }
	/* Break a name only when it genuinely will not fit: break-word leaves the
	   intrinsic sizing alone, so it does not encourage breaks elsewhere. */
	.sponsor-card__name { overflow-wrap: break-word; }
	.sponsor-card { --card-pad: .9rem; padding: 1rem var(--card-pad) 1.25rem; gap: .5rem; }
	.sponsor-card__logo-wrap { min-height: 72px; margin-bottom: .15rem; padding: .4rem var(--card-pad) .85rem; }
	.sponsor-card__logo { max-height: 54px; }
	.sponsor-card__name { font-size: 1.05rem; }
	.sponsor-card__type { font-size: .68rem; padding: .15rem .55rem; }
	/* The blurb goes on a phone. Two cards across wrapped it to about seventeen
	   characters a line — a worse read than no read — and it was most of the
	   card's height. The logo, the name, the tier and the link are what the card
	   is for; the sentence is on the sponsor's own site, one tap away. Removed
	   rather than visually hidden, so a screen reader hears the same card a
	   sighted visitor sees. */
	.sponsor-card__blurb { display: none; }
	.sponsor-card__link { font-size: .9rem; }
	/* "Website →" rather than "Visit website →": at half width the verb is the
	   first thing to go. Hidden visually, not removed, so the accessible name is
	   still "Visit website — <sponsor>, opens in a new tab". capitalize picks up
	   the sentence case that dropping the first word would otherwise lose. */
	.sponsor-card__link-verb {
		position: absolute;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}
	.sponsor-card__link-noun { text-transform: capitalize; }
}
@media (max-width: 440px) {
	.sponsor-tile { padding: 1.5rem; min-height: 164px; }
	.sponsor-tile__logo { max-height: 116px; }
	/* Footer strip stays two-up; grow the tile with the logo so it doesn't clip. */
	.sponsor-footer__item { min-width: 148px; min-height: 78px; padding: .8rem 1rem; }
	.sponsor-footer__logo { max-height: 56px; max-width: 100%; }
}

/* coaches */
.coach-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.75rem; }
.coach-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; display: flex; flex-direction: column; transition: transform .15s, box-shadow .15s; }
.coach-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.coach-card__photo { position: relative; aspect-ratio: 1 / 1; overflow: hidden; background: var(--cream); }
.coach-card__photo img { width: 100%; height: 100%; object-fit: cover; object-position: center 20%; }
.coach-card__initials { position: absolute; inset: 0; display: grid; place-items: center; font-family: var(--font-head); font-size: 3rem; color: var(--green); }
.coach-card__team { position: absolute; left: 0; bottom: 0; background: var(--gold); color: var(--ink); font-family: var(--font-head); text-transform: uppercase; letter-spacing: .06em; font-size: .8rem; font-weight: 600; padding: .35rem .8rem; }
.coach-card__body { padding: 1.25rem 1.35rem 1.5rem; display: flex; flex-direction: column; gap: .3rem; }
.coach-card__name { margin: 0; font-size: 1.35rem; }
.coach-card__role { font-family: var(--font-head); text-transform: uppercase; letter-spacing: .04em; color: var(--accent-ink); font-weight: 600; font-size: .95rem; margin: 0; }
.coach-card__bio { color: var(--slate); margin: .4rem 0 0; font-size: .95rem; }
.coach-card__quals { margin: .5rem 0 0; font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; color: var(--green); font-weight: 600; }
.is-green .coach-card { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.14); }
.is-green .coach-card__name { color: var(--white); }
.is-green .coach-card__bio { color: #CFE3D5; }

/* cta band */
.cta-band { background: linear-gradient(120deg, var(--green-dark), var(--green)); color: var(--white); text-align: center; }
.cta-band__inner { max-width: 44rem; margin-inline: auto; }
.cta-band h2 { color: var(--white); }
.cta-band__body { color: #DCEBE0; font-size: 1.15rem; }
.cta-band .cta-row { justify-content: center; }

/* form embed */
.form-embed__grid { display: grid; grid-template-columns: 1fr 1.3fr; gap: var(--gap); align-items: start; }
.form-embed__panel { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: clamp(1.5rem, 3vw, 2.5rem); box-shadow: var(--shadow-sm); }

/* Bring Fluent Forms into the design system. Its defaults ship 13px controls,
   which makes iOS Safari zoom the page the moment a field is focused — on the
   club's main enquiry path. 16px is the floor that prevents it. */
.form-embed__panel input[type="text"],
.form-embed__panel input[type="email"],
.form-embed__panel input[type="tel"],
.form-embed__panel input[type="number"],
.form-embed__panel select,
.form-embed__panel textarea {
	font-family: var(--font-body); font-size: 1rem; line-height: 1.5; color: var(--ink);
	min-height: 44px; padding: .6rem .8rem;
	border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--white);
}
.form-embed__panel textarea { min-height: 120px; }
.form-embed__panel label { font-weight: 600; color: var(--green-dark); }
.form-embed__panel input::placeholder,
.form-embed__panel textarea::placeholder { color: var(--slate); }
.form-embed__panel button[type="submit"],
.form-embed__panel input[type="submit"] {
	font-family: var(--font-head); text-transform: uppercase; letter-spacing: .04em;
	font-weight: 600; font-size: 1.05rem; min-height: 48px; padding: .85rem 1.6rem;
	border: 0; border-radius: 999px; background: var(--green); color: var(--white);
	transition: background-color .15s ease-out, transform .15s ease-out;
}
.form-embed__panel button[type="submit"]:hover,
.form-embed__panel input[type="submit"]:hover { background: var(--green-dark); }
/*
 * Fluent Forms injects its own rule in the footer:
 *   form.fluent_form_3 .ff-btn-submit:not(.ff_btn_no_style) { background-color: … }
 * That has the same specificity as the hover rule above but comes later in the
 * document, so it won every time and the button never changed on hover. Adding
 * the form element to the selector outweighs it. Scoped to our panel so it
 * cannot leak into any other form the club embeds later.
 */
.form-embed__panel form .ff-btn-submit:not(.ff_btn_no_style):hover,
.form-embed__panel form .ff-btn-submit:not(.ff_btn_no_style):focus-visible {
	background-color: var(--green-dark);
	transform: translateY(-2px);
}
.form-embed__panel form .ff-btn-submit:not(.ff_btn_no_style) {
	transition: background-color .15s ease, transform .15s ease;
}
.form-embed__panel button[type="submit"]:active,
.form-embed__panel input[type="submit"]:active { transform: scale(.96); }
@media (max-width: 820px) { .form-embed__grid { grid-template-columns: 1fr; } }

/* The line above the general enquiry form when registrations are shut. */
.form-embed__closed {
	margin: 0 0 1.25rem; padding: .9rem 1.1rem;
	background: var(--cream); border-inline-start: 4px solid var(--gold-dark);
	border-radius: var(--radius-sm);
	color: var(--ink); font-size: 1.02rem; line-height: 1.5;
}

/* The privacy line sitting above the submit button. */
.dr-form-note p {
	margin: 0; color: var(--slate); font-size: .95rem; line-height: 1.5;
}
/*
 * The privacy link inherited --slate from the paragraph and carried no
 * underline, so it was the same colour as the sentence around it and invisible
 * as a link. WCAG 1.4.1 wants a link inside a block of text to be told apart by
 * more than colour, so it is underlined as well as recoloured: --link is
 * 5.43:1 on white here, and the underline means the distinction survives for
 * anyone who cannot see the colour difference at all.
 */
.dr-form-note a {
	color: var(--link);
	text-decoration: underline;
	text-underline-offset: 2px;
	text-decoration-thickness: 1px;
}
.dr-form-note a:hover,
.dr-form-note a:focus-visible { color: var(--accent-ink); text-decoration-thickness: 2px; }

/*
 * Fields that only apply to some players. They are visible in the markup and
 * hidden from here by main.js — never the other way round, so that a browser
 * running no script shows everything rather than hiding the guardian fields
 * and then failing validation for them.
 */
.dr-cond--off { display: none; }

/* ---- registration banner ---------------------------------------------- */
/*
 * A gold strip directly under the hero on the home page and the four programme
 * pages, driven by the "Registrations open" switch in Rovers Settings.
 *
 * Measured contrast on --gold #F5C518: --ink text 10.95:1, the green button's
 * edge 4.97:1, white on that green 8.09:1. Gold is a fill here, never text —
 * as small text on white it is ~2.2:1 and fails.
 */
.reg-banner { background: var(--gold); color: var(--ink); }
.reg-banner__inner {
	display: flex; flex-wrap: wrap; align-items: center;
	justify-content: space-between; gap: 1rem 2rem;
	padding-block: clamp(1.1rem, 2.5vw, 1.6rem);
}
/* min-width: 0 so a long heading wraps instead of setting a floor that pushes
   the button off the strip — the failure the sponsor grid hit at 320px. */
.reg-banner__copy { min-width: 0; flex: 1 1 22rem; }
.reg-banner__title {
	font-family: var(--font-head); text-transform: uppercase; letter-spacing: .02em;
	font-size: clamp(1.4rem, 3vw, 1.9rem); line-height: 1.05;
	margin: 0; color: var(--ink);
}
.reg-banner__text {
	margin: .35rem 0 0; font-size: 1.02rem; line-height: 1.45;
	max-width: 46ch; color: var(--ink);
}
.reg-banner__cta { flex: 0 0 auto; }

/*
 * Under ~560px the button goes full width rather than sitting stranded on its
 * own line at label width — a thumb target that spans the column reads as the
 * thing to do next.
 */
@media (max-width: 560px) {
	.reg-banner__inner { align-items: stretch; }
	.reg-banner__cta { width: 100%; justify-content: center; text-align: center; }
}

/* fixtures embed */
.fixtures-embed__frame { border-radius: var(--radius-lg); overflow: hidden; border: 1px solid var(--line); background: var(--cream); min-height: 320px; display: grid; place-items: center; }
.fixtures-embed__placeholder { text-align: center; padding: 3rem; color: var(--slate); }
.fixtures-embed__placeholder strong { display: block; font-family: var(--font-head); text-transform: uppercase; color: var(--green-dark); font-size: 1.4rem; margin-bottom: .5rem; }

/* next match / last result block */
/* =========================================================================
   Live fixtures, results and ladders (Dribl)
   Data arrives in the browser — see inc/dribl.php. Layout responds with
   container queries so the same card works in the page, in a narrow column,
   or in the homepage strip.
   ========================================================================= */

/* ---- shared pieces ---- */
.fx-dot { color: var(--slate); }
.fx-pin { display: inline-block; vertical-align: -2px; margin-inline-end: .3rem; flex: 0 0 auto; }
/* Weight and the arrow are this link's non-colour cues: it sits in a run of
   --slate meta text, and no lightness clears 3:1 against a mid-grey while
   still holding 4.5:1 on white — the two are mutually exclusive. */
/* The site-wide inline text link: body font, link green, underline on hover and
   on keyboard focus. .fx-map is the original; the others opt into the same rule
   so a text link looks identical wherever it appears. */
.fx-map, .text-link, .sponsor-card__link, .card__link, .news-card__more, .doc-group__list a {
	font-family: var(--font-body); font-size: 1rem; font-weight: 600;
	text-transform: none; letter-spacing: 0; color: var(--link); text-decoration: none;
	text-underline-offset: 2px; text-decoration-thickness: from-font;
}
/* The arrow comes from here rather than from each template, so every text link
   gets exactly one and none get two. The non-breaking space keeps it attached to
   the last word instead of wrapping onto a line by itself. */
.fx-map::after, .text-link::after, .sponsor-card__link::after,
.card__link::after, .news-card__more::after {
	content: "\00a0\2192";
}
/* It nudges forward on hover — small, and off entirely under reduced motion. */
@media (prefers-reduced-motion: no-preference) {
	.fx-map::after, .text-link::after, .sponsor-card__link::after,
	.card__link::after, .news-card__more::after {
		display: inline-block; transition: transform .15s ease;
	}
	.fx-map:hover::after, .text-link:hover::after, .sponsor-card__link:hover::after,
	.card__link:hover::after, .news-card__more:hover::after {
		transform: translateX(3px);
	}
}
.fx-map { white-space: nowrap; }
.fx-map:hover, .fx-map:focus-visible,
.text-link:hover, .text-link:focus-visible,
.sponsor-card__link:hover, .sponsor-card__link:focus-visible,
.card__link:hover, .card__link:focus-visible,
.news-card__more:hover, .news-card__more:focus-visible,
.doc-group__list a:hover, .doc-group__list a:focus-visible { text-decoration: underline; }
.fx-nowrap { white-space: nowrap; }
/* Unframed, like the homepage strip: club badges are shield-shaped, so a round
   frame clipped their corners off. The initials fallback keeps a soft tile. */
.fx-crest { width: 44px; height: 44px; flex: 0 0 auto; border-radius: var(--radius-sm);
	background: #EAF1EC; display: grid; place-items: center;
	font-family: var(--font-head); font-weight: 700; font-size: .78rem; color: var(--green-dark); }
.fx-crest--img { object-fit: contain; background: none; border-radius: 0; }
.fx-ha { display: inline-flex; align-items: center; font-family: var(--font-head);
	text-transform: uppercase; letter-spacing: .08em; font-weight: 600; font-size: .72rem;
	/* Pill: the extra inline padding keeps the caps optically centred in the curve. */
	padding: .2rem .6rem; border-radius: 999px; white-space: nowrap; }
.fx-ha--h { background: var(--green); color: var(--white); }
.fx-ha--a { background: var(--cream); color: var(--green-dark); border: 1px solid var(--line); }
.fx-vs { font-family: var(--font-head); font-weight: 700; font-size: 1.3rem;
	font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--green-dark); }
/* Before kick-off there's no score, so the separator recedes rather than
   competing with the crests and the date. */
.fx-vs--pre { color: var(--slate); font-weight: 600; letter-spacing: .04em; }
.fx-score--w { color: #1B7F4B; }
.fx-score--l { color: #A3341F; }
.fx-score--d { color: #6B6257; }
/* No W/L badge: the score always reads ours-first, so the numbers carry the
   result and colour is only reinforcing it. */
.fx-noscore { color: var(--slate); font-size: .8rem; font-style: italic; }
/* Called-off games get a border as well as colour, so the state does not rely
   on hue alone. */
.fx-called { font-family: var(--font-head); text-transform: uppercase; letter-spacing: .06em;
	font-weight: 700; font-size: .8rem; white-space: nowrap; color: #A3341F;
	border: 1px solid currentColor; border-radius: 4px; padding: .18rem .45rem; }
.fx-bye { display: inline-flex; align-items: center; font-family: var(--font-head);
	text-transform: uppercase; letter-spacing: .08em; font-weight: 700; font-size: .78rem;
	color: var(--slate); border: 1px dashed currentColor; border-radius: 999px;
	padding: .2rem .7rem; white-space: nowrap; }
.fx-empty, .fx-note { color: var(--slate); }

/* Loading skeleton for the live fixtures list. Same card footprint as a real
   row, so nothing shifts when the data lands. */
/* Reset the grid. A @container rule further down switches .fx-card to
   "when main round" once the list is wide enough, which drops the first bar into
   the empty time column and collapses it to zero width. Both classes are chained
   so this outweighs that rule wherever it sits in the file. */
.fx-card.fx-skeleton__card { display: grid; grid-template-columns: 1fr; grid-template-areas: none; gap: .55rem; justify-items: center; }
.fx-skeleton__bar { display: block; height: 1rem; border-radius: 999px; background: var(--line); }
.fx-skeleton__bar--team { width: min(62%, 22rem); }
.fx-skeleton__bar--meta { width: min(40%, 14rem); height: .75rem; }
/* Each row fades a beat after the one above, so the block reads as loading
   rather than as four identical dead bars. Motion is opt-in: under reduced
   motion the bars simply sit there at a steady tone. */
@media (prefers-reduced-motion: no-preference) {
	.fx-skeleton__bar { animation: fx-pulse 1.4s ease-in-out infinite; }
	.fx-skeleton__card:nth-child(2) .fx-skeleton__bar { animation-delay: .12s; }
	.fx-skeleton__card:nth-child(3) .fx-skeleton__bar { animation-delay: .24s; }
	.fx-skeleton__card:nth-child(4) .fx-skeleton__bar { animation-delay: .36s; }
	@keyframes fx-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
}
/* The fallback and empty states render straight onto the section, so on a green
   band they need the light-on-dark pair — --slate measures 1.06:1 there. */
.section.is-green .fx-empty,
.section.is-green .fx-empty a,
.section.is-green .strip__none { color: #EAF3EC; }
.section.is-green .fx-note { color: var(--ink); }
.fx-note { background: var(--cream); border: 1px solid var(--line);
	border-inline-start: 4px solid var(--gold); border-radius: var(--radius-sm);
	padding: .7rem .9rem; margin: 0 0 1rem; font-size: .92rem; }

/* ---- filters ---- */
/* Hugs its controls rather than spanning the column: fit-content collapses to
   max-content while the viewport allows it, then the max-width lets the row
   wrap on narrow screens. Block padding is 1rem so the outer curve stays
   concentric with the 22px-radius pills inside it. */
.fx-filters { display: flex; flex-wrap: wrap; gap: .75rem 1rem;
	align-items: center; justify-content: center;
	width: fit-content; max-width: 100%; margin-inline: auto;
	padding: 1rem 1.5rem; background: var(--cream); border: 1px solid var(--line);
	/* Half the one-row height (44px control + 2x1rem padding + 2x1px border),
	   so this is a true pill on one row. 999px would follow the box when the
	   row wraps and turn a 300px-tall stack into a lozenge. */
	border-radius: 39px; margin-bottom: 1.25rem; }
.section.is-cream .fx-filters { background: var(--white); }
.fx-select { font: inherit; font-size: 1rem; min-height: 44px; padding: .4rem 2rem .4rem .7rem;
	border: 1px solid var(--line); border-radius: 999px; background: var(--white); color: var(--ink);
	appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234A5750' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
	background-repeat: no-repeat; background-position: right .6rem center; background-size: 16px; }
/* A track with the active option as a pill floating inside it, rather than a
   split capsule — the inactive options read as quiet labels, not buttons. */
.fx-seg { display: inline-flex; gap: .2rem; padding: .25rem;
	border-radius: 999px; background: var(--cream); border: 1px solid var(--line); }
.fx-seg button { border: 0; background: transparent; color: var(--slate);
	border-radius: 999px; font-family: var(--font-head); text-transform: uppercase;
	letter-spacing: .06em; font-weight: 600; font-size: .85rem;
	/* 34px + the 4px track padding either side + 1px border = the 44px the
	   dropdowns stand at, so the row sits on one baseline of control heights. */
	padding: .3rem 1.1rem; min-height: 34px;
	transition: background-color .15s ease-out, color .15s ease-out, transform .15s ease-out; }
.fx-seg button:hover { color: var(--green-dark); }
.fx-seg button[aria-pressed="true"] { background: var(--green); color: var(--white); }
.fx-seg button:active { transform: scale(.96); }
/* The panel is white on the fixtures page, so the track needs its own tint. */
.section.is-cream .fx-seg { background: #EFEDE3; }
@media (prefers-reduced-motion: reduce) {
	.fx-seg button { transition: background-color .15s ease-out, color .15s ease-out; }
	.fx-seg button:active { transform: none; }
}

/* ---- date row (outside the filter panel) ---- */
.fx-date { margin-bottom: 1.25rem; }
/* Just the stepper now — the weekly/season choice sits in the filter bar. */
.fx-datebar { display: flex; align-items: center; justify-content: center;
	gap: .75rem; flex-wrap: wrap; }
.fx-weeknav { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.fx-weekbtn {
	display: grid; place-items: center; width: 44px; height: 44px; flex: 0 0 auto;
	border: 1px solid var(--line); border-radius: 999px; background: var(--white);
	color: var(--green-dark); font-size: 1.1rem; line-height: 1;
	transition: background-color .15s ease-out, transform .15s ease-out, opacity .15s ease-out;
}
.fx-weekbtn:hover:not(:disabled) { background: var(--cream); }
.fx-weekbtn:active:not(:disabled) { transform: scale(.96); }
.fx-weekbtn:disabled { opacity: .4; cursor: default; }
.fx-weeknav__label {
	margin: 0; min-width: 11ch; text-align: center;
	font-family: var(--font-head); text-transform: uppercase; letter-spacing: .05em;
	font-weight: 700; font-size: 1rem; color: var(--green-dark);
	font-variant-numeric: tabular-nums;
}
.fx-weeknav--season .fx-weeknav__label { min-width: 0; }
.fx-weeklink {
	border: 0; background: none; padding: .4rem .2rem; min-height: 44px;
	color: var(--link); font-weight: 600; font-size: .9rem;
	text-underline-offset: 2px; text-decoration-thickness: from-font;
}
.fx-weeklink:hover, .fx-weeklink:focus-visible { color: var(--green-dark); text-decoration: underline; }

/* ---- fixture card ---- */
.fx-day { font-family: var(--font-head); text-transform: uppercase; letter-spacing: .08em;
	font-weight: 700; color: var(--green-dark); font-size: 1.15rem;
	margin: 1.75rem 0 .6rem; }
.fx-day:first-child { margin-block-start: 0; }
/* The container is the list, not the card: an element cannot respond to its own
   container query, and the card's own grid has to switch with its children. */
.fx-list { container-type: inline-size; }
/* The inline inset runs wider than the block one: a line box carries its own
   leading, so equal padding reads tight at the ends of the row. */
.fx-card { display: grid; gap: .55rem; padding: 1rem 1.6rem;
	grid-template-columns: 1fr; grid-template-areas: "main";
	border: 1px solid var(--line); border-radius: var(--radius-sm);
	background: var(--white); margin-bottom: .5rem; }
.section.is-cream .fx-card { background: var(--white); }
/* Narrow: no round, no left column — the time sits inline before the chip. */
.fx-when, .fx-round { display: none; }
.fx-metatime { font-weight: 700; color: var(--green-dark);
	font-variant-numeric: tabular-nums; white-space: nowrap; }
.fx-main { grid-area: main; display: grid; gap: .5rem; justify-items: center; text-align: center; }
.fx-row { display: grid; grid-template-columns: 1fr auto 1fr; gap: .6rem; align-items: center; width: 100%; }
.fx-side { display: grid; gap: .35rem; justify-items: center; min-width: 0; }
.fx-name { font-size: .9rem; line-height: 1.35; overflow-wrap: break-word; }
.fx-meta { color: var(--slate); font-size: .85rem; line-height: 1.45; margin: 0; }
.fx-when { grid-area: when; align-items: baseline; gap: .45rem; flex-wrap: wrap;
	font-family: var(--font-head); text-transform: uppercase; letter-spacing: .04em;
	font-weight: 700; color: var(--green-dark); font-size: 1.05rem; }
.fx-time { font-variant-numeric: tabular-nums; white-space: nowrap; }
.fx-round { grid-area: round; justify-self: end; white-space: nowrap; margin: 0;
	font-family: var(--font-head); text-transform: uppercase; letter-spacing: .08em;
	font-weight: 600; font-size: .8rem; color: var(--slate); }

/* ---- bye ----
   Declared after .fx-card and .fx-row, and scoped through .fx-card--bye, so
   these win on both order and specificity. A bye has no kick-off and no
   opponent: the chip takes the time slot and the team name centres in the
   space a match would fill. The left column stays visible at every width,
   because there's no meta line to carry the chip instead. */
.fx-card--bye { grid-template-columns: auto 1fr; grid-template-areas: "when main";
	align-items: center; column-gap: 1rem; }
.fx-card--bye .fx-when { display: flex; }
.fx-card--bye .fx-main { width: 100%; }
.fx-card--bye .fx-row { display: flex; align-items: center; justify-content: center; width: 100%; }

/* Wide enough for one line: time left, match centre, round right. */
@container (min-width: 520px) {
	.fx-card { grid-template-columns: auto 1fr auto; grid-template-areas: "when main round";
		align-items: center; column-gap: 1.5rem; }
	.fx-when { display: flex; flex-direction: column; gap: .1rem; align-items: flex-start; }
	.fx-round { display: block; align-self: center; }
	.fx-card--bye { grid-template-columns: auto 1fr auto; grid-template-areas: "when main round"; }
	.fx-metatime { display: none; }
	/* Capped and centred: the columns stay aligned down the list so the eye can
	   scan the VS/score, but the two sides pull in instead of stranding the
	   names against the card edges on a wide screen.
	   The cap is measured, not chosen: the longest team name in a full season
	   ("U08 MiniRoos - Joeys Mixed Thomas", 278px) plus its crest needs a 331px
	   side, so one line costs 717px. 780px carries that plus the widest score
	   cell. Cap it tighter and the MiniRoos names wrap to two lines. */
	.fx-row { gap: 1rem; max-width: 780px; margin-inline: auto; }
	.fx-side { display: flex; align-items: center; gap: .55rem; }
	.fx-side--us { flex-direction: row-reverse; justify-content: flex-start; text-align: end; }
	.fx-side--them { justify-content: flex-start; text-align: start; }
	.fx-name { font-size: .98rem; }
	.fx-vs { font-size: 1.45rem; }
}

/* ---- ladder ---- */
.fx-ladder__wrap { margin-top: 2rem; }
.fx-ladder__head { font-family: var(--font-head); text-transform: uppercase;
	letter-spacing: .06em; color: var(--green-dark); margin: 0 0 .6rem; font-size: 1.2rem; }
.fx-tablewrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius-sm); }
.fx-tablewrap:focus-visible { outline: 3px solid var(--green-dark); outline-offset: -3px; }
.fx-ladder { border-collapse: collapse; width: 100%; font-size: .92rem; background: var(--white); }
.fx-ladder th, .fx-ladder td { padding: .5rem .6rem; border-bottom: 1px solid var(--line); text-align: start; }
.fx-ladder thead th { font-family: var(--font-head); text-transform: uppercase; letter-spacing: .08em;
	font-size: .75rem; color: var(--slate); border-bottom-width: 2px; white-space: nowrap; }
.fx-ladder tbody th { font-weight: 600; }
.fx-ladder td { font-variant-numeric: tabular-nums; }
.fx-ladder .fx-pts { font-weight: 700; color: var(--green-dark); }
/* Eight columns of one-letter stats need 382px on a 350px phone. A ladder is the
   one table that should stay a table — stacking it destroys the comparison it
   exists for — so the width comes out of the padding instead. It keeps its
   focusable scroll region either way, for anything narrower still. */
@media (max-width: 720px) {
	.fx-ladder { font-size: .85rem; }
	.fx-ladder th, .fx-ladder td { padding: .5rem .3rem; }
}
.fx-ladder abbr { text-decoration: none; border-bottom: 1px dotted currentColor; }
.fx-ladder tr.is-us { background: #F2F8F3; }
.fx-ladder tr.is-us th { color: var(--green-dark); }
.fx-ladder tbody tr:last-child th, .fx-ladder tbody tr:last-child td { border-bottom: 0; }

/* ---- error / off-season ---- */
.fx-error, .fx-offseason { background: var(--cream); border: 1px solid var(--line);
	border-radius: var(--radius); padding: clamp(1.25rem, 3vw, 2rem); text-align: center; }
.fx-error h3 { margin-top: 0; }
.fx-offseason p:first-child { font-size: 1.1rem; color: var(--ink); }

/* ---- homepage strip ---- */
.matchstrip { display: grid; gap: 1rem; }
.matchstrip__head { margin-bottom: .25rem; }
/* The section reads as one centred block: heading, bars, then the button. */
.matchstrip-section .section__head { margin-inline: auto; text-align: center; }
.matchstrip-section .cta-row { justify-content: center; }
/* No width here — .strip is max-content so the bars hug their own content. */
.matchstrip-section .strip { margin-inline: auto; }

/* One bar per team: a vertical name tab, the next match, then the last result.
   The query container is the wrapper, not .strip — container-type stops an
   element sizing to its own content, and .strip needs to be max-content so the
   bars hug rather than stretching to fill the page. */
.matchstrip, .matchstrip-section .wrap { container-type: inline-size; }
.strip { display: grid; gap: 1rem; grid-template-columns: 1fr;
	width: max-content; max-width: 100%; margin-inline: auto; }
.strip__card {
	display: grid; grid-template-columns: 1fr;
	border-radius: var(--radius); overflow: hidden;
	background: var(--white); color: var(--ink);
	box-shadow: var(--shadow-sm);
}
/* Tab, then three columns: shields, fixture detail, last result. The result
   column sizes to its own content instead of taking a fixed share, so it hugs
   rather than leaving a wide empty gutter. */

/* The extra .section beats `.section.is-green h3 { color: white }`, which would
   otherwise put white on gold at ~1.9:1. Deep green on gold measures 10.4:1. */
.strip__team,
.section .strip__card .strip__team {
	margin: 0; display: grid; place-items: center; padding: .55rem 1rem;
	background: var(--gold); color: var(--green-deep);
	/* Same tracking as the masthead wordmark — dropping the wide letter-spacing
	   buys the room to set it larger. */
	font-family: var(--font-head); text-transform: uppercase; letter-spacing: .01em;
	font-weight: 700; font-size: 1.6rem; white-space: nowrap;
}
/* Panels hug their content: the result column is the shorter of the two, so it
   sets its own height rather than matching the fixture detail beside it. */
.strip__slot { display: grid; gap: .3rem; align-content: center; text-align: start;
	padding: 1rem 1.15rem; }
/* Stacked by default; the query below puts the detail beside the shields.
   Deliberately not flex-wrap: a wrapping flex container reports its widest
   item as its max-content, not the sum, so the auto grid track came out
   narrower than the row needed and only the longest bar wrapped. */
.strip__slot--next { background: var(--white);
	grid-template-columns: 1fr; justify-items: center; text-align: center; gap: .85rem; }
.strip__slot--next .strip__crests { justify-content: center; }
.strip__slot--last { background: var(--cream); border-top: 1px solid var(--line);
	justify-items: start; }

.strip__details { display: grid; gap: .15rem; }
.strip__label { font-family: var(--font-head); text-transform: uppercase; letter-spacing: .11em;
	font-weight: 600; font-size: .72rem; color: var(--accent-ink); margin: 0; }
/* The shields carry the match. No frame at all — the crests are already
   distinct shapes on a plain panel, and the circular clip was cropping them. */
.strip__crests { display: flex; align-items: center; gap: .85rem; }
.strip__crests .fx-crest { border: 0; background: transparent; border-radius: 0; }
.strip__crests .fx-crest--img { object-fit: contain; }
/* The initials fallback still needs a shape to sit in. */
.strip__crests .fx-crest:not(.fx-crest--img) {
	background: #EAF1EC; border-radius: 50%; font-size: 1.1rem; }
.strip__slot--next .strip__crests .fx-crest { width: 96px; height: 96px; }
.strip__slot--last .strip__crests .fx-crest { width: 42px; height: 42px; }
.strip__slot--last .strip__crests { gap: .6rem; }
.strip__slot--next .strip__crests .fx-vs { font-size: 2rem; }
.strip__slot--last .strip__crests .fx-vs { font-size: 1.5rem; }
.strip__crests .fx-noscore { font-size: .75rem; }
.strip__opp { margin: 0; font-weight: 600; font-size: 1.05rem; line-height: 1.3;
	overflow-wrap: break-word; }
/* Date leads, time sits under it at reading size. */
.strip__date { margin: .1rem 0 0; font-family: var(--font-head); text-transform: uppercase;
	font-weight: 700; font-size: 1.6rem; line-height: 1.05; letter-spacing: .02em;
	color: var(--green-dark); }
.strip__time { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums;
	color: var(--green-dark); }
/* line-height matched to .strip__where: the date inherited the body's 1.65,
   which made its box 28px against the opponent's 19px, and centring boxes of
   different heights is what dropped the date below everything else. */
.strip__when { margin: 0; font-family: var(--font-head); text-transform: uppercase;
	font-weight: 700; font-size: 1.05rem; line-height: 1.4; letter-spacing: .04em;
	color: var(--green-dark); font-variant-numeric: tabular-nums; }
.strip__where { margin: 0; color: var(--slate); font-size: .85rem; line-height: 1.4; }
/* Stacked in the tall panel; becomes one baseline-aligned line in the strip. */
.strip__resultline { display: flex; flex-direction: column; }
.strip__none { color: var(--slate); font-style: italic; margin: 0; }
/* Loading state: the same two slots the real card has, so it does not change
   shape when the fixtures land. One wrapper per slot, so the slot's own grid
   sees a single child rather than laying the bars out as columns.
   Bars are sized in rem, not percentages — a percentage of a subgrid column that
   is itself sizing to its content resolves to nothing, and the card collapses. */
.strip__skel { display: grid; gap: .5rem; justify-items: center; padding-block: 1rem; width: 100%; }
.strip__skel--a { width: 11rem; }
.strip__skel--b { width: 7rem; height: .75rem; }
.strip__card.is-loading .strip__slot--next .strip__skel { padding-block: 2.8rem; }
/* The result panel is the narrower of the two, so its bars are sized under that
   column's width — wider bars would push the column past where the real content
   sets it and the card would still shift on load. */
.strip__card.is-loading .strip__slot--last .strip__skel--a { width: 7rem; }
.strip__card.is-loading .strip__slot--last .strip__skel--b { width: 4.5rem; }

/* Both container queries live after the base rules on purpose: a container
   query adds no specificity, so declared earlier they simply lose. */
/* Stacked layout. The strip must give up max-content here or the bar keeps the
   width its widest row wanted and pushes off the side of the screen. */
@container (max-width: 829px) {
	.strip { width: auto; }
	/* The result reads as a strip under the fixture rather than a second block
	   with a tall empty right-hand side. */
	/* The three blocks centre against each other; the opponent and date align on
	   their own baseline inside .strip__resultline. Aligning the whole row on
	   baselines instead pushed the crests up, because an image's baseline is its
	   bottom edge, not its optical centre. */
	.strip__slot--last { display: flex; flex-wrap: wrap; align-items: center;
		justify-content: center; gap: .4rem .8rem; text-align: center;
		padding-block: .85rem; }
	.strip__slot--last .strip__resultline { flex-direction: row; align-items: baseline;
		gap: .8rem; }
	.strip__slot--last .strip__label { margin: 0; }
}
/* Two separate decisions, two thresholds — both measured, and both shared by
   every bar so they change together.
   600px: the detail sits beside the shields. Needs 251px of shields + 18px gap
   + the longest venue at 287px on one line + 37px padding = 593px. This holds
   even while the bar itself is stacked, which is most of the tablet range. */
@container (min-width: 600px) {
	/* Both columns size to content and the pair centres. With a 1fr detail
	   column the group sat hard left of a full-width bar with a wide gap after
	   it — obvious once the bar stops being three columns. */
	.strip__slot--next { grid-template-columns: auto auto; justify-content: center;
		justify-items: stretch; text-align: start; align-items: center; gap: 1.15rem; }
	.strip__slot--next .strip__crests { justify-content: flex-start; }
	.strip__details { justify-items: start; text-align: start; }
	.strip__where { white-space: nowrap; }
}

/* 830px: the bar itself becomes three columns. That needs the 593px panel plus
   the 59px tab and the 175px result panel = 827px, so it lands well after the
   panel can go side by side. */
@container (min-width: 830px) {
	/* One set of column tracks for the whole strip, borrowed by each bar through
	   subgrid — otherwise every bar sizes its own columns and the divider
	   between the panels lands at a different place on each row. */
	.strip { grid-template-columns: auto auto auto; gap: 1rem 0; }
	.strip__card { grid-column: 1 / -1; grid-template-columns: subgrid; align-items: stretch; }
	.strip__team { writing-mode: vertical-rl; transform: rotate(180deg); padding: 1.1rem .6rem; }
	.strip__slot--last { border-top: 0; border-inline-start: 1px solid var(--line); }
	/* Hold the loaded card's column widths while loading. The subgrid sizes to
	   its content, and placeholder bars carry far less of it than crests and a
	   venue line — without these the bar collapses to a sliver and then jumps to
	   full width. The values are the same measured ones the thresholds above are
	   built from: a 592px fixture panel and a 174px result panel. */
	.strip__card.is-loading .strip__slot--next { min-width: 37rem; }
	.strip__card.is-loading .strip__slot--last { min-width: 10.875rem; }
}

/* A card in a swipe row is 85% of the screen, not all of it. Measured at 390px:
   the card box lands at 264px while the fixture panel wants 283 — the shields
   alone are 246 and will not wrap — so the far crest, the venue line and the
   result date were each clipped at the card's edge.

   Two fixes, both needed. The shields come down enough to fit, and the panels
   are allowed to shrink: a grid item defaults to min-width: auto, so it refuses
   to go below its own content and pushes out of the card rather than reflowing
   inside it.

   A media query rather than a container query on purpose — this constraint
   comes from the swipe utility, which is driven by the viewport, not by how
   wide the strip's wrapper happens to be. */
@media (max-width: 720px) {
	.strip__slot--next .strip__crests .fx-crest { width: 76px; height: 76px; }
	.strip__slot--next .strip__crests .fx-vs { font-size: 1.6rem; }
	.strip__crests { max-width: 100%; }
	.strip__slot,
	.strip__details,
	.strip__resultline { min-width: 0; }
	/* Venue names run long — "Bosnia and Herzegovina Centre · Pitch 2" — and a
	   long one should break rather than widen the card. */
	.strip__opp,
	.strip__where,
	.strip__when { overflow-wrap: anywhere; }
	/* Opponent and date sit side by side while they fit, and the date drops
	   underneath when they do not. Held in a rigid row they each wrapped
	   instead — "Rowville Eagles / SC" beside "FRI, 31 / JULY" — which is two
	   ragged columns where one clean pair of lines will do. */
	.strip__slot--last .strip__resultline { flex-wrap: wrap; justify-content: center; }
}


/* map section */
/* The map takes the room; the address column sizes to its content so the text
   does not stretch across a full half of a wide screen. */
.map-grid { display: grid; gap: var(--gap); align-items: start; }
@media (min-width: 900px) { .map-grid { grid-template-columns: 1fr minmax(15rem, 20rem); } }
.map-frame { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--cream); }
/* Aspect ratio rather than a fixed height, so the frame reserves its space
   before the iframe loads and nothing below it jumps. */
.map-frame iframe { display: block; width: 100%; aspect-ratio: 16 / 10; border: 0; }
@media (max-width: 600px) { .map-frame iframe { aspect-ratio: 4 / 3; } }
.map-details__addr { font-size: 1.0625rem; line-height: 1.6; }
.map-details__addr strong { font-family: var(--font-head); text-transform: uppercase; font-size: 1.15rem; color: var(--green-dark); }

/* news-teaser header row */
/* max-width: none overrides .section__head's 46rem measure. That measure is
   right for a heading and lede, but here the row also carries the button, and
   space-between was pushing it to the end of a 46rem box rather than the end of
   the section. */
.section__head--row { display: flex; justify-content: space-between; align-items: flex-end; gap: 1rem; flex-wrap: wrap; max-width: none; }
/* The heading column keeps the readable measure the row gave up. */
.section__head--row > :first-child { max-width: 46rem; }
.section__head--row h2 { margin: 0; }

/* prose helper on cream/plain sections already covered */

/* =========================================================================
   Motion preference — site-wide
   The build animates the nav slide, the submenu fade and button lifts. All of
   it is decorative, so it comes off entirely when the OS asks for less motion.
   Placed last so it wins over the component rules above.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	*, *::before, *::after {
		animation-duration: .01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .01ms !important;
		transition-delay: 0s !important;
	}
	.btn:hover { transform: none; }
	.primary-nav__list .sub-menu { transform: none; }
}

/* 404 — was an inline style on the section element. */
.error-404 { min-height: 52vh; display: flex; align-items: center; }

/* The header is sticky, so anchored headings need clearance or they land
   underneath it. */
:target { scroll-margin-top: calc(var(--header-h, 80px) + 1rem); }

/* ============================================================================
   SWIPE ROWS — utilities, deliberately last
   ----------------------------------------------------------------------------
   These have to beat component rules. Twice now an equal-specificity component
   selector declared later in the file has quietly won: .strip { display: grid }
   left the match strip stacked, and .matchstrip-section .strip
   { margin-inline: auto } cancelled the full bleed so cards stopped short of
   the screen edge. Escalating the selector each time is an arms race, so the
   block lives at the end instead — equal specificity now resolves in the
   utility's favour by order. The doubled attribute stays, to clear single-class
   rules without depending on position alone.
   ========================================================================== */

/* A four-card grid stacks on a phone, which is four screens of scrolling to get
   past a section a visitor may not want. Below 720px these rows scroll sideways:
   one card at a time, centred, with its neighbours peeking either side.

   Native overflow, not a carousel script. Momentum, keyboard scrolling and
   screen-reader access come free, there is no library, and nothing auto-advances
   to fail WCAG 2.2.2 or steal focus.

   Deliberately NOT applied to the primary sponsor tiles: those are the club's
   paying partners, and every one stays on screen without being swiped to. */
@media (max-width: 720px) {
	[data-swipe][data-swipe] {
		display: flex;
		gap: 1rem;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scroll-snap-type: x mandatory;
		/* Full bleed. The wrap insets content by 1.25rem, so pulling back by the
		   same amount spans the screen and lets a card run to the very edge
		   rather than stopping short of it. */
		margin-inline: -1.25rem;
		/* .strip sets width: max-content and max-width: 100%, which pinned it to
		   the wrap and left it 40px short of the other rows. A swipe row is
		   always as wide as the screen, so the utility releases both. */
		width: auto;
		max-width: none;
		/* The padding is what centres a card: 10% each side leaves the card
		   filling the remaining 80%, and the snap centre lands in the middle of
		   the screen. It also sets the peek — about 21px of the next card at
		   375px, once the gap is taken off. */
		padding-inline: 10%;
		/* The peek is the affordance; a scrollbar on top of it is just noise. */
		scrollbar-width: none;
		/* This one line is the difference between a page that sits still and a
		   page that slides sideways on a phone.

		   overflow-x: auto stops the row's content being *drawn* outside it, but
		   Chrome still counts that content in the document's layout width: with
		   three match cards the document measured 892px against a 375px screen.
		   Scrolling was suppressed by the guard on <html>, so scrollLeft stayed
		   0 and it looked fine here — but iOS Safari honoured the 892 and the
		   page really did slide.

		   contain: paint promises the browser nothing paints outside this box,
		   so the scroll content stops propagating and the document goes back to
		   375. Measured: 892 -> 375.

		   Worth recording what did NOT work, because they all look plausible:
		   overflow-x: clip on the section, the wrap or the body all left it at
		   892, and removing the full bleed made it 946. */
		contain: paint;
	}
	[data-swipe][data-swipe]::-webkit-scrollbar { display: none; }
	[data-swipe][data-swipe] > * {
		flex: 0 0 100%;
		/* Centre rather than start: the card sits in the middle of the screen
		   with a neighbour showing on both sides, which reads as "there is more
		   either way" instead of "there is more to the right". */
		scroll-snap-align: center;
	}
	/* Only ever focusable when main.js finds no link inside to tab to. */
	[data-swipe][data-swipe][tabindex]:focus-visible {
		outline: 3px solid var(--accent-ink);
		outline-offset: 3px;
		border-radius: var(--radius);
	}
}
