/*
Theme Name:   PatchNoteHub
Description:  Standalone editorial theme for PatchNoteHub — a gaming patch-notes/tier-list blog. Formerly a child theme of Newspaper (tagDiv); now fully independent, no parent theme required.
Author:       PatchNoteHub
Version:      1.10.2
Text Domain:  newspaper-child
*/

/* Custom overrides go below this line */

/* Standard WP visually-hidden-but-accessible utility — used on the mobile
	nav toggle button's label (header.php) since its accessible name is
	conveyed via this text plus the decorative hamburger glyph, not visible
	text. Un-hidden on keyboard focus per the usual convention (WP core's
	own default.css / _s underscores.js pattern) so a focused element inside
	it is never invisible. */
.screen-reader-text {
	border: 0;
	clip: rect(1px, 1px, 1px, 1px);
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	width: 1px;
	word-wrap: normal !important;
}

.screen-reader-text:focus {
	background-color: var(--pnh-surface);
	border-radius: var(--pnh-radius);
	box-shadow: var(--pnh-shadow);
	clip: auto !important;
	clip-path: none;
	color: var(--pnh-text);
	display: block;
	font-size: 14px;
	font-weight: 700;
	height: auto;
	left: 5px;
	line-height: normal;
	padding: 10px var(--pnh-space-2); /* 14px was near-16px, tokenized; 10px left as-is */
	text-decoration: none;
	top: 5px;
	width: auto;
	z-index: 100000;
}

/* Ad zones — real AdSense unit markup (functions.php pnh_ad_zone()), dims
	reserved to standard IAB sizes to avoid CLS. The reserved width/min-height
	below live on this wrapper, not on the `<ins>` itself, so CLS stays at 0
	whether or not the AdSense script actually runs (see functions.php doc
	comment on pnh_ad_zone()). */
.ad-zone {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	margin: var(--pnh-space-3) auto; /* was 24px — exact match, tokenized */
	/* Tokenized (was hardcoded #f4f5f7/#c8ccd2) so the reserved ad slot
		flips to the dark surface/border tokens instead of staying a pale
		gray-on-gray box against a near-black page — the tokens aren't
		defined yet at this point in the file, but custom properties resolve
		at computed-value time, not stylesheet source order, so that's fine. */
	background: var(--pnh-surface-alt);
	border: 1px dashed var(--pnh-border);
	overflow: hidden;
	box-sizing: content-box;
}

.ad-zone__label {
	font-size: 11px;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--pnh-text-muted);
}

/* `<ins class="adsbygoogle">` has no default display value of its own
	(browsers treat `<ins>` as inline) — Google's own AdSense snippet sets
	`display:block` on it inline; doing it here as a class rule instead keeps
	this theme's no-inline-styles rule intact for the same effect. */
.ad-zone .adsbygoogle {
	display: block;
	width: 100%;
}

.ad-zone--header,
.ad-zone--footer {
	width: 728px;
	max-width: 100%;
	min-height: 90px;
}

/* Full-width centered block between paragraphs, NOT floated beside the text.
	This used to be `float: right`, which nothing cleared — so on short posts
	the float escaped .pnh-single__content entirely and rendered on top of the
	comment form. A block-level unit in normal flow can't overlap anything,
	and it also keeps the body text at a single consistent measure instead of
	squeezing a few paragraphs into a narrow column beside the ad. */
.ad-zone--in-content {
	width: 100%;
	min-height: 300px;
	margin: var(--pnh-space-4) auto;
	clear: both;
}

.ad-zone--sidebar {
	width: 300px;
	max-width: 100%;
	min-height: 250px;
}

/* Standard IAB skyscraper. */
.ad-zone--rail-left,
.ad-zone--rail-right {
	width: 160px;
	min-height: 600px;
}

/* --------------------------------------------------------------------
	Article side rails (single.php only) — sticky skyscraper ads in the
	blank space either side of the centered article column.

	Grid, not two independently-fixed-positioned elements: `position: fixed`
	rails would float over the footer once the (short) article column
	finishes scrolling past them, since a fixed element has no relationship
	to where the article content ends. Making `.pnh-single-rails` a 3-column
	grid as tall as the article itself, with `position: sticky` on the rails
	inside it, means each rail is confined to — and stops scrolling at the
	bottom of — its own grid row's height, i.e. exactly as long as the
	center column's real content, with zero JS.

	Hidden below 1600px: at 1200px (--pnh-container-width) the leftover
	space per side is (viewport - 1200) / 2, so a 160px rail plus a
	comfortable 24px clearance from the content column needs at least
	2 * (160 + 24) = 368px of extra width, i.e. viewport >= ~1568px. 1600px
	rounds that up with a little headroom rather than clipping right at the
	measured minimum.
	-------------------------------------------------------------------- */
/* Hidden by default (below 1600px, .pnh-single-rails isn't a grid, so these
	would otherwise stack as plain blocks above/below the article). */
.pnh-single-rails__rail {
	display: none;
}

@media (min-width: 1600px) {
	.pnh-single-rails__rail {
		display: block;
	}

	.pnh-single-rails {
		display: grid;
		grid-template-columns: minmax(0, 1fr) minmax(0, var(--pnh-container-width)) minmax(0, 1fr);
		align-items: start;
		column-gap: var(--pnh-space-3);
	}

	.pnh-single-rails .pnh-single {
		/* The grid's center column is already exactly --pnh-container-width
			wide, so .pnh-single's own max-width/centering (needed at every
			narrower breakpoint where this grid doesn't apply) would just be
			redundant here, not conflicting — left as-is, no override needed. */
		grid-column: 2;
	}

	.pnh-single-rails__rail {
		position: sticky;
		top: var(--pnh-space-4);
		justify-self: center;
	}

	.pnh-single-rails__rail--left {
		grid-column: 1;
		grid-row: 1;
	}

	.pnh-single-rails__rail--right {
		grid-column: 3;
		grid-row: 1;
	}
}

@media (max-width: 780px) {
	.ad-zone--header,
	.ad-zone--footer {
		width: 320px;
		min-height: 50px;
	}

	.ad-zone--in-content {
		min-height: 250px;
		margin: var(--pnh-space-3) auto;
	}
}

/* ==========================================================================
	PatchNoteHub — red/neutral design tokens (design-system.md, Phase 2
	sign-off — see tokens.css at the repo root for the reference artifact
	this block wires in)
	==========================================================================
	Replaces the previous dark navy/blue/pitch-green/magenta/gold palette
	(PALETTE.md/LAYOUT.md — both explicitly marked SUPERSEDED) with a
	light-first reading surface + a single red accent scale, per
	design-system.md §2. Every color below is still a custom property under
	the SAME --pnh-* names the templates already reference (header/nav,
	buttons/links, category badges, post cards, footer) — only the values
	changed, so no template/component markup needed to be rewired for this
	pass. Contrast checked against WCAG 2.1 AA (4.5:1 body text, 3:1 large
	text/UI) — see design-system.md §2's contrast table for the exact
	pairing each value below was validated against; nothing here uses a
	pairing that isn't in that table.
	========================================================================== */
