/*
Theme Name: Skier & Snowboarder
Theme URI: https://www.skierandsnowboarder.com/
Author: Will Dobson — Willpowered Design
Author URI: https://willpowered.design
Description: Lightweight block theme for Skier & Snowboarder Magazine. Replaces the legacy "opti" theme. Design tokens derived from the Spring 2026 print issue — see DESIGN-TOKENS.md.
Version: 0.106
Requires at least: 6.5
Tested up to: 6.9
Requires PHP: 8.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: skier-snowboarder
Tags: blog, news, magazine, block-patterns, full-site-editing
*/

/* ==========================================================================
   1. Foundations
   ========================================================================== */

:root {
	--ss-shell: 1280px;
	--ss-radius: 2px;          /* near-square: the display face is geometric */
	--ss-line: #e3e8ec;
	--ss-shadow: 0 1px 2px rgb(35 31 32 / 6%), 0 8px 24px rgb(35 31 32 / 6%);
	--ss-ease: cubic-bezier(.2, .7, .3, 1);

	/* Brand blue with a touch of depth. The two stops are only ~9% apart in
	   lightness, so it reads as one colour that happens to have life in it
	   rather than as a gradient — anything wider starts to look like a 2010
	   web page. Used on the large panels only; on a small pill the shift is
	   invisible and just costs a paint. */
	--ss-brand-gradient: linear-gradient(135deg, #005c81 0%, #0077a7 100%);
	--ss-brand-gradient-v: linear-gradient(180deg, #005c81 0%, #0077a7 100%);

	/* Hairline around advert creatives, so artwork supplied on a white
	   background still reads as a defined, bought space. Kept as a token
	   because it needs inverting in dark mode. */
	--ss-ad-outline: rgb(0 0 0 / 15%);
}

/* Good Times ships as a single Regular weight. Without this, a browser asked
   for a bold heading synthesises one by smearing the strokes, which looks crude
   on a geometric display face. If the Bold/Light cuts are ever licensed, add
   them as @font-face entries in theme.json and this rule can relax. */
h1, h2, h3, h4, h5, h6,
.wp-block-post-title,
.wp-block-site-title,
.ss-kicker,
.ss-wordmark {
	font-synthesis: none;
	-webkit-font-smoothing: antialiased;
}

/* Respect users who ask for less motion — all transitions here are decorative. */
@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: .01ms !important;
		transition-duration: .01ms !important;
		scroll-behavior: auto !important;
	}
}

/* Keyboard focus must always be visible and never rely on colour alone. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
	outline: 3px solid var(--wp--preset--color--accent);
	outline-offset: 2px;
	border-radius: var(--ss-radius);
}

/* Visually hidden, but still announced by screen readers. Block themes don't
   ship this reliably, and the skip link depends on it. */
.screen-reader-text {
	position: absolute !important;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* The skip link must become visible when focused, or it helps nobody. */
.skip-link:focus {
	position: fixed !important;
	top: .5rem;
	left: .5rem;
	z-index: 999;
	width: auto;
	height: auto;
	margin: 0;
	padding: .75rem 1.1rem;
	clip-path: none;
	background: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
	font-family: var(--wp--preset--font-family--display);
	font-size: .85rem;
	text-transform: uppercase;
	letter-spacing: .04em;
	text-decoration: none;
}

img {
	max-width: 100%;
	height: auto;
}

/* ==========================================================================
   2. Header
   ========================================================================== */

/* Sticky belongs on the TEMPLATE PART WRAPPER, not on .ss-header.

   A sticky element only travels inside its own parent's box, and WordPress
   wraps the header part in an element exactly as tall as the header — 184px of
   parent for a 184px child. There was nowhere to stick to, so the header simply
   scrolled off: measured at top = -2096px with scrollY 2096. */
header.wp-block-template-part:has( .ss-header ) {
	position: sticky;
	top: 0;
	z-index: 100;
	transition: transform .28s var(--ss-ease);
}

/* Hidden while the reader is scrolling DOWN — they are heading into the page
   and the navigation is not what they want. It slides back in the moment they
   scroll up, which is the gesture that means "I want to get somewhere else".
   The transform sits on the sticky wrapper because that is the element that
   is actually pinned. */
header.wp-block-template-part:has( .ss-header.is-hidden ) {
	transform: translateY( -100% );
}

@media (prefers-reduced-motion: reduce) {
	header.wp-block-template-part:has( .ss-header ) {
		transition: none;
	}
}

.ss-header {
	background: var(--wp--preset--color--base);
	border-bottom: 1px solid var(--ss-line);
	transition: box-shadow .25s var(--ss-ease);
}

/* Condensed state, applied by enhance.js once the reader is past the lead.
   At full size the masthead is 184px — a fifth of a laptop viewport — far too
   much to keep pinned. Condensed, it keeps the wordmark, the search and the
   whole navigation, and drops the masthead advert: that is the tallest element
   and the one impression the reader has already had. */
.ss-header.is-condensed {
	box-shadow: 0 2px 14px rgb(35 31 32 / 12%);
}

/* Condensed = the navigation strip ALONE. The masthead row — logo, advert and
   search — is dropped entirely rather than shrunk. A reader who has scrolled
   back up wants to get somewhere; a miniature version of the masthead is just
   a smaller obstacle. Home is still one click away via the first nav item. */
.ss-header.is-condensed .ss-header__bar {
	display: none;
}

.ss-header__bar,
.ss-logo {
	transition: padding .22s var(--ss-ease), width .22s var(--ss-ease);
}

@media (prefers-reduced-motion: reduce) {
	.ss-header,
	.ss-header__bar,
	.ss-logo {
		transition: none;
	}
}

.ss-header__bar {
	padding-block: .7rem;
}

/* THE reason the masthead kept reading "too high" across three attempts.

   WordPress's flow layout puts `margin-block-start: 24px` on every sibling
   after the first, and the nav strip is a sibling of the bar inside .ss-header.
   That 24px of white sat BELOW the bar and above the nav, so the white band a
   reader actually sees was 136px tall while the bar itself was 112px. The logo,
   advert and search box were all dead-centre of the BAR — and therefore 7px
   above the centre of the BAND, which is what the eye was picking up.

   Measuring the bar said "centred". Measuring the band said "7px high". The
   band is the thing a person looks at, so the band is what had to be measured.
   Removing the stray margin collapses the two into one. */
.ss-header > * {
	margin-block: 0 !important;
}

/* The masthead row and the nav strip must line up with the 1440px grid below,
   not the 800px reading column. Left as a constrained group they inherited the
   content width and the header looked narrow against the page. */
.ss-header__bar > .wp-block-group,
.ss-nav > .wp-block-group,
.ss-nav > .wp-block-navigation,
.ss-nav__inner,
.ss-header__inner {
	max-width: var(--wp--style--global--wide-size) !important;
	margin-inline: auto !important;
	width: 100%;
}

.ss-header__inner {
	gap: 1rem;
}

/* Masthead logo.
   The SVG is generated from the Good Times glyphs, so it reproduces the printed
   masthead exactly. It is applied as a CSS mask rather than an <img> so the
   colour is controlled from the stylesheet — one file serves both the blue
   version and the reversed white one in the footer. */
.ss-logo {
	display: block;
	flex: 0 0 auto;
	width: clamp(150px, 20vw, 260px);
	aspect-ratio: 3.18;
	background-color: var(--wp--preset--color--brand);
	/* The mask box is centred by the flex row, but the artwork is not centred
	   inside the box — the SVG carries ~6px more empty space below the glyphs
	   than above, so the wordmark reads high. Measured on the rendered page:
	   ink occupied rows 15-91 of a 15-97 box. Nudging the mask down by half
	   that slack centres the ink rather than the box. */
	-webkit-mask: url("assets/logo.svg") no-repeat center calc(50% + 3px) / contain;
	mask: url("assets/logo.svg") no-repeat center calc(50% + 3px) / contain;
	transition: background-color .18s var(--ss-ease);
}

.ss-logo:hover,
.ss-logo:focus-visible {
	background-color: var(--wp--preset--color--accent);
}

.ss-footer .ss-logo {
	background-color: var(--wp--preset--color--base);
	width: clamp(150px, 18vw, 220px);
}

/* Legacy text wordmark — retained for the footer fallback. */
.ss-wordmark a,
.ss-wordmark {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 400;
	text-transform: uppercase;
	letter-spacing: .02em;
	color: var(--wp--preset--color--brand);
	text-decoration: none;
	font-size: clamp(.92rem, 2.4vw, 1.5rem);
	line-height: 1.06;
}

/* The site title is long ("Skier and Snowboarder Magazine"), so it must be
   allowed to wrap — pinned to one line it measures 414px and forces a
   horizontal scrollbar on a 390px phone. Wrapping to two lines also echoes the
   printed masthead, which stacks SKIER over & SNOWBOARDER. `min-width: 0` is
   required because a flex item defaults to min-content width and would
   otherwise refuse to shrink. */
.ss-wordmark {
	min-width: 0;
	max-width: 14ch;
	overflow-wrap: break-word;
}

.ss-header__inner {
	flex-wrap: nowrap;
}

.ss-header__actions {
	flex: 0 0 auto;
}

.ss-wordmark a:hover {
	color: var(--wp--preset--color--accent);
}

/* Navigation strip beneath the masthead — the section rail. */
.ss-nav {
	background: var(--ss-brand-gradient);
}

.ss-nav .wp-block-navigation {
	font-family: var(--wp--preset--font-family--display);
	font-size: .78rem;
	text-transform: uppercase;
	letter-spacing: .045em;
	gap: 0;
}

/* WordPress flow layout puts a 24px top margin on <main> as the next sibling,
   leaving a 25px band of white between the nav strip and the hero. Cut by 60%
   as asked — 25px to 10px. !important because the flow-layout rule beats a
   plain selector. */
.wp-site-blocks > main {
	margin-block-start: .56rem !important;
}

/* Reversed-out links on the brand strip. The navigation block also carries
   `textColor: base` in the template — belt and braces, because without white
   here the links inherit body ink and land at 2.21:1 against the brand blue,
   which fails WCAG AA badly. Verify with a contrast measurement, not by eye. */
.ss-nav a,
.ss-nav .wp-block-navigation a,
.ss-nav .wp-block-navigation-item__content,
.ss-nav .wp-block-navigation-item__content:visited {
	color: var(--wp--preset--color--base);
	text-decoration: none;
	/* Horizontal padding is what separates the items, so it also sets how wide
	   each hover block is — hence padding rather than a flex gap, which would
	   leave dead space between targets.

	   Vertical was .95rem (15.2px a side, a 70px strip). Now .51rem, taking the
	   requested 14px off the strip. Still clears the 24px WCAG 2.5.8 minimum
	   target height with room to spare. */
	padding: .51rem 1.15rem;
	display: block;
	transition: background-color .18s var(--ss-ease);
}

.ss-nav a:hover,
.ss-nav .wp-block-navigation-item__content:hover,
.ss-nav .current-menu-item > .wp-block-navigation-item__content {
	background: rgb(255 255 255 / 14%);
	color: var(--wp--preset--color--base);
	text-decoration: none;
}

/* The tighter item padding that keeps the nine menu items on one line.

   This used to stop at 1279px, which produced a genuinely odd measurement: one
   row at 1220px, two rows at 1280px. The bar was wrapping again at the exact
   width the padding reverted to its roomier value. It now applies at every
   width where the inline bar is shown.

   Handover history, each measured rather than guessed: 1220 -> 1280 when
   Subscribe became the ninth item (one-line threshold 1274px), then
   1280 -> 1460 on 27 Aug 2026 when "Summer Slopes" became the tenth
   (David, 27 Aug). Measured threshold: wraps at 1441px, single line at
   1450px, so the breakpoint sits at 1460 for clearance. The masthead is
   genuinely full at ten items - nav 1128px + social 192px against a bar
   of viewport minus 112 - so an eleventh cannot be added without either
   dropping one or reworking the bar. */
@media (min-width: 1460px) {
	.ss-nav a,
	.ss-nav .wp-block-navigation a,
	.ss-nav .wp-block-navigation-item__content {
		padding-inline: .7rem;
	}

	.ss-nav .wp-block-navigation-item.has-child > .wp-block-navigation-item__content,
	.ss-nav .wp-block-navigation-submenu > .wp-block-navigation-item__content {
		padding-right: .25rem;
	}

	.ss-nav .wp-block-navigation__submenu-icon {
		margin-right: .7rem;
	}
}

/* The mobile overlay toggle sits on the brand strip, so it must be reversed. */
.ss-nav .wp-block-navigation__responsive-container-open,
.ss-nav .wp-block-navigation__responsive-container-close {
	color: var(--wp--preset--color--base);
}

/* The dropdown chevron must read as belonging to its own label.
   By default the parent link keeps its full right padding and the chevron sits
   outside it, which put the arrow ~16px from "Offers" but flush against the
   next item's hover background — so it looked like it belonged to the wrong
   word. Trimming the parent's right padding pulls the chevron in, and a margin
   after it keeps the next item's hover area clear. */
.ss-nav .wp-block-navigation-item.has-child > .wp-block-navigation-item__content,
.ss-nav .wp-block-navigation-submenu > .wp-block-navigation-item__content {
	padding-right: .3rem;
}

/* Right margin matches the other items' padding so the spacing stays even
   across the whole bar, whether or not an item has a dropdown. */
.ss-nav .wp-block-navigation__submenu-icon {
	margin: 0 1.15rem 0 0;
	padding: 0;
}

/* Hovering either the label or the arrow should highlight the whole item,
   rather than the arrow appearing to belong to nothing. */
.ss-nav .wp-block-navigation-item.has-child:hover > .wp-block-navigation-item__content,
.ss-nav .wp-block-navigation-item.has-child:focus-within > .wp-block-navigation-item__content {
	background: rgb(255 255 255 / 14%);
}

/* Dropdowns. Countries nest under "Resorts", which is what lets the nav drop
   from 22 flat items to 7. */
.ss-nav .wp-block-navigation__submenu-container {
	background: var(--wp--preset--color--base);
	border: 1px solid var(--ss-line);
	box-shadow: var(--ss-shadow);
	min-width: 12rem;
	padding-block: .25rem;
}

.ss-nav .wp-block-navigation__submenu-container a,
.ss-nav .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
	color: var(--wp--preset--color--brand);
	padding: .55rem .9rem;
}

.ss-nav .wp-block-navigation__submenu-container a:hover {
	background: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--accent);
}

/* Inside the mobile overlay the panel is white, so links must revert to brand
   blue — white-on-white would otherwise render them invisible. */
.ss-nav .wp-block-navigation__responsive-container.is-menu-open a,
.ss-nav .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content {
	color: var(--wp--preset--color--brand);
}

@media (prefers-color-scheme: dark) and (min-width: 999999px) { /* dark mode off — see §12 */
	.ss-nav .wp-block-navigation__submenu-container {
		background: #1c2126;
		border-color: #2c3238;
	}

	.ss-nav .wp-block-navigation__submenu-container a,
	.ss-nav .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
		color: var(--wp--preset--color--brand-tint);
	}

	.ss-nav .wp-block-navigation__submenu-container a:hover {
		background: #262c32;
	}
}

/* Social icons, sitting at the right end of the brand strip. */
.ss-nav__inner {
	gap: 1rem;
}

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

.ss-social a {
	display: flex;
	align-items: center;
	justify-content: center;
	/* 40px clears the WCAG 2.2 24px minimum comfortably — icon-only controls
	   are the easiest thing to mis-tap on a phone. */
	width: 40px;
	height: 40px;
	color: var(--wp--preset--color--base);
	border-radius: 2px;
	transition: background-color .18s var(--ss-ease);
}

.ss-social a:hover,
.ss-social a:focus-visible {
	background: rgb(255 255 255 / 16%);
	color: var(--wp--preset--color--base);
}

.ss-social svg {
	width: 19px;
	height: 19px;
	display: block;
}

/* Hidden between 601px and 1023px so the seven menu items keep a single row —
   the icons were taking ~120px and tipping the bar onto a second line at 900px.
   Nothing is lost there: the same three icons sit in the footer.

   Below 601px they come BACK, because the menu has collapsed to a burger by
   then and the blue strip would otherwise be an empty bar of colour. On a phone
   the strip carries the social icons and nothing else. */
/* ---- Collapse the menu across the whole tablet range -------------------- */

/* The block editor's "mobile" overlay setting hands over to a burger at 600px,
   which is WordPress's figure, not this menu's. Measured against the real menu,
   eight items only fit on one line between 900px and 1000px:

       1024px  two rows (73px)      860px  two rows
        960px  one row  (36px)      768px  two rows
        900px  one row  (36px)      600px  two rows

   So iPad portrait AND landscape both sat in a band where the full menu wrapped
   onto two lines and stranded "Contact" on its own. The burger now takes over
   everywhere below 1024px, which is the first width the whole menu reliably
   fits. Below that the phone treatment applies — burger by the search, social
   icons on the strip.

   These override the block library's own rules, which switch the inline menu on
   at 600px. The extra `.ss-header` in each selector is what wins the cascade:
   core uses three classes, so a two-class selector here would tie and depend on
   stylesheet order. */
