/*
Theme Name: GLHF.MT Light
Theme URI: https://glhf.mt
Author: GLHF.MT
Author URI: https://glhf.mt
Description: Light and dark editorial gaming & esports news theme for Malta, with an Anton display hero and a reader-controlled colour mode. Squared-off tone cards, sticky ad rails, scheduled sponsored posts, and a continuous article reader that tells you when you have reached the end. Every colour it ships is checked against WCAG contrast in tests. No categories, no comments.
Version: 5.7.0
Requires at least: 5.8
Tested up to: 6.6
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: glhf-mt-light
Tags: news, two-columns, custom-colors, custom-logo, custom-menu, featured-images, translation-ready
*/

/* =========================================================================
   TOKENS
   ========================================================================= */

:root {
	/*
	 * Light. The site reads as print: white page, near-black type, colour used
	 * only where it means something.
	 *
	 * Every value below was chosen against measured WCAG contrast rather than by
	 * eye, and tests/test-contrast.php asserts each pairing. Inverting a palette
	 * is where that matters most — a colour that worked as a link on charcoal is
	 * almost never the right one on white, because it has to travel to the
	 * opposite end of the lightness scale to keep the same contrast.
	 */
	--bg: #ffffff;
	--bg-elev: #f5f6f8;
	--bg-elev-2: #eceef2;
	--line: #dfe3e8;
	--line-soft: #eef0f3;

	--text: #14161a;
	--text-dim: #545c67;
	--text-faint: #6e7681;

	/* Overridden inline from the Customizer. */
	--accent: #1f5fa8;
	--accent-2: #2c74c4;

	/* Text that sits ON the accent — white here, where the dark theme needed
	   near-black. The token exists so that flip is one line rather than a hunt. */
	--accent-ink: #ffffff;

	--label: #8a5a1a;
	--alert: #b3261e;

	/*
	 * The brand orange, exactly as sampled from the reference.
	 *
	 * It is a FILL, never text on white: #f97316 on white measures 2.80, which
	 * fails even the 3.0 that WCAG allows for large bold type. Black on it
	 * measures 7.49, so it works as a block with dark type over it.
	 */
	--brand: #f97316;

	/*
	 * Text that sits ON the brand orange, which is near-black and not white:
	 * white on #f97316 measures 2.80 and near-black measures 6.46. The footer
	 * button's hover shipped the white version for about ten minutes.
	 */
	--brand-ink: #14161a;

	/*
	 * The same orange darkened until it can be read as text on white — 4.87,
	 * clearing AA outright. This is the one in the hero. It is visibly deeper
	 * than the reference; that is what putting orange type on a white page
	 * costs, and no amount of choosing by eye avoids it.
	 */
	--brand-text: #bd5005;

	/* Squared-off design: no rounded corners anywhere. The loading spinner is
	   the one exception and sets its own 50% radius. */
	--radius: 0;
	--radius-sm: 0;

	--w-page: 1680px;
	--w-read: 760px;
	--w-side: 300px;

	/* Measured from the real header by js/main.js — the logo image height is
	   user-configurable, so this cannot be hardcoded. Value here is the
	   fallback used before the script runs / with JS disabled. */
	--header-h: 128px;
	--stick: 14px;

	--gap: 1.5rem;
	--font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
	/*
	 * The reading face — now the same Inter the rest of the site uses, rather
	 * than Georgia. Kept as its own token so body copy can be given a
	 * different face again in one line, without hunting for which rules count
	 * as "reading".
	 */
	--font-read: var(--font);

	/* The hero, and nothing else. A display face used for body copy is a
	   headline shouting through a paragraph. */
	--font-display: "Anton", "Arial Narrow", "Helvetica Neue Condensed", Impact, sans-serif;

	/* The page colour with an alpha, for the sticky masthead. It cannot be
	   derived from --bg in plain CSS, so it is declared alongside it. */
	--bg-blur: rgba(255, 255, 255, 0.96);

	/* The page's ink at low alpha: the placeholder glyph and the faint wash
	   behind it. Not --text-faint, which is a solid colour meant to be read;
	   these are meant to be barely there. */
	--ink-ghost: rgba(20, 22, 26, 0.16);
	--ink-wash: rgba(20, 22, 26, 0.05);

	--shadow: 0 6px 20px rgba(20, 22, 26, 0.10);
	--ease: 0.22s cubic-bezier(0.4, 0, 0.2, 1);
}

/* =========================================================================
   DARK MODE
   ========================================================================= */

/*
 * The same site after dark, not a different one: the structure, the type and
 * the card colours are identical, and only the tokens move.
 *
 * Three selectors, and all three are needed:
 *
 *   [data-theme="dark"]                  the visitor pressed the toggle
 *   prefers-color-scheme + :not(light)   their system is dark and they have
 *                                        not overridden it
 *
 * The :not([data-theme="light"]) guard is the part that is easy to leave out
 * and the part that breaks things: without it, a visitor on a dark-by-default
 * system who presses the toggle to get light gets dark anyway, because the
 * media query has nothing telling it to stand down.
 *
 * Every value here is measured, exactly as the light set is, and
 * tests/test-contrast.php runs the same pairings against both.
 */
:root[data-theme="dark"] {
	--bg: #101216;
	--bg-elev: #171a20;
	--bg-elev-2: #1e222a;
	--line: #2a2f38;
	--line-soft: #20242b;

	--text: #eef0f4;
	--text-dim: #a8b0bc;
	--text-faint: #8b93a0;

	/* Overridden inline from the Customizer, lightened to clear the dark page.
	   The Customizer stores one accent; PHP derives this one from it. */
	--accent: #6ea8e8;
	--accent-2: #8fc0f5;

	/* Near-black on a light blue, where the light theme puts white on a dark
	   one. This is the flip the token exists for. */
	--accent-ink: #0b0d10;

	--label: #e0a34a;
	--alert: #f2837c;

	/* The fill and its ink do not move: near-black on the brand orange
	   measures 6.46 whatever the page behind it is doing. */
	--brand: #f97316;
	--brand-ink: #14161a;

	/*
	 * Orange as TYPE, and here it can be the bright one. The light theme has
	 * to darken to #bd5005 to survive a white page; on #101216 the near-stock
	 * #fb923c measures 8.28. The hero looks more like the brand after dark
	 * than it does in daylight, which is the opposite of what you would guess.
	 */
	--brand-text: #fb923c;

	/*
	 * The card hairline, and NOT --line.
	 *
	 * Seven of the eight card colours are pale and separate from the dark page
	 * by 10:1 or better. The eighth is Charcoal #242a33, which measures 1.30
	 * against #101216 — the card dissolves into the page and the headline
	 * appears to float on nothing. Its own text is still readable, so nothing
	 * about the card looks broken; it just stops being a card.
	 *
	 * A hairline fixes it, but only if it can be seen against BOTH the page
	 * and the card it encloses. --line at #2a2f38 manages 1.07 against
	 * Charcoal, which is no edge at all. This one measures 2.16 against the
	 * page and 1.66 against the darkest card.
	 */
	--card-edge: #434c5a;

	--bg-blur: rgba(16, 18, 22, 0.96);
	--ink-ghost: rgba(238, 240, 244, 0.18);
	--ink-wash: rgba(238, 240, 244, 0.06);

	--shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) {
		--bg: #101216;
		--bg-elev: #171a20;
		--bg-elev-2: #1e222a;
		--line: #2a2f38;
		--line-soft: #20242b;

		--text: #eef0f4;
		--text-dim: #a8b0bc;
		--text-faint: #8b93a0;

		--accent: #6ea8e8;
		--accent-2: #8fc0f5;
		--accent-ink: #0b0d10;

		--label: #e0a34a;
		--alert: #f2837c;

		--brand: #f97316;
		--brand-ink: #14161a;
		--brand-text: #fb923c;

		--card-edge: #434c5a;

		--bg-blur: rgba(16, 18, 22, 0.96);
		--ink-ghost: rgba(238, 240, 244, 0.18);
		--ink-wash: rgba(238, 240, 244, 0.06);

		--shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
	}
}

/* Form controls, scrollbars and the like are drawn by the browser, and it
   needs telling which way round the page is or they arrive light on dark. */
:root[data-theme="dark"] {
	color-scheme: dark;
}

:root[data-theme="light"] {
	color-scheme: light;
}

/* =========================================================================
   THE DISPLAY FACE
   ========================================================================= */