:root {
	/* Native UI (scrollbars, form controls, canvas before paint) follows the
		theme — without this, native widgets stay light-styled on a dark page
		(design-system.md §9.3). */
	color-scheme: light;

	/* base surfaces — light reading surface is primary (design-system.md §2).
		--pnh-surface/--pnh-surface-alt/--pnh-border now back masthead, cards,
		hero and the ticker — all light. The footer is the one deliberate
		exception: design-system.md scopes a dark surface to "the secondary
		game-filter bar and footer only", so .pnh-footer overrides these with
		its own --pnh-dark-* values below instead of reusing these tokens. */
	--pnh-bg: #FAFAFB;          /* page background — gray-50 */
	--pnh-surface: #FFFFFF;     /* card/header panels — white, 16.86:1 text contrast */
	--pnh-surface-alt: #F4F5F7; /* hover state / secondary panel — gray-100 */
	--pnh-border: #E1E3E7;      /* decorative dividers/borders only — gray-300, not relied on for required contrast */

	/* Nav surface — its own token (design-system.md §8.6), deliberately NOT
		aliased inline to --pnh-surface, so this one value can be retuned
		without touching every other white card/panel on the site. Phase 3 QA
		checkpoint applied: plain white (matching --pnh-surface) read as
		completely flush against the gray-50 page on a real rendered
		screenshot — no visible tone step at all beyond the 1px border. Per
		design-system.md §8.6's explicit fallback instruction, stepped to
		gray-100 (same hex already used for --pnh-surface-alt) so the nav
		reads as a deliberate, distinct band. Dark mode does NOT get this
		retune — see the dark-mode override further down, which resets this
		back to --pnh-surface (gray-900, matching design-system.md §8.5's
		dark --color-bg-nav = --color-bg-surface exactly). */
	--pnh-bg-nav: var(--pnh-surface-alt);

	/* text */
	--pnh-text: #1B1D22;        /* primary text — gray-900, 16.86:1 on white / 16.16:1 on --pnh-bg */
	--pnh-text-muted: #5E626B;  /* meta/dates/excerpts — gray-600, 6.11:1 on white / 5.86:1 on --pnh-bg */

	/* accents — one systematic 3-color set, reused across all 8 categories
		(see pnh_get_category_accent() in functions.php) rather than a unique
		color per category. Per the task brief, the old 4-hue rotation
		(blue/pitch-green/magenta/gold) is replaced with red/neutral
		variations instead of unrelated hues — every fill below is validated
		in design-system.md's contrast table as a solid badge/button fill
		with white label text. */
	--pnh-accent-primary: #C81E2C; /* red-600 — primary accent: links, buttons, active nav/filter state, category label. 5.71:1 on white */
	--pnh-accent-pitch: #1B1D22;   /* gray-900 — neutral dark variation for the category rotation's 2nd slot. 16.86:1 on white (as a text/fill contrast partner) */
	--pnh-accent-cta: #A6141F;     /* red-700 — deeper red variation for the category rotation's 3rd slot / hover-pressed accent. 7.66:1 on white */
	--pnh-accent-hover: #A6141F;   /* red-700 — hover/pressed state for the primary accent (buttons/links), design-system.md §2 */
	--pnh-chip-bg: #FDEAEC;        /* red-100 — quiet tint background for the in-card category chip (design-system.md §6's patch-card spec: solid badges are for hero/article context, cards get a softer tint). 14.58:1 gray-900-on-tint / 6.62:1 red-700-on-tint */
	--pnh-chip-ink: #A6141F;       /* red-700 — label color on --pnh-chip-bg */
	--pnh-accent-neutral: #4B4F58; /* gray-700 — quiet secondary marker (see .pnh-badge--version below), 8.21:1 on white.
		Replaces the old --pnh-accent-gold (renamed — this file is its only consumer, no template touches this
		property name directly). Reads as a deliberately quieter marker next to the solid-fill category badge,
		same scoping rule as before: NOT part of pnh_get_category_accent()'s primary/pitch/cta rotation. */
	--pnh-on-accent: #FFFFFF;      /* white text placed on top of the accent fills above — all fills are now
		dark/red, so the previous "dark text on a bright fill" pairing is inverted to white-on-dark/red */

	/* Accent as INK — mode-aware, kept separate from the accent-primary/
		-hover/-cta FILL tokens above, which stay byte-identical in both
		themes (a red-600 button/badge fill never changes, design-system.md
		§8.1 rule 1). #C81E2C measures 2.95:1 on the dark card/nav surface and
		3.23:1 on the dark page background — both fail AA (4.5:1 text, 3:1
		non-text/UI) — so anything using red as ink (link text, icon stroke,
		underline/focus indicators) resolves through this token instead of
		--pnh-accent-primary directly (design-system.md §8.4). Light-mode
		value is identical to --pnh-accent-primary; only the dark override
		further down (and its @media fallback) changes it, to red-400. */
	--pnh-accent-ink: var(--pnh-accent-primary);

	/* Icon default color — a step quieter than --pnh-text, design-system.md
		§8.5's dedicated icon tier (gray-500 light / gray-400 dark) rather
		than reusing full-strength body text color for every glyph. */
	--pnh-text-icon: #71757E; /* gray-500 — 4.62:1 on white */

	/* Focus-ring glow color as raw r,g,b so the comment-form's rgba()
		box-shadow can share the same mode-aware swap as --pnh-accent-ink
		without duplicating the logic in its own custom property. */
	--pnh-focus-ring-rgb: 200, 30, 44; /* red-600 */

	/* Dark surface — scoped ONLY to .pnh-footer (design-system.md §2: "a dark
		surface is scoped to the secondary game-filter bar and footer only —
		never forced as a full-site dark theme"). This theme has no separate
		game-filter-bar component today, so these three are only consumed by
		the footer rules further down. Deliberately not using
		--pnh-accent-primary/--pnh-accent-cta (red-600/red-700) for text or
		hover states against this background — neither pairing is in
		design-system.md's contrast table for a dark surface, only red-400
		is (and only for large text/icons, which nothing in the footer is). */
	--pnh-dark-surface: #1B1D22;      /* gray-900 */
	--pnh-dark-text: #FFFFFF;         /* white — 16.86:1 on gray-900 */
	--pnh-dark-text-muted: #C7C9CF;   /* on-dark-secondary — 10.18:1 on gray-900 */

	--pnh-radius: 6px;
	--pnh-shadow: 0 10px 24px rgba(27, 29, 34, 0.12); /* softened for a light UI — design-system.md's card
		spec calls for "no drop shadow by default... flat, deliberately lower visual weight than the current
		theme"; kept as a subtle lift for hover/focus states (.pnh-card:hover, skip-link popup) rather than
		removed outright, since removing the hover mechanic entirely is a component-behavior change beyond
		this pass's color-migration scope — this is a value change only, same custom property, same callers. */

	/* Single container standard — was three independent hardcoded
		`max-width: 1200px` declarations (masthead, footer, #main) before this;
		now one token + one .pnh-container class applied everywhere a
		max-width/centered/gutter-padded content column is needed. */
	--pnh-container-width: 1200px;
	--pnh-gutter: 24px;

	/* 8px spacing scale — still used by nav/footer/card/ticker spacing
		site-wide (unchanged in this pass; provenance note below is historical,
		not a live constraint on these values going forward). Article-specific
		spacing (single.php's heading/paragraph rhythm) uses its own dedicated
		--pnh-article-space-* scale further down instead of this one — the two
		scales use different step sizes (this one has no 4px/12px/64px steps)
		and this scale's existing consumers (nav/footer/card/ticker) are
		outside this pass's declared article-template scope, so remapping
		them onto design-system.md's spacing scale wasn't done here. */
	--pnh-space-1: 8px;
	--pnh-space-2: 16px;
	--pnh-space-3: 24px;
	--pnh-space-4: 32px;
	--pnh-space-5: 40px;
	--pnh-space-6: 48px;

	/* Reserved height for the fixed/overlay front-page header (see
		.pnh-masthead--overlay + body.pnh-overlay-header below) — approximates the
		real rendered height of .pnh-masthead__inner's 12px top/bottom padding plus
		its tallest child. Not pixel-exact to whatever the header happens to render
		at, just close enough that the reserved gap doesn't visibly clip or leave a
		large mismatch. */
	--pnh-header-height: 80px;

	/* System font stacks only (no webfonts, no third-party font request) —
		matches design-system.md §3 exactly (tokens.css --font-sans/--font-serif).
		"Manrope" was dropped from the sans stack here since design-system.md's
		signed-off stack doesn't include it — every remaining fallback is a
		real, always-available OS/browser default, so there's no
		flash-of-unstyled-text to guard against either. */
	--pnh-font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
	--pnh-font-serif: Charter, "Bitstream Charter", "Sitka Text", Cambria, Georgia, serif;

	/* ---------------------------------------------------------------- */
	/* Typography — type scale (design-system.md §3 / tokens.css)       */
	/* Desktop (>=1024px) values live here; the <1024px breakpoint below */
	/* swaps in the compact heading sizes, and the true-mobile (<=767px) */
	/* breakpoint further down separately steps body text down to 17px  */
	/* (owner call after seeing 19px rendered on a real phone width —   */
	/* see that block's comment). Small/meta text stays fixed at every  */
	/* size — never was part of either step-down.                      */
	/* ---------------------------------------------------------------- */
	--pnh-text-h1-size: 2.625rem;   /* 42px */
	--pnh-text-h1-line: 1.15;
	--pnh-text-h1-weight: 800;

	--pnh-text-h2-size: 2.25rem;    /* 36px */
	--pnh-text-h2-line: 1.2;
	--pnh-text-h2-weight: 800;

	--pnh-text-h3-size: 1.5rem;     /* 24px */
	--pnh-text-h3-line: 1.3;
	--pnh-text-h3-weight: 700;

	--pnh-text-h4-size: 1.25rem;    /* 20px */
	--pnh-text-h4-line: 1.35;
	--pnh-text-h4-weight: 700;

	--pnh-text-body-size: 1.1875rem; /* 19px desktop/tablet — steps down to 17px on true mobile, see the 767px block below */
	--pnh-text-body-line: 1.7;       /* ~32px */

	--pnh-text-small-size: 0.875rem; /* 14px, meta/caption */

	/* Article measure: `ch` self-adjusts to the rendered font, targets the
		65-75ch readability band (design-system.md §3). */
	--pnh-measure-article: 70ch;

	/* ---------------------------------------------------------------- */
	/* Article spacing scale (design-system.md §4) — kept as its own     */
	/* dedicated set rather than remapped onto --pnh-space-1..6 above,   */
	/* which is a coarser, differently-stepped scale already relied on   */
	/* by nav/footer/card/ticker spacing outside this pass's scope.      */
	/* Reusing those names here and changing their values would silently */
	/* ripple spacing changes into those components too.                 */
	/* ---------------------------------------------------------------- */
	--pnh-article-space-paragraph: 16px;        /* paragraph -> paragraph */
	--pnh-article-space-heading-attach: 12px;   /* H2/H3 -> their own following content */
	--pnh-article-space-heading-attach-tight: 8px; /* H4 -> its own following content */
	--pnh-article-space-before-h4: 16px;        /* before a new H4 — same tier as paragraph rhythm, not a section break */
	--pnh-article-space-before-h3: 24px;        /* before a new H3 — sub-section break, 1.5x paragraph gap */
	--pnh-article-space-before-h2: 32px;        /* before a new H2 — major section break, 2x paragraph gap */
	--pnh-article-space-region: 64px;           /* major region separation: hero(incl. H1) -> entry-content, content -> comments */
	--pnh-article-space-image-gap: 16px;        /* hero image -> meta row -> H1, design-system.md §6 */
	--pnh-article-caption-gap: 8px;             /* image -> its caption */

	/* Hero image — universal 16:9 hard rule (design-system.md §5 / tokens.css). */
	--pnh-hero-aspect-ratio: 16 / 9;
}

/* ==========================================================================
	DARK MODE — semantic token overrides (design-system.md §8; Phase 3 wiring
	of tokens.css's dark block into this theme's own --pnh-* custom
	properties — see tokens.css at the repo root for the reference artifact
	these values were validated against). Only the values below change;
	every component rule elsewhere in this file already reads through these
	same --pnh-* names, so nothing else needed rewriting to "support" dark
	mode — it falls out of the token flip alone, except for the handful of
	selectors further down that were using --pnh-accent-primary for INK
	(text/icon/underline) instead of the new --pnh-accent-ink — those are
	called out individually where they occur.

	Applied by the inline no-FOUC boot script (functions.php
	pnh_theme_boot_script(), wp_head priority 0 — design-system.md §9.2),
	which sets data-theme="dark"|"light" on <html> before first paint.

	Deliberately NOT overridden here (mode-invariant FILLS, design-system.md
	§8.1 rule 1 — "a red-600 button/chip fill is byte-identical in both
	themes"): --pnh-accent-primary, --pnh-accent-hover, --pnh-accent-cta,
	--pnh-on-accent.

	!! Keep byte-identical with the @media (prefers-color-scheme: dark)
	!! fallback block immediately below — edit both or neither.
	========================================================================== */
