/**
 * NPMP theme styles.
 *
 * Ported from npmp-concept-d.html. Class names are frozen by BUILD-CONTRACT.md #5 —
 * do not rename any of them without telling Lead Dev.
 *
 * BUILD-CONTRACT.md #8: theme.json defines these same tokens for the block editor
 * and core blocks, but a hybrid theme's theme.json styles do NOT reach our
 * hand-written template markup — so the palette is declared a second time here,
 * as plain CSS custom properties, for every classic template to use. If the
 * palette ever changes, change it in BOTH places.
 *
 * Known mockup defect fixed here (BUILD-CONTRACT.md #6.1): line ~100 of the
 * mockup had `color:#b4ae a2` (invalid CSS, stray space inside the hex value).
 * Corrected below to `#b4aea2`.
 */

:root {
	--ink: #111010;
	--panel: #1B1917;
	--paper: #F7F3EA;
	--red: #D81E3E;
	--grey: #A6A29A;
	--line: rgba(247, 243, 234, .14);
}

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
	font-family: 'Archivo', system-ui, sans-serif;
	background: var(--paper);
	color: var(--ink);
	-webkit-font-smoothing: antialiased;
	line-height: 1.5;
	/*
	 * Sticky footer. Short pages (NPMP casework, Campaigns, anything still on
	 * placeholder copy) don't fill the viewport, and without this the dark
	 * footer stops partway down and the paper body background shows beneath it
	 * as a stray light strip. Flex column + `footer { margin-top: auto }`
	 * pushes the footer to the bottom without needing a <main> wrapper, since
	 * header, sections and footer are all direct children of body.
	 */
	display: flex;
	flex-direction: column;
	min-height: 100vh;
}
img { max-width: 100%; display: block; }
.mono { font-family: 'IBM Plex Mono', monospace; }
.wrap { max-width: 1320px; margin: 0 auto; padding: 0 30px; }
.screen-reader-text {
	position: absolute !important;
	width: 1px; height: 1px;
	overflow: hidden;
	clip: rect(1px, 1px, 1px, 1px);
	white-space: nowrap;
}

/* The mockup's ticker bar was removed at the client's request (2026-08-26).
 * Its rules (.ticker/.live/.dot/@keyframes pulse) went with it — nothing else
 * used them. The header is now the first thing on the page. */

/* nav
 *
 * Client moved the primary nav from 10 flat tabs to 5 (3 with a one-level
 * dropdown) — with fewer, wider tabs the row no longer needs to wrap, so
 * this pass widens the breathing room the old cramped 10-tab layout never
 * had room for (gap/padding below), rather than changing the visual
 * language of Concept D.
 */
/* Skip link: off-screen until focused, then a real button at the top left.
   Sits above the sticky header's z-index so it is not hidden behind it. */
.skip-link {
	position: absolute; left: -9999px; top: 0; z-index: 100;
	background: var(--ink); color: var(--paper);
	padding: 12px 18px; border-radius: 0 0 8px 0;
	font-weight: 700; font-size: 15px; text-decoration: none;
}
.skip-link:focus {
	left: 0;
	outline: 3px solid var(--red); outline-offset: 2px;
}
/* The skip target must not draw a focus ring of its own: focus lands there
   silently so the next Tab continues into the content. */
#main:focus { outline: none; }

header { position: sticky; top: 0; z-index: 50; background: var(--paper); border-bottom: 1px solid rgba(17, 16, 16, .14); }
/* Header row: brand hard left, tabs centred in the bar, actions hard right.
 *
 * The container is wider than the 1320px content rail on purpose — the brand
 * wordmark is long, and giving the header its own wider rail is what stops the
 * five tabs wrapping onto a second line. `.brand` and `.nav-actions` are both
 * flex:0 0 auto so they only take the width they need; `.navlinks` takes the
 * slack with margin:0 auto, which centres it between them. */
nav { display: flex; align-items: center; gap: 28px; padding: 20px 34px; max-width: 1560px; margin: 0 auto; }
/* Brand lockup: real client logo + wordmark, whole thing links home. */
.brand { white-space: nowrap; flex: 0 0 auto; display: flex; align-items: center; gap: 13px; color: inherit; text-decoration: none; }
.brand-logo { width: 46px; height: auto; display: block; flex: 0 0 auto; }
.brand-text { display: flex; flex-direction: column; }
.brand .name { display: block; font-weight: 900; font-size: 18px; letter-spacing: -.015em; text-transform: uppercase; line-height: 1.1; }
/* "Police" in the wordmark was red. Client asked on 2026-09-07 to see it in
   black instead. Commented rather than deleted: she asked to look at it, which
   is not the same as deciding, and reverting is uncommenting one line.
   The hero wordmark is handled the same way, at h1.hero-title .red below. */