/*
 * Anton, bundled rather than pulled from Google Fonts.
 *
 * A hero built on a webfont that arrives from someone else's server is a hero
 * that is missing, or in the wrong typeface, for as long as that request takes
 * — and on a shared host serving Malta that is the slowest thing on the page.
 * It is 18KB for the latin subset, it is SIL Open Font Licence, and it lives
 * in the theme.
 *
 * font-display: swap so the headline is readable in the fallback immediately
 * and re-renders when Anton lands, rather than the page holding a blank space.
 */
/*
 * Inter, bundled.
 *
 * The stylesheet has named "Inter" as the first family since the theme was
 * written and has never loaded it. Every page has been rendering in whatever
 * the operating system happened to provide — Helvetica on a Mac, Segoe on
 * Windows, Roboto on Android — so the site has looked materially different on
 * every visitor's machine and none of them was the design.
 *
 * One variable file covers every weight from 100 to 900, which is why the
 * headline can go to 800 without a second download.
 *
 * The latin-ext subset is not optional on a Malta site: c, g, h and z with
 * their diacritics — U+010A, U+0120, U+0126, U+017B and their lower cases —
 * all live in Latin Extended-A. Without it every Maltese proper noun falls
 * back to a different typeface in the middle of a word. The unicode-range
 * means a page that never uses one does not download the file.
 */
@font-face {
	font-family: "Inter";
	font-style: normal;
	font-weight: 100 900;
	font-display: swap;
	src: url("fonts/inter-latin-var.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
		U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191,
		U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: "Inter";
	font-style: normal;
	font-weight: 100 900;
	font-display: swap;
	src: url("fonts/inter-latin-ext-var.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
		U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
		U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
		U+A720-A7FF;
}

@font-face {
	font-family: "Anton";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("fonts/anton-latin-400.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
		U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122,
		U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: "Anton";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("fonts/anton-latin-ext-400.woff2") format("woff2");
	unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
		U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* =========================================================================
   RESET
   ========================================================================= */

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

html {
	-webkit-text-size-adjust: 100%;
	color-scheme: light;
	scroll-behavior: smooth;
	scroll-padding-top: calc(var(--header-h) + var(--stick));
}

body {
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font-family: var(--font);
	font-size: 16px;
	line-height: 1.65;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	/* `clip` rather than `hidden`. On <body> either one is safe for the sticky
	   ad rails (overflow set on body propagates to the viewport), but `clip`
	   never establishes a scroll container, so this stays safe if the rule is
	   ever moved onto a wrapper element. */
	overflow-x: clip;
}

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

img {
	height: auto;
	display: block;
}

a {
	color: var(--accent);
	text-decoration: none;
}

a:hover {
	text-decoration: underline;
}

h1, h2, h3, h4, h5, h6 {
	margin: 0 0 0.6em;
	line-height: 1.22;
	font-weight: 750;
	letter-spacing: -0.015em;
}

p {
	margin: 0 0 1.15em;
}

button {
	font: inherit;
	color: inherit;
	cursor: pointer;
}

figure {
	margin: 0;
}

ul, ol {
	padding-left: 1.25rem;
}

:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
	border-radius: 0;
}

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

.skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 999;
}

.skip-link:focus {
	left: 1rem;
	top: 1rem;
	width: auto;
	height: auto;
	clip: auto;
	padding: 0.6rem 1rem;
	background: var(--accent);
	color: var(--accent-ink);
	border-radius: 0;
	font-weight: 700;
}

[hidden] {
	display: none !important;
}

/* =========================================================================
   HEADER  — centred masthead
   ========================================================================= */

.site-header {
	position: sticky;
	top: 0;
	z-index: 100;
	/* Denser than the dark theme's 0.94: on a white page anything showing
	   through a translucent bar reads as a smudge rather than as depth.
	   Tokenised because it is the page colour with an alpha, and a literal
	   here left the whole masthead white after dark — logo, tagline and menu
	   all near-white on near-white. */
	background: var(--bg-blur);
	backdrop-filter: saturate(140%) blur(14px);
	-webkit-backdrop-filter: saturate(160%) blur(12px);
	border-bottom: 1px solid var(--line);
}

/*
 * 1fr / auto / 1fr keeps the logo optically centred regardless of how wide the
 * menu button or the social icons happen to be.
 */
.header-bar {
	max-width: var(--w-page);
	margin: 0 auto;
	padding: 1rem 1.5rem;
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: 1rem;
}

.header-side {
	display: flex;
	align-items: center;
	min-width: 0;
}

.header-side-start {
	justify-content: flex-start;
}

.header-side-end {
	justify-content: flex-end;
}

.site-logo {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.3rem;
	text-align: center;
	min-width: 0;
}

.logo-lockup,
.logo-lockup:hover {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	color: var(--text);
	text-decoration: none;
}

.logo-mark {
	width: 36px;
	height: 36px;
	display: grid;
	place-items: center;
	border-radius: 0;
	background: linear-gradient(135deg, var(--accent), var(--accent-2));
	color: var(--accent-ink);
	flex-shrink: 0;
}

.logo-mark svg {
	width: 20px;
	height: 20px;
	fill: currentColor;
}

.logo-name {
	font-size: clamp(1.4rem, 3.4vw, 2rem);
	font-weight: 850;
	letter-spacing: -0.035em;
	line-height: 1.05;
	white-space: nowrap;
}

/* Uploaded logo image: free to be any shape, capped by --logo-h. */
.custom-logo-link {
	display: block;
	line-height: 0;
}

.custom-logo {
	max-height: var(--logo-h, 56px);
	width: auto;
	height: auto;
	max-width: min(78vw, 460px);
	object-fit: contain;
	border-radius: 0;
}

.logo-tagline {
	font-size: 0.7rem;
	color: var(--text-dim);
	letter-spacing: 0.16em;
	text-transform: uppercase;
	line-height: 1.3;
}

/* --- nav row --- */

.site-nav {
	border-top: 1px solid var(--line-soft);
}

/*
 * Centre the nav row within the page, and cap it like the header bar above it.
 *
 * Named children rather than `.site-nav > *`, and that is the whole fix. The
 * universal selector scores (0,1,0), exactly the same as `.nav-menu` — and
 * `.nav-menu` comes later in this file and begins with the usual list reset,
 * `margin: 0`. Equal specificity, later wins, so `margin-inline: auto` was
 * quietly cancelled by a line whose job was removing bullet-list indentation.
 *
 * It was invisible up to 1680px, because below the cap the menu fills the
 * width and justify-content does the centring. Past it the block stops growing
 * and stays pinned to the left edge, so the menu drifts further off-centre the
 * wider the monitor — perfectly centred on a laptop, visibly wrong on a
 * desktop, which is why it survived every screenshot taken at 1440.
 *
 * Two classes score (0,2,0) and win regardless of source order, so a future
 * reset cannot silently undo it again. test-layout.js now measures this at
 * 1920 and 2560.
 */
.site-nav > .nav-menu,
.site-nav > .social-links {
	max-width: var(--w-page);
	margin-inline: auto;
}

.nav-menu {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-wrap: wrap;
	gap: 0.35rem 1.6rem;
	list-style: none;
	margin: 0;
	padding: 0.7rem 1.5rem;
}

.nav-menu a {
	/* inline-block + padding so the clickable area is a comfortable ~28px
	   rather than the ~15px that font metrics alone would give an inline <a>. */
	display: inline-block;
	padding: 0.32rem 0.2rem;
	color: var(--text-dim);
	font-size: 0.84rem;
	font-weight: 650;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	text-decoration: none;
	transition: color var(--ease);
	white-space: nowrap;
}

.nav-menu a:hover,
.nav-menu .current-menu-item > a {
	color: var(--accent);
	text-decoration: none;
}

/* Social list shown inside the mobile panel only. Needs to outrank the
   generic .social-links{display:flex} below it, hence the descendant. */
.site-nav .nav-social {
	display: none;
}

.social-links {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.social-links a {
	width: 34px;
	height: 34px;
	display: grid;
	place-items: center;
	border-radius: 0;
	color: var(--text-dim);
	background: var(--bg-elev);
	border: 1px solid var(--line);
	transition: color var(--ease), border-color var(--ease), transform var(--ease);
}

.social-links a:hover {
	color: var(--accent);
	border-color: var(--accent);
	transform: translateY(-2px);
}

.social-links svg {
	width: 17px;
	height: 17px;
}

/* -------------------------------------------------------------------------
   THE DARK MODE TOGGLE
   ---------------------------------------------------------------------- */

/*
 * Sized and bordered to match a social icon exactly, because it sits in the
 * same row and anything a few pixels off reads as a mistake rather than a
 * different kind of control.
 */
.theme-toggle {
	width: 34px;
	height: 34px;
	flex: 0 0 auto;
	display: grid;
	place-items: center;
	margin-right: 0.5rem;
	padding: 0;
	border-radius: 0;
	color: var(--text-dim);
	background: var(--bg-elev);
	border: 1px solid var(--line);
	cursor: pointer;
	transition: color var(--ease), border-color var(--ease), transform var(--ease);
}

.theme-toggle:hover {
	color: var(--accent);
	border-color: var(--accent);
	transform: translateY(-2px);
}

.theme-toggle svg {
	width: 17px;
	height: 17px;
	display: block;
}

/*
 * Which icon shows.
 *
 * The button offers the mode you are NOT in, so the moon shows on the light
 * page and the sun after dark. Both icons are in the markup and CSS picks,
 * rather than JS swapping them: the server cannot know which mode a visitor
 * will resolve into, and an icon chosen by script arrives a frame late in
 * exactly the way the boot script exists to prevent.
 */
.theme-toggle .icon-sun {
	display: none;
}

:root[data-theme="dark"] .theme-toggle .icon-moon {
	display: none;
}

:root[data-theme="dark"] .theme-toggle .icon-sun {
	display: block;
}

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) .theme-toggle .icon-moon {
		display: none;
	}

	:root:not([data-theme="light"]) .theme-toggle .icon-sun {
		display: block;
	}
}

/* The button ships hidden and main.js reveals it. A control that cannot do
   its job without script should not be offered to someone without script. */
.theme-toggle[hidden] {
	display: none;
}

.nav-toggle {
	display: none;
	/* flex column, NOT grid: grid rows stretch to fill the button, which put
	   the bars ~15px apart and made the "open" transform render as a chevron
	   instead of an X. Flex keeps them exactly `gap` apart. */
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 4px;
	width: 42px;
	height: 42px;
	background: var(--bg-elev);
	border: 1px solid var(--line);
	border-radius: 0;
	padding: 0;
}

.nav-toggle span {
	display: block;
	width: 17px;
	height: 2px;
	background: var(--text);
	border-radius: 0;
	transition: transform var(--ease), opacity var(--ease);
}

.nav-toggle.is-open span:nth-child(1) {
	transform: translateY(6px) rotate(45deg);
}

.nav-toggle.is-open span:nth-child(2) {
	opacity: 0;
}

.nav-toggle.is-open span:nth-child(3) {
	transform: translateY(-6px) rotate(-45deg);
}

/* =========================================================================
   LAYOUT  — sidebars are part of the shell, so every template has them
   ========================================================================= */

.site-main {
	max-width: var(--w-page);
	margin: 0 auto;
	padding: 2rem 1.5rem 4rem;
	display: grid;
	grid-template-columns: var(--w-side) minmax(0, 1fr) var(--w-side);
	gap: 2.5rem;
	align-items: stretch;
	/* Do NOT add overflow (hidden/auto/scroll) here or on any wrapper between
	   <body> and .sidebar-sticky — it creates a scroll container and silently
	   kills the sticky ad rails. Verified in tests/test-layout.js. */
}

.site-content {
	min-width: 0;
}

.sidebar {
	min-width: 0;
	/* Load-bearing. Without a full-height column the sticky child has no travel
	   range and scrolls out of view on the first flick — which is exactly what
	   happened in 4.0.0. Keep this even if align-items changes. */
	height: 100%;
}

.sidebar-sticky {
	position: sticky;
	top: calc(var(--header-h) + var(--stick));
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	/* If an ad stack is taller than the viewport, let it scroll internally
	   rather than clipping the bottom of it. */
	max-height: calc(100vh - var(--header-h) - (var(--stick) * 2));
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-width: thin;
	scrollbar-color: var(--line) transparent;
}

.sidebar-sticky::-webkit-scrollbar {
	width: 6px;
}

.sidebar-sticky::-webkit-scrollbar-thumb {
	background: var(--line);
	border-radius: 0;
}

.sidebar-sticky::-webkit-scrollbar-track {
	background: transparent;
}

.widget {
	background: var(--bg-elev);
	border: 1px solid var(--line);
	border-radius: 0;
	padding: 1.1rem;
	font-size: 0.92rem;
}

.widget:empty {
	display: none;
}

.widget-title {
	margin: 0 0 0.8rem;
	font-size: 0.72rem;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--text-faint);
	font-weight: 700;
}