:root[data-theme="dark"] {
	color-scheme: dark;

	--pnh-bg: #121418;          /* gray-950 — page background, deliberately not #000, design-system.md §8.2 */
	--pnh-surface: #1B1D22;     /* gray-900 — cards AND nav, one rule, design-system.md §8.6 */
	--pnh-surface-alt: #22252B; /* gray-850 */
	--pnh-border: #2C2F36;      /* gray-800 — decorative dividers only, still not for required contrast */
	/* Resets the light-mode-only gray-100 nav retune (see :root above) back
		to matching the card surface — design-system.md §8.5's dark
		--color-bg-nav is gray-900, the SAME value as --color-bg-surface,
		unlike the light-mode fallback. */
	--pnh-bg-nav: var(--pnh-surface);

	--pnh-text: #E8EAED;        /* gray-200 — 15.30:1 on page / 13.99:1 on surface */
	--pnh-text-muted: #A2A7B0;  /* gray-350 — 7.63:1 on page / 6.98:1 on surface */
	--pnh-text-icon: #8B909A;   /* gray-400 — 5.75:1 on page / 5.26:1 on surface */

	/* Accent as ink only — red-600 fails AA as text/icon/ring on both dark
		surfaces (design-system.md §8.4), so this is the one accent token
		that actually flips; the fills above (badges/buttons) stay
		red-600/red-700, byte-identical to light mode. */
	--pnh-accent-ink: #F0576A;  /* red-400 — 5.51:1 page / 5.04:1 surface */
	--pnh-focus-ring-rgb: 240, 87, 106; /* red-400, same swap as --pnh-accent-ink */

	--pnh-chip-bg: #421A1E;   /* red-900 — dark counterpart of red-100, design-system.md §8's chip pairing */
	--pnh-chip-ink: #FF8A96; /* red-300 — 6.67:1 on --pnh-chip-bg */

	/* --pnh-accent-neutral is ink+border only (the quiet .pnh-badge--version
		chip) — its light-mode value (gray-700) measures ~2:1 against a dark
		surface, well under AA, so it steps to the dark-mode meta-text tier
		instead (gray-350, already validated above at 6.98-7.63:1). */
	--pnh-accent-neutral: #A2A7B0;

	/* --pnh-accent-pitch is a solid FILL (.pnh-badge--pitch), not ink — but
		its light-mode value (gray-900) IS this theme's own dark card/nav
		surface color, so left unchanged it would render invisible against
		the surrounding dark UI. Not a value design-system.md's token table
		covers directly (it's a category-badge rotation color, not a semantic
		surface/text/accent alias) — judgement call: step it to gray-700,
		which keeps a solid, clearly-separated fill against every dark
		surface (gray-850/900/950) and still clears AA against white
		on-accent text (8.21:1, same pairing §2 already validates on white). */
	--pnh-accent-pitch: #4B4F58;

	/* Footer / secondary-chrome band steps UP so it stays a distinct band
		off both the page and the card/nav surface (design-system.md §8.5's
		--color-bg-chrome note). */
	--pnh-dark-surface: #22252B;    /* gray-850 */
	--pnh-dark-text: #E8EAED;       /* gray-200 — 12.74:1 on gray-850 */
	--pnh-dark-text-muted: #C7C9CF; /* unchanged — same token, both modes */

	/* Card-hover lift shadow: the light-mode value (a soft, low-opacity dark
		shadow) reads as almost nothing against an already-dark surface —
		darker/more opaque so the hover lift stays visible in dark mode too.
		Not in design-system.md's token table (elevation shadow isn't a
		contrast-checked pairing); a visual judgement call, not a contrast fix. */
	--pnh-shadow: 0 10px 24px rgba(0, 0, 0, 0.45);
}

/* ==========================================================================
	DARK MODE — prefers-color-scheme fallback. Covers the window before JS
	runs and the case where JS is disabled entirely, so the site still
	honours the OS preference on its own (design-system.md §9.2). The
	:not([data-theme="light"]) guard is LOAD-BEARING — it stops this block
	from overriding a user who explicitly chose light on a dark-preferring
	OS. Do not simplify it to a bare :root.

	!! Byte-identical to :root[data-theme="dark"] above by design.
	========================================================================== */
@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) {
		color-scheme: dark;

		--pnh-bg: #121418;
		--pnh-surface: #1B1D22;
		--pnh-surface-alt: #22252B;
		--pnh-border: #2C2F36;
		--pnh-bg-nav: var(--pnh-surface);

		--pnh-text: #E8EAED;
		--pnh-text-muted: #A2A7B0;
		--pnh-text-icon: #8B909A;

		--pnh-accent-ink: #F0576A;
		--pnh-focus-ring-rgb: 240, 87, 106;
		--pnh-chip-bg: #421A1E;
		--pnh-chip-ink: #FF8A96;
		--pnh-accent-neutral: #A2A7B0;
		--pnh-accent-pitch: #4B4F58;

		--pnh-dark-surface: #22252B;
		--pnh-dark-text: #E8EAED;
		--pnh-dark-text-muted: #C7C9CF;

		--pnh-shadow: 0 10px 24px rgba(0, 0, 0, 0.45);
	}
}

/* ------------------------------------------------------------------ */
/* Theme switching is INSTANT — no transition, in either direction. A  */
/* transition on color/background-color would animate FROM the wrong  */
/* theme on every page load, which is the exact flash artefact the    */
/* no-FOUC boot script exists to prevent (design-system.md §9.1).     */
/* Do not add one.                                                    */
/* ------------------------------------------------------------------ */

/* Compact typography overrides (mobile + tablet, single breakpoint at
	1024px — design-system.md §3 explains why one breakpoint, not two:
	inspection found tablet/mobile H1 sizes were identical in 3 of 4
	samples, one step down from desktop then flat). */
@media (max-width: 1023.98px) {
	:root {
		--pnh-text-h1-size: 1.75rem;  /* 28px */
		--pnh-text-h2-size: 1.625rem; /* 26px */
		--pnh-text-h3-size: 1.375rem; /* 22px */
		--pnh-text-h4-size: 1.125rem; /* 18px */
	}
}

/* --------------------------------------------------------------------
	Container standard — used by header.php, footer.php, front-page.php,
	archive.php, single.php, search.php, 404.php, page.php, index.php.
	One max-width + one gutter, instead of three separate hardcoded
	`max-width: 1200px` rules on three unrelated selectors.
	-------------------------------------------------------------------- */
.pnh-container {
	max-width: var(--pnh-container-width);
	margin-inline: auto;
	padding-inline: var(--pnh-gutter);
}

@media (max-width: 767px) {
	:root {
		--pnh-gutter: 16px;

		/* Body text DOES shrink on true mobile — overrides the "never
			shrinks" 19px set above. That rule came from the Phase-1
			inspection (reference sites hold ~18-22px across viewports), but
			seeing it actually rendered on a 343px-wide phone column showed
			the real problem the inspection didn't measure: at 19px/1.7,
			a 5-6 word line and ~32px line-height reads oversized and forces
			excess scrolling — a direct, concrete owner call once rendered
			beats the earlier inference. 17px keeps the same 1.7 line-height
			ratio (line-height recalculates automatically, it's unitless)
			and stays comfortably within any accessible-reading-size range;
			headings are untouched — the existing 1023.98px step-down for
			H1-H4 was itself backed by a specific inspection finding
			("tablet/mobile H1 sizes were identical in 3 of 4 samples") and
			looked correctly sized in the same screenshot that flagged the
			body-text problem. */
		--pnh-text-body-size: 1.0625rem; /* 17px */
	}
}

/* --------------------------------------------------------------------
	Base / links / focus
	-------------------------------------------------------------------- */
body {
	background-color: var(--pnh-bg);
	color: var(--pnh-text);
	font-family: var(--pnh-font-sans);
}

a:hover,
a:focus {
	color: var(--pnh-accent-ink); /* mode-aware ink, design-system.md §8.4 */
}

:focus-visible {
	/* Mode-aware ink token, not the fill (design-system.md §8.4 — red-600
		measures 2.95:1 on the dark card/nav surface, under the 3:1 non-text/
		UI threshold a focus ring needs). */
	outline: 2px solid var(--pnh-accent-ink);
	outline-offset: 2px;
}

/* --------------------------------------------------------------------
	Header / masthead — logo + primary nav (header.php)
	-------------------------------------------------------------------- */
.pnh-masthead {
	background-color: var(--pnh-bg-nav);
	border-bottom: 1px solid var(--pnh-border);
}

/* --------------------------------------------------------------------
	Front-page-only fixed/transparent-over-hero header (LAYOUT.md structural
	match — leagueoflegends.com's nav is fixed + transparent while the
	cinematic hero is in view, opaque once scrolled past). `.pnh-masthead--overlay`
	is only ever rendered on the front page (see header.php, is_front_page()) —
	single/archive/search/404/page templates keep the header as a normal,
	non-fixed, always-opaque bar exactly as before, since there's no hero
	underneath it to sit over there.

	Progressive enhancement, same pattern as .pnh-nav--js (nav-toggle.js):
	`.pnh-masthead--overlay` alone (no JS) is a perfectly usable fixed header
	with the SAME solid --pnh-surface background as the base rule above — a
	visitor with JS disabled never sees a permanently see-through/unreadable
	nav bar. Only assets/js/header-scroll.js's `.pnh-masthead--js` gate class
	(added once it confirms both this header and a real `.pnh-hero` element
	exist) unlocks the transparent-then-opaque swap, which it then drives by
	toggling `.pnh-masthead--transparent` / `.pnh-masthead--solid` via an
	IntersectionObserver on `.pnh-hero`.
	-------------------------------------------------------------------- */
.pnh-masthead--overlay {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: 100;
	transition: background-color 0.25s ease, border-color 0.25s ease;
}

.pnh-masthead--overlay.pnh-masthead--js.pnh-masthead--transparent {
	background-color: transparent;
	border-bottom-color: transparent;
}

.pnh-masthead--overlay.pnh-masthead--js.pnh-masthead--solid {
	background-color: var(--pnh-bg-nav);
	border-bottom-color: var(--pnh-border);
}

/* Reserves exactly the fixed header's height at the very top of the page so
	page content (ad-zone--header first, then #main) renders below it instead
	of being covered — the header floats transparently over this reserved
	band on load, not over the ad zone (see functions.php's `body_class`
	filter for where `.pnh-overlay-header` is added: front page only). */
body.pnh-overlay-header {
	padding-top: var(--pnh-header-height);
}

.pnh-masthead__inner {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--pnh-space-2); /* was 16px — exact match, tokenized */
	padding-block: 12px;
	/* Deliberately wider than the sitewide 1200px .pnh-container cap this
		element also carries as a class. The always-visible search + theme
		toggle icons (design-system.md §10.2 — present at every breakpoint,
		never move into the hamburger drawer) added ~90px to this row that
		didn't exist when the 1200px/1280px-breakpoint pair was originally
		tuned; tightening .pnh-nav/.pnh-nav__menu's gaps clawed back some of
		that but not all of it (measured: still ~69px short at 1300-1600px
		after tightening). Rather than crush nav-item spacing further or
		shrink the 44px icon tap targets below their spec'd size, the
		masthead row alone gets a bit more room — the hero/grid/footer below
		it keep the standard 1200px container untouched. This means the
		header reads ~100px wider than the body content at very large
		desktop widths (>=1348px viewport); a disclosed trade-off, not an
		oversight. */
	max-width: 1300px;
}

/* "The Patch Mark" — design-system.md §1. Inline SVG (pnh_logo_mark() in
	functions.php) + wordmark text, single link, single accessible name (see
	header.php's aria-label on .pnh-logo__link) — the mark and wordmark
	spans are both aria-hidden so a screen reader announces the logo once,
	not twice. Sized well under half the masthead's own height so it never
	dominates the header the way the old illustrative mascot mark did. */
.pnh-logo__link {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	text-decoration: none;
	color: var(--pnh-text);
}