@media (max-width: 1459px) {
	.ss-header .ss-nav .wp-block-navigation__responsive-container:not(.is-menu-open) {
		display: none;
	}

	.ss-header .ss-nav .wp-block-navigation__responsive-container-open:not(.always-shown) {
		display: flex;
	}
}

/* ---- Phone and tablet: burger up to the masthead, social on the strip ---- */
@media (max-width: 1459px) {
	/* The burger belongs beside the search, where a thumb already is, rather
	   than marooned on the left of a colour band. It is moved with absolute
	   positioning rather than by relocating the node, because the navigation
	   block's own overlay script expects its button to stay inside its own
	   subtree — moving the element breaks opening the menu. */
	.ss-header {
		position: relative;
	}

	/* .ss-nav must NOT be a positioned ancestor, or the button resolves against
	   the strip rather than the header and lands at y=84 instead of y=14. */
	.ss-nav {
		position: static;
	}

	/* Hiding the search freed width the leaderboard immediately grew into,
	   leaving it 1px from the burger at 1140px. The burger is absolutely
	   positioned, so it cannot reserve its own space — the actions column keeps
	   a gutter wide enough for the 45px control plus clearance. */
	.ss-header .ss-header__actions {
		min-width: 4rem;
	}

	/* The MASTHEAD search is hidden while the burger is shown: side by side, the
	   pair ate into the masthead leaderboard at exactly the widths where that
	   advert has least room to give. Search is not lost — a second copy sits
	   inside the slide-out menu (.ss-search--in-menu) — and the burger takes
	   the search's own slot at the right margin instead of sitting inboard. */
	.ss-header .ss-header__actions > .wp-block-search {
		display: none;
	}

	/* Vertically centred on the MASTHEAD, at any masthead height.

	   `top: 50%` would centre it on the whole header — masthead plus strip —
	   which is the old bug that put it at y=94. A fixed `top` is no better: the
	   masthead grows with the leaderboard, measured at 70px on a phone, 85px at
	   1000px and 114px from ~1100px up, so one offset is wrong nearly
	   everywhere (it sat 20px high at 1140px).

	   Instead the button SPANS the masthead — pinned to the header top and
	   stopped 66px short of the bottom, the nav strip's height, which measures a
	   constant 66px at every width the burger appears at. Its bars then centre
	   themselves through the flex box, whatever the masthead is doing. If the
	   strip's height ever changes, this one number changes with it. */
	.ss-nav .wp-block-navigation__responsive-container-open {
		position: absolute;
		top: 0;
		bottom: 66px;
		right: var(--wp--style--root--padding-right, 1rem);
		z-index: 3;
		display: flex;
		align-items: center;
		justify-content: center;
		width: 48px;
		height: auto;
		margin: 0;
		padding: 0;
	}

	/* It was inheriting white from the blue strip it used to sit on. Now that it
	   is on the white masthead, white-on-white made it invisible while still
	   measuring 45x45 — present, focusable, and impossible to see. */
	.ss-nav .wp-block-navigation__responsive-container-open,
	.ss-nav .wp-block-navigation__responsive-container-open svg {
		color: var(--wp--preset--color--brand);
		fill: currentColor;
	}

	/* Core ships a TWO-line icon, which reads as an unfinished burger. The SVG
	   is replaced with three CSS bars drawn on the button itself — brand colour,
	   no template change and no JS, so the block's own open/close script is
	   untouched. The outer pair are the ::before box's borders; the middle bar
	   is ::after, centred on the button (which spans the masthead), so all three
	   stay together wherever the button sits. */
	.ss-nav .wp-block-navigation__responsive-container-open svg {
		display: none;
	}

	.ss-nav .wp-block-navigation__responsive-container-open::before {
		content: "";
		width: 32px;
		height: 23px;
		border-top: 3px solid currentColor;
		border-bottom: 3px solid currentColor;
		box-sizing: border-box;
	}

	.ss-nav .wp-block-navigation__responsive-container-open::after {
		content: "";
		position: absolute;
		left: 50%;
		top: 50%;
		width: 32px;
		height: 3px;
		margin-left: -16px;
		margin-top: -1.5px;
		background: currentColor;
	}

	.ss-nav .ss-social {
		display: flex;
		justify-content: center;
		align-items: center;
		width: 100%;
		gap: 2.5rem;
		padding-block: .35rem;
	}

	/* Without this the strip keeps the height it reserved for a row of menu
	   items, leaving a deep band of blue around three small icons. */
	.ss-nav .ss-social a {
		padding: .45rem;
	}

	/* The strip is now a social bar, so it should not reserve menu height. */
	.ss-nav {
		padding-block: 0;
	}

	.ss-nav .wp-block-navigation {
		position: static;
	}

	/* ---- Slide-in menu panel ----
	   WordPress's default overlay fills the screen and leaves every submenu
	   expanded beneath its parent, which is why this read as a sitemap dumped
	   on a white page rather than a menu. It now slides in from the right and
	   leaves a strip of the blurred page visible, so the reader keeps their
	   place on the page behind. */
	.ss-nav .wp-block-navigation__responsive-container.is-menu-open {
		background: rgb(10 22 30 / 45%) !important;
		backdrop-filter: blur(7px);
		-webkit-backdrop-filter: blur(7px);
		padding: 0;
		animation: ss-menu-fade .22s var(--ss-ease);
	}

	@keyframes ss-menu-fade {
		from { opacity: 0; }
		to   { opacity: 1; }
	}

	/* The panel itself — 86% wide, so a sliver of the page stays on show. */
	.ss-nav .wp-block-navigation__responsive-container.is-menu-open
		.wp-block-navigation__responsive-dialog {
		position: absolute;
		inset: 0 0 0 auto;
		width: 86%;
		max-width: 380px;
		margin: 0;
		padding: 1.25rem 1.4rem 2rem;
		overflow-y: auto;
		background: var(--ss-brand-gradient-v) !important;
		box-shadow: -12px 0 40px rgb(0 0 0 / 30%);
		isolation: isolate;
		animation: ss-menu-slide .28s var(--ss-ease);
	}

	.ss-nav .wp-block-navigation__responsive-container.is-menu-open
		.wp-block-navigation__responsive-dialog::before {
		content: "";
		position: absolute;
		inset: 0;
		z-index: -1;
		/* Photograph plus a scrim, in one layer. Measured with the glyphs hidden,
		   the bare image at 20% left 1.5% of the panel below 3:1 against white
		   text — the snow in the lower half is the brightest possible subject, the
		   same problem the lead headline had. The gradient darkens exactly that
		   region rather than dimming the whole picture. */
		background-image:
			linear-gradient(180deg, rgb(0 46 66 / 30%) 0%, rgb(0 46 66 / 55%) 55%, rgb(0 46 66 / 72%) 100%),
			url("assets/menu-bg-900.webp");
		background-size: cover, cover;
		background-position: center, center;
		background-repeat: no-repeat, no-repeat;
		opacity: .55;
		pointer-events: none;
	}

	@keyframes ss-menu-slide {
		from { transform: translateX(100%); }
		to   { transform: translateX(0); }
	}

	.ss-nav .wp-block-navigation__responsive-container-close {
		top: 1.1rem;
		right: 1.2rem;
		padding: .5rem;
		color: #fff;
	}

	.ss-nav .wp-block-navigation__responsive-container-close svg {
		width: 26px;
		height: 26px;
	}

	/* Top-level items: bigger, and given room to breathe. */
	.ss-nav .is-menu-open .wp-block-navigation__container {
		gap: 0;
		padding-top: 2.5rem;
	}

	/* The list item is the flex row, NOT the link. WordPress renders the chevron
	   as a SIBLING of the link, so making the link a flex container left the
	   chevron to fall onto its own line beneath the label. Laying out the <li>
	   puts label and chevron on one line, with the submenu wrapping full-width
	   underneath. */
	.ss-nav .is-menu-open .wp-block-navigation__container > .wp-block-navigation-item {
		position: relative;
		display: block;
		width: 100%;
		border-bottom: 1px solid rgb(255 255 255 / 16%);
	}

	/* The link must NOT claim the whole row, or the chevron wraps beneath it —
	   measured at 56px below the label before this. Capping the width leaves
	   exactly the chevron's 34px plus a little breathing room. */
	.ss-nav .is-menu-open .wp-block-navigation__container
		> .wp-block-navigation-item > .wp-block-navigation-item__content {
		flex: 1 1 auto;
		width: auto !important;
		padding-right: 46px !important;
	}

	/* Positioned absolutely rather than laid out in the flex row.
	   Three attempts at flex all left the chevron wrapping to its own line 56px
	   below the label — the block's own markup and styles keep forcing the link
	   to a full-width row. Taking the chevron out of the flow sidesteps that
	   entirely: it now sits on the label's optical centre, at a fixed offset
	   derived from the row's 1.6rem padding plus half the line box. */
	.ss-nav .is-menu-open .wp-block-navigation__container
		> .wp-block-navigation-item > .wp-block-navigation__submenu-icon {
		position: absolute;
		top: calc(1.6rem - 4px);
		right: 0;
		flex: 0 0 auto;
	}

	/* Submenu takes the whole next row rather than sitting in the flex flow. */
	.ss-nav .is-menu-open .wp-block-navigation__container
		> .wp-block-navigation-item > .wp-block-navigation__submenu-container {
		flex: 1 0 100%;
	}

	.ss-nav .is-menu-open .wp-block-navigation-item__content {
		display: block;
		font-size: 1.05rem;
		letter-spacing: .06em;
		/* !important: core ships `.wp-block-navigation .has-child
		   .wp-block-navigation-item__content { padding-right: .3em }`, which ties
		   this selector on specificity and loads later — so the rows collapsed to
		   28px and only the parents looked different. Measured, not assumed. */
		padding: 1.6rem .2rem !important;
		color: #fff !important;
	}

	/* ---- Submenus as accordions ----
	   Closed by default; tapping the parent opens one. Previously all of them
	   were expanded at once inside white cards, which ran the menu to three
	   screens. */
	.ss-nav .is-menu-open .wp-block-navigation__submenu-container {
		display: block;
		max-height: 0 !important;
		height: 0 !important;
		min-height: 0 !important;
		/* Padding and line-height still occupied ~24px while closed, which made the
		   two parent rows taller than the five plain ones — the uneven gaps. */
		padding: 0 !important;
		line-height: 0 !important;
		font-size: 0 !important;
		overflow: hidden !important;
		opacity: 0 !important;
		visibility: hidden;
		border: 0 !important;
		margin: 0 !important;
		background: transparent;
		border: 0;
		padding: 0;
		margin: 0;
		position: static;
		width: 100%;
		min-width: 0;
		transition: max-height .3s var(--ss-ease), opacity .2s var(--ss-ease);
	}

	.ss-nav .is-menu-open .has-child.ss-open > .wp-block-navigation__submenu-container {
		max-height: 32rem !important;
		height: auto !important;
		line-height: normal !important;
		font-size: 1rem !important;
		opacity: 1 !important;
		visibility: visible;
		padding-bottom: .5rem;
	}

	.ss-nav .is-menu-open .wp-block-navigation__submenu-container
		.wp-block-navigation-item__content {
		font-size: .92rem;
		padding: .62rem .2rem .62rem 1.1rem;
		color: rgb(255 255 255 / 82%) !important;
		text-transform: none;
		letter-spacing: .02em;
	}

	/* The chevron doubles as the open/shut indicator. */
	.ss-nav .is-menu-open .wp-block-navigation__submenu-icon {
		display: inline-flex !important;
		align-items: center;
		justify-content: center;
		width: 34px;
		height: 34px;
		flex: 0 0 auto;
		margin: 0 .1rem 0 auto;
		padding: 0;
		color: #fff;
		background: rgb(255 255 255 / 14%);
		border-radius: 50%;
		transition: transform .25s var(--ss-ease), background-color .2s var(--ss-ease);
	}

	.ss-nav .is-menu-open .wp-block-navigation__submenu-icon svg {
		width: 16px;
		height: 16px;
		fill: currentColor;
	}

	.ss-nav .is-menu-open .has-child.ss-open .wp-block-navigation__submenu-icon {
		transform: rotate(180deg);
	}

	@media (prefers-reduced-motion: reduce) {
		.ss-nav .wp-block-navigation__responsive-container.is-menu-open,
		.ss-nav .wp-block-navigation__responsive-container.is-menu-open
			.wp-block-navigation__responsive-dialog {
			animation: none;
		}
		.ss-nav .is-menu-open .wp-block-navigation__submenu-container {
			transition: none;
		}
	}
}

/* Desktop dropdown — more room between items than the block default. */
.ss-nav .wp-block-navigation__submenu-container {
	padding-block: .45rem;
}

.ss-nav .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
	padding-block: .72rem;
	padding-inline: 1.15rem;
}

/* The block that used to tighten the inline bar between 601px and 1023px has
   been removed. Below 1024px the inline bar no longer renders at all — the
   burger takes over — so those rules could only ever have styled something
   invisible. */

/* Contact page set: larger, in brand blue on a light background. */
.ss-social--contact {
	gap: .4rem;
	margin: .5rem 0 0;
}

.ss-social--contact a {
	width: 48px;
	height: 48px;
	color: var(--wp--preset--color--base);
	background: var(--ss-brand-gradient);
	border-radius: 3px;
}

.ss-social--contact a:hover,
.ss-social--contact a:focus-visible {
	background: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--base);
}

.ss-social--contact svg {
	width: 22px;
	height: 22px;
}

.ss-search .wp-block-search__button {
	background: transparent;
	border: 1px solid var(--ss-line);
	color: var(--wp--preset--color--brand);
	padding: .45rem .6rem;
}

/* ==========================================================================
   3. Kicker, section headings, meta
   ========================================================================== */

/* Category label — the white-on-blue chip lifted from the printed page. */
.ss-kicker a,
.ss-chip {
	display: inline-block;
	padding: .3em .65em;
	margin: 0 .3em .3em 0;
	background: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
	font-family: var(--wp--preset--font-family--display);
	text-transform: uppercase;
	letter-spacing: .045em;
	text-decoration: none;
	line-height: 1.35;
	transition: background-color .18s var(--ss-ease);
}

.ss-kicker a:hover {
	background: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--base);
	text-decoration: none;
}

/* Articles here carry four or five categories each ("News", "France",
   "Holiday Offers", "Resort News & Reports"…). Rendering them all turns every
   card into a wall of chips that outweighs the headline, so show only the
   first and drop the separators. The full taxonomy is still in the markup for
   search engines — it is only hidden visually. */
.ss-kicker a:not(:first-of-type),
.ss-kicker .wp-block-post-terms__separator {
	display: none;
}

/* On a single article the full set is useful context, so allow them back. */
.ss-article__head .ss-kicker a:not(:first-of-type) {
	display: inline-block;
}

.ss-article__head .ss-kicker .wp-block-post-terms__separator {
	display: none;
}

/* Section heading with a rule that matches the width of the heading TEXT,
   not the column — the device used throughout the magazine. */
.ss-section-heading {
	width: fit-content;
	max-width: 100%;
	padding-bottom: .12em;
	margin-block: 2.4rem .9rem;
	border-bottom: 3px solid var(--wp--preset--color--brand);
}

/* The page title on a section page — Holiday News, Equipment, Racing and the
   rest. It was sitting tight against the nav strip above and the first row of
   cards below, so it read as a label attached to the first card rather than as
   the heading of the page.

   Targeted on `h1.ss-section-heading` specifically. Both page.html and
   archive.html give their title `className: ss-section-heading` at level 1,
   while the in-page section headings ("Latest", "Related stories") are h2 with
   the same class — so the element selector is what separates the page title
   from the headings within the page. One rule, so every section page matches
   every other. */
/* A touch more air under an in-page section heading ("Latest", "More stories")
   on desktop only — on a phone the cards are already stacked and the extra gap
   just pushes content below the fold. */
@media (min-width: 782px) {
	h2.ss-section-heading {
		margin-bottom: calc(.9rem + 10px);
	}
}

h1.ss-section-heading,
.wp-block-query-title.ss-section-heading {
	/* !important because WordPress's flow-layout rules zero the margins on a
	   block-level title and win here — measured margin-top and margin-bottom
	   were both 0px with the plain class selector, which is why the first
	   attempt at this had no effect at all on Holiday News or Equipment. */
	margin-block: clamp(2.5rem, 5vw, 4rem) clamp(2rem, 4vw, 3rem) !important;
}

.ss-meta,
.ss-byline {
	color: var(--wp--preset--color--grey);
	font-family: var(--wp--preset--font-family--body);
	letter-spacing: .01em;
}

.ss-byline {
	gap: .55rem;
	margin-top: .9rem;
}

.ss-byline__name {
	color: var(--wp--preset--color--ink);
	font-weight: 600;
}

.ss-byline__name::after {
	content: "·";
	margin-left: .55rem;
	color: var(--wp--preset--color--grey);
	font-weight: 400;
}