.widget ul {
	list-style: none;
	margin: 0;
	padding: 0;
}

.widget li + li {
	margin-top: 0.5rem;
}

.widget a {
	color: var(--text-dim);
	text-decoration: none;
}

.widget a:hover {
	color: var(--accent);
}

.widget img {
	border-radius: 0;
	margin-inline: auto;
}

/* =========================================================================
   SECTION HEADINGS
   ========================================================================= */

.section-head {
	display: flex;
	align-items: center;
	gap: 1rem;
	margin-bottom: 1.1rem;
}

/*
 * The furniture shouts; the news does not.
 *
 * Anton on the section heads and the hero, Inter on everything that carries a
 * story. It is the arrangement most modern outlets land on, and the reason is
 * practical rather than fashionable: a condensed all-caps face is superb at
 * four words and tiring at fourteen, so it belongs on labels — LATEST, MORE —
 * and never on a headline whose length nobody controls.
 */
.section-title {
	margin: 0;
	font-family: var(--font-display);
	font-weight: 400;
	font-size: clamp(1.6rem, 2.6vw, 2.125rem);
	line-height: 1;
	letter-spacing: 0;
	text-transform: uppercase;
	white-space: nowrap;
}

.section-rule {
	flex: 1;
	height: 1px;
	background: linear-gradient(90deg, var(--line), transparent);
}

.archive-description {
	color: var(--text-dim);
	margin-bottom: 1.5rem;
}

/* =========================================================================
   9:16 PORTRAIT CARD GRID
   Photo on top, solid colour block beneath carrying the headline.
   --card-bg / --card-ink are set per card from the post's chosen colour.
   ========================================================================= */

/*
 * Denser, but not narrower.
 *
 * The obvious way to fit more stories on a screen is another column, and it is
 * the wrong one here: the two ad rails leave the grid about 712px wide on a
 * 1440 laptop, so a fourth column would put the cards at roughly 165px. A 20px
 * headline in a 165px card is eight characters a line. The minimum below is
 * what keeps three columns at that width and four when the rails drop away.
 *
 * So the density comes out of the space between things instead — the gap and
 * the card's own padding — which costs nothing a reader wants.
 */
.card-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
	gap: 0.9rem;

	/*
	 * Cards match each other again, but the empty space that made that look
	 * broken is dealt with at the source rather than by letting the grid go
	 * ragged. See .card-title: the headline block is always at least three
	 * lines tall and its text sits on the bottom of it, so the slack ends up
	 * under the picture as breathing room instead of as a hole between the
	 * headline and the date.
	 */
	align-items: stretch;
}