.pnh-logo__mark {
	display: block;
	flex-shrink: 0;
	width: 32px;
	height: 32px;
	/* Mode-aware ink, not the fill (design-system.md §8.4: "the mark's three
		strokes must bind to mode-aware tokens... in dark mode the strokes
		render red-400"). */
	color: var(--pnh-accent-ink);
}

.pnh-logo__wordmark {
	font-size: 20px;
	font-weight: 700;
	line-height: 1;
	color: var(--pnh-text);
	white-space: nowrap;
}

.pnh-logo__wordmark-accent {
	color: var(--pnh-accent-ink); /* mode-aware ink, design-system.md §8.4 */
}

@media (max-width: 480px) {
	.pnh-logo__mark {
		width: 26px;
		height: 26px;
	}

	.pnh-logo__wordmark {
		font-size: 17px;
	}
}

.pnh-nav {
	display: flex;
	align-items: center;
	/* Tightened from 20px: adding the always-visible icon cluster (search +
		theme toggle, ~90px) to this row meant the previous gap value pushed
		total required width past what fits at the existing 1280px collapse
		breakpoint (measured: overflowed by ~100px+ at 1300/1600px before this
		change) — see the .pnh-masthead__inner max-width note further down for
		the other half of this fix. */
	gap: 12px;
}

/* --------------------------------------------------------------------
	Icon cluster — search, theme toggle, hamburger (design-system.md §10.2,
	§11). All three share one 44x44px tap-target button style; only the
	hamburger keeps a second, breakpoint-gated display rule layered on top
	(it's the one icon that's conditionally hidden — the other two are
	visible at every viewport, per spec).

	The hamburger's old CSS-drawn 3-bar glyph (::before/::after divs) is
	replaced with the same hand-built inline SVG set as search/theme-toggle
	(pnh_icon_hamburger() / pnh_icon_close() in functions.php) — a
	deliberate choice for visual consistency now that it sits directly next
	to two other SVG icon buttons in the same cluster (same stroke weight,
	same 20px-in-44px sizing, same hover/focus treatment). Its
	aria-expanded/aria-controls wiring and assets/js/nav-toggle.js are
	unchanged — only the glyph markup and its CSS moved.
	-------------------------------------------------------------------- */
.pnh-nav__icons {
	display: flex;
	align-items: center;
	gap: 2px;
	flex-shrink: 0;
}

.pnh-nav__icon-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 44px;
	height: 44px;
	padding: 0;
	border: none;
	border-radius: var(--pnh-radius);
	background-color: transparent;
	color: var(--pnh-text-icon);
	cursor: pointer;
	list-style: none; /* <summary>'s default marker triangle */
}

.pnh-nav__icon-btn::-webkit-details-marker {
	display: none;
}

.pnh-nav__icon-btn:hover,
.pnh-nav__icon-btn:focus-visible {
	color: var(--pnh-accent-ink);
	background-color: var(--pnh-surface-alt);
}

.pnh-icon {
	display: block;
}

/* Hamburger stays hidden by default (only shown <=1280px, see the media
	query further down) — the shared .pnh-nav__icon-btn rule above sets
	`display: inline-flex` unconditionally, so this has to win back to
	`none` for the default/no-JS state. Same specificity as the shared
	rule (one class each); wins on source order (declared after it). */
.pnh-nav__toggle {
	display: none;
}

/* Theme toggle ships `hidden` in the markup (header.php) until
	assets/js/theme-toggle.js confirms it can run (design-system.md §9.2/
	§9.4 — "no dead button without JS"). `[hidden]` is a UA-stylesheet rule,
	and author styles always win over UA styles regardless of specificity —
	so without this explicit override, the `display: inline-flex` above
	would defeat the whole point of shipping it hidden. */
.pnh-theme-toggle[hidden] {
	display: none;
}

/* Sun/moon pair — only one is ever visually present. Default (no
	data-theme attribute yet, or data-theme="light") shows the moon: "moon
	visible while the site is light" (design-system.md §9.1). */
.pnh-theme-toggle .pnh-icon--sun {
	display: none;
}

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

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

/* Hamburger <-> close swap, driven by the aria-expanded state
	assets/js/nav-toggle.js already sets — no extra JS needed for the icon
	swap itself (design-system.md §11.6: "always paired with an open state"). */
.pnh-nav__toggle .pnh-icon--close {
	display: none;
}

.pnh-nav__toggle[aria-expanded="true"] .pnh-icon--hamburger {
	display: none;
}

.pnh-nav__toggle[aria-expanded="true"] .pnh-icon--close {
	display: block;
}

/* Search <-> close swap, driven by the native <details>'s [open] state —
	no JS at all (see the doc comment on the <details class="pnh-nav__search">
	markup in header.php). */
.pnh-nav__search .pnh-icon--close {
	display: none;
}

.pnh-nav__search[open] .pnh-icon--search {
	display: none;
}

.pnh-nav__search[open] .pnh-icon--close {
	display: block;
}

/* Dynamic accessible name for the search disclosure ("Search" / "Close
	search"), the same pattern the theme toggle's aria-label uses — except
	here it's pure CSS, not JS, since <details>'s [open] attribute is
	already the state signal. */
.pnh-nav__search-label--open {
	display: none;
}

.pnh-nav__search[open] .pnh-nav__search-label--closed {
	display: none;
}

.pnh-nav__search[open] .pnh-nav__search-label--open {
	display: inline-block;
}

/* The search panel itself — same "absolute, full-width dropdown below the
	masthead row" technique as .pnh-nav__collapse's mobile panel further
	down, so the two look and behave consistently regardless of which one
	is open. Positions relative to .pnh-masthead__inner (nearest positioned
	ancestor, see .pnh-masthead__inner below), not the <details> itself. */
.pnh-nav__search-panel {
	position: absolute;
	top: 100%;
	left: 0;
	right: 0;
	z-index: 10;
	padding: var(--pnh-space-2) 20px;
	background-color: var(--pnh-bg-nav);
	border-bottom: 1px solid var(--pnh-border);
}

.pnh-nav__search-panel .search-form {
	display: flex;
	gap: 8px;
}

.pnh-nav__search-panel .search-field {
	box-sizing: border-box;
	width: 100%;
	min-width: 0;
	padding: 10px 12px;
	font-family: inherit;
	font-size: 1rem;
	color: var(--pnh-text);
	background-color: var(--pnh-bg);
	border: 1px solid var(--pnh-border);
	border-radius: var(--pnh-radius);
}

.pnh-nav__search-panel .search-field:focus-visible {
	outline: none;
	border-color: var(--pnh-accent-ink);
	box-shadow: 0 0 0 3px rgba(var(--pnh-focus-ring-rgb), 0.25);
}

.pnh-nav__search-panel input[type="submit"] {
	flex-shrink: 0;
}

.pnh-nav__collapse {
	display: flex;
	align-items: center;
	gap: 20px;
}

/* `nowrap` is the whole point: with 8 top-level items this menu used to wrap
	onto a second line and break the masthead's height. It never wraps now —
	instead the whole nav collapses to the hamburger below (at 1100px, not the
	usual ~768px, because 8 items simply need that much room). */
.pnh-nav__menu {
	display: flex;
	flex-wrap: nowrap;
	align-items: center;
	gap: 8px; /* tightened from 12px — see the .pnh-nav gap comment above */
	margin: 0;
	padding: 0;
	list-style: none;
}

.pnh-nav__menu a {
	display: block;
	color: var(--pnh-text);
	text-decoration: none;
	font-size: 14px;
	font-weight: 600;
	line-height: 1.2;
	white-space: nowrap;
}

.pnh-nav__menu a:hover,
.pnh-nav__menu a:focus {
	color: var(--pnh-accent-ink); /* mode-aware ink, design-system.md §8.4 */
}

/* Current section gets a solid underline rather than just a color change —
	color alone is never the only signal (WCAG 1.4.1 Use of Color). */
.pnh-nav__menu .current-menu-item > a,
.pnh-nav__menu .current-menu-ancestor > a,
.pnh-nav__menu .current-menu-parent > a {
	color: var(--pnh-accent-ink);
	box-shadow: inset 0 -2px 0 0 var(--pnh-accent-ink);
}

/* The `.pnh-nav--js` gate below (added by assets/js/nav-toggle.js once it
	initializes) means the collapsing/hidden behavior only ever applies when
	the script has actually run. Without JS, `.pnh-nav__toggle` stays
	`display: none` (its base rule above) and `.pnh-nav__collapse` keeps its
	un-gated `display: flex` from above at every viewport width — the full
	menu always renders as a plain, reachable, keyboard-navigable list, never
	hidden behind a button that would otherwise do nothing.

	Breakpoint is 1360px (raised from the original 1280px when the
	always-visible search + theme-toggle icons were added to this row —
	design-system.md §10.2/§9 — since those needed ~90px this nav didn't
	previously reserve). Below this width the full 8-item menu + icon
	cluster no longer fit on one line even with .pnh-masthead__inner's
	widened 1300px cap (see that rule's comment) and the tightened nav/
	menu gaps above, and `nowrap` means they'd overflow and clip rather
	than wrap. Measured empirically at 1260/1280/1300/1360/1600px after
	each change, not guessed — collapse early instead of allowing any
	overflow window. */
@media (max-width: 1360px) {
	.pnh-nav--js .pnh-nav__toggle {
		display: inline-flex;
	}

	.pnh-nav--js .pnh-nav__collapse {
		display: none;
		position: absolute;
		top: 100%;
		left: 0;
		right: 0;
		z-index: 10;
		flex-direction: column;
		align-items: flex-start;
		gap: 12px;
		padding: var(--pnh-space-2) 20px; /* 16px was exact, tokenized; 20px left as-is */
		background-color: var(--pnh-bg-nav);
		border-bottom: 1px solid var(--pnh-border);
	}

	.pnh-nav--js .pnh-nav__collapse.is-open {
		display: flex;
	}

	.pnh-nav__menu {
		flex-direction: column;
		align-items: flex-start;
		gap: 12px;
	}
}

/* --------------------------------------------------------------------
	Header logo — plain and static, no animation, no custom-logo theme
	mod/Customizer image dependency. `.pnh-logo` is just a layout wrapper
	around the `.pnh-logo__link` lockup (see the "Patch Mark" rules further
	down and pnh_logo_mark() in functions.php) rendered unconditionally by
	header.php.
	-------------------------------------------------------------------- */

.pnh-logo {
	display: inline-flex;
	align-items: center;
}

/* --------------------------------------------------------------------
	Buttons and links

	Primary/secondary CTA buttons (this input[type="submit"] rule — the
	search form's and comment form's submit buttons — and .pnh-btn, e.g. the
	hero's "Read the Patch Notes" CTA) get `border-radius: 0` per LAYOUT.md's
	structural match to leagueoflegends.com's square-cornered primary CTAs.
	Deliberately NOT applied to the pill-shaped category/version badges
	(.pnh-badge*, below) — a different, intentionally-rounded component,
	out of scope for this change.
	-------------------------------------------------------------------- */