/* ==========================================================================
   4. Lead story
   ========================================================================== */

.ss-lead {
	margin-bottom: 2rem;
}

/* On a phone the lead carousel was the only thing running edge to edge, so it
   read as a different site from the cards beneath it. `alignfull` blocks are
   pulled out to the viewport edges by WordPress, which is right for a desktop
   hero and wrong here — at 390px a full-bleed image beside inset text just
   looks like a mistake. Inset it to match everything else. */
@media (max-width: 600px) {
	/* !important is required: WordPress pulls an `alignfull` block out to the
	   viewport edges with `margin-left/right: calc(-1 * var(--padding))` and
	   its own !important, so a plain rule here measured leadLeft = 0 while the
	   cards below sat at 16. */
	.ss-lead,
	.ss-carousel {
		margin-inline: 0 !important;
		width: auto !important;
		max-width: none;
	}

	.ss-lead__image img {
		border-radius: var(--ss-radius);
	}
}

.ss-lead__card {
	position: relative;
	isolation: isolate;
}

/* ---- carousel ---- */

.ss-carousel {
	position: relative;
}

/* Slides stack on top of each other. The first is visible; the rest are faded
   out but still occupy the grid cell, so the hero never changes height as it
   cycles — a jumping hero is worse than no carousel at all. */
.ss-carousel__track {
	display: grid;
	list-style: none;
	margin: 0;
	padding: 0;
}

.ss-carousel__track > * {
	grid-area: 1 / 1;
	margin: 0 !important;
}

.ss-carousel__slide {
	opacity: 0;
	visibility: hidden;
	transition: opacity .9s var(--ss-ease), visibility 0s linear .9s;
}

.ss-carousel__slide.is-active {
	opacity: 1;
	visibility: visible;
	transition: opacity .9s var(--ss-ease), visibility 0s;
}

/* Without JavaScript no slide gets .is-active, so show the first one and the
   hero degrades to a static lead story rather than an empty box. */
.ss-carousel__track > *:first-child {
	opacity: 1;
	visibility: visible;
}

.ss-carousel__track > *.is-active ~ *:first-child {
	opacity: 0;
}

.ss-carousel__nav {
	position: absolute;
	right: clamp(1rem, 3vw, 2rem);
	bottom: clamp(1rem, 3vw, 1.75rem);
	z-index: 3;
	display: flex;
	align-items: center;
	gap: .5rem;
}

.ss-carousel__dot {
	width: 30px;
	height: 30px;          /* clears the 24px minimum target size */
	padding: 0;
	border: 0;
	background: transparent;
	cursor: pointer;
	display: grid;
	place-items: center;
}

.ss-carousel__dot::after {
	content: "";
	width: 10px;
	height: 10px;
	border-radius: 50%;
	background: rgb(255 255 255 / 45%);
	box-shadow: 0 0 0 1px rgb(0 0 0 / 25%);
	transition: background-color .2s var(--ss-ease), transform .2s var(--ss-ease);
}

.ss-carousel__dot[aria-pressed="true"]::after {
	background: #fff;
	transform: scale(1.35);
}

.ss-carousel__pause {
	width: 30px;
	height: 30px;
	margin-left: .25rem;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: rgb(0 0 0 / 35%);
	cursor: pointer;
	display: grid;
	place-items: center;
}

.ss-carousel__pause-icon {
	width: 8px;
	height: 10px;
	border-left: 3px solid #fff;
	border-right: 3px solid #fff;
}

/* Pause button becomes a play triangle once stopped. */
.ss-carousel.is-paused .ss-carousel__pause-icon {
	width: 0;
	height: 0;
	border: 0;
	border-left: 9px solid #fff;
	border-top: 6px solid transparent;
	border-bottom: 6px solid transparent;
	margin-left: 2px;
}

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

.ss-lead__image img {
	width: 100%;
	aspect-ratio: 16 / 9;
	object-fit: cover;
	display: block;
}

.ss-lead__body {
	padding: 1.25rem 0 0;
}

.ss-lead__title {
	font-size: clamp(1.9rem, 5.2vw, 3.4rem);
	line-height: 1.04;
	margin: .5rem 0 .6rem;
}

.ss-lead__title a {
	color: var(--wp--preset--color--brand);
	text-decoration: none;
}

.ss-lead__title a:hover {
	color: var(--wp--preset--color--accent);
	text-decoration: none;
}

.ss-lead__standfirst {
	font-size: clamp(1.02rem, 1.7vw, 1.2rem);
	line-height: 1.5;
	color: var(--wp--preset--color--ink);
	max-width: 60ch;
}

/* Every slide occupies the same grid cell so the hero cannot change height as
   it cycles — which is right, but it means the shortest caption inherits the
   tallest one's height. Measured on a phone: roughly 200px of empty space
   between the date and the dots, because one of the three headlines runs to
   four lines and another to two.

   Capping both to a fixed number of lines evens the slides out, so the dead
   space goes without reintroducing a jumping hero. Three lines of headline is
   enough for every current lead story. */
@media (max-width: 1023px) {
	.ss-lead__title a {
		display: -webkit-box;
		-webkit-line-clamp: 3;
		-webkit-box-orient: vertical;
		overflow: hidden;
	}

	.ss-lead__standfirst {
		display: -webkit-box;
		-webkit-line-clamp: 2;
		-webkit-box-orient: vertical;
		overflow: hidden;
	}

	/* The nav was pinned to the bottom of the whole carousel, so it sat below
	   whatever empty space the shortest slide left. Placed in flow beneath the
	   caption instead, it follows the text it belongs to. */
	.ss-carousel__nav {
		position: static;
		justify-content: flex-end;
		margin-block: .75rem 0;
	}
}

/* From tablet up the lead becomes an overlay — the cover-image treatment. */
@media (min-width: 782px) {
	.ss-lead__card {
		display: grid;
	}

	.ss-lead__card > * {
		grid-area: 1 / 1;
	}

	.ss-lead__image img {
		aspect-ratio: 21 / 9;
	}

	/* The scrim behind the lead headline.
	   Measured, not guessed: with a lighter gradient, 26% of the area behind the
	   headline was too bright for white text and the worst pixel sat at 1.64:1 —
	   a clear WCAG failure. This matters more here than on most sites because
	   ski photography is overwhelmingly snow, which is the brightest subject
	   there is. The gradient is therefore heavy where the text sits and clears
	   quickly above it, so the photograph still reads. */
	/* On a wide screen the lead text should start on the same vertical line as
	   the card grid below it. Left unset, WordPress's constrained layout centres
	   it at the 800px content width, so on a 1920px display the headline floats
	   a third of the way in while the grid begins much further left — two
	   different left edges, which reads as accidental. */
	.ss-lead__body {
		align-self: end;
		padding-block: clamp(1.5rem, 5vw, 3rem);
		padding-inline: max(
			clamp(1.5rem, 5vw, 3rem),
			calc((100% - var(--wp--style--global--wide-size)) / 2)
		);
		background: linear-gradient(
			to top,
			rgb(8 16 22 / 96%) 0%,
			rgb(8 16 22 / 92%) 45%,
			rgb(8 16 22 / 64%) 74%,
			rgb(8 16 22 / 0%) 100%
		);
	}

	/* Children left-aligned within the wide container rather than centred, and
	   individually capped so a long headline still breaks at a sensible width.
	   `!important` is required, not lazy: WordPress's constrained-layout rule
	   emits `margin-left: auto !important` on every child, so a plain override
	   silently loses and the text stays centred. */
	.ss-lead__body > * {
		max-width: none;
		margin-inline: 0 !important;
	}

	.ss-lead__title {
		max-width: 19ch;
	}

	.ss-lead__standfirst {
		max-width: 54ch;
	}

	/* Backstop for an exceptionally bright frame. Kept subtle — a heavy drop
	   shadow is the dated look we deliberately left in the print issue. */
	.ss-lead__title a,
	.ss-lead__standfirst {
		text-shadow: 0 1px 3px rgb(0 0 0 / 45%);
	}

	.ss-lead__title a,
	.ss-lead__standfirst,
	.ss-lead__body .ss-meta {
		color: #fff;
	}

	.ss-lead__title a:hover {
		color: var(--wp--preset--color--brand-tint);
	}
}

/* ==========================================================================
   5. Story grid and cards
   ========================================================================== */

.ss-grid {
	display: grid;
	gap: clamp(1.25rem, 3vw, 2rem);
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
	list-style: none;
	align-items: stretch;
	/* The grid is a <ul> in several templates, and a bare `padding-left: 0` was
	   not enough: the browser's default list indent is `padding-inline-start`,
	   which wins on the logical axis. Measured, the grid sat at x=39 while the
	   pagination and the related-posts block below it sat at x=16 — a 23px
	   stagger down the left edge of every listing page. Zero both axes and
	   both properties so the grid shares one edge with everything around it. */
	padding-inline: 0;
	padding-left: 0;
	/* !important is measured, not defensive. Something in the plugin stylesheet
	   stack sets `margin-left: 22.96px` (1.435em) on this <ul> and beats a
	   plain class selector, which pushed the grid to x=79 while the pagination
	   and related-posts block below it sat at x=56 — a visible stagger down the
	   left edge of every listing page. Confirmed live after a version bump, so
	   it is a real rule rather than a stale stylesheet. */
	margin-inline: 0 !important;
}

/* WordPress's flow-layout rule applies margin-block-start to every sibling
   after the first. Inside a grid that margin shifts each card down within its
   own cell, so row tops stop lining up. Zero it on direct children. */
.ss-grid > * {
	margin-block: 0 !important;
}

.ss-card {
	display: flex;
	flex-direction: column;
	height: 100%;
}

.ss-card__image {
	overflow: hidden;
	margin-bottom: .75rem;
	background: var(--wp--preset--color--surface);
}

.ss-card__image img {
	width: 100%;
	aspect-ratio: 3 / 2;
	object-fit: cover;
	display: block;
	transition: transform .5s var(--ss-ease);
}

.ss-card:hover .ss-card__image img {
	transform: scale(1.045);
}

.ss-card__title {
	margin: .35rem 0 .4rem;
	line-height: 1.15;
}

.ss-card__title a {
	color: var(--wp--preset--color--brand);
	text-decoration: none;
}

.ss-card:hover .ss-card__title a {
	color: var(--wp--preset--color--accent);
}

.ss-card .ss-meta {
	margin-top: auto;
	padding-top: .4rem;
}

/* ==========================================================================
   6. Article
   ========================================================================== */

.ss-article__head {
	padding-top: clamp(1.5rem, 4vw, 2.75rem);
}

.ss-article__title {
	font-size: clamp(1.9rem, 4.6vw, 3.1rem);
	line-height: 1.05;
	margin: .5rem 0 .65rem;
}

.ss-standfirst {
	font-size: clamp(1.05rem, 1.8vw, 1.28rem);
	line-height: 1.5;
	font-weight: 300;
	color: var(--wp--preset--color--ink);
	max-width: 62ch;
	margin-bottom: 0;
}

.ss-article__hero {
	margin-block: clamp(1.5rem, 4vw, 2.5rem);
}

.ss-article__hero img {
	width: 100%;
	aspect-ratio: 16 / 9;
	object-fit: cover;
}

/* Body copy at 17px in an 800px container.
   That puts the line length near the top of the comfortable range, so the
   68ch cap below is doing real work here rather than being a formality — it
   holds the measure back from the container's full width. */
.ss-article__body {
	font-size: 1.0625rem;
	line-height: 1.7;
}

/* Hard cap regardless of container, so an unusually wide viewport or a future
   layout change can never stretch a line beyond comfortable reading. */
.ss-article__body p,
.ss-article__body li {
	max-width: 68ch;
}

.ss-standfirst {
	max-width: 64ch;
}

.ss-article__body > *:first-child {
	margin-top: 0;
}

/* Pull quote in the display face, brand blue — the magazine's device. */
.ss-article__body .wp-block-pullquote,
.ss-article__body blockquote {
	border-left: 3px solid var(--wp--preset--color--brand-tint);
	padding-left: 1.1rem;
	margin-block: 2rem;
}

/* Tags render as small outlined pills, deliberately quieter than the solid
   category chip — a reader should still be able to tell at a glance which is
   the section and which are the subjects. */
.ss-tags {
	display: flex;
	flex-wrap: wrap;
	gap: .3rem;
	margin: .5rem 0 0;
}

.ss-tags a {
	display: inline-flex;
	align-items: center;
	min-height: 24px;          /* WCAG 2.2 target size */
	padding: .15em .6em;
	border: 1px solid var(--ss-line);
	border-radius: 999px;
	color: var(--wp--preset--color--grey);
	text-decoration: none;
	line-height: 1.4;
	transition: border-color .18s var(--ss-ease), color .18s var(--ss-ease), background-color .18s var(--ss-ease);
}

.ss-tags a:hover,
.ss-tags a:focus-visible {
	border-color: var(--wp--preset--color--brand);
	background: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
}

/* The block outputs a separator between terms; the flex gap does that job. */
.ss-tags .wp-block-post-terms__separator {
	display: none;
}

.ss-tags__heading {
	margin: 1.5rem 0 0;
	font-family: var(--wp--preset--font-family--display);
	text-transform: uppercase;
	letter-spacing: .08em;
	color: var(--wp--preset--color--grey);
}

/* On a card the tags are supporting detail and must not push the headline
   around, so they stay on ONE line and scroll sideways instead of wrapping.

   The previous approach capped the height and hid the overflow, which cut the
   fourth tag in half — a clipped word looks like a bug, and the tag was still
   in the tab order while being invisible. Scrolling keeps every tag reachable. */
.ss-tags--card {
	position: relative;
	margin-top: .4rem;
	flex-wrap: nowrap;
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scroll-behavior: smooth;
	scroll-snap-type: x proximity;
	/* Room for the nudge button, added only when the strip actually overflows. */
	padding-block: 2px;
	scrollbar-width: none;          /* Firefox */
	-ms-overflow-style: none;
}

.ss-tags--card::-webkit-scrollbar {
	display: none;
}

.ss-tags--card a {
	font-size: .68rem;
	padding: .1em .5em;
	min-height: 22px;
	flex: 0 0 auto;                 /* never squash a pill to fit */
	scroll-snap-align: start;
	white-space: nowrap;
}

/* Wrapper added by JavaScript so the button has something to sit against. */
.ss-tagstrip {
	position: relative;
}

/* Fade at the right edge, so it reads as "there is more" rather than as a tag
   that happens to end at the card boundary. Sits under the button. */
.ss-tagstrip.is-overflowing::after {
	content: "";
	position: absolute;
	inset-block: 0;
	right: 0;
	width: 3.2rem;
	pointer-events: none;
	background: linear-gradient(to right, rgb(255 255 255 / 0%), var(--wp--preset--color--base) 62%);
}

/* Hidden once the strip is scrolled to the end — an arrow that does nothing is
   worse than no arrow. */
.ss-tagstrip.is-end::after {
	opacity: 0;
}

.ss-tagstrip__next {
	position: absolute;
	/* 4px below centre. The pills have more visual weight below their baseline
	   than above, so a mathematically centred arrow reads as sitting high
	   against them. */
	top: calc( 50% + 4px );
	right: 0;
	transform: translateY(-50%);
	opacity: .4;
	z-index: 1;
	display: none;
	align-items: center;
	justify-content: center;
	/* WCAG 2.2 SC 2.5.8 minimum. */
	width: 24px;
	height: 24px;
	padding: 0;
	background: var(--wp--preset--color--base);
	border: 1px solid var(--ss-line);
	border-radius: 999px;
	color: var(--wp--preset--color--brand);
	cursor: pointer;
	transition: background-color .18s var(--ss-ease), color .18s var(--ss-ease), opacity .18s var(--ss-ease);
}

.ss-tagstrip.is-overflowing .ss-tagstrip__next {
	display: inline-flex;
}

.ss-tagstrip.is-end .ss-tagstrip__next {
	opacity: 0;
	pointer-events: none;
}

/* Full strength on interaction — a 0.4 control that stays faint when you reach
   for it reads as disabled. */
.ss-tagstrip__next:hover,
.ss-tagstrip__next:focus-visible {
	opacity: 1;
	background: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
	border-color: var(--wp--preset--color--brand);
}