.card {
	position: relative;
	display: grid;
	/* auto media + 1fr body: the colour block takes whatever height the
	   headline needs. Cards in the same grid row still line up because grid
	   items stretch, so nothing is clipped and nothing is a fixed box. */
	grid-template-rows: auto 1fr;
	background: var(--card-bg, #b1bfd3);
	/* --card-edge is only declared after dark, where the darkest swatch needs
	   a stronger hairline than --line to stay visible as a card. */
	border: 1px solid var(--card-edge, var(--line));
	text-decoration: none;
	isolation: isolate;
	overflow: hidden;
	transition: transform var(--ease), box-shadow var(--ease);
}

.card:hover {
	transform: translateY(-5px);
	box-shadow: var(--shadow);
	text-decoration: none;
}

.card-media {
	position: relative;
	overflow: hidden;
	background: var(--bg-elev-2);
	aspect-ratio: var(--card-media-ar, 9 / 16);
}

/* Fallback for browsers without aspect-ratio. */
@supports not (aspect-ratio: 1 / 1) {
	.card-media {
		height: 0;
		padding-bottom: var(--card-media-pb, 177.7778%);
	}

	.card-media > * {
		position: absolute;
		inset: 0;
	}
}

.card-media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

.card:hover .card-media img {
	transform: scale(1.05);
}

.card-placeholder {
	width: 100%;
	height: 100%;
	display: grid;
	place-items: center;
	color: var(--ink-ghost);
	background:
		radial-gradient(circle at 50% 35%, var(--ink-wash), transparent 60%),
		var(--bg-elev-2);
}

.card-placeholder svg {
	width: 52px;
	height: 52px;
	fill: none;
}

/* --- the colour block --- */

.card-body {
	position: relative;
	min-height: 0;
	display: flex;
	flex-direction: column;
	gap: 0.35rem;

	/*
	 * The whole block sits on the bottom of the card.
	 *
	 * When a row stretches to fit one long headline, the shorter cards in it
	 * gain height somewhere. Left to itself that space opened up between the
	 * headline and the date — a hole in the middle of the card. Pushed to the
	 * bottom, the same space sits under the picture and reads as margin.
	 */
	justify-content: flex-end;

	/* The 2.4rem foot was clearing the corner arrow with room to spare. */
	padding: 0.8rem 0.85rem 2rem;
	background: var(--card-bg, #b1bfd3);
	color: var(--card-ink, #000000);
}

.card-label {
	align-self: flex-start;
	padding: 0.2rem 0.5rem;
	border-radius: 0;
	/* Inverted out of the card colour, so it works on any background. */
	background: var(--card-ink, #000000);
	color: var(--card-bg, #b1bfd3);
	font-size: 0.6rem;
	font-weight: 800;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	line-height: 1.5;
}

/*
 * The headline is the product, and it was set smaller than the body text
 * beside it: 15px on a desktop and 12.5px on a phone, against a 16.8px
 * standfirst. At that size a headline reads as a caption for the picture above
 * it rather than as the reason to click.
 *
 * Sentence case, not uppercase. Capitals cost roughly a fifth of the reading
 * speed at a glance because the word shapes go flat, and a card grid is
 * nothing but glances. The measured text column inside a card is about 194px,
 * which at this size carries around eighteen characters a line — enough for a
 * headline to break where it means to.
 */
.card-title {
	margin: 0;
	color: var(--card-ink, #000000);
	font-size: clamp(1.05rem, 1.45vw, 1.25rem);
	font-weight: 800;
	line-height: 1.2;
	letter-spacing: -0.021em;

	/*
	 * Never shorter than three lines, never longer than four, and the text
	 * sits on the bottom of that box.
	 *
	 * Both limits are needed to make the cards match. The floor stops a
	 * two-word headline collapsing its card; the ceiling stops one very long
	 * headline stretching every card in its row around it. Between them a card
	 * body varies by a single line instead of by five.
	 *
	 * Measured over ten real headlines at this size: seven fit three lines
	 * whole on a desktop, so the floor costs nothing. The ceiling puts an
	 * ellipsis on the three that run past four lines — the tail of a long
	 * headline, on a card whose whole job is to get the click. Both numbers
	 * are here to be changed if that trade ever feels wrong.
	 *
	 * Bottom-aligned because it puts the leftover space somewhere harmless: a
	 * one-line headline in a three-line box leaves two lines of nothing, and
	 * above the text that reads as air under the picture rather than as a hole
	 * between the headline and the date.
	 *
	 * In em, so it is lines of whatever size the clamp above lands on rather
	 * than lines of one particular breakpoint.
	 */
	--card-lines-min: 3;
	--card-lines-max: 4;

	min-height: calc(var(--card-lines-min) * 1.2em);
	display: grid;
	align-content: end;
	overflow-wrap: break-word;
}

/*
 * The clamp lives on the span because -webkit-box, which is the only thing
 * that can cut text at a line count, cannot also bottom-align it. The h2 owns
 * the box; this owns the ending.
 */
.card-title span {
	display: -webkit-box;
	-webkit-line-clamp: var(--card-lines-max);
	line-clamp: var(--card-lines-max);
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.card-meta {
	/*
	 * No margin-top: auto any more. It existed to pin the date to the foot of
	 * a stretched card, which the body's own bottom alignment now does — and
	 * leaving it in put the slack back between the headline and the date,
	 * which is the gap all of this is here to remove.
	 */
	display: flex;
	align-items: center;
	gap: 0.4rem;
	font-size: 0.7rem;
	font-weight: 600;
	color: var(--card-ink, #000000);
	opacity: 0.62;
	padding-right: 2.2rem;
}

.card-meta .dot {
	opacity: 0.6;
}

.card-arrow {
	position: absolute;
	right: 0.7rem;
	bottom: 0.7rem;
	width: 24px;
	height: 24px;
	display: grid;
	place-items: center;
	border-radius: 0;
	color: var(--card-ink, #000000);
	border: 1.5px solid currentColor;
	background: transparent;
	opacity: 0.85;
	transition: transform var(--ease), background-color var(--ease),
		color var(--ease), opacity var(--ease);
}

/*
 * On hover the box fills and the glyph reverses out of it.
 *
 * The fill is --card-ink and the glyph is --card-bg, rather than a literal
 * black box with a white arrow. On seven of the eight card colours those ARE
 * black and white, so it looks exactly like the plain version. On the eighth
 * — Charcoal, whose ink is already white — a black fill would be a black box
 * on a near-black card, and the arrow would disappear at the moment you point
 * at it. Stated as an inversion it flips the other way there instead, and
 * stays readable.
 *
 * It also inherits a guarantee for free: the contrast test already proves
 * --card-ink clears 4.5 against every swatch, and the hovered arrow is that
 * same pairing with the two roles swapped, which measures identically.
 *
 * Keyboard focus gets the same treatment. An effect that only exists on hover
 * is an effect half the people using the site never see.
 */
.card:hover .card-arrow,
.card:focus-visible .card-arrow {
	transform: translate(2px, -2px);
	background: var(--card-ink, #000000);
	color: var(--card-bg, #b1bfd3);
	border-color: var(--card-ink, #000000);
	opacity: 1;
}

.card-arrow svg {
	width: 13px;
	height: 13px;
}

/* Sponsored cards keep a gradient edge. */
.card-ad {
	border-color: transparent;
	background:
		linear-gradient(var(--card-bg, #b1bfd3), var(--card-bg, #b1bfd3)) padding-box,
		linear-gradient(140deg, var(--accent), var(--accent-2)) border-box;
	border: 2px solid transparent;
}

.card-ad:hover {
	box-shadow: 0 8px 26px color-mix(in srgb, var(--accent) 20%, transparent);
}

/* =========================================================================
   LOAD MORE / SPINNER / END MESSAGE
   ========================================================================= */

.grid-more {
	margin-top: 2.25rem;
	text-align: center;
}

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	padding: 0.72rem 1.5rem;
	border: 1px solid var(--line);
	border-radius: 0;
	background: var(--bg-elev);
	color: var(--text);
	font-size: 0.9rem;
	font-weight: 650;
	text-decoration: none;
	transition: background var(--ease), border-color var(--ease), color var(--ease), transform var(--ease);
}

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

.stream-spinner {
	display: flex;
	justify-content: center;
	padding: 2.25rem 0;
}

.spinner {
	width: 26px;
	height: 26px;
	border: 3px solid var(--line);
	border-top-color: var(--accent);
	border-radius: 50%;
	animation: glhf-spin 0.8s linear infinite;
}

@keyframes glhf-spin {
	to {
		transform: rotate(360deg);
	}
}

@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}

	.spinner {
		animation-duration: 2s;
	}

	*,
	*::before,
	*::after {
		transition-duration: 0.01ms !important;
	}
}

/* The sentinel is an invisible trigger, never a visible gap. */
.stream-sentinel {
	width: 100%;
	height: 1px;
	margin: 0;
	pointer-events: none;
}

.stream-end {
	text-align: center;
	padding: 2.5rem 1rem 1rem;
}

.stream-end-rule {
	display: block;
	width: 44px;
	height: 1px;
	margin: 0 auto 1.3rem;
	background: var(--line);
}

.stream-end-text {
	margin: 0 0 0.3rem;
	font-size: 0.92rem;
	font-weight: 400;
	color: var(--text-faint);
	letter-spacing: 0.06em;
}

.stream-end-sub {
	margin: 0 0 1.4rem;
	color: var(--text-faint);
	opacity: 0.7;
	font-size: 0.8rem;
	font-weight: 400;
}

/* =========================================================================
   ARTICLE
   ========================================================================= */

.article-stream {
	max-width: var(--w-read);
	margin: 0 auto;
}

.article {
	padding-bottom: 1rem;
}

/* Divider between consecutively streamed articles. */
.js-article + .js-article {
	margin-top: 3.5rem;
	padding-top: 3.5rem;
	border-top: 1px solid var(--line);
	position: relative;
}

.js-article + .js-article::before {
	content: "Next article";
	position: absolute;
	top: -0.72rem;
	left: 50%;
	transform: translateX(-50%);
	padding: 0.18rem 0.85rem;
	background: var(--bg);
	color: var(--text-faint);
	font-size: 0.66rem;
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	white-space: nowrap;
}

.article-head {
	margin-bottom: 1.5rem;
}

.article-kicker {
	position: relative;
	margin-bottom: 0.9rem;
}

.article-kicker .card-label {
	position: static;
	display: inline-block;
}

.article-title {
	font-size: clamp(1.75rem, 4.2vw, 2.6rem);
	line-height: 1.14;
	margin: 0 0 0.75rem;
	letter-spacing: -0.028em;
}

.article-meta {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 0.45rem;
	color: var(--text-faint);
	font-size: 0.86rem;
}

.article-hero {
	margin: 0 0 1.9rem;
	border-radius: 0;
	overflow: hidden;
	background: var(--bg-elev);
}

.article-hero img {
	/*
	 * LOAD-BEARING: width:auto, not 100%. A 9:16 featured image used to be
	 * served through a hard-cropped 16:9 size, so the article hero threw away
	 * most of the picture. It is now shown whole and simply capped in height;
	 * a portrait image centres itself, a landscape one still fills the column.
	 */
	display: block;
	margin: 0 auto;
	width: auto;
	max-width: 100%;
	/*
	 * Capped so a portrait hero does not put a full screen of picture between
	 * the headline and the first paragraph. The figure keeps its full width, so
	 * the spare space either side reads as a deliberate mat rather than a gap.
	 */
	max-height: min(62vh, 620px);
	height: auto;
}

.article-hero figcaption {
	padding: 0.7rem 0.9rem;
	font-size: 0.8rem;
	color: var(--text-faint);
	background: var(--bg-elev);
}

.article-body {
	font-family: var(--font-read);

	/*
	 * Smaller than the 1.115rem Georgia had, because Inter's x-height is
	 * 0.56em against Georgia's 0.50em — measured, not guessed. Left at the
	 * same number the type would have jumped about 11% larger on the day the
	 * face changed, which reads as "the article got bigger" rather than "the
	 * font changed".
	 */
	font-size: 1.06rem;
	line-height: 1.72;
	/* Was #e6e6e6 — near-white body copy, hardcoded, which on a white page is
	   an article you cannot read. Tokens exist so an inversion is one edit. */
	color: var(--text);
}

/*
 * The measure — how many characters land on a line.
 *
 * Comfortable reading is 45–75 characters; past about 85 the eye starts
 * losing its place on the return sweep. This column was running at 105,
 * measured by walking a Range through the rendered text and recording where
 * the line actually broke, not by estimating from an average glyph width.
 *
 * Georgia was partly hiding it: serifs help the eye track along a long line,
 * so swapping to a sans at the same width would have made reading measurably
 * worse while looking like nothing but a font change. 58ch brings it to 79.
 *
 * In ch rather than px so it follows the font size — and note that ch is the
 * width of a "0", which in Inter is wider than the average character, so 58ch
 * is about 79 real characters rather than 58.
 *
 * Applied to the text blocks, not to .article-body itself, so photographs,
 * figures and tables still use the full width of the column.
 */
.article-body > p,
.article-body > ul,
.article-body > ol,
.article-body > h2,
.article-body > h3,
.article-body > blockquote {
	max-width: 58ch;
}

.article-body h2 {
	font-family: var(--font);
	font-size: 1.5rem;
	margin-top: 2.1rem;
}

.article-body h3 {
	font-family: var(--font);
	font-size: 1.22rem;
	margin-top: 1.7rem;
}

.article-body a {
	color: var(--accent);
	text-decoration: underline;
	text-underline-offset: 3px;
	text-decoration-thickness: 1px;
}

.article-body img,
.article-body figure {
	border-radius: 0;
	margin: 1.7rem 0;
}

.article-body figcaption {
	font-family: var(--font);
	font-size: 0.8rem;
	color: var(--text-faint);
	text-align: center;
	margin-top: 0.55rem;
}

.article-body blockquote {
	margin: 1.8rem 0;
	padding: 0.3rem 0 0.3rem 1.3rem;
	border-left: 3px solid var(--accent);
	color: var(--text-dim);
	font-style: italic;
}

.article-body ul,
.article-body ol {
	padding-left: 1.4rem;
	margin: 0 0 1.3rem;
}

.article-body li {
	margin-bottom: 0.5rem;
}

.article-body code {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 0.88em;
	background: var(--bg-elev-2);
	padding: 0.15em 0.42em;
	border-radius: 0;
}

.article-body pre {
	background: var(--bg-elev);
	border: 1px solid var(--line);
	border-radius: 0;
	padding: 1rem 1.1rem;
	overflow-x: auto;
	font-size: 0.88rem;
}

.article-body pre code {
	background: none;
	padding: 0;
}

.article-body table {
	width: 100%;
	border-collapse: collapse;
	font-family: var(--font);
	font-size: 0.94rem;
	margin: 1.5rem 0;
	display: block;
	overflow-x: auto;
}

.article-body th,
.article-body td {
	border: 1px solid var(--line);
	padding: 0.6rem 0.8rem;
	text-align: left;
}

.article-body th {
	background: var(--bg-elev);
}

.article-body hr {
	border: 0;
	border-top: 1px solid var(--line);
	margin: 2rem 0;
}

.article-tags {
	display: flex;
	flex-wrap: wrap;
	gap: 0.45rem;
	margin: 1.9rem 0 0;
}

.article-tags a {
	padding: 0.3rem 0.7rem;
	border-radius: 0;
	background: var(--bg-elev);
	border: 1px solid var(--line);
	color: var(--text-dim);
	font-size: 0.78rem;
	text-decoration: none;
	transition: color var(--ease), border-color var(--ease);
}

.article-tags a:hover {
	color: var(--accent);
	border-color: var(--accent);
}

/* --- Share row --- */

.article-share {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-top: 1.9rem;
	padding-top: 1.4rem;
	border-top: 1px solid var(--line);
}

.share-label {
	font-size: 0.7rem;
	font-weight: 800;
	letter-spacing: 0.13em;
	text-transform: uppercase;
	color: var(--text-faint);
	margin-right: 0.35rem;
}

.share-btn {
	display: inline-flex;
	align-items: center;
	gap: 0.42rem;
	padding: 0.46rem 0.85rem;
	border-radius: 0;
	border: 1px solid var(--line);
	background: var(--bg-elev);
	color: var(--text-dim);
	font-size: 0.8rem;
	font-weight: 600;
	text-decoration: none;
	transition: color var(--ease), border-color var(--ease), background var(--ease);
}

.share-btn:hover {
	color: var(--accent);
	border-color: var(--accent);
	text-decoration: none;
}

.share-btn.is-copied {
	color: var(--accent-ink);
	background: var(--accent);
	border-color: var(--accent);
}

.share-btn svg {
	width: 15px;
	height: 15px;
	flex-shrink: 0;
}

.inarticle-ad {
	margin-top: 2rem;
}

.inarticle-ad .widget {
	text-align: center;
}

/* Sponsored article banner. */
.article-ad > .article-head::before {
	content: "Sponsored content";
	display: block;
	margin-bottom: 0.85rem;
	padding: 0.55rem 0.9rem;
	border-radius: 0;
	background: linear-gradient(135deg,
		color-mix(in srgb, var(--accent) 14%, transparent),
		color-mix(in srgb, var(--accent-2) 14%, transparent));
	border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
	color: var(--text-dim);
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

/* =========================================================================
   POST NAV / PAGINATION
   ========================================================================= */

.post-nav {
	max-width: var(--w-read);
	margin: 2.5rem auto 0;
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 1rem;
}

.post-nav a {
	display: block;
	padding: 1rem 1.1rem;
	border: 1px solid var(--line);
	border-radius: 0;
	background: var(--bg-elev);
	color: var(--text);
	text-decoration: none;
	font-weight: 650;
	line-height: 1.35;
	transition: border-color var(--ease);
}

.post-nav a:hover {
	border-color: var(--accent);
	text-decoration: none;
}

.post-nav span {
	display: block;
	font-size: 0.68rem;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--text-faint);
	margin-bottom: 0.3rem;
	font-weight: 700;
}

.post-nav-next {
	text-align: right;
}

.pagination {
	margin-top: 2.25rem;
	display: flex;
	justify-content: center;
}

.nav-links {
	display: flex;
	gap: 0.4rem;
	flex-wrap: wrap;
	justify-content: center;
}

.page-numbers {
	display: grid;
	place-items: center;
	min-width: 40px;
	height: 40px;
	padding: 0 0.7rem;
	border-radius: 0;
	border: 1px solid var(--line);
	background: var(--bg-elev);
	color: var(--text-dim);
	text-decoration: none;
	font-size: 0.88rem;
	font-weight: 600;
}

.page-numbers:hover,
.page-numbers.current {
	background: var(--accent);
	border-color: var(--accent);
	color: var(--accent-ink);
	text-decoration: none;
}

.page-links {
	margin-top: 1.5rem;
	font-family: var(--font);
	font-size: 0.9rem;
	color: var(--text-dim);
}

/* =========================================================================
   SEARCH / EMPTY STATES
   ========================================================================= */

.search-form {
	display: flex;
	gap: 0.5rem;
	max-width: 420px;
}

.search-field {
	flex: 1;
	min-width: 0;
	padding: 0.62rem 0.9rem;
	border-radius: 0;
	border: 1px solid var(--line);
	background: var(--bg-elev);
	color: var(--text);
	font: inherit;
	font-size: 0.9rem;
}

.search-field::placeholder {
	color: var(--text-faint);
}

.search-field:focus {
	outline: none;
	border-color: var(--accent);
}

.search-submit {
	display: grid;
	place-items: center;
	width: 44px;
	border-radius: 0;
	border: 1px solid var(--line);
	background: var(--bg-elev);
	color: var(--text-dim);
	transition: color var(--ease), border-color var(--ease);
}

.search-submit:hover {
	color: var(--accent);
	border-color: var(--accent);
}

.search-submit svg {
	width: 17px;
	height: 17px;
}

.empty-state {
	max-width: var(--w-read);
	margin: 3rem auto;
	text-align: center;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.6rem;
}

.empty-code {
	margin: 0;
	font-size: clamp(3.5rem, 12vw, 6rem);
	line-height: 1;
	background: linear-gradient(135deg, var(--accent), var(--accent-2));
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}

.empty-title {
	margin: 0;
	font-size: 1.25rem;
	font-weight: 750;
	color: var(--text);
}

.empty-sub {
	margin: 0 0 0.8rem;
	color: var(--text-faint);
}

/* =========================================================================
   FOOTER
   ========================================================================= */

.site-footer {
	border-top: 1px solid var(--line);
	background: var(--bg-elev);
	margin-top: 2rem;
}

.footer-inner {
	max-width: var(--w-page);
	margin: 0 auto;
	padding: 3.25rem 1.5rem 1.75rem;
}

.footer-cols {
	display: grid;
	grid-template-columns: minmax(0, 1.7fr) repeat(3, minmax(0, 1fr));
	gap: 2.5rem;
	padding-bottom: 2.25rem;
	border-bottom: 1px solid var(--line);
}

.footer-col {
	min-width: 0;
}

.footer-brand-name {
	font-size: 1.2rem;
	letter-spacing: -0.025em;
	margin: 0 0 0.6rem;
	color: var(--text);
}

.footer-about {
	color: var(--text-dim);
	font-size: 0.92rem;
	max-width: 40ch;
	margin-bottom: 1.1rem;
}

.footer-heading {
	font-size: 0.72rem;
	letter-spacing: 0.15em;
	text-transform: uppercase;
	color: var(--text-faint);
	font-weight: 750;
	margin: 0 0 0.95rem;
}

.footer-links,
.footer-contact {
	list-style: none;
	margin: 0;
	padding: 0;
}

.footer-links li {
	margin-bottom: 0.15rem;
}

.footer-links a {
	display: inline-block;
	padding: 0.35rem 0;
	color: var(--text-dim);
	font-size: 0.9rem;
	text-decoration: none;
	transition: color var(--ease);
}

.footer-links a:hover {
	color: var(--accent);
}

.footer-contact li {
	display: flex;
	align-items: flex-start;
	gap: 0.55rem;
	margin-bottom: 0.75rem;
	color: var(--text-dim);
	font-size: 0.9rem;
	line-height: 1.45;
}

.footer-contact svg {
	width: 16px;
	height: 16px;
	flex-shrink: 0;
	margin-top: 0.18rem;
	color: var(--text-faint);
}

.footer-contact a {
	display: inline-block;
	padding: 0.15rem 0;
	color: var(--text-dim);
	text-decoration: none;
	word-break: break-word;
}

.footer-contact a:hover {
	color: var(--accent);
}

.footer-social {
	margin-top: 0.25rem;
}

.footer-widget-area {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 1.5rem;
	padding: 1.75rem 0;
	border-bottom: 1px solid var(--line);
}

.footer-widget {
	background: none;
	border: 0;
	padding: 0;
}

.footer-bottom {
	padding-top: 1.5rem;
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 1rem;
	flex-wrap: wrap;
	font-size: 0.84rem;
	color: var(--text-faint);
}

.footer-bottom-text {
	display: flex;
	align-items: center;
	gap: 0.55rem;
	flex-wrap: wrap;
}

.footer-bottom-text .footer-tagline::before {
	content: "•";
	margin-right: 0.55rem;
	opacity: 0.5;
}

.back-to-top {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
	color: var(--text-faint);
	font-size: 0.82rem;
	font-weight: 600;
	text-decoration: none;
	transition: color var(--ease);
}

.back-to-top:hover {
	color: var(--accent);
	text-decoration: none;
}

.back-to-top svg {
	width: 15px;
	height: 15px;
}

/* =========================================================================
   RESPONSIVE
   ========================================================================= */

/* Below 1400px: drop the left ad rail. */
@media (max-width: 1400px) {
	.site-main {
		grid-template-columns: minmax(0, 1fr) var(--w-side);
	}

	.sidebar-left {
		display: none;
	}
}

/* Below 1200px: four footer columns are too tight. */
@media (max-width: 1200px) {
	.footer-cols {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 2rem 2.5rem;
	}

	.footer-col-brand {
		grid-column: 1 / -1;
	}
}

/* Below 1100px: single column, both ad rails hidden. */
@media (max-width: 1100px) {
	.site-main {
		grid-template-columns: minmax(0, 1fr);
		gap: 0;
	}

	.sidebar-right {
		display: none;
	}
}

/* Tablet and below: nav collapses behind the hamburger. */
@media (max-width: 900px) {
	.header-bar {
		padding: 0.75rem 1rem;
	}

	.nav-toggle {
		display: flex;
	}

	/* Socials move from the masthead into the menu panel. */
	.header-social {
		display: none;
	}

	.site-nav .nav-social {
		display: flex;
		justify-content: center;
		padding: 0 1rem 1rem;
	}

	.site-nav {
		display: none;
		border-top: 1px solid var(--line);
	}

	.site-nav.is-open {
		display: block;
		animation: glhf-slide 0.2s ease;
	}

	.nav-menu {
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		padding: 0.5rem 1rem;
	}

	.nav-menu li {
		border-bottom: 1px solid var(--line-soft);
	}

	.nav-menu li:last-child {
		border-bottom: 0;
	}

	.nav-menu a {
		display: block;
		padding: 0.85rem 0.25rem;
		font-size: 0.95rem;
	}
}

@keyframes glhf-slide {
	from {
		opacity: 0;
		transform: translateY(-6px);
	}
}

@media (max-width: 720px) {
	.site-main {
		padding: 1.5rem 1rem 3rem;
	}

	.card-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 0.9rem;
	}

	/*
	 * Phones are where most of this traffic is, and this was the smallest type
	 * in the design: 12.48px, in a 172px card, for the one element that has to
	 * do the work. 16px costs about 13% of the card's height and buys 28% more
	 * headline, which on the primary element is a trade worth making twice.
	 *
	 * Two cards still sit side by side. At this width that is the limit — a
	 * larger headline than this starts breaking mid-word.
	 */
	.card-title {
		font-size: 1rem;
	}

	.card-body {
		padding: 0.65rem 0.7rem 0.7rem;
	}

	.card-meta {
		font-size: 0.65rem;
		padding-right: 1.7rem;
	}

	.card-arrow {
		width: 20px;
		height: 20px;
		right: 0.5rem;
		bottom: 0.55rem;
	}

	.card-arrow svg {
		width: 11px;
		height: 11px;
	}


	.js-article + .js-article {
		margin-top: 2.5rem;
		padding-top: 2.5rem;
	}

	.post-nav {
		grid-template-columns: 1fr;
	}

	.footer-inner {
		padding: 2.5rem 1rem 1.5rem;
	}

	.footer-cols {
		grid-template-columns: minmax(0, 1fr);
		gap: 1.9rem;
	}

	.footer-bottom {
		flex-direction: column;
		align-items: flex-start;
		gap: 0.75rem;
	}
}

@media (max-width: 520px) {
	.logo-tagline {
		letter-spacing: 0.1em;
		font-size: 0.62rem;
	}

	.section-title {
		font-size: 1.3rem;
	}

	.article-share {
		gap: 0.4rem;
	}

	.share-btn span {
		display: none;
	}

	.share-btn {
		padding: 0.55rem;
	}

	.share-label {
		width: 100%;
		margin-bottom: 0.2rem;
	}
}

@media (max-width: 720px) {
	/* Bigger tap targets where thumbs are the input device. */
	.footer-links a {
		padding: 0.55rem 0;
	}

	.footer-contact li {
		margin-bottom: 0.5rem;
	}

	.footer-contact a {
		padding: 0.35rem 0;
	}
}

/* Only the narrowest phones drop to a single column — two 9:16 tiles side by
   side shows far more of the feed per screen than one giant card. */
@media (max-width: 359px) {
	.card-grid {
		grid-template-columns: minmax(0, 1fr);
	}

	/* One card to a row here, so the headline has the whole width to use. */
	.card-title {
		font-size: 1.15rem;
	}

	.header-bar {
		gap: 0.5rem;
	}

	.stream-end {
		padding-inline: 0;
	}
}

/* =========================================================================
   WORDPRESS CORE CLASSES
   ========================================================================= */

.alignleft {
	float: left;
	margin: 0.4rem 1.4rem 1rem 0;
}

.alignright {
	float: right;
	margin: 0.4rem 0 1rem 1.4rem;
}

.aligncenter {
	margin-inline: auto;
}

.alignwide {
	width: min(100vw - 3rem, 980px);
	margin-inline: calc(50% - min(50vw - 1.5rem, 490px));
	max-width: none;
}

.sticky .card {
	border-color: var(--accent);
}

.wp-caption {
	max-width: 100%;
}

.wp-caption-text {
	font-size: 0.8rem;
	color: var(--text-faint);
	text-align: center;
}

.says,
.bypostauthor {
	display: initial;
}

/* =========================================================================
   FOOTER CALLS TO ACTION
   ========================================================================= */

.footer-cta {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
	margin-top: 1.15rem;
}

.footer-btn {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.62rem 0.95rem;
	border: 1px solid var(--border);
	border-radius: 0;
	font-size: 0.82rem;
	font-weight: 700;
	letter-spacing: 0.01em;
	text-decoration: none;
	color: var(--text);
	background: var(--bg-elev);
	transition: background var(--ease), border-color var(--ease), color var(--ease), transform var(--ease);
}

.footer-btn svg {
	width: 17px;
	height: 17px;
	flex: none;
}

.footer-btn:hover {
	transform: translateY(-2px);
	text-decoration: none;
}

.footer-btn-pitch:hover {
	border-color: var(--accent);
	color: var(--accent);
}

/*
 * The coffee button is the warm one on purpose. Two identical outlined buttons
 * side by side read as one control split in half; giving support a filled
 * treatment separates "tell us something" from "chip in".
 */
.footer-btn-coffee {
	background: var(--label);
	border-color: var(--label);
	color: var(--accent-ink);
}

.footer-btn-coffee:hover {
	background: var(--brand);
	border-color: var(--brand);
	color: var(--brand-ink);
}

/* =========================================================================
   COVER YOUR STORY
   ========================================================================= */

.pitch-page {
	max-width: 44rem;
	margin: 0 auto;
}

.pitch-intro {
	margin-bottom: 2rem;
}

.pitch-form {
	display: grid;
	gap: 1.15rem;
}

.pitch-grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 1.15rem;
}

.pitch-field {
	margin: 0;
	display: grid;
	gap: 0.35rem;
	min-width: 0;
}

.pitch-field label,
.pitch-types legend {
	font-size: 0.78rem;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--text-muted);
}

.pitch-req {
	color: var(--accent);
}

.pitch-field input[type="text"],
.pitch-field input[type="email"],
.pitch-field input[type="tel"],
.pitch-field input[type="url"],
.pitch-field textarea {
	width: 100%;
	padding: 0.7rem 0.8rem;
	background: var(--bg-elev);
	border: 1px solid var(--border);
	border-radius: 0;
	color: var(--text);
	font: inherit;
	font-size: 0.95rem;
}

.pitch-field textarea {
	resize: vertical;
	min-height: 9rem;
	line-height: 1.6;
}

.pitch-field input:focus,
.pitch-field textarea:focus {
	outline: 2px solid var(--accent);
	outline-offset: 1px;
	border-color: transparent;
}

.pitch-hint {
	font-size: 0.78rem;
	color: var(--text-faint);
	text-transform: none;
	letter-spacing: 0;
	font-weight: 400;
}

.pitch-types {
	border: 0;
	padding: 0;
	margin: 0;
	display: grid;
	gap: 0.4rem;
}

.pitch-choice {
	display: flex !important;
	align-items: center;
	gap: 0.55rem;
	padding: 0.6rem 0.75rem;
	background: var(--bg-elev);
	border: 1px solid var(--border);
	cursor: pointer;
	text-transform: none !important;
	letter-spacing: 0 !important;
	font-weight: 500 !important;
	font-size: 0.92rem !important;
	color: var(--text) !important;
}

.pitch-choice:hover {
	border-color: var(--accent);
}

.pitch-consent label {
	display: flex !important;
	align-items: flex-start;
	gap: 0.55rem;
	text-transform: none;
	letter-spacing: 0;
	font-weight: 400;
	font-size: 0.9rem;
	color: var(--text-muted);
}

/*
 * The honeypot. Off-screen rather than display:none — some bots skip anything
 * that is not rendered, and this needs to look reachable to a machine while
 * being unreachable to a person. aria-hidden and tabindex="-1" on the field
 * keep it away from screen readers and keyboard users.
 */
/*
 * The honeypot, off screen.
 *
 * Both class names, because the markup moved into a shared helper and the
 * stakes of missing one are absurd for a styling rule: an unhidden honeypot is
 * a visible "leave this field empty" box that real people dutifully fill in,
 * and every one of them is then silently discarded by the trap. The form would
 * appear to work perfectly and quietly bin its best enquiries.
 */
.pitch-hp,
.glhf-hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.pitch-actions {
	margin: 0;
}

.btn-pitch {
	font-size: 0.95rem;
	padding: 0.85rem 1.6rem;
}

.pitch-note {
	padding: 1rem 1.15rem;
	border-left: 3px solid var(--accent);
	background: var(--bg-elev);
}

.pitch-note h2 {
	margin: 0 0 0.4rem;
	font-size: 1.25rem;
}

.pitch-note p:last-child,
.pitch-note ul:last-child {
	margin-bottom: 0;
}

.pitch-note-bad {
	border-left-color: var(--alert);
}

.pitch-note-bad ul {
	margin: 0.4rem 0 0;
	padding-left: 1.1rem;
}

@media (max-width: 640px) {
	.pitch-grid {
		grid-template-columns: 1fr;
	}
}

/*
 * The pitch button is the page's whole purpose, so it gets the accent rather
 * than the same outline as a "back to top" link.
 */
.btn-pitch {
	background: var(--accent);
	border-color: var(--accent);
	/*
	 * --accent-ink, not black.
	 *
	 * The literal came from the dark theme, where the accent was a bright
	 * blue and black on it read fine. On the light theme's #1f5fa8 that same
	 * black measures 3.26 — under AA for a button label — while the token
	 * gives white at 6.44. The token exists precisely so this flip happens on
	 * its own, and the one rule that ignored it is the one that broke.
	 */
	color: var(--accent-ink);
}

.btn-pitch:hover {
	background: var(--bg);
	border-color: var(--bg);
	color: var(--text);
}

/* =========================================================================
   THE HERO
   ========================================================================= */

/*
 * The masthead statement: two lines of very large condensed type, the second
 * in the brand orange, over a hairline rule and the news below.
 *
 * Sized in clamp() against the viewport rather than at breakpoints, because
 * the whole point of a hero like this is that it fills the width — a fixed
 * size either overflows a phone or looks timid on a desktop.
 */
.hero {
	padding: clamp(2.5rem, 7vw, 5.5rem) 0 clamp(1.75rem, 4vw, 3rem);
	border-bottom: 1px solid var(--line);
	margin-bottom: var(--gap);
}

.hero-title {
	margin: 0;
	font-family: var(--font-display);
	font-weight: 400;
	text-transform: uppercase;

	/*
	 * 1.04 rather than the usual heading 1.15. Anton's caps nearly fill its
	 * em box, so normal heading leading opens a visible gap between the two
	 * lines and the block stops reading as one statement.
	 */
	line-height: 1.04;
	letter-spacing: -0.005em;

	/* Floor 2.6rem so it still lands on a 320px phone; ceiling 7.5rem so it
	   does not become a billboard on a 27-inch monitor. */
	font-size: clamp(2.6rem, 9.2vw, 7.5rem);

	/* --hero-1 and --hero-2 are set inline only when the Customizer holds a
	   colour. Unset, the hero falls through to the theme tokens, so the
	   default is defined in one place rather than duplicated in PHP. */
	color: var(--hero-1, var(--text));
}

.hero-title span {
	display: block;
}

/* The line that carries the name. */
.hero-title .hero-accent {
	color: var(--hero-2, var(--brand-text));
}

.hero-sub {
	margin: clamp(0.9rem, 2vw, 1.4rem) 0 0;
	max-width: 46ch;
	color: var(--text-dim);
	font-size: clamp(0.9rem, 1.4vw, 1.05rem);
	line-height: 1.6;
}

/* Anton has no lowercase distinction worth keeping and no bold; asking for
   one gives a synthesised smear on some browsers. State the weight. */
.hero-title,
.hero-title span {
	font-synthesis: none;
}

@media (prefers-reduced-motion: no-preference) {
	.hero-title span {
		animation: hero-rise 0.5s cubic-bezier(0.2, 0.7, 0.3, 1) both;
	}

	.hero-title span:nth-child(2) {
		animation-delay: 0.08s;
	}

	@keyframes hero-rise {
		from {
			opacity: 0;
			transform: translateY(0.18em);
		}
	}
}

/* =========================================================================
   ADVERTISE WITH GLHF
   ========================================================================= */

/*
 * The landing page. Everything here paints with tokens, so it arrives in the
 * dark palette without a second set of rules — and tests/test-contrast.php
 * enforces that rather than trusting it.
 */

.advertise-page {
	padding-bottom: 3rem;
}

.advertise-article {
	max-width: var(--w-read);
	margin-inline: auto;
}

.advertise-head {
	padding: clamp(1.5rem, 4vw, 3rem) 0 clamp(1.25rem, 3vw, 2rem);
	border-bottom: 1px solid var(--line);
}

.advertise-eyebrow {
	margin: 0 0 0.5rem;
	color: var(--label);
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
}

/* The display face, like the homepage hero — this is the one other page that
   is a statement rather than an article. */
.advertise-title {
	margin: 0;
	font-family: var(--font-display);
	font-weight: 400;
	text-transform: uppercase;
	line-height: 1.04;
	letter-spacing: -0.005em;
	font-size: clamp(2.1rem, 6vw, 3.6rem);
	color: var(--text);
}

.advertise-intro {
	margin-top: 1rem;
	color: var(--text-dim);
	font-size: clamp(1rem, 1.5vw, 1.1rem);
	line-height: 1.65;
}

.advertise-intro p {
	margin: 0 0 0.85rem;
}

.advertise-head-actions {
	margin: 1.4rem 0 0;
}

.advertise-section {
	margin-top: clamp(2rem, 5vw, 3.25rem);
}

.advertise-note {
	margin: 0 0 1.25rem;
	color: var(--text-dim);
	line-height: 1.65;
}

/* ---- Why here ---- */

.advantage-grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 1px;
	background: var(--line);
	border: 1px solid var(--line);
}

/*
 * A one-pixel gap over a --line background, rather than borders on each cell.
 * Borders double up between neighbours and leave the outer edge a different
 * weight from the inner ones, which reads as a rendering fault at some zoom
 * levels and not at others.
 */
.advantage {
	background: var(--bg);
	padding: 1.1rem 1.15rem 1.25rem;
}

.advantage-title {
	margin: 0 0 0.4rem;
	font-size: 1rem;
	font-weight: 800;
	letter-spacing: -0.01em;
	color: var(--text);
}

.advantage-body {
	margin: 0;
	color: var(--text-dim);
	font-size: 0.92rem;
	line-height: 1.6;
}

/* ---- The formats accordion ---- */

.ad-formats {
	border: 1px solid var(--line);
}

.ad-format + .ad-format {
	border-top: 1px solid var(--line);
}

.ad-format-summary {
	display: flex;
	align-items: center;
	gap: 1rem;
	padding: 0.95rem 1.1rem;
	cursor: pointer;
	background: var(--bg-elev);
	transition: background var(--ease);

	/* The default triangle is replaced by the chevron below. list-style works
	   in Firefox; ::-webkit-details-marker is needed for Safari. */
	list-style: none;
}

.ad-format-summary::-webkit-details-marker {
	display: none;
}

.ad-format-summary:hover {
	background: var(--bg-elev-2);
}

/*
 * A visible focus ring on the summary. <details> is keyboard operable for
 * free, but only if you can see where you are — and the outline has to be
 * offset inwards or the neighbouring rows clip it.
 */
.ad-format-summary:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: -3px;
}

.ad-format-heading {
	display: grid;
	gap: 0.15rem;
	flex: 1 1 auto;
	min-width: 0;
}

.ad-format-name {
	font-weight: 800;
	font-size: 1.02rem;
	letter-spacing: -0.01em;
	color: var(--text);
}

.ad-format-lede {
	color: var(--text-dim);
	font-size: 0.88rem;
	line-height: 1.45;
}

.ad-format-mark {
	flex: 0 0 auto;
	width: 26px;
	height: 26px;
	display: grid;
	place-items: center;
	color: var(--text-dim);
	border: 1px solid var(--line);
	transition: transform var(--ease), color var(--ease), border-color var(--ease);
}

.ad-format-mark svg {
	width: 15px;
	height: 15px;
	display: block;
}

.ad-format[open] .ad-format-mark {
	transform: rotate(180deg);
	color: var(--accent);
	border-color: var(--accent);
}

.ad-format-body {
	padding: 0.2rem 1.1rem 1.25rem;
	color: var(--text-dim);
	line-height: 1.65;
}

.ad-format-body p {
	margin: 0.85rem 0 0;
}

.ad-format-best {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	align-items: baseline;
	margin-top: 1.1rem !important;
	padding-top: 0.85rem;
	border-top: 1px solid var(--line);
	font-size: 0.9rem;
}

.ad-format-best-label {
	color: var(--label);
	font-size: 0.7rem;
	font-weight: 700;
	letter-spacing: 0.1em;
	text-transform: uppercase;
}

@media (prefers-reduced-motion: no-preference) {
	.ad-format[open] .ad-format-body > * {
		animation: ad-format-in 0.24s ease both;
	}

	@keyframes ad-format-in {
		from {
			opacity: 0;
			transform: translateY(-0.3em);
		}
	}
}

/* ---- The enquiry form's extra pieces ---- */

.pitch-field-wide {
	grid-column: 1 / -1;
}

.pitch-field select {
	width: 100%;
	padding: 0.6rem 0.7rem;
	border-radius: 0;
	border: 1px solid var(--line);
	background: var(--bg-elev);
	color: var(--text);
	font: inherit;
	font-size: 0.95rem;
}

.pitch-field select:focus {
	outline: 2px solid var(--accent);
	outline-offset: -2px;
}

.advertise-formats {
	border: 1px solid var(--line);
	padding: 0.9rem 1rem 1rem;
	margin: 0;
	min-width: 0;
}

.advertise-formats legend {
	padding: 0 0.4rem;
	color: var(--text-dim);
	font-size: 0.78rem;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

/*
 * These are choices, not field labels.
 *
 * .pitch-field label is uppercase, bold and letter-spaced, which is right for
 * "YOUR NAME" above an input and wrong for a list of five options — rendered
 * that way the checkboxes read as five separate form sections being shouted at
 * you. The properties are restated rather than the selector loosened, because
 * the field labels around them still want the original treatment.
 *
 * Two classes deep, and it has to be: .pitch-field label is (0,1,1) and a bare
 * .advertise-check is (0,1,0), so the first attempt at this rule lost the
 * cascade and changed nothing at all. Caught by looking at the render.
 */
.advertise-formats .advertise-check {
	display: flex;
	align-items: flex-start;
	gap: 0.55rem;
	padding: 0.3rem 0;
	color: var(--text);
	font-size: 0.95rem;
	font-weight: 400;
	letter-spacing: normal;
	text-transform: none;
	cursor: pointer;
}

.advertise-formats .advertise-check input {
	margin-top: 0.2rem;
	flex: 0 0 auto;
	accent-color: var(--accent);
}

.advertise-direct {
	margin-top: 1.5rem;
	color: var(--text-dim);
	font-size: 0.95rem;
}

@media (max-width: 720px) {
	.ad-format-summary {
		padding: 0.85rem 0.9rem;
		gap: 0.7rem;
	}

	.ad-format-body {
		padding: 0.2rem 0.9rem 1.1rem;
	}
}