input[type="submit"] {
	background-color: var(--pnh-accent-primary);
	color: var(--pnh-on-accent);
	border: none;
	border-radius: 0;
	font-weight: 700;
	cursor: pointer;
	transition: background-color 0.2s ease;
}

/* Hover/pressed uses the exact validated red-700 fill from
	design-system.md's contrast table ("Button label on hover accent fill:
	white/red-700, 7.66:1 PASS") instead of an arbitrary filter()-derived
	color that was never checked against any pairing. */
input[type="submit"]:hover {
	background-color: var(--pnh-accent-hover);
}

.pnh-btn {
	display: inline-block;
	padding: 12px var(--pnh-space-3); /* 24px was exact, tokenized */
	border-radius: 0;
	background-color: var(--pnh-accent-primary);
	color: var(--pnh-on-accent);
	font-weight: 700;
	font-size: 14px;
	text-decoration: none;
	transition: background-color 0.2s ease, transform 0.2s ease;
}

.pnh-btn:hover,
.pnh-btn:focus {
	color: var(--pnh-on-accent);
	background-color: var(--pnh-accent-hover);
	transform: translateY(-1px);
	text-decoration: none;
}

/* --------------------------------------------------------------------
	Category badges — 3-color accent system shared by all categories
	-------------------------------------------------------------------- */
.pnh-badge {
	display: inline-block;
	padding: 4px 10px;
	border-radius: 4px;
	font-size: 11px;
	font-weight: 700;
	line-height: 1.4;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	text-decoration: none;
	color: var(--pnh-on-accent);
	background-color: var(--pnh-accent-primary);
}

.pnh-badge--primary {
	background-color: var(--pnh-accent-primary);
}

.pnh-badge--pitch {
	background-color: var(--pnh-accent-pitch);
}

.pnh-badge--cta {
	background-color: var(--pnh-accent-cta);
}

.pnh-badge:hover,
.pnh-badge:focus {
	color: var(--pnh-on-accent);
	filter: brightness(1.12);
	text-decoration: none;
}

/* Version-badge chip — e.g. "v1.05", "Title Update #4" (see
	pnh_extract_version_badge() in functions.php). Deliberately NOT part of
	the primary/pitch/cta rotation above: reserved for --pnh-accent-neutral
	only (design-system.md §2's gray-700). Reads as a quieter secondary
	marker next to the solid-fill category badge — thin neutral border +
	neutral text on transparent background, not a solid fill. border-box
	keeps its rendered size matching the borderless category badges it sits
	next to despite the added 1px border. */
.pnh-badge--version {
	box-sizing: border-box;
	background-color: transparent;
	border: 1px solid var(--pnh-accent-neutral);
	color: var(--pnh-accent-neutral);
	font-variant-numeric: tabular-nums;
}

.pnh-badge--version:hover,
.pnh-badge--version:focus {
	background-color: transparent;
	color: var(--pnh-accent-neutral);
	filter: none;
}

/* --------------------------------------------------------------------
	Footer (footer.php) — the one deliberate dark-surface exception
	(design-system.md §2), everything else on the page is light.
	-------------------------------------------------------------------- */
.pnh-footer {
	background-color: var(--pnh-dark-surface);
	color: var(--pnh-dark-text-muted);
	border-top: 1px solid var(--pnh-dark-surface);
}

.pnh-footer__inner {
	padding-block: var(--pnh-space-4); /* was 32px — exact match, tokenized */
}

/* The footer ad zone sits on --pnh-dark-surface (the footer's own always-
	dark background), not the page background .ad-zone's default styling
	assumes — in dark mode --pnh-surface-alt and --pnh-dark-surface are now
	the SAME gray-850, which made the reserved ad box nearly invisible
	against the footer (only the dashed border remained visible). Same
	translucent-white technique .pnh-footer__search .search-field already
	uses below, for the same reason: it reads correctly regardless of theme
	since the footer itself is always a dark surface in both modes. */
.pnh-footer .ad-zone {
	background-color: rgba(255, 255, 255, 0.08);
	border-color: rgba(255, 255, 255, 0.25);
}

.pnh-footer .ad-zone__label {
	color: var(--pnh-dark-text-muted);
}

.pnh-footer__widgets {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	gap: var(--pnh-space-3); /* was 24px — exact match, tokenized */
	margin-bottom: var(--pnh-space-3); /* was 24px — exact match, tokenized */
}

.pnh-footer__widget-title {
	margin: 0 0 var(--pnh-space-1); /* was 8px — exact match, tokenized */
	font-size: 13px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--pnh-dark-text);
}

.pnh-footer__menu {
	display: flex;
	flex-wrap: wrap;
	gap: var(--pnh-space-2); /* was 16px — exact match, tokenized */
	margin: 0 0 20px;
	padding: 0;
	list-style: none;
}

.pnh-footer__menu a {
	color: var(--pnh-dark-text-muted);
	text-decoration: none;
	font-size: 13px;
}

/* Brightens to full white + an underline on hover/focus rather than an
	accent color — red-600/red-700 aren't validated against the dark
	gray-900 footer background at small text sizes (see the --pnh-dark-*
	token comment above), so this uses only the two pairings design-system.md
	does validate on a dark surface (white and on-dark-secondary). */
.pnh-footer__menu a:hover,
.pnh-footer__menu a:focus {
	color: var(--pnh-dark-text);
	text-decoration: underline;
}

.pnh-footer__about {
	font-size: 13px;
	line-height: 1.6;
}

.pnh-footer__about a {
	color: var(--pnh-dark-text-muted);
}

.pnh-footer__about a:hover,
.pnh-footer__about a:focus {
	color: var(--pnh-dark-text);
}

.pnh-footer__copyright {
	margin-top: 12px;
	font-size: 12px;
}

/* ==========================================================================
	"Latest Updates" ticker — GPU-accelerated auto-scrolling marquee.

	`.pnh-ticker__track` holds two identical `.pnh-ticker__list` copies
	(front-page.php renders the second one `aria-hidden` + `tabindex="-1"`
	links) side by side, then loops `transform: translateX(0 -> -50%)` —
	exactly one list-width — so it lands back on an identical copy instead
	of jumping. `transform` (not `left`/`margin`) keeps this on the
	compositor thread. `.pnh-ticker__viewport` stays `overflow-x: auto` at
	all times (not just under reduced-motion) so the strip is always
	swipeable/manually scrollable on top of the running animation, per the
	task brief.
	========================================================================== */
.pnh-ticker {
	display: flex;
	align-items: center;
	gap: var(--pnh-space-2); /* was 18px — near-16px rhythm, tokenized */
	margin: 20px 0;
	padding: 10px var(--pnh-space-2); /* 18px was near-16px, tokenized; 10px left as-is */
	background-color: var(--pnh-surface);
	border: 1px solid var(--pnh-border);
	border-radius: var(--pnh-radius);
}

.pnh-ticker__label {
	flex: 0 0 auto;
	padding-right: var(--pnh-space-2); /* was 16px — exact match, tokenized */
	border-right: 1px solid var(--pnh-border);
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--pnh-accent-ink); /* mode-aware ink, design-system.md §8.4 */
	white-space: nowrap;
}

.pnh-ticker__viewport {
	flex: 1 1 auto;
	min-width: 0;
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
}

.pnh-ticker__viewport::-webkit-scrollbar {
	display: none;
}

.pnh-ticker__track {
	display: flex;
	align-items: center;
	gap: var(--pnh-space-3); /* was 22px — near-24px rhythm, tokenized */
	width: max-content;
	animation: pnh-ticker-scroll 26s linear infinite;
}

@keyframes pnh-ticker-scroll {
	from {
		transform: translateX(0);
	}

	to {
		transform: translateX(-50%);
	}
}

/* Pause on hover/keyboard-focus (desktop + any input) and for the duration
	of a touch (assets/js/ticker.js toggles `.pnh-ticker--touch-active` on
	touchstart/touchend since touch has no persistent :hover equivalent). */
.pnh-ticker:hover .pnh-ticker__track,
.pnh-ticker:focus-within .pnh-ticker__track,
.pnh-ticker.pnh-ticker--touch-active .pnh-ticker__track {
	animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
	.pnh-ticker__track {
		animation: none;
	}

	/* No auto-scroll to fill the seam for — drop the duplicate list so the
		viewport's manual overflow-x scroll only ever covers real content. */
	.pnh-ticker__list[aria-hidden="true"] {
		display: none;
	}

	/* Manual scrolling is the only way to browse the ticker in this mode,
		so make the native scrollbar discoverable again instead of hiding it. */
	.pnh-ticker__viewport {
		scrollbar-width: auto;
	}

	.pnh-ticker__viewport::-webkit-scrollbar {
		display: block;
	}
}

.pnh-ticker__list {
	display: flex;
	flex-wrap: nowrap;
	align-items: center;
	gap: var(--pnh-space-3); /* was 22px — near-24px rhythm, tokenized */
	margin: 0;
	padding: 0;
	list-style: none;
}

.pnh-ticker__item {
	flex: 0 0 auto;
	white-space: nowrap;
	position: relative;
}

.pnh-ticker__item + .pnh-ticker__item::before {
	content: "";
	position: absolute;
	left: -12px;
	top: 50%;
	transform: translateY(-50%);
	width: 4px;
	height: 4px;
	border-radius: 50%;
	background-color: var(--pnh-border);
}

.pnh-ticker__item a {
	color: var(--pnh-text);
	font-size: 13px;
}

.pnh-ticker__item a:hover,
.pnh-ticker__item a:focus {
	color: var(--pnh-accent-ink); /* mode-aware ink, design-system.md §8.4 */
}

@media (max-width: 767px) {
	.pnh-ticker {
		padding: 10px var(--pnh-space-2); /* 14px was near-16px, tokenized */
		gap: 12px;
	}

	.pnh-ticker__label {
		padding-right: 12px;
	}
}

/* ==========================================================================
	Hero row — hero card + a 2x2 grid of compact recirc cards beside it
	(front-page.php). Structural pattern: a large featured story next to a
	dense small-story grid, common to editorial homepages generally — sized
	and styled entirely with this site's own tokens, not a specific
	competitor's exact proportions.
	========================================================================== */
.pnh-hero-row {
	display: grid;
	grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
	align-items: start;
	gap: var(--pnh-space-4);
	margin: 0 0 var(--pnh-space-5); /* was 40px — exact match, tokenized */
}