.ss-tagstrip__next svg {
	width: 11px;
	height: 11px;
	fill: none;
	stroke: currentColor;
	stroke-width: 2.2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

[data-theme="dark"] .ss-tagstrip.is-overflowing::after {
	background: linear-gradient(to right, rgb(28 33 38 / 0%), #1c2126 62%);
}

[data-theme="dark"] .ss-tagstrip__next {
	background: #1c2126;
	border-color: #2c3238;
	color: #9fd0e4;
}

[data-theme="dark"] .ss-tags a {
	border-color: #2c3238;
	color: #9aa5ad;
}

/* ==========================================================================
   7. Advertising slots
   ========================================================================== */

/* Space is RESERVED at the correct height before any creative loads. An ad that
   appears and pushes content down is a Core Web Vitals (CLS) failure and the
   single most irritating thing a news site does to a reader. Sizes match the
   agreed units in agents.md §7. */
.ss-ad {
	display: flex;
	align-items: center;
	justify-content: center;
	margin-block: clamp(1.75rem, 4vw, 2.75rem);
	background: var(--wp--preset--color--surface);
	border: 1px dashed var(--ss-line);
	overflow: hidden;
}

/* The label must be pinned to the top of the slot. Positioned only by a
   transform, it inherited the flex container's centring and printed straight
   across the middle of the advert — which looked like a fault in the creative,
   not in the CSS. */
.ss-ad::before {
	content: "Advertisement";
	position: absolute;
	top: .3rem;
	left: 50%;
	transform: translateX(-50%);
	font-family: var(--wp--preset--font-family--body);
	font-size: .625rem;
	line-height: 1;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--grey);
	pointer-events: none;
}

.ss-ad {
	position: relative;
	padding-top: 1.55rem;
}

/* Masthead leaderboard, sitting in the white space beside the logo.
   Hidden below 1100px: the slot is sold as a 728x90 IAB unit, and there is no
   honest way to fit that beside a logo and a search box on a narrower screen.
   Shrinking it would mean advertisers paying for a size they do not get. */
.ss-ad--header {
	display: none;
}

@media (min-width: 1100px) {
	.ss-ad--header {
		display: flex;
		flex: 0 1 auto;
		margin: 0;
		padding-top: 0;
		background: transparent;
		border: 0;
		min-height: 90px;      /* reserved, so the masthead never jumps */
		align-items: center;
	}

	/* No "Advertisement" label here — the unit sits outside the editorial
	   column in the masthead, where it reads unambiguously as advertising. A
	   label inside the header would add clutter without adding clarity. */
	.ss-ad--header::before {
		content: none;
	}

	.ss-ad--header img {
		display: block;
		max-width: 728px;
		height: auto;
	}
}

/* ---- Sponsor row (directory) ---- */

/* Four box adverts filling the width on desktop, stepping down to two and then
   one. Labelled once for the whole strip rather than four times, and set on a
   tinted band so the row reads as advertising at a glance. */
.ss-ad-row {
	background: var(--wp--preset--color--surface);
	padding: .9rem clamp(.75rem, 2vw, 1.25rem) 1.1rem;
	margin-block: clamp(1.5rem, 4vw, 2.25rem);
}

.ss-ad-row__label {
	margin: 0 0 .6rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: .625rem;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--grey);
	text-align: center;
}

/* An advert row with nothing in it must not leave an "Advertisement" heading
   over five empty cells. That happens wherever adverts are switched off for a
   page (the privacy policy) and would happen again if a pool ever emptied.

   :has() asks the real question — "does this row contain an image?" — rather
   than hard-coding which pages are affected, so it stays correct on any page
   added later. */
.ss-ad-row:not(:has(img)) {
	display: none;
}

.ss-ad-row__inner {
	display: grid;
	gap: clamp(.75rem, 2vw, 1.5rem);
	grid-template-columns: 1fr;
	align-items: center;
	justify-items: center;
}


/* ---- Share links (replaces Shareaholic — plain anchors, no tracking) ---- */
.ss-share {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .5rem .75rem;
	margin-block-start: 1.5rem;
	padding-block-start: 1rem;
	border-top: 1px solid var(--ss-line);
}

.ss-share__label {
	font-size: .72rem;
	font-weight: 600;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--grey);
	margin-inline-end: .25rem;
}

.ss-share__link {
	display: inline-flex;
	align-items: center;
	gap: .4rem;
	/* 24px minimum tap target, matching the rest of the site */
	min-height: 2.25rem;
	padding: .3rem .8rem;
	border: 1px solid var(--ss-line);
	border-radius: 999px;
	font-size: .82rem;
	font-weight: 500;
	text-decoration: none;
	color: var(--wp--preset--color--ink);
	transition: background-color .15s ease, border-color .15s ease;
}

.ss-share__link:hover,
.ss-share__link:focus-visible {
	background-color: var(--wp--preset--color--brand);
	border-color: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
}

/* How many adverts, and how they sit, at each size.

   Driven by the artwork rather than by taste: the supplied creatives are 280px
   wide, so a cell narrower than that means an advertiser is paying for a size
   the reader does not receive. Measured before this change, five across gave
   164px cells at 1024px — an advert rendered at well under sixty per cent of
   its own artwork.

   phone      1 advert,  one column
   tablet     4 adverts, two columns   -> 272px at 600px, 330px at 768px
   desktop    5 adverts, five columns  -> ~238px each at 1280px+

   Four on a tablet as two rows of two, rather than four squeezed across. A
   four-across row needs about 1200px of content before each cell reaches the
   280px the artwork actually is — measured at 185px on a 900px screen and
   210px at 1024px, both of which shortchange the advertiser. Two up keeps
   every advert at full size, and it is still four adverts. */
@media (min-width: 600px) and (max-width: 767px) {
	.ss-ad-row__inner {
		grid-template-columns: repeat(2, 1fr);
	}
}

/* Three across from 768px. With two rows running site-wide (26 Aug 2026) a
   tablet is sent nine adverts, and at two columns that was a 1147px wall —
   five deep, taller than the iPad viewport, with a ragged gap at the end.
   Nine divides into three exactly, so three columns gives a clean 3×3 and
   about 700px instead.

   Cell width holds up: 236px at 820px wide and 320px at 1024px, against 280px
   artwork — the same range desktop already shows (238px at 1440px). Two
   columns stay below 768px, where three would drop cells to ~180px and
   genuinely shortchange the advertiser. */
@media (min-width: 768px) and (max-width: 1279px) {
	/* The two rows must flow as ONE grid here, not two.

	   A tablet is sent nine adverts: four survive in row one (its fifth slot
	   is desktop-only) and five in row two. Left as two independent grids
	   they wrap 3+1 then 3+2, which puts a conspicuous hole beside the fourth
	   advert. Collapsing the wrappers with display:contents lets all nine
	   flow into a single 3x3 — no gap, and no markup change.

	   The label is a direct child of .ss-ad-row, so it has to be told to span
	   the full width or it would sit in the first cell. */
	.ss-ad-row {
		display: grid;
		grid-template-columns: repeat(3, 1fr);
		gap: clamp(.75rem, 2vw, 1.5rem);
		align-items: center;
	}

	.ss-ad-row__label {
		grid-column: 1 / -1;
		margin-bottom: 0;
	}

	.ss-ad-row__inner {
		display: contents;
	}
}

@media (min-width: 1280px) {
	.ss-ad-row__inner {
		grid-template-columns: repeat(5, 1fr);
	}
}

/* The fifth advert is a desktop-only position. Below 1280px the row carries
   four, so this cell is not part of the layout. */
@media (max-width: 1279px) {
	.ss-ad-row__inner .ss-ad-row__cell:nth-child(5) {
		display: none;
	}
}

/* One advert only on a phone.

   Four stacked adverts on a 390px screen is a wall a reader scrolls past, and
   it pushes the editorial a long way down. The other three are hidden rather
   than removed from the markup, because this is one shared component used on
   every page — which cells survive is a layout decision, not a template one.

   ⚠️ ACT ON THIS BEFORE GO-LIVE. Advanced Ads Tracking is now installed, and a
   hidden advert still loads its image and still registers an impression. A
   phone reader is therefore counted as having seen four adverts when they saw
   one, and advertisers are billed on that number. CSS cannot fix it: the
   suppression has to happen before the advert is rendered. The correct route is
   Advanced Ads Pro's device display conditions on placements ad-row-2 to
   ad-row-5 — set them to desktop and tablet only. Until that is done the
   mobile impression figures are overstated and must not be shown to an
   advertiser.

   Note the doubled selector. `.ss-ad-row__cell { display: flex }` is declared
   immediately below, and at equal specificity the later rule wins — so a plain
   `.ss-ad-row__cell--extra { display: none }` was silently overridden and all
   four adverts still showed on a phone. Two classes beat one, whatever the
   source order. */
@media (max-width: 599px) {
	.ss-ad-row__inner .ss-ad-row__cell--extra {
		display: none;
	}
}

/* ⚠️ The tenth slot serves to tablets whatever its device condition says.

   Measured repeatedly on 26 Aug: placements ad-row-5 and ad-row-9 honour a
   desktop-only condition exactly, but the fifth cell of the SECOND row does
   not — it is delivered to a tablet even after the placement was deleted and
   rebuilt from scratch with a clean slug. The stored condition reads
   "desktop"; the render ignores it.

   The dangerous half of that is not the extra advert, it is the accounting:
   a delivered advert registers an impression, so hiding it in CSS would bill
   an advertiser for a view no reader ever had. That is the one fault this
   whole device-condition design exists to prevent.

   So the slot is set to tablet+desktop and SHOWN on a tablet, rather than
   served and hidden. A tablet gets nine adverts, all nine visible, and the
   impression figures stay honest. Revisit if a plugin update fixes the
   underlying behaviour. */
@media (max-width: 1279px) {
	.ss-ad-row__inner + .ss-ad-row__inner .ss-ad-row__cell:nth-child(5) {
		display: flex;
	}
}

/* The directory carries two rows (David's "second position", 16 Aug 2026).
   They are separate grids rather than one ten-cell grid so the :nth-child(5)
   and --extra rules above apply to each row identically. */
.ss-ad-row__inner + .ss-ad-row__inner {
	margin-top: clamp(.75rem, 2vw, 1.5rem);
}

/* Same reasoning as .ss-ad-row:not(:has(img)) above, one level down: if the
   pool ever runs short the second row must disappear rather than sit as a
   band of empty cells under the first. */
.ss-ad-row__inner:not(:has(img)) {
	display: none;
}

.ss-ad-row__cell {
	min-width: 0;
	width: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* Each advert fills its cell, so the row reads as four equal paid positions
   rather than four stamps floating in white space.

   Note the trade-off: the cell is ~330px on a 1440px screen and most creatives
   are 280x180, so they are being shown about 18% larger than supplied and will
   look slightly soft. That is the argument for the 600x500 re-supply in the
   client report — at that size this row would be sharp on every screen. */
.ss-ad-row__cell img {
	display: block;
	width: 100%;
	max-width: 100%;
	height: auto;
}

[data-theme="dark"] .ss-ad-row {
	background: #1c2126;
}

/* Leaderboard above the footer — shown at EVERY width, phones included.

   This was hidden below 820px on the reasoning that a 728x90 scaled to phone
   width is "a size the advertiser did not buy". Measured on 27 Aug 2026 with
   the five real creatives, that was wrong:

     - at 390px wide the unit renders 390x48, which is essentially the IAB
       standard mobile banner (320x50) — a normal shape, not a squeezed one;
     - the artwork is 728px wide, so a 2x phone needs 780 device pixels and
       gets 728 — no visible softening. Nothing is cropped;
     - all five creatives stayed legible (logo plus a short headline is exactly
       what survives downscaling).

   Hiding it meant advertisers got NOTHING on mobile, which is a worse deal for
   them than a smaller rendering. The masthead slot stays desktop-only — that
   one genuinely has no room beside the logo and search.

   ⚠️ The placement device conditions must stay in step with this. If CSS shows
   it and the placement withholds it, the slot is empty; if the placement sends
   it and CSS hides it, the advert is counted and never seen. */
.ss-ad--footer-lb {
	display: flex;
		/* Was clamp(2rem, 5vw, 3rem) — 48px on desktop, on top of the directory
		   panel's own bottom margin and this slot's label gutter. Three sources
		   of space for one join. */
	margin-block: clamp(.75rem, 1.5vw, 1.25rem) 0;
	padding-top: 1.55rem;   /* room for the label above the creative */
	align-items: flex-end;
}

/* aspect-ratio reserves the slot's height at any width, so the page does not
   shift as the creative loads. A fixed 90px min-height would leave a 42px gap
   under a phone-sized banner. */
.ss-ad--footer-lb img {
	width: 100%;
	max-width: 728px;
	height: auto;
	aspect-ratio: 728 / 90;
}

@media (min-width: 820px) {
	.ss-ad--footer-lb {
		min-height: calc(90px + 1.55rem);
	}
}

/* In-content leaderboard — shown at every width, for the reasons set out on the
   footer slot above. Measured, not assumed. */
.ss-ad--leaderboard {
	display: flex;
}

.ss-ad--leaderboard img {
	width: 100%;
	max-width: 728px;
	height: auto;
	aspect-ratio: 728 / 90;
}

@media (min-width: 820px) {
	.ss-ad--leaderboard {
		min-height: calc(90px + 1.55rem);
	}
}

@media (min-width: 782px) {
	.ss-ad--leaderboard {
		min-height: calc(90px + 1.55rem);
	}
}

/* Leaderboard once enhance.js has moved it into the story grid, after the
   fourth card. It must span every column — left in a single cell it would take
   one card's width and push the rest of the row out of alignment. */
.ss-grid > .ss-ad--in-grid {
	grid-column: 1 / -1;
	margin-block: clamp(.5rem, 1.5vw, 1rem);
}

/* MPU slot. Sized for the 280x180 creative the magazine's advertisers actually
   supply — 78 of 83 existing adverts are that size — rather than the 300x250
   IAB standard, which no current advertiser has artwork for. If the inventory
   is ever restandardised, raise this to 250px and the slot still works. */
.ss-ad--mpu {
	min-height: calc(180px + 1.55rem);
}

.ss-ad img {
	display: block;
	max-width: 100%;
	height: auto;
}

/* Outline on the CREATIVE, not on the slot.

   A good many advertisers supply artwork on a white background, which then
   dissolves into the page and reads as stray text rather than as a bought
   position. The slot is full width with the creative centred inside it, so a
   border on the slot would float well away from the advert — it has to sit on
   the image itself.

   `box-sizing: border-box` matters wherever the image is width:100% (the
   four-across row), or the 2px of border breaks the column widths. */
.ss-ad img,
.ss-ad img[src],
.ss-ad-row__cell img {
	border: 1px solid var(--ss-ad-outline);
	box-sizing: border-box;
}

/* An empty slot must not leave a labelled grey box on the page. When a
   placement has no advert assigned — the leaderboard, until artwork arrives —
   the slot collapses entirely. */
.ss-ad:not(:has(img)):not(:has(iframe)) {
	display: none;
}

/* Once a real creative is present the placeholder chrome is unnecessary. */
.ss-ad:has(img),
.ss-ad:has(iframe) {
	background: transparent;
	border-color: transparent;
}

/* ==========================================================================
   8. Directory
   ========================================================================== */

.ss-directory-grid {
	display: grid;
	gap: var(--wp--preset--spacing--40);
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
	align-items: stretch;
	padding-left: 0;
	list-style: none;
}

.ss-directory-grid > * {
	margin-block: 0 !important;
}

.ss-listing-card {
	border: 1px solid var(--ss-line);
	padding: var(--wp--preset--spacing--30);
	height: 100%;
	transition: box-shadow .2s var(--ss-ease), transform .2s var(--ss-ease);
}

.ss-listing-card:hover {
	box-shadow: var(--ss-shadow);
	transform: translateY(-2px);
}

/* A listing opens in a new tab, so say so visibly.

   The screen-reader text on the title already announces it, but a sighted
   reader had nothing to go on — a tab appearing unasked is disorienting. The
   conventional glyph for it is the box-with-an-arrow, bottom right, where the
   eye finishes reading the card.

   Decorative only: aria-hidden by virtue of being a pseudo-element, and
   pointer-events: none so it never intercepts the click it is describing. */
.ss-listing-card {
	position: relative;
}

.ss-listing-card::after {
	content: "";
	position: absolute;
	right: var(--wp--preset--spacing--30);
	bottom: var(--wp--preset--spacing--30);
	width: 15px;
	height: 15px;
	pointer-events: none;
	opacity: .45;
	transition: opacity .2s var(--ss-ease);
	background: no-repeat center / contain
		url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23005C81' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
}

.ss-listing-card:hover::after,
.ss-listing-card:focus-within::after {
	opacity: 1;
}

/* The stroke colour is baked into the data URI, so dark mode needs its own. */
[data-theme="dark"] .ss-listing-card::after {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237FC4E0' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
}

/* Room for it, so a long address or telephone number never runs underneath. */
.ss-listing-card .ss-listing__meta,
.ss-listing-card > *:last-child {
	padding-right: 1.4rem;
}

.ss-listing-card .wp-block-post-title {
	margin-block: .2em .4em;
}

.ss-listing__meta > * {
	margin-block: 0;
}

/* Field label, echoing the TEL / EMAIL / WEB labels in the printed directory. */
.ss-label {
	font-family: var(--wp--preset--font-family--display);
	text-transform: uppercase;
	letter-spacing: .04em;
	color: var(--wp--preset--color--brand);
	margin-block-start: .9em !important;
	margin-block-end: .1em !important;
}

.ss-value {
	margin-block: 0 !important;
	white-space: pre-line; /* the address field is multi-line */
}

/* A bound field with no stored value renders an empty paragraph; collapse it so
   listings without an email don't gain a phantom gap. */
.ss-value:empty,
.ss-value:has(> br:only-child) {
	display: none;
}

/* Phone and email are the whole point of a directory on a phone, so tap targets
   must clear WCAG 2.2 SC 2.5.8 (24x24 CSS px minimum). */
.ss-value a {
	display: inline-flex;
	align-items: center;
	min-height: 24px;
	padding-block: 2px;
	overflow-wrap: anywhere; /* long emails must not force a horizontal scroll */
}

.ss-terms a {
	display: inline-block;
	padding: .3em .7em;
	margin-inline-end: .3em;
	background: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
	font-family: var(--wp--preset--font-family--display);
	text-transform: uppercase;
	letter-spacing: .04em;
	text-decoration: none;
}

.ss-terms a:hover,
.ss-terms a:focus-visible {
	background: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--base);
}