/* .brand .red { color: var(--red); } */
.brand .est { display: block; font-family: 'IBM Plex Mono', monospace; font-size: 11px; letter-spacing: .1em; color: #6b665e; text-transform: uppercase; margin-top: 3px; }
/* nowrap is load-bearing: with flex-wrap:wrap the tabs stacked onto a second
 * row as soon as the brand and buttons squeezed them. They should stay on one
 * line and the header should get wider instead. */
.navlinks { display: flex; align-items: center; gap: 26px; font-weight: 600; font-size: 14px; margin: 0 auto; flex-wrap: nowrap; }
.navlinks a { color: var(--ink); text-decoration: none; transition: color .15s; white-space: nowrap; }
.navlinks a:hover, .navlinks a.current { color: var(--red); }
.nav-actions { display: flex; align-items: center; gap: 14px; flex: 0 0 auto; }
.support {
	background: transparent; color: var(--ink); border: 2px solid var(--ink);
	padding: 11px 20px; border-radius: 8px; font-weight: 700; font-size: 14px;
	text-decoration: none; display: inline-flex; align-items: center; gap: 8px;
	white-space: nowrap; transition: transform .12s;
}
.support:hover { transform: translateY(-1px); }
/* Donate — brief requirement absent from the mockup; sits right of the nav tabs, red like .btn-red */
.donate {
	background: var(--red); color: #fff; padding: 12px 22px; border-radius: 8px;
	font-weight: 700; font-size: 14px; text-decoration: none; display: inline-flex;
	align-items: center; gap: 8px; white-space: nowrap; transition: transform .12s;
}
.donate:hover { transform: translateY(-1px); }
.burger { display: none; background: none; border: none; font-size: 26px; cursor: pointer; color: var(--ink); line-height: 1; }

/* nav dropdowns — client decision (confirmed, supersedes the brief's original
 * "flat, no dropdowns" instruction). Parents are <button>s (see inc/nav.php).
 *
 * Opens on HOVER (client request), but hover is not the only way in:
 *   :hover        — mouse users
 *   :focus-within — keyboard users tabbing through
 *   .is-open      — set by assets/js/nav.js on click/Enter/Space, which is what
 *                   touch devices and screen readers actually use
 * A hover-only menu would be unreachable on a phone, so all three must stay.
 *
 * Panel styling follows the reference the client supplied: a flat rectangular
 * card on the paper background with a hairline rule between every item, a red
 * rule across the top tying it to the parent tab, and a red left marker on the
 * hovered row. Square corners are deliberate — this is the one place the design
 * drops Concept D's rounded surfaces, because the reference is emphatic about it. */
.navlinks .nav-item { position: relative; }
.navlinks .nav-toggle {
	background: none; border: none; margin: 0; font: inherit;
	color: var(--ink); font-weight: 600; font-size: 14px; cursor: pointer;
	display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
	/* Vertical padding builds a hover bridge down to the panel, so the menu
	 * doesn't snap shut in the gap as the pointer travels toward it. */
	padding: 18px 0;
}
.navlinks .nav-toggle:hover,
.navlinks .nav-item:hover .nav-toggle,
.navlinks .nav-toggle[aria-expanded="true"] { color: var(--red); }
.navlinks .nav-caret { font-size: 10px; line-height: 1; transition: transform .15s; }
.navlinks .nav-item:hover .nav-caret,
.navlinks .nav-item.is-open .nav-caret { transform: rotate(180deg); }

.sub-menu {
	display: none;
	list-style: none;
	position: absolute;
	top: 100%;
	left: -18px;
	min-width: 268px;
	margin: 0;
	padding: 0;
	background: var(--paper);
	border: 1px solid rgba(17, 16, 16, .16);
	border-top: 3px solid var(--red);
	border-radius: 0;
	box-shadow: 0 18px 34px rgba(17, 16, 16, .16);
	z-index: 60; /* above header's own z-index:50, so it clears the hero below */
}
/*
 * Focus and click work on every device. Hover is gated to devices that actually
 * have a hovering pointer.
 *
 * Without this guard a touchscreen laptop or a tablet in landscape (1024–1366px,
 * so above the 960px breakpoint that turns the menu into an accordion) hits the
 * desktop hover rule, where a tap registers as a hover and leaves the menu stuck
 * open. `pointer: fine` additionally excludes a coarse pointer that reports
 * hover capability, such as a stylus.
 */
.navlinks .nav-item:focus-within > .sub-menu,
.nav-item.is-open > .sub-menu { display: block; }

@media (hover: hover) and (pointer: fine) {
	.navlinks .nav-item:hover > .sub-menu { display: block; }
}

.sub-menu li { list-style: none; border-bottom: 1px solid rgba(17, 16, 16, .12); }
.sub-menu li:last-child { border-bottom: none; }
.sub-menu a {
	display: block; color: var(--ink); text-decoration: none;
	font-weight: 700; font-size: 13.5px; letter-spacing: .02em; text-transform: uppercase;
	padding: 15px 20px; border-radius: 0;
	white-space: nowrap; transition: color .12s, background .12s, box-shadow .12s;
}
.sub-menu a:hover,
.sub-menu a:focus-visible,
.sub-menu a.current {
	color: var(--ink);
	background: rgba(17, 16, 16, .05);
	/* Red marker down the left edge, drawn with an inset shadow so it costs no
	 * layout and the text never shifts on hover. */
	box-shadow: inset 3px 0 0 0 var(--red);
}

/* hero */
/*
 * Hero background: a photograph of a UFFC demonstration at Whitehall.
 *
 * The image is DELIBERATELY BLURRED, and blurred in the file itself rather than
 * with `filter: blur()`. Faces of people at a demonstration must not be
 * recoverable from a police monitoring organisation's website: a CSS filter only
 * changes what a browser draws, leaving the sharp original downloadable at its
 * URL. scripts/blur-hero-image.ps1 discards the detail by downscaling and
 * re-enlarging, so it cannot be sharpened back.
 *
 * The filename says "anonymised" on purpose. Do not replace this with a sharp
 * version, and if you swap the photograph, run it through that script first.
 *
 * The gradient over it is not decoration, it is what keeps the text readable.
 * The photo has a bright grey sky and pale stone buildings; paper-coloured type
 * straight on top of that would fail contrast badly. The gradient is heaviest on
 * the left, where the headline and body copy sit, and lifts towards the right so
 * the banners and portraits people are holding stay visible.
 *
 * `background-position: center 82%` favours the lower part of the frame, where
 * the banners and the portraits families are holding are. At the default centre
 * the hero cropped to sky and stonework, which says nothing.
 */
.hero {
	background-color: var(--ink);
	background-image:
		linear-gradient(100deg, rgba(17,16,16,.94) 0%, rgba(17,16,16,.86) 38%, rgba(17,16,16,.58) 100%),
		url('../images/hero-anonymised.jpg');
	background-size: cover;
	background-position: center 82%;
	background-repeat: no-repeat;
	color: var(--paper);
	position: relative;
	overflow: hidden;
	padding: 70px 0 64px;
}
/*
 * The CSS-drawn crowd silhouettes were a stand-in for exactly this photograph.
 * Kept in the markup (template-parts/hero.php) rather than deleted, so removing
 * the image falls back to the original design instead of a flat black band.
 */
.crowd { display: none; }
.fig { position: relative; display: flex; flex-direction: column; align-items: center; }
.fig .head { width: 66px; height: 66px; border-radius: 50%; background: #2b2825; }
.fig .body { width: 38px; height: 210px; background: #2b2825; margin-top: -2px; }
.fig.sign .placard { position: absolute; top: -64px; left: 50%; transform: translateX(-6px); width: 76px; height: 60px; background: #2b2825; }
.fig.sign .stick { position: absolute; top: -6px; left: 50%; width: 4px; height: 70px; background: #2b2825; }
.hero-inner { position: relative; z-index: 2; }
.eyebrow { display: flex; align-items: center; gap: 12px; font-family: 'IBM Plex Mono', monospace; font-size: 13px; letter-spacing: .12em; text-transform: uppercase; color: var(--grey); margin-bottom: 34px; }
.eyebrow .bar { width: 28px; height: 2px; background: var(--red); display: inline-block; }
h1.hero-title { font-weight: 900; font-size: clamp(44px, 7.4vw, 116px); line-height: .94; letter-spacing: -.01em; text-transform: uppercase; max-width: 18ch; }
/* Paired with .brand .red above: both halves of the wordmark change together,
   or the header and the hero disagree with each other. */
/* h1.hero-title .red { color: var(--red); } */
/*
 * Single-sentence standfirst (client wording, 2026-09-01). The measure is wider
 * and the type a step smaller than the mockup's: that line is ~140 characters,
 * and at the original 26ch / 32px it wrapped to six lines and swamped the
 * headline above it. 42ch keeps it to two or three lines at desktop width.
 *
 * .hero-lede is gone with the second paragraph it styled.
 */
.hero-sub { font-size: clamp(20px, 2.1vw, 27px); color: var(--grey); max-width: 42ch; margin: 30px 0 34px; line-height: 1.34; }
/* .hero-btns is gone with the last button in it (2026-09-10). .btn and its
   variants stay — they are still used on the 404, search, resource and event
   templates. */
.btn { padding: 15px 24px; border-radius: 8px; font-weight: 700; font-size: 15px; text-decoration: none; display: inline-flex; align-items: center; gap: 9px; transition: transform .12s; border: none; cursor: pointer; font-family: inherit; }
.btn:hover { transform: translateY(-2px); }
.btn-red { background: var(--red); color: #fff; }
.btn-out { background: transparent; color: var(--paper); border: 2px solid rgba(247, 243, 234, .5); }
.hero-social { display: flex; gap: 16px; margin-top: 28px; }
.hero-social a { color: var(--paper); opacity: .8; font-size: 13px; font-weight: 700; text-decoration: none; letter-spacing: .04em; }
.hero-social a:hover { opacity: 1; color: var(--red); }

/* three actions */
.actions { padding: 56px 0 10px; }
.action-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.acard { border: 2px solid var(--ink); border-radius: 14px; padding: 28px; display: flex; flex-direction: column; min-height: 180px; }
.acard h3 { font-weight: 800; font-size: 20px; text-transform: uppercase; letter-spacing: -.005em; margin-bottom: 10px; }
.acard p { font-size: 14.5px; color: #4a453d; margin-bottom: auto; }
.acard .go { font-weight: 800; margin-top: 18px; text-decoration: none; color: var(--red); }

/* latest — dark band */
/* Latest from us, and the same list on the archives, the posts index and search.
 *
 * Light since 2026-09-08, at the client's request. It was a dark band, which on
 * the homepage now sits directly under a dark hero and directly above a dark
 * footer: three dark blocks with one light strip of header on top. On the
 * archives it was worse, because .page-hero above it is dark too, so those pages
 * were dark from top to bottom.
 *
 * This is var(--paper), the same off-white as the header and every other light
 * section, rather than pure #fff. Pure white against the paper header reads as a
 * mistake rather than a choice. If literal white is wanted, this one value is
 * the only thing to change.
 *
 * Two tokens cannot come along unchanged: --line is cream at 14% opacity and
 * --grey is a pale grey, both built for dark backgrounds. On paper the first is
 * invisible and the second is unreadable, so the rows below set their own. */
.latest { background: var(--paper); color: var(--ink); padding: 66px 0; }
.sec-head { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; margin-bottom: 26px; }
.sec-title { font-weight: 900; font-size: clamp(30px, 4.6vw, 50px); text-transform: uppercase; letter-spacing: -.01em; }
.seeall { color: var(--red); font-weight: 700; font-size: 14px; text-decoration: none; white-space: nowrap; }
/* Rules and dates are set for a LIGHT background: --line and --grey are both
   defined for the dark panels and disappear here. See the note on .latest. */
/* Date column was 82px, which fitted "30 JUL". It carries a year since the
   archive migration (see npmp_row_date) and "30 JUL 2022" needs about 104px at
   13px in the mono face. */
/* Last column is auto, not a fixed 60px: it held "Read →" until resource rows
   started saying "↓ Download", which is half as wide again and would have
   pushed out of a fixed column. */
.row { display: grid; grid-template-columns: 104px 130px 1fr auto; gap: 18px; align-items: center; padding: 20px 0; border-top: 1px solid rgba(17, 16, 16, .14); transition: padding-left .16s; }
.row:last-child { border-bottom: 1px solid rgba(17, 16, 16, .14); }
.row .date { font-family: 'IBM Plex Mono', monospace; font-weight: 600; font-size: 13px; color: #5a544c; }
.row .kind { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; background: var(--red); color: #fff; padding: 4px 10px; border-radius: 999px; justify-self: start; }
.row .htxt { font-weight: 700; font-size: 17px; }
.row .read { color: var(--red); font-weight: 700; font-size: 14px; text-align: right; text-decoration: none; }
.row:hover { padding-left: 8px; }

/* dark inset band. Used by archive-npmp_event.php. */
.band { padding: 64px 0; }
.band-inner { background: var(--panel); color: var(--paper); border-radius: 18px; padding: 48px; display: grid; grid-template-columns: 1.15fr 1fr; gap: 44px; }
.band h2 { font-weight: 900; font-size: clamp(26px, 3.4vw, 38px); line-height: 1.1; text-transform: uppercase; margin-bottom: 18px; }
.band h2 .red { color: var(--red); }
.band p { font-size: 15px; color: #bdb7ab; max-width: 44ch; margin-bottom: 26px; }
/* Currently unused: the homepage casework band these styled was removed on
   2026-09-04 (see front-page.php). Kept so restoring it is a markup paste. */
.steps { display: flex; flex-direction: column; gap: 12px; }
.step { background: rgba(247, 243, 234, .06); border: 1px solid var(--line); padding: 16px 18px; font-size: 14px; display: flex; gap: 12px; border-radius: 10px; }
.step b { color: var(--paper); }
.step span:last-child { color: #b4aea2; }
.step .n { font-family: 'IBM Plex Mono', monospace; color: var(--red); font-weight: 700; }

/* resources */
.res { padding: 20px 0 84px; }
.filters { display: flex; gap: 10px; flex-wrap: wrap; margin: 24px 0 28px; }
.chip { border: 2px solid var(--ink); background: transparent; color: var(--ink); border-radius: 999px; padding: 9px 18px; font-weight: 600; font-size: 14px; cursor: pointer; font-family: inherit; transition: all .12s; }
.chip[aria-pressed="true"] { background: var(--ink); color: var(--paper); }
.res-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.rcard { border: 2px solid var(--ink); border-radius: 14px; padding: 24px; display: flex; flex-direction: column; }
.rcard .type { font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--red); margin-bottom: 12px; }
.rcard h4 { font-weight: 700; font-size: 18px; margin-bottom: 8px; }
.rcard p { font-size: 14px; color: #5a544c; margin-bottom: auto; }
.rcard .dl { margin-top: 16px; font-weight: 700; color: var(--ink); text-decoration: none; }
.hidden { display: none; }
.no-results { font-size: 15px; color: #5a544c; padding: 20px 0; display: none; }
.no-results.is-visible { display: block; }

/* events */
.events-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; padding: 20px 0 84px; }
.ecard { border: 2px solid var(--ink); border-radius: 14px; padding: 24px; display: flex; flex-direction: column; }
.ecard .when { font-family: 'IBM Plex Mono', monospace; font-size: 12px; font-weight: 700; color: var(--red); margin-bottom: 10px; text-transform: uppercase; letter-spacing: .04em; }
.ecard h4 { font-weight: 700; font-size: 18px; margin-bottom: 8px; }
.ecard .where { font-size: 13px; color: var(--grey); margin-bottom: 10px; }
.ecard p { font-size: 14px; color: #5a544c; margin-bottom: auto; }
.ecard .go { margin-top: 16px; font-weight: 700; color: var(--red); text-decoration: none; }

/* updates (solidarity space) */
/* "Our next solidarity space" — the single newest update.

   The poster sits BESIDE the details rather than above them. These posters are
   square, because they are made for Instagram, and a square image at full
   container width is around 1100px tall on a desktop: it swamps the page and
   pushes the date and venue below the fold. Beside it, the poster stays large
   enough to read and the practical details stay visible at the same time.

   The image is never cropped. It carries the date, time and place, so cutting
   it to fit would throw away the actual information. */
.next-space {
	border: 2px solid var(--ink); border-radius: 14px; overflow: hidden; margin: 20px 0 0;
	display: grid; grid-template-columns: minmax(0, 440px) 1fr; align-items: start;
}
.next-space-image { line-height: 0; background: var(--panel); }
.next-space-image img { width: 100%; height: auto; display: block; }
.next-space-body { padding: 30px 32px 34px; }

/* No poster yet: the details take the full width rather than sitting in a
   narrow column with dead space next to them. */
.next-space.no-image { grid-template-columns: 1fr; }
.next-space-body .when { display: block; font-family: 'IBM Plex Mono', monospace; font-size: 12px; font-weight: 700; color: var(--red); margin-bottom: 10px; text-transform: uppercase; letter-spacing: .04em; }
.next-space-body h3 { font-weight: 900; font-size: clamp(20px, 2.4vw, 26px); line-height: 1.15; margin-bottom: 10px; }
.next-space-body .u-body { font-size: 15px; color: #4a453d; }
.next-space-body .u-body p + p { margin-top: 12px; }

.updates-list { display: flex; flex-direction: column; gap: 20px; padding: 20px 0 84px; }
.ucard { border: 2px solid var(--ink); border-radius: 14px; padding: 24px; }
.ucard .when { font-family: 'IBM Plex Mono', monospace; font-size: 12px; font-weight: 700; color: var(--red); margin-bottom: 10px; text-transform: uppercase; letter-spacing: .04em; }
.ucard h4 { font-weight: 700; font-size: 18px; margin-bottom: 8px; }
.ucard .u-body { font-size: 14px; color: #4a453d; }
.ucard img { border-radius: 10px; margin-top: 14px; }

/* The Instagram strip (.instagram-feed / .ig-grid / .ig-tile) was removed on
 * 2026-09-01: the client does not want photographs of people's faces on the
 * site. Rules deleted with it; nothing else used them. See front-page.php. */

/* generic content pages (page.php, single.php, archive.php, search.php, 404.php) */
.page-hero { background: var(--ink); color: var(--paper); padding: 56px 0; }
.page-hero .eyebrow { margin-bottom: 18px; }
.page-hero h1 { font-weight: 900; font-size: clamp(32px, 5vw, 58px); text-transform: uppercase; line-height: 1.02; letter-spacing: -.01em; max-width: 22ch; }
.page-hero .page-lede { font-size: 16px; color: #c9c4b9; max-width: 56ch; margin-top: 18px; }
/*
 * The content rail matches the page hero above it (both use .wrap), so the
 * body copy lines up under the H1 instead of starting a couple of hundred
 * pixels to its right. The readable measure is constrained on .entry-content
 * itself rather than by narrowing and re-centring the container — that was
 * what pushed the text out of alignment with the heading.
 */
.content-band { padding: 56px 0 84px; }

/* Solidarity space pulls its two halves together. The intro copy ("...building
   community resistance and campaigns.") and "Our next solidarity space" are one
   continuous thought, not two subjects, so the default 84px + 20px gap between
   sections read as a break that is not really there.

   Scoped rather than applied to .content-band and .res globally: those two are
   shared with the homepage and the resources page, where the sections either
   side genuinely are separate subjects and need the full separation. */
/* Photographs inside page copy.
 *
 * Client photographs arrive straight from a phone, often portrait, and a
 * portrait image at full column width runs well over a thousand pixels tall.
 * Three of them in a row turned the Campaigns page into a wall of photographs
 * with the copy stranded above and below. Capping the height keeps a photograph
 * an illustration rather than an interruption. Width auto so nothing distorts.
 *
 * Galleries are exempt: those are already constrained by their own columns, and
 * capping height there crops faces out of the frame. */
.entry-content .wp-block-image img { max-height: 460px; width: auto; border-radius: 10px; }
.entry-content .wp-block-gallery img { max-height: none; border-radius: 10px; }
.entry-content figure { margin: 22px 0; }
.entry-content figcaption { font-size: 13px; color: #5a544c; margin-top: 8px; }

/* A three-column gallery stops working on a phone.
 *
 * At 375px each image came out about 146px wide, and because the photographs
 * are a mix of portrait and landscape the row went ragged: two shared a line and
 * the third took the full width on its own. At that size a photograph of a room
 * full of people conveys nothing anyway.
 *
 * Below 600px the gallery becomes a single column at full width. Three images
 * is a reasonable amount of scrolling, and each one is actually legible. */
@media (max-width: 600px) {
	.entry-content .wp-block-gallery.has-nested-images {
		display: flex; flex-direction: column; gap: 14px;
	}
	/* `:not(#individual-image)` is not decoration. WordPress core sizes gallery
	   items with that exact selector, which buys it ID-level specificity while
	   matching every figure. Without repeating the hack, core's
	   `width: calc(33.33% - ...)` wins and the images stay a third of the screen
	   wide even though this rule has already turned the row into a column. */
	.entry-content .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) {
		width: 100%; flex-basis: auto; margin: 0;
	}
	.entry-content .wp-block-gallery.has-nested-images figure.wp-block-image img {
		width: 100%; height: auto; max-height: none;
	}
}

.content-band.tight-bottom { padding-bottom: 32px; }
.res.tight-top { padding-top: 0; }
.content-band.wide .wrap { max-width: 1320px; }
.entry-content { font-size: 16px; color: #2c2a27; max-width: 74ch; }
.entry-content h2 { font-weight: 800; font-size: 26px; text-transform: uppercase; margin: 34px 0 14px; }
.entry-content h2:first-child { margin-top: 0; }
.entry-content h3 { font-weight: 700; font-size: 20px; margin: 26px 0 12px; }
.entry-content p { margin-bottom: 16px; line-height: 1.65; }
.entry-content ul, .entry-content ol { margin: 0 0 16px 22px; }
.entry-content li { margin-bottom: 6px; }
.entry-content a { color: var(--red); }
.entry-content blockquote { border-left: 3px solid var(--red); padding-left: 18px; color: #4a453d; margin: 20px 0; }
/*
 * Core's File block, used wherever a post offers a PDF. Left alone it renders a
 * near-black pill with the theme's red link colour inside it, which is both
 * off-brand and close to unreadable. Styled here to match .btn-red, so a
 * download reached from a post looks like a download reached from a resource.
 *
 * The filename link is separated from the button rather than sitting flush
 * against it: they are two different actions (open in the browser, save to the
 * device) and core gives them no gap at all.
 */
.entry-content .wp-block-file { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin: 22px 0; }
.entry-content .wp-block-file a:first-child { font-weight: 700; }
.entry-content .wp-block-file__button {
	background: var(--red);
	color: #fff;
	border-radius: 8px;
	padding: 11px 20px;
	font-weight: 700;
	font-size: 14px;
	text-decoration: none;
	transition: transform .12s;
}
.entry-content .wp-block-file__button:hover { color: #fff; transform: translateY(-2px); }
.entry-meta { font-family: 'IBM Plex Mono', monospace; font-size: 12px; color: var(--grey); text-transform: uppercase; letter-spacing: .06em; margin-bottom: 20px; }
.archive-list { display: flex; flex-direction: column; }
.pagination { display: flex; gap: 10px; justify-content: center; margin-top: 40px; flex-wrap: wrap; }
.pagination a, .pagination span { border: 2px solid var(--ink); border-radius: 8px; padding: 8px 14px; text-decoration: none; color: var(--ink); font-weight: 700; font-size: 14px; }
.pagination .current { background: var(--ink); color: var(--paper); }
.search-form { display: flex; gap: 10px; max-width: 520px; margin-bottom: 10px; }
.search-form input[type="search"] { flex: 1; border: 2px solid var(--ink); border-radius: 8px; padding: 12px 16px; font-family: inherit; font-size: 15px; background: var(--paper); color: var(--ink); }
.search-form button { border: none; }
.error-404 .wrap { padding: 90px 30px; text-align: center; }
.error-404 h1 { font-size: clamp(60px, 10vw, 140px); font-weight: 900; color: var(--red); line-height: 1; }
.error-404 p { margin: 18px 0 28px; font-size: 17px; color: #4a453d; }

/* footer */
/* margin-top:auto is what actually pins the footer to the bottom of a short
 * page — see the flex rules on body. flex-shrink:0 stops it being squashed
 * when content is tall. */
footer { background: var(--ink); color: var(--paper); padding: 52px 0 36px; margin-top: auto; flex-shrink: 0; }
/* First column was 2fr to hold the strapline paragraph. That went on 2026-09-10
   and left the widest column holding only a two-line wordmark and three social
   links, so the footer read as a gap with three columns pushed to the right. */
.foot-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1.3fr; gap: 30px; }
.foot h5 { font-weight: 900; font-size: 19px; text-transform: uppercase; margin-bottom: 14px; letter-spacing: -.005em; }
.foot p { font-size: 13px; color: var(--grey); max-width: 30ch; }
.foot a { color: var(--paper); text-decoration: none; display: block; font-size: 14px; margin-bottom: 9px; }
.foot a:hover { color: var(--red); }
.foot .label { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: #6b665e; margin-bottom: 14px; font-weight: 700; }
.foot .mail { color: var(--red); font-weight: 700; }
.foot .social-row { display: flex; gap: 14px; margin-top: 14px; }
.foot .social-row a { margin-bottom: 0; }
.foot-bottom { border-top: 1px solid var(--line); margin-top: 36px; padding-top: 20px; display: flex; justify-content: space-between; font-size: 12px; color: #6b665e; flex-wrap: wrap; gap: 8px; }
.foot-bottom a { display: inline; color: #6b665e; }
.foot-bottom a:hover { color: var(--red); }

@media (max-width: 960px) {
	.navlinks { display: none; }
	.donate { display: none; }
	.support { display: none; }
	.burger { display: block; }
	.action-grid, .res-grid, .foot-grid, .events-grid { grid-template-columns: 1fr; }
	.band-inner { grid-template-columns: 1fr; }
	.next-space { grid-template-columns: 1fr; }

	/* The hero headline was overflowing its own container on phones.
	 *
	 * `clamp(44px, 7.4vw, 116px)` has a 44px FLOOR, so below about 595px the
	 * fluid middle value stops doing anything. At 375px the type stayed 44px
	 * while the column shrank to 300px, and "MONITORING" is 319px wide at that
	 * size. At 320px it spilled by 74px.
	 *
	 * This did not show up in the earlier overflow sweep because the hero clips
	 * rather than scrolls, so the document width never changed. Comparing each
	 * element's scrollWidth against its own clientWidth is what caught it.
	 *
	 * A steeper vw factor with a lower floor keeps the headline filling the
	 * width on a phone without any single word outgrowing the column. */
	h1.hero-title { font-size: clamp(28px, 9.5vw, 44px); max-width: 100%; }

	/* Same column, same problem: the eyebrow is a flex row that would not wrap,
	   so "COMMUNITY LED · INDEPENDENT · ABOLITIONIST" pushed 44px past the
	   edge at 320px. */
	.eyebrow { flex-wrap: wrap; gap: 8px 12px; margin-bottom: 22px; }
	/* The date carries a year now. There is no room for "30 JUL 2022" on one
	   line at this width without eating the title, so the column is widened a
	   little and the date wraps to two lines inside it. align-items on .row is
	   centre, so a two-line date stays centred against the title. */
	/* Same reasoning as the desktop rule: auto, so the column fits whichever
	   label the row carries. At 390px "↓ Download" still leaves the title
	   around 195px, which is enough for two or three words per line. */
	.row { grid-template-columns: 58px 1fr auto; gap: 10px; }
	.row .date { line-height: 1.35; }
	/* 46px was one pixel or two short of "Read →", so every row in every
	   archive broke it across two lines. */
	.row .read { white-space: nowrap; }
	.row .kind { display: none; }
	.crowd { opacity: .28; }

	/* mobile nav — burger opens a stacked panel; see assets/js/nav.js */
	nav { position: relative; flex-wrap: wrap; padding: 16px 18px; gap: 14px; }

	/* THE BRAND LOCKUP MUST WRAP BELOW 960px.
	 *
	 * `.brand` carries white-space:nowrap, which is load-bearing on desktop: it
	 * keeps the five tabs on one row. On a phone it did the opposite of its job.
	 * The wordmark plus "EST. 2012 · NORTH OF ENGLAND" is one unbreakable 473px
	 * line, so at 375px EVERY PAGE ON THE SITE scrolled sideways by 147px.
	 *
	 * Found by measuring scrollWidth against clientWidth on 2026-09-05, not by
	 * looking at screenshots, which is why it survived this long: a full-page
	 * capture renders at the document's own width, so horizontal overflow is
	 * invisible in one. It shows up on a real phone immediately.
	 *
	 * The tabs are display:none at this width, so releasing nowrap here costs
	 * nothing it was protecting. */
	/* flex-basis 0, not auto: with `auto` the brand claims its full natural
	   width (342px of a 375px row) and the burger has nowhere to sit. */
	.brand { white-space: normal; flex: 1 1 0; min-width: 0; gap: 10px; }
	.brand-text { min-width: 0; }
	.brand .name { font-size: 15px; }
	.brand .est { font-size: 10px; letter-spacing: .06em; }
	.brand-logo { width: 38px; }

	/* Put the burger back beside the logo.
	 *
	 * In the DOM it is last (brand, navlinks, nav-actions, burger), which is
	 * right for the desktop row. With flex-wrap on a narrow screen it dropped to
	 * a line of its own under the brand, and once the panel was open it ended up
	 * BELOW the Donate button, so the control that closes the menu sat at the
	 * bottom of it.
	 *
	 * Ordering the flex children fixes it without touching the markup, which
	 * keeps the source order sensible for screen readers and keyboard tabbing. */
	.brand { order: 1; }
	.burger { order: 2; }
	.navlinks { order: 3; }
	.nav-actions { order: 4; }

	/* Long unbroken URLs in the client's own copy (Legal support has several
	   pasted in full) push the page wider than the screen. Break them rather
	   than editing their text. */
	.entry-content a, .entry-content p { overflow-wrap: anywhere; }

	/* The burger was 26x23px, well under the 44px minimum, and on a phone it is
	   the ONLY way to reach any other page. */
	.burger {
		font-size: 26px; line-height: 1; padding: 9px 10px;
		margin: 0 0 0 auto; /* auto pushes it to the right of its row. Do NOT use
		                       the margin shorthand with a negative left value
		                       here: it silently cancels that. */
		min-width: 44px; min-height: 44px; flex: 0 0 auto;
		display: flex; align-items: center; justify-content: center;
	}
}

/* Small phones. 320px is still real: the first-generation iPhone SE, and any
   Android whose owner has turned the display size up. Two things only fail
   this far down, and both are a row that refuses to become two rows. */
@media (max-width: 400px) {
	/* Section title and its "see all" link stop competing for one line. */
	.sec-head { flex-wrap: wrap; gap: 6px 16px; }
	.sec-head .seeall { flex: 0 0 auto; }

	/* The 404 search box and its button. */
	.search-form { flex-wrap: wrap; }
	.search-form input[type="search"] { flex: 1 1 100%; }
	.search-form button { flex: 1 1 100%; }
	.nav-open .navlinks,
	.nav-open .nav-actions {
		display: flex;
		flex-direction: column;
		align-items: flex-start;
		width: 100%;
		gap: 14px;
		margin-top: 18px;
		padding-top: 18px;
		border-top: 1px solid rgba(17, 16, 16, .14);
	}
	.nav-open .support,
	.nav-open .donate { display: inline-flex; }
	.nav-open .nav-actions { flex-direction: row; }

	/* Nested dropdowns become an inline accordion on mobile: tapping a
	 * parent expands its children in place (no navigation, no floating
	 * panel) — the absolutely-positioned desktop dropdown would either
	 * overflow the narrow viewport or float disconnected from its parent. */
	.navlinks .nav-item { position: static; width: 100%; }
	.navlinks .nav-toggle {
		width: 100%; justify-content: space-between; padding: 6px 0;
	}
	.sub-menu {
		position: static;
		width: 100%;
		margin-top: 8px;
		padding: 0 0 0 16px;
		min-width: 0;
		background: transparent;
		border: none;
		border-top: none;
		box-shadow: none;
	}
	/*
	 * Suppress hover and focus opening at this width: the burger panel is an
	 * accordion, where only an explicit tap should open anything. Inside the
	 * panel, focus-within would otherwise expand a submenu the moment focus
	 * entered it.
	 *
	 * `:not(.is-open)` is load-bearing, and its absence was a real bug.
	 *
	 * These two selectors are (0,4,0). The rule that opens a submenu was
	 * `.nav-item.is-open > .sub-menu`, only (0,3,0), so `display: none` won
	 * every time. And because a tap FOCUSES the <button> it lands on,
	 * `:focus-within` matched immediately — so on every device, phone included,
	 * tapping a dropdown parent added `.is-open` and the CSS instantly overrode
	 * it. Four of the five nav items are parents, which left Contact us as the
	 * only page reachable from the menu on a phone.
	 *
	 * Excluding `.is-open` here says what is actually meant, rather than relying
	 * on source order to break a specificity tie. Found 2026-09-05 by driving
	 * the menu in a real browser; no screenshot would have shown it.
	 */
	.navlinks .nav-item:hover:not(.is-open) > .sub-menu,
	.navlinks .nav-item:focus-within:not(.is-open) > .sub-menu { display: none; }
	.navlinks .nav-item.is-open > .sub-menu { display: block; }

	.sub-menu li { border-bottom: 1px solid rgba(17, 16, 16, .10); }
	.sub-menu li:last-child { border-bottom: none; }
	.sub-menu a { color: var(--ink); padding: 11px 0; font-size: 13px; }
	.sub-menu a:hover, .sub-menu a.current { color: var(--red); background: none; box-shadow: none; }
}