/* Hero — latest Patch Notes post. Stacked (image on top, full width of its
	own column, body below) rather than the old side-by-side split: this
	used to be the row's ONLY content at full container width, but now it
	shares the row with .pnh-hero-row__side, so a second internal split
	would leave the body text too narrow to read comfortably. Stacking also
	makes this structurally just "the largest card in the grid," the same
	media-top/body-below shape as .pnh-card below, rather than a bespoke
	layout. */
.pnh-hero {
	display: flex;
	flex-direction: column;
	background-color: var(--pnh-surface);
	border: 1px solid var(--pnh-border);
	border-radius: var(--pnh-radius);
	overflow: hidden;
}

/* 16:9, not the old clamp(420px, 65vh, 720px) — that box was sized off a
	single-viewport measurement of leagueoflegends.com (see the removed
	comment history) and was flagged as a LAYOUT.md structural remnant that
	never got revisited. Every other image box on the site (article hero,
	card thumbnails) already uses a fixed aspect-ratio; this brings the
	homepage hero in line with that one convention instead of carrying a
	second, viewport-height-driven sizing rule. */
.pnh-hero__media {
	position: relative;
	aspect-ratio: 16 / 9;
	background-color: var(--pnh-surface-alt);
}

.pnh-hero__thumb-link {
	display: block;
	width: 100%;
	height: 100%;
}

.pnh-hero__thumb {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	margin: 0 !important;
}

.pnh-hero__thumb-placeholder {
	position: absolute;
	inset: 0;
}

.pnh-hero__body {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: var(--pnh-space-2); /* was 14px — near-16px rhythm, tokenized */
	padding: var(--pnh-space-4) 36px; /* 32px was exact, tokenized; 36px left as-is */
}

/* LAYOUT.md heading treatment (uppercase/700/tight tracking) */
.pnh-hero__title {
	margin: 0;
	font-size: clamp(22px, 2.6vw, 34px);
	font-weight: 700;
	line-height: 1.2;
	text-transform: uppercase;
	letter-spacing: 0.02em;
}

/* Headline links aren't underlined at rest — a permanently underlined
	multi-line headline reads as dated and hurts scanability. The underline
	comes back on hover/focus so the affordance is never lost. */
.pnh-hero__title a {
	color: var(--pnh-text);
	text-decoration: none;
}

.pnh-hero__title a:hover,
.pnh-hero__title a:focus-visible {
	color: var(--pnh-accent-ink); /* mode-aware ink, design-system.md §8.4 */
	text-decoration: underline;
}

.pnh-hero__excerpt {
	margin: 0;
	color: var(--pnh-text-muted);
	font-size: 15px;
	line-height: 1.6;
}

.pnh-hero__meta time {
	font-size: 12px;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--pnh-text-muted);
}

.pnh-hero__cta {
	margin-top: 4px;
}

/* Row stacks to one column before the 2x2 side grid gets uncomfortably
	narrow — same breakpoint the hero used previously, so this doesn't
	introduce a second breakpoint to reason about. */
@media (max-width: 1018px) {
	.pnh-hero-row {
		grid-template-columns: 1fr;
	}

	.pnh-hero__body {
		padding: var(--pnh-space-3); /* was 24px — exact match, tokenized */
	}
}

/* Side grid: 2x2 on desktop/tablet, single column stacked below the hero on
	the true mobile breakpoint (matches .pnh-grid's own mobile stacking further
	down) — 4 compact cards in one column reads better than a cramped 2x2 at
	phone widths. */
.pnh-hero-row__side {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: var(--pnh-space-3);
}

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

/* Compact card variant (design-system.md §12.6) — same .pnh-card markup,
	excerpt omitted (see pnh_render_card()'s $compact param), body padding
	and title size stepped down so 4 of these read as a dense recirc grid
	rather than 4 full-size cards. */
.pnh-card--compact .pnh-card__body {
	padding: var(--pnh-space-2);
}

.pnh-card--compact .pnh-card__title {
	font-size: 14px;
}

/* True mobile breakpoint (matches this file's other mobile-only overrides,
	e.g. .pnh-container's gutter) — swaps the desktop vh-based height (above)
	for a fixed tall 0.55 portrait aspect-ratio instead. This one was
	confirmed reliable at the one mobile width it was measured at, and a
	simple portrait crop is a reasonable mobile-only exception rather than
	extending the vh-based formula down to mobile too. object-fit: cover
	(already on .pnh-hero__thumb) keeps the image filling either box without
	distortion. */
@media (max-width: 767px) {
	.pnh-hero__media {
		/* height must go back to auto here — aspect-ratio is a no-op once
			both width and height are already definite, and the desktop rule
			above now sets an explicit height (clamp()), so without this
			reset the mobile portrait ratio below would never apply. */
		height: auto;
		aspect-ratio: 0.55;
	}

	.pnh-hero__body {
		padding: 20px;
	}
}

/* ==========================================================================
	Post-card grid — shared by the front page (front-page.php) and every
	archive-type template (archive.php, search.php, index.php — all three
	via get_template_part( 'loop-archive' )) so the grid looks and behaves
	the same no matter which template rendered it.
	========================================================================== */
.pnh-section__title {
	display: inline-block;
	margin: 0 0 20px;
	padding-bottom: var(--pnh-space-1); /* was 8px — exact match, tokenized */
	font-size: 15px;
	font-weight: 700;
	text-transform: uppercase;
	/* 0.02em — LAYOUT.md heading treatment, matched here so this "Latest
		Posts"-style section heading, the hero headline, and card titles all
		use the same tracking value (was 0.04em before this pass). */
	letter-spacing: 0.02em;
	color: var(--pnh-text);
	border-bottom: 2px solid var(--pnh-accent-ink); /* mode-aware ink, design-system.md §8.4 */
}

.pnh-grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 28px var(--pnh-space-3); /* column-gap 24px was exact, tokenized; row-gap 28px left as-is */
	margin: 0 0 var(--pnh-space-4); /* was 32px — exact match, tokenized */
	padding: 0;
	list-style: none;
}

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

@media (max-width: 767px) {
	.pnh-grid {
		grid-template-columns: 1fr;
		gap: 20px;
	}
}

/* Flat: no border, no radius, no hover animation (lift/shadow/border-color
	all removed per the owner's explicit ask). Separation from the page
	comes from --pnh-surface/--pnh-bg alone. The one thing kept is a
	:focus-visible outline — not a "hover", a WCAG 2.4.7 requirement for
	keyboard users to see where they are, and this project's own coding
	standards rule out dropping accessibility basics for a visual ask that
	didn't ask for that. Nothing shows on mouse hover or on click/touch. */
.pnh-card {
	display: flex;
	flex-direction: column;
	background-color: var(--pnh-surface);
	overflow: hidden;
}

.pnh-card:focus-within {
	outline: 2px solid var(--pnh-accent-ink); /* mode-aware ink, design-system.md §8.4 */
	outline-offset: 2px;
}

.pnh-card__media {
	position: relative;
	aspect-ratio: 16 / 9;
	overflow: hidden;
	background-color: var(--pnh-surface-alt);
}

.pnh-card__media-link {
	display: block;
	width: 100%;
	height: 100%;
}

.pnh-thumb {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	margin: 0 !important;
}

/* Wrapper for the category badge + optional version-badge chip. Moved off
	the thumbnail into the card body (design-system.md §6's patch-card spec
	— chip sits below the image, above the headline, not overlaid on the
	media). flex-wrap lets the version chip (can be as long as "Title Update
	#4") drop to its own line on narrow cards instead of overflowing. */
.pnh-card__badges {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: 6px;
	margin-bottom: 2px;
}

/* In-card category chip: quiet tint (red-100/red-700, dark-mode-aware),
	NOT the solid accent-primary/pitch/cta fill used on the hero/article
	header. Cards sit dense in a grid next to muted meta text, so a loud
	solid badge would outweigh the headline it's meant to introduce —
	design-system.md §6 scopes the softer chip to this context specifically. */
.pnh-card__badges .pnh-badge {
	background-color: var(--pnh-chip-bg);
	color: var(--pnh-chip-ink);
}

.pnh-card__badges .pnh-badge:hover,
.pnh-card__badges .pnh-badge:focus-visible {
	color: var(--pnh-chip-ink);
	filter: none;
	text-decoration: underline;
}

.pnh-card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: var(--pnh-space-1); /* was 8px — exact match, tokenized */
	/* top/left/right were 16px/18px/18px — 18px is a near-16px rhythm like the
		nav/ticker gaps above, tokenized to match; 20px bottom left as-is */
	padding: var(--pnh-space-2) var(--pnh-space-2) 20px;
}

/* LAYOUT.md heading treatment (uppercase/700/tight tracking) — applied here
	even though 17px is on the small side for uppercase body-ish text (see
	CLAUDE.md task note on legibility at small sizes); kept because titles are
	short 2-4 word patch-note headlines, not long sentences, and this is one
	of the three elements the task explicitly names. Checked visually at both
	viewport widths in the phase-3 screenshots — reads fine, not broken. */
.pnh-card__title {
	margin: 0;
	font-size: 17px;
	font-weight: 700;
	line-height: 1.3;
	text-transform: uppercase;
	letter-spacing: 0.02em;
}

.pnh-card__title a {
	color: var(--pnh-text);
	text-decoration: none;
}

.pnh-card__title a:hover,
.pnh-card__title a:focus-visible {
	color: var(--pnh-accent-ink); /* mode-aware ink, design-system.md §8.4 */
	text-decoration: underline;
}


/* Relative time ("3h ago"), not an absolute date — design-system.md §6's
	patch-card meta line. `margin-top: auto` — not a fixed value — now that
	the excerpt (which used to be the flex-filler keeping cards in the same
	grid row bottom-aligned despite different title lengths) is gone: this
	sits close under a long 2-line title and still lands at the same
	baseline as a short 1-line title's date in the row next to it. */
.pnh-card__date {
	margin-top: auto;
	padding-top: 2px;
	font-size: 13px;
	color: var(--pnh-text-muted);
}

/* --------------------------------------------------------------------
	CSS-only thumbnail placeholder — used whenever has_post_thumbnail()
	is false (every post today, see CLAUDE.md placeholder-media note).
	No third-party image is fetched; a real featured image just replaces
	this automatically once uploaded (see newspaper_child_thumbnail_markup()
	in functions.php).
	-------------------------------------------------------------------- */
/* Dark plate, not a pale tint. These used to ramp from --pnh-surface-alt
	(gray-100, near-white), which washed the whole thing out to a pale pink
	wherever a post had no featured image — the dominant look on the site
	today, since most posts are still placeholder content. Ramping between
	gray-900 and the red accent instead reads as a deliberate empty state. */
.pnh-thumb-placeholder {
	display: block;
	width: 100%;
	height: 100%;
	background-color: var(--pnh-accent-pitch);
	background-image: linear-gradient(135deg, #1B1D22 0%, #3A1016 55%, var(--pnh-accent-primary) 140%);
}

.pnh-thumb-placeholder::after {
	content: "";
	display: block;
	width: 100%;
	height: 100%;
	background-image: repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.05) 0, rgba(255, 255, 255, 0.05) 2px, transparent 2px, transparent 10px);
}