/* ==========================================================================
   9. Directory promo panel
   ========================================================================== */

.ss-promo {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	background: var(--ss-brand-gradient);
	color: var(--wp--preset--color--base);
	padding: clamp(1.75rem, 4vw, 3rem) clamp(1.5rem, 4vw, 2.75rem);
	/* Asymmetric on purpose. The advert below brings its own top margin plus a
	   label gutter above the creative, so an equal bottom margin here stacked
	   up into a 140px void between the panel and the advert. Generous above,
	   tight below, and the advert's own spacing finishes the job. */
	margin-block: clamp(2rem, 5vw, 3.5rem) clamp(.5rem, 1vw, .9rem);
}

/* Photograph behind the gradient. Held at 20% so it reads as texture rather
   than as an image with text dumped on it — the text still sits on flat brand
   blue as far as the eye is concerned, which is what keeps the contrast honest.
   `aria-hidden` is unnecessary: a CSS background is already invisible to
   assistive technology, and this carries no information. */
.ss-promo::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background-image: url("assets/directory-bg-2400.webp");
	background-size: cover;
	background-position: center 42%;
	background-repeat: no-repeat;
	opacity: .2;
	pointer-events: none;
}

/* On a narrow screen the panel is much taller relative to its width, so cover
   crops away most of the frame; the smaller file is all that is needed. */
@media (max-width: 781px) {
	.ss-promo::before {
		background-image: url("assets/directory-bg-1400.webp");
	}
}

/* The constrained layout centres each child inside the content column, and it
   does so with `margin-left/right: auto !important`. That is why the heading,
   the paragraph and the button did not share a left edge — the paragraph is
   narrower (56ch) so it was being centred within the column while its wider
   siblings were not. Overriding the margin puts every child on one edge. */
.ss-promo > * {
	max-width: 62ch;
	margin-inline: 0 !important;
}

.ss-promo__title {
	color: var(--wp--preset--color--base) !important;
	margin-top: 0;
	margin-bottom: .6rem;
}

.ss-promo__text {
	margin-bottom: 1.35rem;
}

.ss-promo .wp-block-button__link {
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--brand);
	font-family: var(--wp--preset--font-family--display);
	text-transform: uppercase;
	letter-spacing: .045em;
	font-size: .8rem;
	padding: .8em 1.4em;
	border-radius: var(--ss-radius);
}

.ss-promo .wp-block-button__link:hover {
	background: var(--wp--preset--color--brand-tint);
	color: var(--wp--preset--color--ink);
}

/* The whole panel is clickable, via the button's own pseudo-element stretched
   across it — rather than wrapping heading, text and button in one large <a>.

   That distinction matters for assistive technology: a wrapping anchor would
   swallow the heading, so a screen-reader user navigating by headings loses
   "The Directory" as a landmark and instead hears the entire panel announced
   as one very long link. This way there is still exactly ONE link, carrying the
   button's own label, and the clickable area simply happens to be the panel. */
.ss-promo .wp-block-button__link::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
}

.ss-promo {
	cursor: pointer;
}

/* Keep the button above the stretched area so its own focus ring still shows. */
.ss-promo .wp-block-button {
	position: relative;
	z-index: 2;
}

/* Hovering anywhere on the panel lights the button, so the affordance covers
   the whole area rather than only the 140px the button occupies. */
.ss-promo:hover .wp-block-button__link {
	background: var(--wp--preset--color--brand-tint);
	color: var(--wp--preset--color--ink);
}

/* ==========================================================================
   9b. Newsletter section
   ========================================================================== */

/* Replaces the Boxzilla popup. The popup covered roughly a third of the
   viewport on every page load, and pulled in Brevo's stylesheet, its JavaScript
   bundle and three Roboto font files — none of which this site otherwise needs.
   This posts to the same Brevo endpoint with the same field names, so
   subscriptions continue to land in the same list, but it is styled in the
   site's own type and colour and it interrupts nobody. */
.ss-newsletter {
	background: var(--wp--preset--color--surface);
	border-top: 3px solid var(--wp--preset--color--brand);
	padding-block: clamp(2.25rem, 5vw, 3.5rem);
	margin-top: clamp(2.5rem, 6vw, 4rem);
}

.ss-newsletter__inner {
	display: grid;
	gap: clamp(1.5rem, 4vw, 3rem);
	align-items: start;
}

@media (min-width: 900px) {
	.ss-newsletter__inner {
		grid-template-columns: 1fr 1.15fr;
	}
}

.ss-newsletter__inner > * {
	margin-block: 0 !important;
}

.ss-newsletter__title {
	margin: 0 0 .5rem;
}

.ss-newsletter__text {
	margin: 0;
	max-width: 42ch;
	color: var(--wp--preset--color--ink);
}

.ss-newsletter__fields {
	display: grid;
	gap: 1rem;
	grid-template-columns: 1fr;
}

/* Labels align along the top. With `align-items: end` the field without a hint
   beneath it was pushed down, so "First name" sat lower than "Email address"
   and the two labels never lined up. */
@media (min-width: 560px) {
	.ss-newsletter__fields {
		grid-template-columns: 1fr 1fr auto;
		align-items: start;
	}

	/* The button has no label above it, so its cell is one line short and the
	   button floated above the input tops.

	   The previous fix was a hand-computed `margin-top: calc(.72rem + .3rem +
	   2px)` — a guess at the label's rendered height, which is why it never
	   quite landed. This instead puts an empty box in the label's place, set in
	   the label's own font and size, so the browser measures the height rather
	   than us predicting it. It stays correct if the label type ever changes,
	   and the existing .3rem column gap does the rest. */
	.ss-field--submit::before {
		content: "";
		display: block;
		font-family: var(--wp--preset--font-family--display);
		font-size: .72rem;
		line-height: 1.4;
		height: calc(1.4em + 2px);
	}
}

/* `min-width: 0` is required. A grid item defaults to min-width:auto, which
   refuses to shrink below its content's intrinsic size — an <input> reports a
   default size of about 20 characters, so at 390px the fields pushed 12px past
   the viewport and produced a horizontal scrollbar. */
.ss-field {
	margin: 0;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: .3rem;
}

.ss-field label {
	font-family: var(--wp--preset--font-family--display);
	font-size: .72rem;
	text-transform: uppercase;
	letter-spacing: .05em;
	color: var(--wp--preset--color--brand);
}

.ss-req {
	color: #b3261e;
}

.ss-field input[type="text"],
.ss-field input[type="email"] {
	font: inherit;
	padding: .7rem .8rem;
	border: 1px solid #c3ced6;
	border-radius: 2px;
	background: #fff;
	color: var(--wp--preset--color--ink);
	min-height: 46px;
	width: 100%;
	max-width: 100%;
	box-sizing: border-box;
}

.ss-field input:focus-visible {
	outline: 3px solid var(--wp--preset--color--accent);
	outline-offset: 1px;
	border-color: var(--wp--preset--color--brand);
}

.ss-hint {
	font-size: .78rem;
	color: var(--wp--preset--color--grey);
}

.ss-newsletter__button {
	font-family: var(--wp--preset--font-family--display);
	text-transform: uppercase;
	letter-spacing: .05em;
	font-size: .85rem;
	background: var(--ss-brand-gradient);
	color: #fff;
	border: 0;
	border-radius: 2px;
	padding: 0 1.5rem;
	min-height: 46px;
	cursor: pointer;
	transition: background-color .18s var(--ss-ease);
}

.ss-newsletter__button:hover,
.ss-newsletter__button:focus-visible {
	background: var(--wp--preset--color--accent);
}

/* Consent must be an explicit, unticked action — pre-ticking it would not be
   valid consent under UK GDPR. */
.ss-consent {
	margin: 1.1rem 0 0;
	font-size: .85rem;
	line-height: 1.5;
}

.ss-consent label {
	display: flex;
	align-items: flex-start;
	gap: .6rem;
	cursor: pointer;
}

.ss-consent input[type="checkbox"] {
	flex: 0 0 auto;
	width: 20px;
	height: 20px;
	margin-top: .12em;
	accent-color: var(--wp--preset--color--brand);
}

.ss-newsletter__legal {
	margin: .8rem 0 0;
	font-size: .76rem;
	line-height: 1.5;
	color: var(--wp--preset--color--grey);
	max-width: 62ch;
}

/* Hidden by clipping rather than by pushing it off-canvas with a large negative
   offset — that technique can extend the scrollable area and cause a stray
   horizontal scrollbar. It stays in the DOM and submittable so the spam trap
   still works, but is removed from the tab order and the accessibility tree. */
.ss-honeypot {
	position: absolute !important;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

[data-theme="dark"] .ss-newsletter {
	background: #1c2126;
}

[data-theme="dark"] .ss-newsletter__text {
	color: #e6eaed;
}

[data-theme="dark"] .ss-field input[type="text"],
[data-theme="dark"] .ss-field input[type="email"] {
	background: #14181b;
	border-color: #2c3238;
	color: #e6eaed;
}

/* ==========================================================================
   10. Footer
   ========================================================================== */

.ss-footer {
	color: var(--wp--preset--color--base);
	padding-block: clamp(2.25rem, 6vw, 3.5rem) 1.5rem;
	margin-top: clamp(2.5rem, 6vw, 4rem);
}

/* The newsletter panel is already a full-width band with its own generous
   padding, so the footer's top margin on top of it left a strip of empty page
   between two solid blocks. Where the two meet, they meet. The margin stays for
   any page that ends without the newsletter, where the footer does need
   separating from the content above it. */
/* Note the selector: the newsletter is a template part, so WordPress wraps it
   in its own <div> and `.ss-newsletter + .ss-footer` matches nothing — the two
   are not siblings. It is the WRAPPER that sits next to the footer. */
.wp-block-template-part:has( .ss-newsletter ) + .ss-footer {
	margin-top: 0;
}

.ss-newsletter {
	margin-bottom: 0;
}

/* Four columns: brand block plus three link lists. Collapses to two on tablet
   and one on a phone. */
.ss-footer__top {
	display: grid;
	gap: clamp(1.75rem, 4vw, 3rem);
	grid-template-columns: 1fr;
	align-items: start;
}

@media (min-width: 600px) {
	.ss-footer__top {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (min-width: 900px) {
	.ss-footer__top {
		grid-template-columns: 1.6fr 1fr 1fr 1fr;
	}
}

.ss-footer__top > * {
	margin-block: 0 !important;
}

.ss-footer__brand {
	max-width: 40ch;
}

/* Monotone white mark — the mask means the same file serves the blue header
   version and this reversed one, so there is no second asset to keep in sync. */
.ss-logo--footer {
	background-color: #fff;
	width: clamp(160px, 16vw, 210px);
	margin-bottom: 1rem;
}

.ss-logo--footer:hover,
.ss-logo--footer:focus-visible {
	background-color: var(--wp--preset--color--brand-tint);
}

.ss-footer__blurb {
	color: #b9c2c9;
	margin: 0 0 1.1rem;
}

.ss-social--footer a {
	width: 36px;
	height: 36px;
	color: #d7dee3;
}

.ss-social--footer a:hover,
.ss-social--footer a:focus-visible {
	background: rgb(255 255 255 / 12%);
	color: #fff;
}

.ss-footer__heading {
	color: var(--wp--preset--color--brand-tint) !important;
	text-transform: uppercase;
	letter-spacing: .1em;
	margin: 0 0 .75rem;
	font-family: var(--wp--preset--font-family--display);
}

.ss-footer__list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.ss-footer__list li {
	margin: 0 0 .1rem;
}

.ss-footer__list a {
	display: inline-flex;
	align-items: center;
	min-height: 30px;          /* comfortable tap target in a dense list */
	color: #d7dee3;
	text-decoration: none;
	font-size: .9rem;
}

.ss-footer__list a:hover,
.ss-footer__list a:focus-visible {
	color: #fff;
	text-decoration: underline;
}

.ss-footer__menu a {
	color: #d7dee3;
	text-decoration: none;
}

.ss-footer__menu a:hover {
	color: var(--wp--preset--color--base);
	text-decoration: underline;
}

.ss-footer__bottom {
	border-top: 1px solid rgb(255 255 255 / 14%);
	margin-top: clamp(2rem, 4vw, 3rem);
	padding-top: 1.25rem;
	gap: 1rem;
}

.ss-footer__legal,
.ss-footer__credit,
.ss-footer__legal a {
	color: #9aa5ad;
	margin: 0;
}

/* No underline, and inline rather than inline-flex.

   An inline-flex box takes its baseline from its FIRST item — here the bolt
   SVG, which has no text baseline — so "Willpowered" sat slightly off the line
   that "Site by" sits on. Plain inline puts the text back on the shared
   baseline, and the bolt is nudged optically instead. */
.ss-footer__credit a {
	display: inline;
	color: #b9c2c9;
	text-decoration: none;
	border-bottom: 0;
}

/* Build number, so it is obvious at a glance which version of the site you are
   looking at — useful while the review copy and the local build move separately. */
.ss-version {
	margin-left: .55em;
	font-size: .82em;
	letter-spacing: .04em;
	color: rgb(255 255 255 / 38%);
}

/* Willpowered's bolt. Sized in em so it tracks the credit line's type, and
   nudged onto the text's optical centre now the link is inline rather than a
   flex container doing the vertical alignment. */
.ss-bolt {
	width: 1em;
	height: 1em;
	display: inline-block;
	vertical-align: -.14em;
	margin-right: .22em;
	color: #ea580c;
	transition: color .18s var(--ss-ease), transform .18s var(--ss-ease);
}

.ss-footer__credit a:hover .ss-bolt,
.ss-footer__credit a:focus-visible .ss-bolt {
	color: #f97316;
	transform: scale(1.15);
}

.ss-footer__credit a:hover,
.ss-footer__credit a:focus-visible {
	color: #fff;
}

.ss-footer__legal a:hover {
	color: var(--wp--preset--color--base);
}

/* ==========================================================================
   10b. Editorial patterns
   ========================================================================== */

/* Archive head */
.ss-archive__head {
	padding-top: clamp(1.25rem, 3vw, 2rem);
}

/* Resort fact box — scannable stats beside a resort report. */
.ss-factbox {
	background: var(--wp--preset--color--surface);
	border-left: 4px solid var(--wp--preset--color--brand);
	padding: clamp(1.1rem, 3vw, 1.75rem);
	margin-block: clamp(1.75rem, 4vw, 2.5rem);
}

.ss-factbox__title {
	margin-top: 0;
	margin-bottom: 1rem;
}

.ss-factbox__grid {
	display: grid;
	gap: 1rem 1.5rem;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 8.5rem), 1fr));
}

.ss-factbox__grid > * {
	margin-block: 0 !important;
}

.ss-fact__label {
	font-family: var(--wp--preset--font-family--display);
	font-size: .7rem;
	text-transform: uppercase;
	letter-spacing: .07em;
	color: var(--wp--preset--color--brand);
	margin: 0 0 .15rem;
}

.ss-fact__value {
	font-size: 1.25rem;
	line-height: 1.2;
	margin: 0;
	font-weight: 600;
}

/* Feature split — image beside a headline and standfirst. */
.ss-split {
	display: grid;
	gap: clamp(1.25rem, 4vw, 2.5rem);
	align-items: center;
	margin-block: clamp(2rem, 5vw, 3rem);
}

.ss-split > * {
	margin-block: 0 !important;
}

@media (min-width: 782px) {
	.ss-split {
		grid-template-columns: 1.15fr 1fr;
	}
}

.ss-split__image img {
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	display: block;
}

.ss-split__body > *:first-child {
	margin-top: 0;
}

.ss-split__body h2 {
	margin-block: .5rem .6rem;
	line-height: 1.08;
}

@media (prefers-color-scheme: dark) and (min-width: 999999px) { /* dark mode off — see §12 */
	.ss-factbox {
		background: #1c2126;
	}

	.ss-fact__label {
		color: var(--wp--preset--color--brand-tint);
	}
}

/* ==========================================================================
   10c. Boxzilla popup
   ========================================================================== */

/* Ported from opti's Additional CSS. Boxzilla is a plugin, not a theme feature,
   so this styling would otherwise have been lost when the theme changed — the
   rest of that customiser CSS targeted opti's own markup and is safely gone. */
.boxzilla-content h2 {
	margin-bottom: 20px;
	font-size: 1.5em;
}