.pnh-thumb-placeholder[data-accent="primary"] {
	background-image: linear-gradient(135deg, #1B1D22 0%, var(--pnh-accent-primary) 140%);
}

.pnh-thumb-placeholder[data-accent="pitch"] {
	background-image: linear-gradient(135deg, #1B1D22 0%, #4B4F58 140%);
}

.pnh-thumb-placeholder[data-accent="cta"] {
	background-image: linear-gradient(135deg, #1B1D22 0%, var(--pnh-accent-cta) 140%);
}

/* ==========================================================================
	Single post / page / archive / search / 404 / comments — minimal
	structural container so these new templates aren't full-bleed-to-the-
	edge with zero spacing. Deliberately basic (shared #main width/padding,
	no bespoke layout) — this gets a proper editorial design pass in the
	follow-up palette-refresh task per CLAUDE.md; this pass only needs
	these templates to render correctly and readably.
	========================================================================== */
#main {
	padding-top: var(--pnh-space-3); /* was 24px — exact match, tokenized */
	padding-bottom: var(--pnh-space-6); /* was 48px — exact match, tokenized */
}

/* Front page wants a tighter gap under the ticker/header than every other
	template's default 24px top offset above — #main.pnh-home (ID + class)
	deliberately out-specifies the plain #main rule above it. */
#main.pnh-home {
	padding-top: 8px;
}

/* Article page (single.php) — major page-region separation, design-system.md
	§4: "header->hero, hero->content, content->footer: --space-8 (64px)".
	#main.pnh-single (ID+class, same out-specifying pattern as #main.pnh-home
	above) overrides the generic #main padding for this template only —
	archive/search/front-page/page padding is untouched, out of this pass's
	declared article-template scope. */
#main.pnh-single {
	padding-top: var(--pnh-article-space-region);
	padding-bottom: var(--pnh-article-space-region);
}

/* One column for the whole article. The hero and H1 used to span the full
	1200px container while the body sat at a 70ch measure, so every post read
	as two different layouts stacked on each other. Pinning the article to the
	measure means hero, meta, title and body all share one edge. */
.pnh-single__article {
	max-width: var(--pnh-measure-article);
	margin-inline: auto;
}

/* ==========================================================================
	Article body typography (design-system.md §3) — replaces ad-hoc 17px/1.7
	with the signed-off 19px/1.7 body scale, a 70ch measure, and an explicit
	heading/paragraph rhythm (design-system.md §4) in place of unstyled
	browser-default h2-h6/p margins, which is where most of this template's
	previous "excess gaps" actually came from (nothing here declared its own
	spacing before this pass, so every gap was whatever the browser's UA
	stylesheet happened to apply).
	========================================================================== */
/* Centered, not left-aligned: the article hero and H1 run the full container
	width, so a measure-limited body column pinned to the left edge left a
	large dead gutter down the right of every post. */
/* Sans body, not serif. The original plan (and design-system.md §3) carried a
	serif reading face over from the old generic-news build, but the Phase-1
	and nav/form inspections found serif body copy absent from gaming patch-
	notes sites entirely — all four references use a system sans at 18-22px.
	Serif reads as broadsheet here, not games press. --pnh-font-serif is left
	defined but no longer applied to article body copy. */
/* Summary/TL;DR box (single.php, editor-entered via the "Article Extras"
	meta box) — same measure as the article body so it lines up with the
	content column below it, visually distinct via a tinted panel rather
	than a border (matches the borderless-card direction elsewhere on the
	site). Renders only when the editor actually filled the field in. */
.pnh-summary {
	max-width: var(--pnh-measure-article);
	margin: 0 auto var(--pnh-article-space-paragraph);
	padding: var(--pnh-space-3);
	background-color: var(--pnh-surface-alt);
	border-radius: var(--pnh-radius);
}

.pnh-summary__label {
	margin: 0 0 4px;
	font-family: var(--pnh-font-sans);
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--pnh-accent-ink);
}

.pnh-summary__text {
	margin: 0;
	font-family: var(--pnh-font-sans);
	font-size: 15px;
	line-height: 1.6;
	color: var(--pnh-text);
}

/* Table of contents (single.php, pnh_render_toc()) — auto-built from the
	post's own H2/H3s, hidden entirely under 2 headings (pnh_render_toc()
	itself returns '' in that case, not a CSS display:none — no empty box
	ever ships to the DOM). Same tinted-panel treatment as .pnh-summary so
	the two read as one family of "meta" boxes sitting above the article
	body. */
.pnh-toc {
	max-width: var(--pnh-measure-article);
	margin: 0 auto var(--pnh-article-space-paragraph);
	padding: var(--pnh-space-3);
	background-color: var(--pnh-surface-alt);
	border-radius: var(--pnh-radius);
}

.pnh-toc__title {
	margin: 0 0 var(--pnh-space-1);
	font-family: var(--pnh-font-sans);
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--pnh-text-muted);
}

.pnh-toc__list {
	margin: 0;
	padding: 0;
	list-style: none;
	font-family: var(--pnh-font-sans);
	font-size: 14px;
	line-height: 1.7;
}

/* H3 entries indent under their parent H2 — same nesting relationship the
	headings themselves have in the article body. */
.pnh-toc__item--h3 {
	padding-left: var(--pnh-space-3);
}

.pnh-toc__list a {
	color: var(--pnh-text);
	text-decoration: none;
}

.pnh-toc__list a:hover,
.pnh-toc__list a:focus-visible {
	color: var(--pnh-accent-ink);
	text-decoration: underline;
}

.entry-content {
	max-width: var(--pnh-measure-article);
	margin-inline: auto;
	font-family: var(--pnh-font-sans);
	font-size: var(--pnh-text-body-size);
	line-height: var(--pnh-text-body-line);
	color: var(--pnh-text);
}

/* Baseline rhythm: every direct child gets 16px margin-top from the sibling
	before it (design-system.md: "apply via gap... not stacked margin-bottom
	— avoids margin-collapse edge cases"; margin-top-only with an explicit
	reset below achieves the same "nothing to collapse against" effect
	without requiring a flex/grid wrapper, which would fight arbitrary
	block-editor markup — figures, lists, embeds — inside real post content). */
.entry-content > * {
	margin: 0;
}

.entry-content > * + * {
	margin-top: var(--pnh-article-space-paragraph);
}

.entry-content > h2 {
	margin-top: var(--pnh-article-space-before-h2); /* major section break, e.g. "Buffs" -> "Nerfs" */
}

.entry-content > h3 {
	margin-top: var(--pnh-article-space-before-h3); /* sub-section break, e.g. one champion/weapon entry */
}

.entry-content > h4 {
	margin-top: var(--pnh-article-space-before-h4); /* same tier as paragraph rhythm — not a section break */
}

/* Heading-to-own-content: tighter than the paragraph baseline above, "binds
	the heading to its own content" (design-system.md §4). Higher specificity
	than the universal `* + *` rule above (a type selector beats a universal
	one), so these correctly win. */
.entry-content > h2 + *,
.entry-content > h3 + * {
	margin-top: var(--pnh-article-space-heading-attach);
}

.entry-content > h4 + * {
	margin-top: var(--pnh-article-space-heading-attach-tight);
}

.entry-content h2 {
	font-size: var(--pnh-text-h2-size);
	font-weight: var(--pnh-text-h2-weight);
	line-height: var(--pnh-text-h2-line);
	color: var(--pnh-text);
}

.entry-content h3 {
	font-size: var(--pnh-text-h3-size);
	font-weight: var(--pnh-text-h3-weight);
	line-height: var(--pnh-text-h3-line);
	color: var(--pnh-text);
}

/* h5/h6 aren't in design-system.md's scale (it only defines H1-H4) — a
	patch-notes article isn't expected to nest that deep, this is just a
	sane fallback so a heading level is never left completely unstyled if
	one shows up, using the same H4 treatment as the deepest defined level. */
.entry-content h4,
.entry-content h5,
.entry-content h6 {
	font-size: var(--pnh-text-h4-size);
	font-weight: var(--pnh-text-h4-weight);
	line-height: var(--pnh-text-h4-line);
	color: var(--pnh-text);
}

.entry-content p {
	max-width: 100%;
}

.entry-content ul,
.entry-content ol {
	padding-left: 1.25em;
}

/* Not an explicit design-system.md value — patch-notes buff/nerf lists are a
	chain of short related points rather than standalone paragraphs, so this
	uses a tighter-than-paragraph gap in the same spirit as the rest of the
	scale's "tighter where it's not a real break" bias. */
.entry-content li + li {
	margin-top: var(--pnh-article-space-heading-attach-tight);
}

.entry-content blockquote {
	padding-left: var(--pnh-space-3);
	border-left: 3px solid var(--pnh-border);
	color: var(--pnh-text-muted);
	font-style: italic;
}

/* In-article images (design-system.md §5): constrained to the article
	measure, never full-bleed, native lazy-loading (WP's own
	wp_filter_content_tags() already adds `loading="lazy"` to these
	automatically — nothing extra needed here). height:auto plus the
	intrinsic width/height attributes WP already writes on block-editor
	images is what actually reserves the layout box and avoids the "collapses
	to 0x0 before intersection" failure design-system.md calls out. */
.entry-content img {
	display: block;
	height: auto;
	max-width: 100%;
	border-radius: var(--pnh-radius);
}

.entry-content figure {
	margin-top: var(--pnh-article-space-before-h4);
}

/* Caption style (design-system.md §5): below-image, gray-600, 14px,
	8px gap, left-aligned. Covers both the HTML5 <figcaption> this theme's
	add_theme_support('html5', ['caption']) produces and the legacy
	.wp-caption-text class some older/imported content may still use. */
.entry-content figcaption,
.entry-content .wp-caption-text,
.pnh-media-caption {
	margin-top: var(--pnh-article-caption-gap);
	font-size: var(--pnh-text-small-size);
	line-height: 1.5;
	color: var(--pnh-text-muted);
	text-align: left;
}

/* Breadcrumb (single.php, RankMath's own rank_math_the_breadcrumbs() output
	— see the doc comment there for why it's not hand-rolled). Only the
	wrapper's spacing/size/color is styled here; RankMath prints its own
	`.rank-math-breadcrumb` / `a` / `.last` structure inside, which these
	descendant selectors target rather than assuming exact markup depth (it
	can change across RankMath versions). */
.pnh-breadcrumb {
	margin-bottom: var(--pnh-space-2);
	font-size: 13px;
	color: var(--pnh-text-muted);
}

.pnh-breadcrumb p {
	margin: 0;
}

.pnh-breadcrumb a {
	color: var(--pnh-text-muted);
	text-decoration: none;
}

.pnh-breadcrumb a:hover,
.pnh-breadcrumb a:focus-visible {
	color: var(--pnh-accent-ink);
	text-decoration: underline;
}

.pnh-breadcrumb .last {
	color: var(--pnh-text);
}

.pnh-single__header,
.pnh-page__header,
.pnh-archive__header,
.pnh-search__header {
	margin-bottom: 20px;
}

/* Article hero unit (design-system.md §6) — the header block itself carries
	the big "hero -> content" major-region gap (design-system.md §4) below
	it, separate from the plain 20px used by page/archive/search headers
	above (those templates aren't part of this pass's article-template
	scope). */
.pnh-single__header {
	margin-bottom: var(--pnh-article-space-region);
}

.pnh-single__meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--pnh-space-1); /* 8px — small gap between the badge group and the date */
	margin-bottom: var(--pnh-article-space-image-gap); /* 16px -> H1, design-system.md §6 */
}

/* Category badge + optional version-badge chip on single.php — plain inline
	flow (not overlaid on media like .pnh-card__badges), still flex-wrap so
	a long "Title Update #4"-style chip never crowds the category badge. */
.pnh-single__badges {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--pnh-space-1); /* was 8px — exact match, tokenized */
}

.pnh-single__date {
	font-size: var(--pnh-text-small-size);
	line-height: 1.5;
	color: var(--pnh-text-muted);
}

.pnh-single__title,
.pnh-page__title {
	margin: 0;
	font-size: var(--pnh-text-h1-size);
	font-weight: var(--pnh-text-h1-weight);
	line-height: var(--pnh-text-h1-line);
	color: var(--pnh-text);
}

.pnh-archive__title,
.pnh-search__title {
	margin: 0;
	font-size: clamp(22px, 2.6vw, 30px);
}

/* Hero image — mandatory 16:9 at every viewport (design-system.md §5, a hard
	rule with no exceptions). aspect-ratio lives on this wrapper (not the
	<img> itself) with the child absolutely positioned to fill it — the same
	robust, CLS-safe pattern .pnh-card__media already uses elsewhere in this
	file, so the reserved box is defined purely by the wrapper's own
	aspect-ratio and never depends on the image having loaded, per
	design-system.md's explicit "never let a lazy-loaded image collapse to
	zero before it triggers" note (this image is eager anyway, but the same
	box-reservation technique applies). */
.pnh-single__media {
	position: relative;
	aspect-ratio: var(--pnh-hero-aspect-ratio);
	margin: 0;
	background-color: var(--pnh-surface-alt);
	border-radius: var(--pnh-radius);
	overflow: hidden;
}

.pnh-single__media .pnh-thumb,
.pnh-single__media .pnh-thumb-placeholder {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* "full content-column width on desktop (not full-viewport bleed),
	edge-to-edge on mobile" — design-system.md §5. Cancels .pnh-container's
	own gutter (the parent .pnh-single is a .pnh-container) just for the hero,
	pulling it flush to the viewport edges below the mobile breakpoint; the
	rounded corners come off too since a bled-to-the-edge image with rounded
	corners would look like an unintentional gap, not a deliberate radius. */
@media (max-width: 767px) {
	.pnh-single__media {
		margin-inline: calc(-1 * var(--pnh-gutter));
		border-radius: 0;
	}
}

.pnh-no-results {
	padding: var(--pnh-space-4) 0; /* was 32px — exact match, tokenized */
}

/* content -> comments: also a major page-region boundary (design-system.md
	§4 lists "content->footer" as one of the three explicit 64px examples;
	comments sit between the article body and the site footer, so the same
	token applies here). */
.pnh-comments {
	margin-top: var(--pnh-article-space-region);
	padding-top: var(--pnh-space-3); /* was 24px — exact match, tokenized */
	border-top: 1px solid var(--pnh-border);
}

.pnh-comments__list {
	max-width: var(--pnh-measure-article);
	margin: 0 auto 20px;
	padding: 0;
	list-style: none;
}

/* --------------------------------------------------------------------
	Comment list + comment form

	Previously this whole region had no styling beyond the two rules above,
	so it rendered as the raw WordPress default: unpadded hairline inputs,
	stacked full-width labels, and a tiny default submit button. The form is
	a real surface panel now, with name/email/url sharing one row on desktop
	and collapsing to a single column on mobile.
	-------------------------------------------------------------------- */

.pnh-comments__title,
.pnh-comments .comment-reply-title {
	max-width: var(--pnh-measure-article);
	margin: 0 auto var(--pnh-space-3);
	font-family: var(--pnh-font-sans);
	font-size: var(--pnh-text-h3-size, 1.5rem);
	font-weight: 700;
	line-height: 1.25;
	color: var(--pnh-text);
}

.pnh-comments__list .comment-body {
	padding: var(--pnh-space-3);
	margin-bottom: var(--pnh-space-2);
	background-color: var(--pnh-surface);
	border: 1px solid var(--pnh-border);
	border-radius: var(--pnh-radius);
}

.pnh-comments__list .children {
	margin: 0 0 0 var(--pnh-space-3);
	padding: 0;
	list-style: none;
}

.pnh-comments__closed {
	color: var(--pnh-text-muted);
}

/* The form panel itself. */
.pnh-comments .comment-form {
	max-width: var(--pnh-measure-article);
	margin-inline: auto;
	padding: var(--pnh-space-4);
	background-color: var(--pnh-surface);
	border: 1px solid var(--pnh-border);
	border-radius: var(--pnh-radius);
}

.pnh-comments .comment-notes,
.pnh-comments .comment-form-cookies-consent label {
	font-size: var(--pnh-text-small-size);
	color: var(--pnh-text-muted);
}

.pnh-comments .comment-notes {
	margin: 0 0 var(--pnh-space-3);
}

.pnh-comments .comment-form p {
	margin: 0 0 var(--pnh-space-3);
}

.pnh-comments .comment-form label {
	display: block;
	margin-bottom: 6px;
	font-family: var(--pnh-font-sans);
	font-size: var(--pnh-text-small-size);
	font-weight: 600;
	color: var(--pnh-text);
}

.pnh-comments .comment-form input[type="text"],
.pnh-comments .comment-form input[type="email"],
.pnh-comments .comment-form input[type="url"],
.pnh-comments .comment-form textarea {
	box-sizing: border-box;
	width: 100%;
	padding: 10px 12px;
	font-family: inherit;
	font-size: 1rem;
	line-height: 1.5;
	color: var(--pnh-text);
	background-color: var(--pnh-bg);
	border: 1px solid var(--pnh-border);
	border-radius: var(--pnh-radius);
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.pnh-comments .comment-form textarea {
	min-height: 140px;
	resize: vertical;
}

/* Focus ring is a real 2px accent outline, not the UA default that the
	custom border would otherwise visually swallow (WCAG 2.4.7). */
.pnh-comments .comment-form :is(input, textarea):focus-visible {
	outline: none;
	/* Mode-aware ink token (design-system.md §8.4) plus the matching
		--pnh-focus-ring-rgb swap so the glow color always matches the
		border color instead of staying a fixed light-mode red in dark mode. */
	border-color: var(--pnh-accent-ink);
	box-shadow: 0 0 0 3px rgba(var(--pnh-focus-ring-rgb), 0.25);
}

.pnh-comments .comment-form-cookies-consent {
	display: flex;
	align-items: flex-start;
	gap: 8px;
}

.pnh-comments .comment-form-cookies-consent label {
	margin-bottom: 0;
	font-weight: 400;
}

.pnh-comments .form-submit {
	margin-bottom: 0;
}

.pnh-comments .form-submit input[type="submit"] {
	padding: 12px 24px;
	font-family: var(--pnh-font-sans);
	font-size: 1rem;
	border-radius: var(--pnh-radius);
}

.pnh-comments .form-submit input[type="submit"]:hover,
.pnh-comments .form-submit input[type="submit"]:focus-visible {
	background-color: var(--pnh-accent-cta);
}

/* --------------------------------------------------------------------
	Footer columns (footer.php)

	Three-column layout: About / Sections (generated from the real category
	taxonomy) / Search + curated links. Collapses to a single column on
	small screens. Same dark-surface pairings as the footer rules above —
	white and on-dark-secondary only, never the red accents, which aren't
	validated against gray-900 at small text sizes.
	-------------------------------------------------------------------- */
.pnh-footer__cols {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: var(--pnh-space-4);
	margin-top: var(--pnh-space-4);
}

.pnh-footer__heading {
	margin: 0 0 var(--pnh-space-2);
	font-family: var(--pnh-font-sans);
	font-size: 13px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--pnh-dark-text);
}

.pnh-footer__links {
	display: flex;
	flex-direction: column;
	gap: 8px;
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: 13px;
}

.pnh-footer__links li {
	margin: 0;
}

.pnh-footer__links a {
	color: var(--pnh-dark-text-muted);
	text-decoration: none;
}

.pnh-footer__links a:hover,
.pnh-footer__links a:focus-visible {
	color: var(--pnh-dark-text);
	text-decoration: underline;
}

.pnh-footer__search .search-form {
	display: flex;
	gap: 8px;
	margin-bottom: var(--pnh-space-3);
}

.pnh-footer__search .search-field {
	box-sizing: border-box;
	width: 100%;
	min-width: 0;
	padding: 8px 10px;
	font-family: inherit;
	font-size: 14px;
	color: var(--pnh-dark-text);
	background-color: rgba(255, 255, 255, 0.08);
	border: 1px solid rgba(255, 255, 255, 0.25);
	border-radius: var(--pnh-radius);
}

.pnh-footer__search .search-field::placeholder {
	color: var(--pnh-dark-text-muted);
}

.pnh-footer__search .search-field:focus-visible {
	outline: 2px solid var(--pnh-dark-text);
	outline-offset: 1px;
}

.pnh-footer__search input[type="submit"] {
	flex-shrink: 0;
	padding: 8px 14px;
	font-size: 14px;
	border-radius: var(--pnh-radius);
}

/* Bottom bar: copyright + sitemap link, separated from the columns above. */
.pnh-footer__bottom {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--pnh-space-2);
	margin-top: var(--pnh-space-4);
	padding-top: var(--pnh-space-3);
	border-top: 1px solid rgba(255, 255, 255, 0.15);
}

.pnh-footer__bottom .pnh-footer__copyright {
	margin: 0;
}

.pnh-footer__sitemap {
	font-size: 12px;
	color: var(--pnh-dark-text-muted);
	text-decoration: none;
}

.pnh-footer__sitemap:hover,
.pnh-footer__sitemap:focus-visible {
	color: var(--pnh-dark-text);
	text-decoration: underline;
}