.boxzilla-content input {
	width: 100%;
	display: block;
	font-size: inherit;
	font-family: inherit;
	padding: 2%;
	box-sizing: border-box;
	margin-top: 10px;
}

.boxzilla-content label {
	font-weight: bold;
}

/* ==========================================================================
   10d. Tap targets
   ========================================================================== */

/* WCAG 2.2 SC 2.5.8 sets a 24x24 CSS px minimum for interactive targets.
   Measured across four page types and eight viewport widths, two elements
   failed: pagination numbers at 10x20px and the footer legal links at 45x16px.
   Both are small enough to be genuinely fiddly on a phone. */
.ss-pagination .page-numbers,
.wp-block-query-pagination-numbers .page-numbers,
.wp-block-query-pagination-previous,
.wp-block-query-pagination-next {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 40px;
	min-height: 40px;
	padding: 0 .5em;
	text-decoration: none;
	border: 1px solid var(--ss-line);
	margin: 0 .15rem .35rem 0;
}

.ss-pagination .page-numbers.current,
.wp-block-query-pagination-numbers .page-numbers.current {
	background: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
	border-color: var(--wp--preset--color--brand);
}

.ss-pagination .page-numbers:hover,
.wp-block-query-pagination-numbers .page-numbers:hover {
	background: var(--wp--preset--color--surface);
}

.ss-footer__legal a {
	display: inline-flex;
	align-items: center;
	min-height: 24px;
	padding: 2px 0;
}

/* The navigation dropdown chevron is 7x7px, but it is decorative — the parent
   menu item is the real target and is full height — so it is deliberately not
   enlarged. Making it a 24px target would break the nav rhythm for no gain. */

/* ==========================================================================
   10d-ii. Home actions — View all latest news
   ========================================================================== */

/* Sits below the pagination as the way out to the full archive. Deliberately
   quieter than the pagination it follows: paging is the primary action here,
   this is the escape hatch. */
.ss-actions {
	margin-block: 1.75rem 0;
}

.ss-actions__all {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 48px;
	padding: 0 1.6rem;
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--small);
	letter-spacing: .05em;
	text-transform: uppercase;
	text-decoration: none;
	background: transparent;
	color: var(--wp--preset--color--brand);
	border: 1px solid var(--ss-line);
	border-radius: var(--ss-radius);
	transition: background-color .18s var(--ss-ease), border-color .18s var(--ss-ease);
}

.ss-actions__all:hover,
.ss-actions__all:focus-visible {
	background: var(--ss-brand-gradient);
	border-color: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
}

/* The archive page's own heading block. */
.ss-archive-head {
	margin-block: clamp(1.5rem, 4vw, 2.5rem) 0;
}

/* ==========================================================================
   11c. Related stories (Contextual Related Posts)
   ========================================================================== */

/* The plugin's own "rounded_thumbs" stylesheet has been switched off in its
   settings, so everything below is the theme's. The plugin was painting each
   title as a dark bar across the bottom of the thumbnail, which is why the
   block read as belonging to a different website. Titles now sit below the
   image, in grey, capped at two lines. */
.crp_related {
	margin-block: clamp(2.5rem, 6vw, 3.5rem) 0;
	clear: both;
}

/* Deliberately smaller than the site's other section headings, which run at
   38px. On an article page this is an appendix — "more from us" — not a peer of
   the headline above it, and at 38px it competed with a 49.6px article title.
   Sized as a quiet signpost instead; the rule below it does the separating. */
.crp_related > h2,
.crp_related > h3 {
	font-size: clamp(1.25rem, 1.6vw, 1.5rem);
	line-height: 1.25;
	margin: 0 0 1.1rem;
}

/* Same 22.96px list margin as .ss-grid — see the note there. Without this the
   related row sits 23px to the right of the story grid above it. */
.crp_related ul {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: clamp(1rem, 2.2vw, 1.6rem);
	margin: 0 !important;
	padding: 0;
	list-style: none;
}

@media (min-width: 600px) {
	.crp_related ul {
		grid-template-columns: repeat(3, 1fr);
	}
}

@media (min-width: 1000px) {
	.crp_related ul {
		grid-template-columns: repeat(5, 1fr);
	}
}

.crp_related li {
	margin: 0;
	padding: 0;
	list-style: none;
	min-width: 0;
}

.crp_related .crp_link {
	display: block;
	text-decoration: none;
	color: inherit;
}

/* Landscape and CONTAINED, not square and cropped.

   Several of these thumbnails are magazine covers with the issue name set
   across them. A square crop of a portrait cover cut the wording in half —
   "W ISSUE T W!" — which looks like a broken image rather than a cover. A 3:2
   box with `contain` shows the whole thing, and the tinted surface behind fills
   whatever the image does not. */
.crp_related figure {
	margin: 0 0 .55rem;
	overflow: hidden;
	border-radius: var(--ss-radius);
	background: var(--wp--preset--color--surface);
	aspect-ratio: 3 / 2;
	display: flex;
	align-items: center;
	justify-content: center;
}

.crp_related figure img,
.crp_related img.crp_thumb {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;
	transition: transform .4s var(--ss-ease);
}

.crp_related .crp_link:hover img,
.crp_related .crp_link:focus-visible img {
	transform: scale(1.04);
}

/* Grey, and hard-capped at two lines so an unusually long headline cannot make
   one card taller than its neighbours and break the row. */
.crp_related .crp_title {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	font-family: var(--wp--preset--font-family--body);
	font-size: .82rem;
	line-height: 1.35;
	color: var(--wp--preset--color--grey);
	transition: color .18s var(--ss-ease);
}

.crp_related .crp_link:hover .crp_title,
.crp_related .crp_link:focus-visible .crp_title {
	color: var(--wp--preset--color--brand);
}

/* A post with no featured image now renders with no <figure> at all, rather
   than the plugin's yellow placeholder. Reserve the same square so the row
   still lines up. */
.crp_related li:not(:has(figure)) .crp_title {
	-webkit-line-clamp: 4;
	line-clamp: 4;
	padding: .7rem;
	background: var(--wp--preset--color--surface);
	border-radius: var(--ss-radius);
	aspect-ratio: 1 / 1;
	box-sizing: border-box;
}

[data-theme="dark"] .crp_related figure,
[data-theme="dark"] .crp_related li:not(:has(figure)) .crp_title {
	background: #1c2126;
}

/* ==========================================================================
   10e. Issue cover grid (Read Online)
   ========================================================================== */

.ss-issue-grid {
	display: grid;
	gap: clamp(1rem, 3vw, 2rem);
	grid-template-columns: repeat(auto-fill, minmax(min(45%, 190px), 1fr));
	list-style: none;
	margin: 1.5rem 0 0;
	padding: 0;
}

.ss-issue a {
	display: block;
	text-decoration: none;
	color: inherit;
}

.ss-issue img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 212 / 300;
	object-fit: cover;
	box-shadow: var(--ss-shadow);
	transition: transform .3s var(--ss-ease), box-shadow .3s var(--ss-ease);
}

.ss-issue a:hover img,
.ss-issue a:focus-visible img {
	transform: translateY(-4px);
	box-shadow: 0 6px 12px rgb(35 31 32 / 12%), 0 18px 40px rgb(35 31 32 / 14%);
}

.ss-issue__label {
	display: block;
	margin-top: .6rem;
	font-family: var(--wp--preset--font-family--display);
	font-size: .8rem;
	text-transform: uppercase;
	letter-spacing: .04em;
	color: var(--wp--preset--color--brand);
}

/* ==========================================================================
   10f. Card fade-in
   ========================================================================== */

/* Cards ease in as they enter the viewport. Deliberately subtle — a long or
   showy animation on a news grid gets irritating by the third scroll.
   `.is-in` is added by an IntersectionObserver; without JavaScript the cards
   are simply visible, so nothing depends on the script running. */
.ss-grid--fade .ss-card,
.ss-grid .ss-card {
	opacity: 1;
	transform: none;
}

/* This selector list MUST stay in step with the one in assets/enhance.js.
   Anything named here but not observed there is set to opacity 0 and never
   given `.is-in` — it would simply never appear, which is the worst possible
   failure mode for a decorative effect. */
.js-fade .ss-grid .ss-card,
.js-fade .ss-directory-grid > *,
.js-fade .ss-promo,
.js-fade .ss-ad-row,
.js-fade .crp_related li,
.js-fade .ss-issue-grid > *,
.js-fade .ss-newsletter__inner {
	opacity: 0;
	transform: translateY(14px);
	transition: opacity .5s var(--ss-ease), transform .5s var(--ss-ease);
}

.js-fade .ss-grid .ss-card.is-in,
.js-fade .ss-directory-grid > .is-in,
.js-fade .ss-promo.is-in,
.js-fade .ss-ad-row.is-in,
.js-fade .crp_related li.is-in,
.js-fade .ss-issue-grid > .is-in,
.js-fade .ss-newsletter__inner.is-in {
	opacity: 1;
	transform: none;
}

@media (prefers-reduced-motion: reduce) {
	.js-fade .ss-grid .ss-card,
	.js-fade .ss-directory-grid > *,
	.js-fade .ss-promo,
	.js-fade .ss-ad-row,
	.js-fade .crp_related li,
	.js-fade .ss-issue-grid > *,
	.js-fade .ss-newsletter__inner {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

/* ==========================================================================
   11. Lists — hanging indent
   ========================================================================== */

/* The marker sits in a fixed gutter and wrapped lines align under the first
   line's text, never back under the marker. */
.entry-content ul,
.entry-content ol,
.wp-block-post-content ul,
.wp-block-post-content ol {
	padding-left: 0;
	list-style-position: outside;
	margin-left: 1.35em;
}

.entry-content li,
.wp-block-post-content li {
	padding-left: .35em;
	margin-bottom: .4em;
}

/* ==========================================================================
   12. Dark mode
   ========================================================================== */

/* LIGHT IS THE DEFAULT.
   Dark applies only when the reader chooses it with the footer toggle, which
   sets data-theme="dark" on <html> and remembers the choice. Deliberately NOT
   tied to prefers-color-scheme: a magazine should look the same for everyone
   unless the reader explicitly asks otherwise. */
:root[data-theme="dark"] {
	--ss-line: #2c3238;
	--ss-shadow: 0 1px 2px rgb(0 0 0 / 40%), 0 8px 24px rgb(0 0 0 / 35%);
	/* Inverted: 15% black is invisible against a dark page, and the problem the
	   outline solves is the same one in reverse — most creatives are on white,
	   so here it is the surrounding page that needs separating from them. */
	--ss-ad-outline: rgb(255 255 255 / 18%);
}

[data-theme="dark"] body {
	background: #14181b;
	color: #e6eaed;
}

[data-theme="dark"] .ss-header {
	background: #14181b;
}

[data-theme="dark"] .ss-card__image,
[data-theme="dark"] .ss-ad {
	background: #1c2126;
}

[data-theme="dark"] .ss-card__title a,
[data-theme="dark"] .ss-lead__title a,
[data-theme="dark"] .ss-wordmark a,
[data-theme="dark"] .ss-section-heading {
	color: var(--wp--preset--color--brand-tint);
}

[data-theme="dark"] .ss-logo {
	background-color: var(--wp--preset--color--brand-tint);
}

[data-theme="dark"] .ss-section-heading {
	border-bottom-color: var(--wp--preset--color--brand-mid);
}

[data-theme="dark"] .ss-standfirst,
[data-theme="dark"] .ss-article__body {
	color: #e6eaed;
}

[data-theme="dark"] .ss-label {
	color: var(--wp--preset--color--brand-tint);
}

[data-theme="dark"] .ss-listing-card {
	border-color: #2c3238;
}

/* Brand blue on a dark background is too low-contrast for body links. */
[data-theme="dark"] .ss-article__body a,
[data-theme="dark"] .wp-block-post-content a {
	color: var(--wp--preset--color--brand-tint);
}

/* ---- the toggle control ---- */

.ss-theme-toggle {
	display: inline-flex;
	align-items: center;
	gap: .55rem;
	min-height: 40px;
	padding: .35rem .85rem .35rem .5rem;
	background: transparent;
	border: 1px solid rgb(255 255 255 / 26%);
	border-radius: 999px;
	color: #d7dee3;
	font-family: var(--wp--preset--font-family--body);
	font-size: .8rem;
	cursor: pointer;
	transition: border-color .18s var(--ss-ease), color .18s var(--ss-ease);
}

.ss-theme-toggle:hover,
.ss-theme-toggle:focus-visible {
	border-color: var(--wp--preset--color--brand-tint);
	color: #fff;
}

.ss-theme-toggle__track {
	position: relative;
	width: 34px;
	height: 18px;
	border-radius: 999px;
	background: rgb(255 255 255 / 22%);
	flex: 0 0 auto;
	transition: background-color .2s var(--ss-ease);
}

.ss-theme-toggle__thumb {
	position: absolute;
	top: 2px;
	left: 2px;
	width: 14px;
	height: 14px;
	border-radius: 50%;
	background: #fff;
	transition: transform .2s var(--ss-ease);
}

[data-theme="dark"] .ss-theme-toggle__track {
	background: var(--wp--preset--color--brand-mid);
}

[data-theme="dark"] .ss-theme-toggle__thumb {
	transform: translateX(16px);
}

/* The label states what pressing it will DO, which is clearer than naming the
   current state — "Dark mode" on a dark site is ambiguous. */
.ss-theme-toggle .ss-theme-toggle__on { display: none; }
[data-theme="dark"] .ss-theme-toggle .ss-theme-toggle__on { display: inline; }
[data-theme="dark"] .ss-theme-toggle .ss-theme-toggle__off { display: none; }

/* ==========================================================================
   12. Contact page
   ========================================================================== */

/* No advert and no related stories on this page. Contact is a page a reader
   arrives at with a specific intention — a leaderboard between the heading and
   the form, and a row of unrelated articles beneath it, both get in the way of
   the one thing they came to do. */
.page-id-18 .ss-ad--leaderboard,
.page-id-18 .ss-ad-row,
.page-id-18 .crp_related {
	display: none;
}

.ss-contact {
	gap: clamp(2rem, 5vw, 4rem);
	margin-block: clamp(1rem, 3vw, 2rem) 0;
}

/* ---- The details column ---- */

.ss-contact-card {
	padding: 1.1rem 1.25rem;
	margin-bottom: 1rem;
	background: var(--wp--preset--color--surface);
	border-left: 3px solid var(--wp--preset--color--brand);
	border-radius: var(--ss-radius);
}

.ss-contact-card__role {
	margin: 0 0 .15rem;
	font-family: var(--wp--preset--font-family--display);
	font-size: .68rem;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--wp--preset--color--brand);
}

.ss-contact-card__name {
	margin: 0 0 .5rem;
	font-size: 1.02rem;
	font-weight: 600;
	color: var(--wp--preset--color--ink);
}

.ss-contact-card__lines {
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: .92rem;
}

.ss-contact-card__lines li {
	margin: 0 0 .2rem;
}

/* Tappable, and long enough to hit on a phone — an email address that is only
   text is useless on the device most readers will be holding. */
.ss-contact-card__lines a {
	display: inline-flex;
	align-items: center;
	min-height: 26px;
	color: var(--wp--preset--color--brand);
	text-decoration: none;
	overflow-wrap: anywhere;
	border-bottom: 1px solid transparent;
	transition: border-color .18s var(--ss-ease);
}

.ss-contact-card__lines a:hover,
.ss-contact-card__lines a:focus-visible {
	border-bottom-color: currentColor;
}

/* ---- Contact Form 7 ----
   The plugin ships almost no styling, which is why the fields were browser
   defaults: 200px wide, hairline borders, a grey system button. */

.ss-contact__form .wpcf7 {
	margin-top: 1.25rem;
}

.ss-contact__form .wpcf7 p {
	margin: 0 0 1.1rem;
}

.ss-contact__form .wpcf7 label {
	display: block;
	font-family: var(--wp--preset--font-family--display);
	font-size: .72rem;
	letter-spacing: .05em;
	text-transform: uppercase;
	color: var(--wp--preset--color--brand);
	margin-bottom: .35rem;
}

.ss-contact__form input[type="text"],
.ss-contact__form input[type="email"],
.ss-contact__form input[type="tel"],
.ss-contact__form textarea {
	width: 100%;
	box-sizing: border-box;
	font: inherit;
	font-size: 1rem;
	padding: .8rem .9rem;
	color: var(--wp--preset--color--ink);
	background: #fff;
	border: 1px solid var(--ss-line);
	border-radius: var(--ss-radius);
	transition: border-color .18s var(--ss-ease), box-shadow .18s var(--ss-ease);
}

.ss-contact__form textarea {
	min-height: 190px;
	resize: vertical;
}

.ss-contact__form input:focus,
.ss-contact__form textarea:focus {
	outline: none;
	border-color: var(--wp--preset--color--brand);
	box-shadow: 0 0 0 3px rgb(0 92 129 / 14%);
}

.ss-contact__form input[type="submit"] {
	font-family: var(--wp--preset--font-family--display);
	font-size: .85rem;
	letter-spacing: .05em;
	text-transform: uppercase;
	color: #fff;
	background: var(--ss-brand-gradient);
	border: 0;
	border-radius: var(--ss-radius);
	padding: 0 2rem;
	min-height: 48px;
	cursor: pointer;
	transition: filter .18s var(--ss-ease);
}

.ss-contact__form input[type="submit"]:hover,
.ss-contact__form input[type="submit"]:focus-visible {
	filter: brightness(1.12);
}

/* The plugin's own messages, so validation does not look like a system error. */
.ss-contact__form .wpcf7-not-valid-tip {
	font-size: .82rem;
	color: #b3261e;
	margin-top: .3rem;
}

.ss-contact__form .wpcf7-response-output {
	margin: 1rem 0 0 !important;
	padding: .8rem 1rem !important;
	border-width: 1px !important;
	border-radius: var(--ss-radius);
	font-size: .92rem;
}

.ss-contact-follow {
	margin-top: clamp(2.5rem, 6vw, 4rem);
}

@media (max-width: 781px) {
	.ss-contact {
		gap: 2rem;
	}
	/* Details first on a phone: someone reaching for the site on a handset is
	   far more likely to want to ring or email than to type a long message. */
	.ss-contact__details {
		order: -1;
	}
}

[data-theme="dark"] .ss-contact-card {
	background: #1c2126;
}

[data-theme="dark"] .ss-contact__form input[type="text"],
[data-theme="dark"] .ss-contact__form input[type="email"],
[data-theme="dark"] .ss-contact__form textarea {
	background: #12171b;
	color: #e6eaed;
	border-color: #2c3238;
}

/* ---- Contact page hero ---------------------------------------------------
   400px band, three photographs cross-fading with a slow Ken Burns drift.

   The images are declared HERE, not inline in the template: a relative url()
   inside block-template HTML resolves against the page URL, not the theme
   folder, so an inline reference 404s. In a stylesheet it resolves against the
   stylesheet — which is what makes this work at /dev/ and at the root alike. */
.ss-page-hero {
	position: relative;
	height: 400px;
	display: flex;
	align-items: center;
	overflow: hidden;
	background: var(--ss-brand-gradient);
	margin-bottom: clamp(2.5rem, 6vw, 4.5rem);
}

.ss-page-hero__slides {
	position: absolute;
	inset: 0;
	z-index: 0;
}

.ss-page-hero__slide {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center 55%;
	background-repeat: no-repeat;
	opacity: 0;
	/* 24s cycle, three slides — each is visible for a third of it. */
	animation: ss-hero-fade 24s infinite, ss-hero-kenburns 24s infinite;
}

.ss-page-hero__slide--1 { background-image: url("assets/hero-contact-1.webp"); animation-delay: 0s, 0s; }
.ss-page-hero__slide--2 { background-image: url("assets/hero-contact-2.webp"); animation-delay: 8s, 8s; }
.ss-page-hero__slide--3 { background-image: url("assets/hero-contact-3.webp"); animation-delay: 16s, 16s; }

@keyframes ss-hero-fade {
	0%      { opacity: 0; }
	4%      { opacity: 1; }
	29%     { opacity: 1; }
	37%     { opacity: 0; }
	100%    { opacity: 0; }
}

/* Scale only — no translate. A pan would drift the composition off-centre on a
   narrow screen, where `cover` has already cropped the sides hard. */
@keyframes ss-hero-kenburns {
	0%   { transform: scale(1.02); }
	37%  { transform: scale(1.12); }
	100% { transform: scale(1.12); }
}

/* Scrim. White type over snow is the brightest possible combination — the lead
   headline needed the same treatment, and for the same reason. */
.ss-page-hero::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient(90deg, rgb(0 36 52 / 78%) 0%, rgb(0 36 52 / 58%) 45%, rgb(0 36 52 / 30%) 100%);
	pointer-events: none;
}

.ss-page-hero__title {
	position: relative;
	z-index: 2;
	width: 100%;
	max-width: var(--wp--style--global--wide-size);
	margin-inline: auto !important;
	padding-inline: var(--wp--style--root--padding-left, 1rem);
	color: #fff !important;
	font-size: clamp(2rem, 6vw, 3.6rem);
	line-height: 1.02;
}

/* A 400px banner on a 1024px-tall tablet is two fifths of the screen before a
   reader reaches a word of content. Stepped down so the page title and the
   start of the copy share the first view. */
@media (max-width: 1023px) {
	.ss-page-hero {
		height: 320px;
		margin-bottom: clamp(1.75rem, 4vw, 2.75rem);
	}
}

@media (max-width: 600px) {
	.ss-page-hero {
		height: 260px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.ss-page-hero__slide {
		animation: none;
	}
	/* Without the fade cycle nothing would be visible, so show the first. */
	.ss-page-hero__slide--1 {
		opacity: 1;
	}
}

/* The page template supplies the title, so the shared page heading is not
   needed here — it would repeat "Contact" immediately below the hero. */
.ss-page--contact > .ss-page__head {
	display: none;
}

/* The social list sits inside a column, where the browser's default list indent
   pushes it in from the paragraph above it. Zero it so the icons line up with
   the text, not a phantom bullet gutter. */
.ss-contact__details .ss-social--contact {
	justify-content: flex-start;
	padding-left: 0;
	margin-left: 0;
	list-style: none;
}

.ss-contact__follow-h {
	margin-top: clamp(1.75rem, 4vw, 2.5rem);
}

/* The global list rule gives every <li> a small left padding for the hanging
   indent. On an icon list that is not a hanging indent — it just shunts the
   first icon 6px right of the text above it. Measured: anchor at 834 against a
   paragraph at 828. */
.ss-social li,
.ss-social--contact li {
	padding-left: 0;
	margin-left: 0;
}

/* ---- Still hero, one image per page -------------------------------------- */
.ss-page-hero__still-img {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center 55%;
	background-repeat: no-repeat;
}

.page-id-10250 .ss-page-hero__still-img { background-image: url("assets/hero-offers.webp"); }
.page-id-16    .ss-page-hero__still-img { background-image: url("assets/hero-about.webp"); }
.page-id-1515  .ss-page-hero__still-img { background-image: url("assets/hero-eddie.webp"); }

/* These pages supply their own title in the hero, so the shared heading above
   the content would repeat it immediately below. */
.ss-page--hero > .ss-page__head {
	display: none;
}

/* Related-story thumbnails FILL a rectangle rather than being letterboxed in a
   square. `contain` was chosen earlier to stop magazine-cover wording being
   cropped, but with pages excluded from the results those covers no longer
   appear — so cover is right, and the row reads as a set of photographs. */
.crp_related figure {
	aspect-ratio: 3 / 2;
}

.crp_related figure img,
.crp_related img.crp_thumb {
	object-fit: cover;
}

/* Directory archive hero. The archive title block is replaced by the hero, so
   the heading lives inside it rather than being repeated above the listings. */
.ss-page-hero--directory .ss-page-hero__still-img {
	background-image: url("assets/hero-directory.webp");
}

/* Privacy policy hero. Deliberately the calmest image of the set. */
.page-id-10546 .ss-page-hero__still-img {
	background-image: url("assets/hero-privacy.webp");
}

/* Resorts landing page. It shared the directory's photograph while it was being
   built, which meant the two landing pages opened identically. */
.page-id-10567 .ss-page-hero__still-img {
	background-image: url("assets/hero-resorts.webp");
}

/* Special Offers, given the same banner treatment as Summer Offers but its own
   photograph, so the two offers pages are not mistaken for each other. */
.page-id-4756 .ss-page-hero__still-img {
	background-image: url("assets/hero-offers-special.webp");
}

/* The banner is the first thing inside <main>, and the flow layout was putting
   a ~9px margin above <main> — a thin white band between the blue navigation
   bar and the photograph. Measured at 10px, and it read as a mistake rather
   than as spacing. Only pages that open with a banner are affected; everything
   else keeps its normal top margin.

   `:has()` covers it whatever the page's own class, with the two known classes
   listed as well so the rule still holds in a browser without :has(). */
main:has(> .ss-page-hero),
main.ss-page--hero,
main.ss-page > .ss-page-hero:first-child {
	margin-block-start: 0 !important;
}

/* ==========================================================================
   15b. Offer cards
   ========================================================================== */

/* Priced offers are scanned, not read. As plain paragraphs the price, the
   resort and the booking link all carried the same weight, so nothing could be
   compared at a glance. The advertiser's wording is untouched — only the
   container changed. */
.ss-offer-grid {
	display: grid;
	gap: clamp(1rem, 2.5vw, 1.75rem);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
	margin-block: clamp(1.25rem, 3vw, 2rem);
}

.ss-offer-grid > * {
	margin-block: 0 !important;
}

.ss-offer-card {
	display: flex;
	flex-direction: column;
	border: 1px solid var(--ss-line);
	border-top: 3px solid var(--wp--preset--color--brand);
	padding: clamp(1.1rem, 2.5vw, 1.6rem);
	background: var(--wp--preset--color--base);
}

.ss-offer-card > * {
	margin-block: 0 !important;
}

.ss-offer-card__title {
	font-size: clamp(1.05rem, 2.2vw, 1.25rem);
	line-height: 1.2;
	color: var(--wp--preset--color--brand);
	margin-block-end: .6rem !important;
}

/* flex: 1 pushes the button to the foot of every card, so a row of cards has
   its calls to action on one line however long the descriptions run. */
.ss-offer-card__body {
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.55;
	flex: 1 1 auto;
}

/* Several offers run to two paragraphs. The rule above zeroes margins on every
   direct child of the card, which is right for the title and the button but
   ran the two paragraphs of an offer straight into each other. */
.ss-offer-card__body + .ss-offer-card__body {
	margin-block-start: .8em !important;
}

.ss-offer-card__cta {
	margin-block-start: 1rem !important;
}

.ss-offer-card__button {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding: .55rem 1.2rem;
	background: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--x-small);
	text-transform: uppercase;
	letter-spacing: .04em;
	text-decoration: none;
	transition: background .2s var(--ss-ease);
}

.ss-offer-card__button:hover,
.ss-offer-card__button:focus-visible {
	background: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--base);
	text-decoration: none;
}

[data-theme="dark"] .ss-offer-card {
	background: transparent;
	border-color: rgb(255 255 255 / 14%);
	border-top-color: var(--wp--preset--color--accent);
}

/* ==========================================================================
   16. Resorts landing page
   ========================================================================== */

/* Seven countries as picture cards. "Resorts" in the menu used to lead straight
   to France, which made France look like the whole section and left the other
   six reachable only from the drop-down.

   The photograph on each card is the newest article filed under that country,
   pulled at render time, so the page keeps itself current as coverage moves on. */
/* Four across, so the seven countries read as two rows with one space left
   rather than an uneven five-and-two. Fixed columns rather than auto-fill:
   auto-fill packs as many as will fit, which gave five at 1440px and two at
   tablet width — the row length changed with the window. */
.ss-resort-grid {
	display: grid;
	gap: clamp(1rem, 2.5vw, 1.75rem);
	grid-template-columns: 1fr;
	margin: 0;
	padding: 0;
	list-style: none;
}

@media (min-width: 560px) {
	.ss-resort-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (min-width: 900px) {
	.ss-resort-grid {
		grid-template-columns: repeat(3, 1fr);
	}
}

@media (min-width: 1100px) {
	.ss-resort-grid {
		grid-template-columns: repeat(4, 1fr);
	}
}

/* The line introducing the grid is centred over it, rather than sitting hard
   left above a block of centred cards. Scoped to this page: everywhere else on
   the site running text stays left-aligned. */
.page-id-10567 .wp-block-post-content > p {
	text-align: center;
	max-width: 46rem;
	margin-inline: auto;
}

.ss-resort-grid > li {
	margin: 0;
	padding-left: 0;
}

.ss-resort-card__link {
	display: block;
	position: relative;
	overflow: hidden;
	text-decoration: none;
	background: var(--ss-brand-gradient);
	transition: transform .25s var(--ss-ease), box-shadow .25s var(--ss-ease);
}

.ss-resort-card__link:hover,
.ss-resort-card__link:focus-visible {
	transform: translateY(-3px);
	box-shadow: var(--ss-shadow);
	text-decoration: none;
}

.ss-resort-card__media {
	display: block;
	aspect-ratio: 3 / 2;
	overflow: hidden;
}

.ss-resort-card__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	transition: transform .5s var(--ss-ease);
}

.ss-resort-card__link:hover .ss-resort-card__media img {
	transform: scale(1.04);
}

/* A country with no article yet has no photograph, so the panel carries the
   name on brand colour rather than leaving a hole in the grid. */
.ss-resort-card--plain .ss-resort-card__media {
	background: var(--ss-brand-gradient);
}

/* The name sits over the foot of the picture, as on the magazine's own covers.
   The scrim is what makes it legible: white type over a bright snow photograph
   is the obvious way for this to fail. */
.ss-resort-card__body {
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	display: block;
	padding: 2.5rem .9rem .8rem;
	background: linear-gradient(to top, rgb(0 32 46 / 88%) 0%,
	                            rgb(0 32 46 / 55%) 45%, transparent 100%);
}

.ss-resort-card__name {
	display: block;
	font-family: var(--wp--preset--font-family--display);
	font-size: clamp(1.15rem, 2.4vw, 1.5rem);
	line-height: 1.1;
	color: #fff;
	text-transform: uppercase;
}

.ss-resort-card__count {
	display: block;
	margin-top: .2rem;
	font-size: var(--wp--preset--font-size--x-small);
	color: rgb(255 255 255 / 78%);
}

@media (prefers-reduced-motion: reduce) {
	.ss-resort-card__link,
	.ss-resort-card__media img {
		transition: none;
	}

	.ss-resort-card__link:hover {
		transform: none;
	}

	.ss-resort-card__link:hover .ss-resort-card__media img {
		transform: none;
	}
}

/* Legal copy reads better in a narrower measure than a magazine page. */
.ss-privacy .wp-block-post-content > * {
	max-width: 46rem;
}

/* ==========================================================================
   15. Tablet and phone refinements
   ========================================================================== */

/* ---- Reading measure ----------------------------------------------------- */

/* Measured on the dev site before this change: 84 characters per line in an
   article on a tablet, and 93 on the directory, privacy and offers pages.
   Comfortable running text is 45–75; past about 80 the eye loses its place
   returning to the start of the next line.

   Capped rather than centred, so everything keeps the shared left edge.

   34rem against a 15.8px body is about 68 characters. A first attempt at 40rem
   still measured ~81, because the cap has to be set against the font size
   actually in use rather than picked as a round number.

   Scoped to running text: paragraphs and lists in the content flow, and the
   paragraphs inside the contact columns. Cards and grids are left out.

   Every selector carries a class deliberately. The constrained layout emits
   `.wp-container-xxx > :where(...)` with the content-size max-width, which is
   one class of specificity — enough to beat a plain `main > p`. That is exactly
   what happened on the first attempt: the directory intro kept its 800px cap
   and measured 89 characters, while pages matched by `.wp-block-post-content
   > p` capped correctly at 69. */
@media (min-width: 601px) and (max-width: 1023px) {
	.ss-article__body > p,
	.ss-article__body > ul,
	.ss-article__body > ol,
	.wp-block-post-content > p,
	.wp-block-post-content > ul,
	.wp-block-post-content > ol,
	.ss-page--hero > p,
	.ss-page > p,
	.wp-block-column > p,
	.wp-block-column > ul {
		max-width: 34rem;
		margin-inline: 0 auto;
	}

	/* The standfirst is a deck, so it is allowed to run a little wider than the
	   body it introduces — but not the full column, which is where it was. */
	.ss-standfirst {
		max-width: 38rem;
		margin-inline: 0 auto;
	}
}

/* ---- Headings on a tablet ------------------------------------------------ */

/* The display face is set in caps, so it takes far more room per word than the
   body face and reads much louder. At 41–46px on a 768px screen a two-word
   heading filled the width. Brought down so the hierarchy still reads without
   the page shouting. */
@media (max-width: 1023px) {
	.ss-page-hero__title {
		font-size: clamp(1.75rem, 5.5vw, 2.5rem);
	}

	.ss-article__title {
		font-size: clamp(1.6rem, 5vw, 2.25rem);
	}

	.ss-article__body h2,
	.ss-page .wp-block-post-content h2,
	.ss-privacy .wp-block-post-content h2 {
		font-size: clamp(1.25rem, 3.4vw, 1.6rem);
		line-height: 1.15;
	}

	.ss-article__body h3,
	.ss-page .wp-block-post-content h3,
	.ss-privacy .wp-block-post-content h3 {
		font-size: clamp(1.05rem, 2.6vw, 1.2rem);
		line-height: 1.2;
	}
}

/* ---- Tap targets --------------------------------------------------------- */

/* WCAG 2.2 SC 2.5.8 asks for 24x24 CSS pixels. Two controls failed it, both
   measured on the rendered page rather than inferred from the CSS:
     - the submenu chevron rendered at 7x7
     - the newsletter consent checkbox at 20x20
   Inline links inside a paragraph are exempt under the same rule, so the many
   text links the audit listed are left alone. */
.ss-nav .wp-block-navigation__submenu-icon,
.ss-nav .wp-block-navigation-submenu__toggle {
	min-width: 24px;
	min-height: 24px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
}

/* On the desktop bar the same 24px made the two items with drop-downs sit much
   further from their neighbours than the rest: measured, "Resorts" and "Offers"
   left about 47px after their label against 11px for every other item — the
   24px arrow plus an 18px margin.

   So the arrow goes back to its drawn size and the 24px target is provided by
   an invisible overlay instead. The tap area is unchanged; it simply stops
   taking up room in the line. */
@media (min-width: 1460px) {
	.ss-nav .wp-block-navigation__submenu-icon {
		position: relative;
		width: 6px;
		height: 6px;
		min-width: 0;
		min-height: 0;
		margin: 0;
		padding: 0;
		/* Optically centred on the cap height of the label rather than on the
		   line box, which sits the arrow a little low next to all-caps type. */
		transform: translateY(-1px);
	}

	.ss-nav .wp-block-navigation__submenu-icon svg {
		width: 100%;
		height: 100%;
	}

	.ss-nav .wp-block-navigation__submenu-icon::after {
		content: "";
		position: absolute;
		top: 50%;
		left: 50%;
		width: 24px;
		height: 24px;
		transform: translate(-50%, -50%);
	}

	/* The arrow and this padding together have to equal the 11.2px that every
	   other item carries, or the drop-down items sit closer to their neighbours
	   than the rest — which is what happened at 0: 17px against 23px. A 6px
	   arrow plus 0.33rem lands on 11.2px, so all seven gaps match. */
	.ss-nav .wp-block-navigation-item.has-child > .wp-block-navigation-item__content,
	.ss-nav .wp-block-navigation-submenu > .wp-block-navigation-item__content {
		padding-right: .33rem;
	}
}

.ss-newsletter input[type="checkbox"],
.ss-form input[type="checkbox"],
.wpcf7 input[type="checkbox"] {
	width: 24px;
	height: 24px;
	flex: 0 0 auto;
}

/* Full-width banners inherited from the old site, un-floated in PHP (see
   ss_unfloat_oversized_images) and laid out as blocks here. They are adverts
   and partner banners, so they keep the content's left edge rather than being
   centred, and never exceed the measure. */
.ss-legacy-banner {
	float: none !important;
	display: block;
	max-width: 100%;
	height: auto;
	margin-inline: 0;
	margin-block: 0 var(--wp--preset--spacing--40);
}

/* A grid item must be allowed to shrink below its content's intrinsic width,
   or one long unbroken title can blow the track out. Cheap insurance against
   the same class of fault appearing elsewhere. */
.ss-grid,
.ss-directory-grid {
	min-width: 0;
}

.ss-grid > *,
.ss-directory-grid > * {
	min-width: 0;
}

/* ==========================================================================
   13. About page
   ========================================================================== */

/* The page arrived from the old site as twelve bullet points, every sentence
   given identical weight. The content is now a deck, a lead, a two-column
   split with the current cover, a pull quote and a figures strip — so the eye
   is given somewhere to land and a reason to keep going. */

/* Strapline above the lead. Display face, brand blue, tracked out — it reads
   as a masthead line rather than a heading, which is what it is. */
.ss-about__deck {
	font-family: var(--wp--preset--font-family--display);
	font-size: clamp(1.35rem, 3.2vw, 2rem);
	line-height: 1.1;
	letter-spacing: .01em;
	text-transform: uppercase;
	color: var(--wp--preset--color--brand);
	margin-block: 0 .5em !important;
}

/* The one paragraph everybody reads. Larger than body copy and held to a short
   measure so it stays two or three lines.

   The `margin-inline: 0 auto` is load-bearing. The editor's constrained layout
   centres any child narrower than the container, which put this paragraph 312px
   in from the left while the heading above it started at 56 — a measured
   indent, not an optical one. Every block on this page must share one left
   edge, so the spare width is pushed entirely to the right. */
.ss-about__lead {
	font-size: clamp(1.15rem, 1.9vw, 1.4rem);
	line-height: 1.5;
	max-width: 44rem;
	margin-block: 0 clamp(2rem, 4vw, 3rem) !important;
	margin-inline: 0 auto !important;
}

.ss-about__split {
	gap: clamp(2rem, 5vw, 4rem);
	align-items: flex-start;
	margin-block-end: clamp(2.5rem, 5vw, 4rem);
}

.ss-about__body > * {
	max-width: 38rem; /* a comfortable measure; the column itself is wider */
}

/* Kept deliberately below the strapline's size. "The biggest and the best"
   opens the page; a section heading inside the copy is subordinate to it and
   must not compete — it was reading as the larger of the two. */
.ss-about__body h2,
.wp-block-post-content .ss-about__body h2 {
	font-size: clamp(1.05rem, 2.1vw, 1.45rem);
	line-height: 1.15;
	margin-block: 1.6em .5em;
}

/* The cover is the single most recognisable thing the magazine owns, so it is
   given real size and a shadow that lifts it off the page like a physical
   copy. Sticky on desktop only: it stays alongside the history while the text
   scrolls, which is the whole reason for putting it in a column. */
/* Held to a fixed maximum and pushed to the right edge of its column, so the
   cover reads as a magazine sitting in the corner of the page rather than a
   full-height slab competing with the copy. */
.ss-about__cover {
	margin: 0 0 0 auto;
	max-width: 440px;
}

.ss-about__cover img {
	width: 100%;
	height: auto;
	display: block;
	box-shadow: 0 2px 6px rgb(35 31 32 / 10%), 0 18px 48px rgb(35 31 32 / 18%);
	transition: transform .3s var(--ss-ease), box-shadow .3s var(--ss-ease);
}

.ss-about__cover a:hover img,
.ss-about__cover a:focus-visible img {
	transform: translateY(-4px);
	box-shadow: 0 4px 10px rgb(35 31 32 / 12%), 0 24px 60px rgb(35 31 32 / 22%);
}

.ss-about__cover figcaption {
	margin-block-start: .9em;
	font-size: var(--wp--preset--font-size--x-small);
	color: var(--wp--preset--color--muted, #5b6670);
	text-align: left;
}

@media (min-width: 782px) {
	.ss-about__aside {
		position: sticky;
		top: calc(var(--ss-sticky-offset, 72px) + 1.5rem);
	}
}

/* Lift the cover so it sits alongside the opening lines rather than starting
   below them — the magazine is the first thing worth seeing on this page, and
   it was sinking under the fold of the intro.

   The lead is capped to the left column's share of the width at the same time.
   Without that cap a 44rem paragraph reaches into the space the raised cover
   now occupies, and the two overlap on mid-size screens. Below 1080px the
   columns are too narrow for this, and below 782px they stack. */
@media (min-width: 1080px) {
	/* Selected through the parent deliberately. WordPress's own flex-layout
	   rule, `.is-layout-flex > :is(*, div) { margin: 0 }`, has specificity
	   (0,1,1) and silently beat a plain `.ss-about__aside` (0,1,0) — the margin
	   computed to 0px and the cover did not move at all. Going through
	   `.ss-about__split` takes this to (0,2,0), which wins without !important.

	   192px is measured, not chosen: the cover sat at y=852 and the strapline
	   at y≈655, so this brings the two level. */
	.ss-about__split > .ss-about__aside {
		margin-block-start: -12rem;
	}

	.ss-about__lead {
		max-width: 52%;
	}
}

/* ---- Pull quote ---------------------------------------------------------- */

/* Deliberately not a box. A heavy rule and a large display line carry it, in
   keeping with the rest of the site — a tinted panel with a border would be
   the one piece of furniture on the page that looks like a plugin. */
.ss-pullquote {
	border: 0;
	border-top: 3px solid var(--wp--preset--color--brand);
	border-bottom: 1px solid var(--ss-line);
	padding: clamp(1.75rem, 4vw, 2.75rem) 0;
	margin-block: clamp(2.5rem, 5vw, 4rem);
	background: none;
	text-align: left;
}

.ss-pullquote blockquote {
	margin: 0;
	max-width: 34ch; /* short enough that it reads as a quote, not a paragraph */
}

.ss-pullquote blockquote p {
	font-family: var(--wp--preset--font-family--display);
	font-size: clamp(1.6rem, 4.4vw, 2.9rem);
	line-height: 1.12;
	color: var(--wp--preset--color--brand);
	margin: 0;
	text-transform: none;
}

.ss-pullquote cite {
	display: block;
	margin-block-start: 1.1em;
	font-style: normal;
	font-size: var(--wp--preset--font-size--x-small);
	text-transform: uppercase;
	letter-spacing: .06em;
	color: var(--wp--preset--color--muted, #5b6670);
}

/* ---- Figures strip ------------------------------------------------------- */

.ss-facts {
	display: grid;
	gap: clamp(1.25rem, 3vw, 2rem);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
	/* Generous space beneath, so the strip closes the page rather than running
	   straight into the newsletter panel and the advertising below it. */
	margin-block: clamp(2.5rem, 5vw, 3.5rem) clamp(3.5rem, 8vw, 6rem);
	align-items: start;
}

/* The flow layout gives every block a top margin EXCEPT the first, which left
   the first panel's rule sitting ~20px above the other two. In a grid row that
   margin is pure misalignment, so it goes. */
.ss-facts > * {
	margin-block: 0 !important;
}

.ss-fact {
	border-top: 2px solid var(--wp--preset--color--brand);
	padding-block-start: 1rem;
}

.ss-fact > * {
	margin-block: 0 !important;
}

.ss-fact__figure {
	font-family: var(--wp--preset--font-family--display);
	font-size: clamp(1.5rem, 3.4vw, 2.1rem);
	line-height: 1.05;
	color: var(--wp--preset--color--brand);
}

.ss-fact__label {
	margin-block-start: .45em !important;
	font-size: var(--wp--preset--font-size--x-small);
	line-height: 1.4;
	color: var(--wp--preset--color--muted, #5b6670);
}

/* ==========================================================================
   14. Directory website screenshots
   ========================================================================== */

/* Each listing now carries a screenshot of the business's own site. These are
   web pages, not product photographs: they are widest at the top, so anchoring
   to the top keeps the logo and navigation visible, and `cover` fills the
   frame rather than letterboxing a pale strip above and below. */
.ss-listing-card .wp-block-post-featured-image {
	margin-block-end: var(--wp--preset--spacing--20) !important;
}

.ss-listing-card .wp-block-post-featured-image img {
	width: 100%;
	height: 150px;
	object-fit: cover;
	object-position: top center;
	border: 1px solid var(--ss-line);
	background: #fff;
}

/* On the entry's own page the screenshot is the evidence that the listing is
   current, so it is shown at a size worth looking at.

   The width is capped on the IMAGE, not on the block. Capping the block made it
   narrower than the content column, so the editor's constrained layout centred
   it — and forcing `margin-inline: 0 auto` on the block escaped the column
   altogether: measured, the picture then sat at x=56 while the business name
   and address sat at x=320. Leaving the block to be placed normally, and
   pushing the spare width to the right of the image inside it, puts the picture
   on the same left edge as every line of text. */
.single-ss_directory .wp-block-post-featured-image {
	margin-block-end: var(--wp--preset--spacing--40) !important;
}

.single-ss_directory .wp-block-post-featured-image img {
	display: block;
	width: 100%;
	max-width: 640px;
	margin-inline: 0 auto;
	height: auto;
	max-height: 400px;
	object-fit: cover;
	object-position: top center;
	border: 1px solid var(--ss-line);
	box-shadow: var(--ss-shadow);
	background: #fff;
}

[data-theme="dark"] .ss-listing-card .wp-block-post-featured-image img,
[data-theme="dark"] .single-ss_directory .wp-block-post-featured-image img {
	border-color: rgb(255 255 255 / 14%);
}

/* ---- County filter ------------------------------------------------------- */

/* Real links to the county archives, not a script. Every pill is a page that
   can be bookmarked, shared and indexed, and the whole set works with the
   keyboard without anything being written to make it do so. */
.ss-filter {
	margin-block: clamp(1.25rem, 3vw, 2rem) clamp(1.75rem, 4vw, 2.5rem);
}

/* The listings fade out and back in when a county is chosen, rather than the
   page reloading. 180ms each way: enough to read as a change of content, short
   enough that nobody waits for it.

   Only opacity is animated — moving or resizing the grid would reflow the page
   under the reader's thumb. The height changes as the number of listings does,
   which is honest; it is the swap that is smoothed, not the layout. */
.wp-block-query {
	transition: opacity .18s var(--ss-ease);
}

.wp-block-query.is-swapping {
	opacity: 0;
}

/* While a county is loading the pills stay put and stay readable — dimming the
   control you just pressed makes an interface feel broken. */
.ss-filter.is-loading .ss-filter__list {
	cursor: progress;
}

@media (prefers-reduced-motion: reduce) {
	.wp-block-query {
		transition: none;
	}

	.wp-block-query.is-swapping {
		opacity: 1;
	}
}

.ss-filter__label {
	margin: 0 0 .7rem;
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--x-small);
	text-transform: uppercase;
	letter-spacing: .06em;
	color: var(--wp--preset--color--brand);
}

.ss-filter__list {
	display: flex;
	flex-wrap: wrap;
	gap: .5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

/* The global list rule adds a left padding for hanging indents. On a row of
   pills that is not a hanging indent, it just shunts the first one right. */
.ss-filter__list li {
	margin: 0;
	padding-left: 0;
}

.ss-filter__pill {
	display: inline-flex;
	align-items: center;
	gap: .4rem;
	min-height: 36px;          /* comfortably past the 24px minimum target */
	padding: .35rem .8rem;
	border: 1px solid var(--ss-line);
	color: var(--wp--preset--color--ink);
	font-size: var(--wp--preset--font-size--x-small);
	text-decoration: none;
	transition: background .15s var(--ss-ease), border-color .15s var(--ss-ease),
	            color .15s var(--ss-ease);
}

.ss-filter__pill:hover,
.ss-filter__pill:focus-visible {
	border-color: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--brand);
	text-decoration: none;
}

.ss-filter__pill.is-current {
	background: var(--wp--preset--color--brand);
	border-color: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
}

.ss-filter__count {
	font-variant-numeric: tabular-nums;
	opacity: .6;
}

.ss-filter__pill.is-current .ss-filter__count {
	opacity: .75;
}

[data-theme="dark"] .ss-filter__pill {
	color: var(--wp--preset--color--ink);
	border-color: rgb(255 255 255 / 18%);
}

/* Route back to the directory from a single listing. Given real weight — a
   bordered button rather than a text link — because it is the only way out of
   an entry for a reader who arrived from Google. */
.ss-back {
	margin-block-start: clamp(2rem, 5vw, 3rem);
}

.ss-back__link {
	display: inline-flex;
	align-items: center;
	gap: .5rem;
	min-height: 44px;
	padding: .6rem 1.25rem .6rem 1rem;
	border: 1px solid var(--wp--preset--color--brand);
	color: var(--wp--preset--color--brand);
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--x-small);
	text-transform: uppercase;
	letter-spacing: .04em;
	text-decoration: none;
	transition: background .2s var(--ss-ease), color .2s var(--ss-ease);
}

.ss-back__link:hover,
.ss-back__link:focus-visible {
	background: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--base);
	text-decoration: none;
}

.ss-back__icon {
	width: 18px;
	height: 18px;
	flex: 0 0 auto;
}

[data-theme="dark"] .ss-back__link {
	border-color: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--accent);
}

/* ---- Search inside the slide-out menu -------------------------------------
   The masthead search is hidden below 1280px so it cannot crowd the header
   leaderboard, so the slide-out carries its own copy. It is full width at the
   top of the panel, above the links, where a reader looking for search will
   look first. Hidden at desktop widths, where the masthead control is back. */
.ss-search--in-menu {
	display: none;
}

@media (max-width: 1459px) {
	.wp-block-navigation__responsive-container.is-menu-open .ss-search--in-menu {
		display: block;
		width: 100%;
		max-width: 22rem;
		margin: 0 0 1.5rem;
		order: -1;
	}

	.wp-block-navigation__responsive-container.is-menu-open .ss-search--in-menu .wp-block-search__inside-wrapper {
		border: 1px solid var(--ss-line);
		border-radius: 999px;
		background: #fff;
		padding: .15rem .15rem .15rem .9rem;
	}

	.wp-block-navigation__responsive-container.is-menu-open .ss-search--in-menu .wp-block-search__input {
		border: 0;
		background: transparent;
		color: var(--wp--preset--color--ink);
		font-size: 1rem;
		min-height: 2.75rem;
	}

	.wp-block-navigation__responsive-container.is-menu-open .ss-search--in-menu .wp-block-search__button {
		background: var(--wp--preset--color--brand);
		color: #fff;
		border: 0;
		border-radius: 999px;
		min-width: 2.75rem;
		min-height: 2.75rem;
	}
}
