/* ==========================================================================
   SPLURGE, the design layer.

   theme.json owns the tokens: palette, type scale, spacing, element styles.
   This file owns everything theme.json has no vocabulary for, which is the
   part that actually makes the page look made rather than generated:

     the grain            paper tooth over every ground
     the five moods       the client's approved colour schemes, as bands
     the cross-fade       light-to-light section joins with no visible seam
     the hairlines        champagne rules that draw but never speak

   Ported from the Astro build at web/src/styles/global.css, which stays in
   the repo as the reference. Measurements below were taken there and still
   hold: re-measure before changing a value, do not estimate.
   ========================================================================== */

/*
  BORDER-BOX, EVERYWHERE.

  WordPress applies box-sizing: border-box to its own block elements and to
  nothing else. Every piece of raw markup in this theme, the footer, the
  scroll expansion, the districts list, therefore inherited content-box, where
  `width: 100%` plus padding computes WIDER than the parent and the auto
  margin absorbs the difference as a negative value.

  It surfaced as the whole page scrolling sideways by exactly twice the page
  gutter, which reads like a rogue element rather than a box model. Fixing it
  once here is the only version of this that stays fixed.
*/
*,
*::before,
*::after {
	box-sizing: border-box;
}

:root {
	/*
	  TWO GRAIN TEXTURES, AND TWO BLEND MODES. This is not belt and braces.

	  soft-light was tried first and moved cream by two levels out of 255,
	  so the grain was, in the most literal sense, invisible. Measured by
	  rasterising the tile and reading pixels:

	    on cream      multiply @ 0.12   spread 13   VISIBLE
	    on aubergine  multiply          spread  3   invisible
	    on aubergine  overlay           spread  9   VISIBLE

	  So light grounds multiply and dark grounds overlay, and the dark
	  texture is carried at a higher opacity because it has further to go.
	*/
	--grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)' opacity='0.12'/%3E%3C/svg%3E");
	--grain-inv: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)' opacity='0.18'/%3E%3C/svg%3E");

	/*
	  Hairlines. Decorative separators may be champagne: WCAG 1.4.11 governs
	  controls and meaningful graphics, not decoration. Anything a visitor has
	  to find and act on uses --rule-live instead, which measures 3.25:1.
	  45% was tried and measures 2.83:1, which fails.
	*/
	--rule: color-mix(in srgb, var(--wp--preset--color--champagne) 55%, transparent);
	--rule-live: color-mix(in srgb, var(--wp--preset--color--soft-black) 50%, transparent);

	--ease-brand: cubic-bezier(0.23, 1, 0.32, 1);

	/*
	  THE NAV HEIGHT, as one token.

	  The header and every sticky offset on the site read this. It was two
	  separate hardcoded numbers in the Astro build and they had already
	  drifted by 4px once. The centred masthead composes taller than a
	  toolbar, so the value is stated rather than left to whatever the
	  content happens to add up to.
	*/
	--nav-h: 170px;
}

@media (width >= 60rem) {
	:root { --nav-h: 112px; }
}

/* --------------------------------------------------------------- THE PLATE

   The page carries grain even where no section declares a ground, so a page
   built from plain blocks still sits on paper rather than on a flat fill. */
.splurge-plate {
	position: relative;
	z-index: 10;
	background-color: var(--wp--preset--color--cream);
	background-image: var(--grain);
	background-size: 160px 160px;
	background-blend-mode: multiply;
}

/* ---------------------------------------------------------------- THE BAND

   The grain sits FIRST in the image list, so it is the topmost layer and
   falls across the bloom as well as the ground. A grain layer painted under
   a gradient is a grain layer nobody can see. */
.band {
	background-color: var(--ground, var(--wp--preset--color--cream));
	background-image: var(--grain), var(--bloom, none);
	background-repeat: repeat, no-repeat;
	background-size: 160px 160px, cover;
	background-position: 0 0, center;
	background-blend-mode: multiply, normal;
}

/* ======================================================== THE FIVE MOODS

   The client's guide lists five approved three-colour combinations and names
   the content each is for. Each class sets a ground and, where the scheme
   calls for it, retunes --rule. That one variable is what every hairline on
   the site reads, so a band restyles its own furniture without a single
   component being touched.

   What NEVER changes per band is the voice. Wine at 9.44:1 and aubergine at
   10.45:1 say the things that must be read. Rose (2.12:1), champagne
   (1.78:1) and beige (1.7:1) draw and tint and never speak, on any ground.
   That is a brand rule in the guide and an accessibility floor here, and the
   two happen to agree. */

/* SIGNATURE. cream + aubergine + champagne. Hero and key pages. */
.band-canvas {
	--ground: var(--wp--preset--color--cream);
	--bloom: radial-gradient(86% 62% at 88% -8%, color-mix(in srgb, var(--wp--preset--color--champagne) 26%, transparent), transparent 64%);
}

/* Secondary surface. One step up, for a section that should feel opened. */
.band-raised {
	--ground: var(--wp--preset--color--ivory);
	--bloom: radial-gradient(80% 60% at 10% 0%, color-mix(in srgb, var(--wp--preset--color--champagne) 22%, transparent), transparent 62%);
}

/* The lightest ground in the palette. Used where a page turns over. */
.band-light {
	--ground: var(--wp--preset--color--off-white);
	--bloom: radial-gradient(90% 70% at 92% 0%, color-mix(in srgb, var(--wp--preset--color--rose) 20%, transparent), transparent 66%);
}

/* ROMANTIC. The guide's words: perfume, romance, personal storytelling.
   Which is exactly what the founder section is. */
.band-blush {
	--ground: var(--wp--preset--color--blush);
	--bloom: radial-gradient(78% 62% at 86% 0%, color-mix(in srgb, var(--wp--preset--color--rose) 40%, transparent), transparent 62%);
	--rule: color-mix(in srgb, var(--wp--preset--color--rose) 52%, transparent);
}

/* EDITORIAL. Sand is warm beige pulled into cream, so it is the one light
   band that reads as a tonal step DOWN rather than sideways. It gives the
   page somewhere to go that is not straight to aubergine. */
.band-sand {
	--ground: var(--wp--preset--color--sand);
	--bloom: radial-gradient(92% 66% at 8% 100%, color-mix(in srgb, var(--wp--preset--color--gold) 24%, transparent), transparent 64%);
	--rule: color-mix(in srgb, var(--wp--preset--color--gold) 48%, transparent);
}

/* The dark bands get tooth too. A flat aubergine field is every bit as flat
   as a flat cream one. */
.band-inverted,
.band-deepest {
	background-image: var(--grain-inv);
	background-size: 160px 160px;
	background-blend-mode: overlay;
	--rule: color-mix(in srgb, var(--wp--preset--color--champagne) 45%, transparent);
}

.band-inverted { background-color: var(--wp--preset--color--aubergine); }
.band-deepest  { background-color: var(--wp--preset--color--plum-black); }

/* Champagne is allowed to speak on the dark grounds: it reaches 5.86:1 on
   aubergine and 7.71:1 on plum, so it clears the 4.5:1 body-text floor. */
.band-inverted,
.band-deepest,
.band-inverted :is(h1, h2, h3, h4, p, li),
.band-deepest :is(h1, h2, h3, h4, p, li) {
	color: var(--wp--preset--color--cream);
}

/* =============================================== LIGHT-TO-LIGHT CROSS-FADE

   Two pale grounds meeting at a hard line looks like a rendering seam rather
   than a change of chapter. Between cream and off-white there is barely
   eight percent of luminance in it, so the eye reads the EDGE, the most
   contrasty thing in the frame, instead of the colours.

   A light band directly following another light band is pulled UP over its
   predecessor by --fade, and its own paint is masked from transparent to
   opaque across exactly that distance. The negative margin is cancelled by
   matching padding, so nothing below moves.

   Generic on purpose: it never needs to know which colour is above it,
   because it does not blend two colours, it reveals whatever is there.
   Reorder the page and the fades still work.

   The dark bands carry .band-inverted / .band-deepest and never .band, so
   they are excluded by construction. Cream meeting aubergine is a real edge
   that should stay an edge; softening it would read as a misregistration. */


/* A hairline at a boundary that is also cross-fading is two answers to the
   same question, and the mask would eat it anyway: a top border sits in the
   fully transparent end of the gradient. */
.band + .band.rule-top { border-top: 0; }

.rule-top { border-top: 1px solid var(--rule); }

/* ------------------------------------------------------------------- NAV */
.splurge-nav {
	border-bottom: 1px solid var(--rule);
}

.splurge-wordmark a {
	text-decoration: none;
}

/* ---------------------------------------------------------------- MOTION

   Everything above is paint, so it survives reduced motion untouched. The
   only moving part is the cross-fade's mask, which does not animate. Kept
   here so the next person adding motion has somewhere obvious to put the
   guard rather than inventing one. */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

/* ========================================================== SECTION: HERO */

.splurge-hero {
	display: grid;
	gap: clamp(2.5rem, 5vw, 3.5rem);
	padding-top: clamp(2.5rem, 5vw, 4rem);
	padding-bottom: clamp(2rem, 4vw, 3rem);
	padding-inline: var(--wp--preset--spacing--70);
	margin-inline: auto;
	width: 100%;
	max-width: 1360px;
	align-items: center;
}

@media (width >= 60rem) {
	.splurge-hero {
		/* Deliberately not 50/50. The type column is the wider one: the
		   photograph is placeholder stock until the shoot happens, so the
		   composition must not depend on it carrying the page. */
		grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
		gap: clamp(3rem, 5vw, 5rem);
		/* min(), so a tall monitor does not stretch the hero into a field of
		   empty cream. It fills a laptop and caps out beyond that. */
		min-height: min(calc(100svh - var(--nav-h)), 760px);
		padding-block: clamp(1.5rem, 3vw, 3rem);
	}
}

/* The kicker. Small, tracked, and the only place wine appears before the
   headline, so it reads as a label rather than as a line of copy. */
.kicker {
	letter-spacing: 0.16em;
	text-transform: uppercase;
	font-weight: 560;
	margin: 0;
}

/*
  13ch, tuned to the break rather than picked. The headline is thirteen
  characters at its longest line ("who knows it.") and twelve at its second
  ("London, with"), so this is the narrowest measure that still sets it as
  three full lines instead of five ragged ones.
*/
.hero-title {
	margin-top: 1.5rem;
	max-width: 13ch;
}

/*
  Emphasis is the italic of the SAME family, never a second face, and it is
  the one word allowed to change colour. white-space stops "someone" being
  broken across a line, which would leave the rule below it dangling.
*/
.splurge-hero h1 em,
.wp-block-post-content h1 em {
	font-style: normal;
	color: var(--wp--preset--color--wine);
	white-space: nowrap;
	position: relative;
}

.hero-sub {
	margin-top: 1.5rem;
	max-width: 40ch;
	font-size: clamp(1.05rem, 1.7vw, 1.2rem);
}

.hero-actions {
	margin-top: 2.25rem;
	gap: 1rem;
}

/* The outline button. Same pill, no fill, so the second action is clearly
   secondary without becoming a link nobody sees. */
.wp-block-button.is-style-outline > .wp-block-button__link {
	background: transparent;
	color: var(--wp--preset--color--aubergine);
	border: 1px solid var(--rule-live);
}

@media (hover: hover) and (pointer: fine) {
	.wp-block-button.is-style-outline > .wp-block-button__link:hover {
		background: var(--wp--preset--color--aubergine);
		color: var(--wp--preset--color--cream);
		border-color: var(--wp--preset--color--aubergine);
	}
}

/* ---------------------------------------------------------- HERO FIGURES */

.hero-figure {
	position: relative;
	padding-bottom: 1.5rem;
}

.hero-frame img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 5 / 6;
	object-fit: cover;
}

/*
  The second frame, mounted like a print: a cream border and a hairline, no
  shadow. Rotated a touch so it reads as placed rather than as a UI card.
*/
.hero-inset {
	position: absolute;
	left: -1.5rem;
	bottom: 0;
	z-index: 1;
	width: 42%;
	rotate: -2.5deg;
	background-color: var(--wp--preset--color--cream);
	padding: 0.5rem;
	border: 1px solid var(--rule);
}

.hero-inset img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 1 / 1;
	object-fit: cover;
}

/* Inside the page gutter on small screens, where a negative offset would
   hang the frame off the edge of the plate. */
@media (width < 60rem) {
	.hero-inset {
		left: 0.75rem;
		width: 40%;
	}
}

.hero-figure figure { margin: 0; }

/*
  The champagne rule under the emphasised word. Decoration, so champagne is
  allowed here: WCAG 1.4.11 governs controls and meaningful graphics, and this
  is neither. The word itself is wine at 9.44:1 and carries the meaning.

  The resting state is DRAWN. Every reveal on this site is written this way
  round: if an animation never runs, because scripting is dead or motion is
  reduced, the reader gets the finished design rather than a missing one.
*/
.splurge-hero h1 em::after,
.wp-block-post-content h1 em::after {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0.02em;
	height: 0.055em;
	background: var(--wp--preset--color--champagne);
	transform-origin: left;
}

/* ================================================== SHARED SECTION FURNITURE

   Every ported section is a .splurge-section carrying a band class, with a
   .shell inside it holding the content to the page measure. Padding lives on
   the section so the band's colour runs full width while the words do not. */

.splurge-section {
	padding-block: var(--wp--preset--spacing--60);
}

.shell {
	margin-inline: auto;
	width: 100%;
	max-width: 1360px;
	padding-inline: var(--wp--preset--spacing--70);
}

.shell-narrow {
	margin-inline: auto;
	width: 100%;
	max-width: 940px;
	padding-inline: var(--wp--preset--spacing--70);
}

.section-title {
	margin-top: 0.85rem;
	line-height: 1.05;
}

/* Emphasis inside a section heading: same treatment as the hero, one step
   quieter, and never a second typeface. */
.section-title em {
	font-style: italic;
	color: var(--wp--preset--color--wine);
}

/* ============================================== SECTION: WHY THIS EXISTS */

.problem-grid {
	display: grid;
	gap: 1.75rem;
}

@media (width >= 48rem) {
	.problem-grid {
		grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
		gap: 4rem;
		align-items: start;
	}
}

.problem-body {
	display: grid;
	gap: 1.1rem;
	max-width: 54ch;
	font-size: clamp(1.05rem, 1.5vw, 1.15rem);
}

/*
  The turn. A hairline and a weight step, not a bigger size: this is the
  sentence that pivots from the problem to the offer, and it has to read as a
  conclusion rather than as a second heading.
*/
.problem-turn {
	margin-top: 1.85rem;
	padding-top: 1.4rem;
	border-top: 1px solid var(--rule);
	font-size: clamp(1.05rem, 1.45vw, 1.18rem);
	font-weight: 550;
	line-height: 1.45;
	color: var(--wp--preset--color--aubergine);
}

/* ============================================== SECTION: TWO AUDIENCES

   Two doors, not two products. The frames are deliberately different shapes:
   the street is landscape because it is a place, the detail is portrait
   because it is a thing. Matching frames would have read as a card grid. */

.lede-block { max-width: 46ch; }

.doors {
	margin-top: clamp(1.75rem, 3vw, 2.5rem);
	display: grid;
	gap: clamp(2rem, 3.5vw, 3rem);
}

@media (width >= 48rem) {
	.doors {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		align-items: start;
	}
}

.door figure { margin: 0; }

.door img {
	display: block;
	width: 100%;
	object-fit: cover;
}

.door-wide img { height: auto;
	aspect-ratio: 5 / 4; }
.door-tall img { height: auto;
	aspect-ratio: 4 / 5; }

.door-title {
	margin-top: 1.5rem;
	line-height: 1.08;
}

.door-body {
	margin-top: 0.85rem;
	max-width: 44ch;
}

/* ============================================ SECTION: HOW A SESSION RUNS */

.splurge-how { padding-top: var(--wp--preset--spacing--60); }

.ledger-head {
	display: flex;
	flex-wrap: wrap;
	align-items: end;
	justify-content: space-between;
	gap: 1.5rem;
}

.ledger-more { font-size: 15px; font-weight: 550; margin: 0; }

.section-lede {
	margin-top: 1rem;
	max-width: 52ch;
}

.link-quiet {
	color: var(--wp--preset--color--wine);
	text-underline-offset: 0.25em;
	text-decoration-thickness: 1px;
}

/* ------------------------------------------------------- THE STICKY STACK

   Each panel sticks and the next scrolls over it. No z-index ladder is
   needed: later siblings paint over earlier ones in document order. Anchored
   to the nav height so a pinned panel never hides behind the header. */

.stack { position: relative; }

.stack-panel {
	position: sticky;
	top: var(--nav-h);
	min-height: 68svh;
	display: grid;
	/*
	  align-content, NOT place-content. place-content: center sets
	  justify-content: center too, and a centred grid sizes its column to
	  max-content instead of stretching it. That was invisible while a panel
	  held only a paragraph; the moment it became copy plus photograph, the
	  track grew to whatever the two columns wanted and pushed the picture
	  straight through the page margin and off the right edge.
	*/
	align-content: center;
	overflow: hidden;
	padding-block: clamp(2.5rem, 5vw, 4rem);
	background-color: var(--wp--preset--color--cream);
	background-image: var(--grain);
	background-size: 160px 160px;
	background-blend-mode: multiply;
	border-top: 1px solid var(--rule);
}

/*
  The odd panels invert to aubergine, restored on the client's call and
  matching the approved reference build. A light-only alternation was tried
  under the one-theme rule and rejected: in the stack, where each panel
  covers the last, the alternation IS the legibility.

  Overlay, not multiply: on aubergine, multiply moves the ground three
  levels out of 255 and overlay moves nine. Measured, not guessed.
*/
.stack-panel.is-inverted {
	background-color: var(--wp--preset--color--aubergine);
	background-image: var(--grain-inv);
	background-blend-mode: overlay;
	color: var(--wp--preset--color--cream);
	border-top-color: color-mix(in srgb, var(--wp--preset--color--champagne) 45%, transparent);
}

/*
  Stated explicitly rather than inherited: without these the titles render
  aubergine on aubergine, which measured 1:1 the last time it was left to
  inheritance.
*/
.stack-panel.is-inverted .stack-title,
.stack-panel.is-inverted .stack-body,
.stack-panel.is-inverted .stack-detail {
	color: var(--wp--preset--color--cream);
}

.stack-panel.is-inverted .stack-label {
	color: var(--wp--preset--color--champagne);
}

.stack-panel.is-inverted .stack-detail {
	border-top-color: color-mix(in srgb, var(--wp--preset--color--champagne) 45%, transparent);
}

/* A faint drafting grid, masked to a soft ellipse so it fades out rather
   than ending at a line. Decoration, hence champagne. */
.stack-grid {
	position: absolute;
	inset: 0;
	pointer-events: none;
	background-image:
		linear-gradient(to right, color-mix(in srgb, var(--wp--preset--color--champagne) 20%, transparent) 1px, transparent 1px),
		linear-gradient(to bottom, color-mix(in srgb, var(--wp--preset--color--champagne) 20%, transparent) 1px, transparent 1px);
	background-size: 54px 54px;
	-webkit-mask-image: radial-gradient(ellipse 60% 50% at 50% 0%, #000 70%, transparent 100%);
	mask-image: radial-gradient(ellipse 60% 50% at 50% 0%, #000 70%, transparent 100%);
}

.stack-inner {
	--stack-ratio: 3 / 1;
	position: relative;
	z-index: 1;
	display: grid;
	gap: clamp(1.5rem, 4vw, 2.25rem);
	width: 100%;
}

.stack-copy { max-width: 60ch; }

/* PHONE: the photograph becomes a 3:1 band, which costs about 115px inside a
   570px pinned panel. It sits above the copy so the panel opens on an image. */
.stack-figure { order: -1; }

.stack-figure figure { margin: 0; }

.stack-figure img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: var(--stack-ratio);
	object-fit: cover;
}

.stack-label {
	margin: 0;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	font-weight: 560;
	color: var(--wp--preset--color--wine);
}

.stack-title { margin-top: 0.9rem; line-height: 1.04; }
.stack-body { margin-top: 1.1rem; max-width: 54ch; }

/* The evidence for the claim above it. A hairline and a size step down, so
   it reads as the footnote it is rather than as a second paragraph. */
.stack-detail {
	margin-top: 1.25rem;
	padding-top: 1.1rem;
	border-top: 1px solid var(--rule);
	max-width: 54ch;
	font-size: 15px;
	line-height: 1.55;
}

/* DESKTOP: two real columns and the portrait crop the composition wants. The
   copy column is the wider one, because the photographs are placeholder
   stock until the shoot happens and the panel must not depend on one. */
@media (width >= 64rem) {
	.stack-inner {
		--stack-ratio: 4 / 5;
		grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
		align-items: center;
		gap: clamp(2.5rem, 5vw, 4.5rem);
		max-width: 1360px;
	}
	.stack-figure { order: 0; position: relative; }
}

/* ================================================== SECTION: WHERE WE GO */

.map-shell { margin-top: 3rem; }

.districts {
	display: grid;
	list-style: none;
	margin: 0;
	padding: 0;
	border-top: 1px solid var(--rule);
}

.district {
	display: flex;
	align-items: center;
	gap: 1.25rem;
	padding-block: 1.4rem;
	border-bottom: 1px solid var(--rule);
}

/* PHONE AND TABLET: there is no hover to spend on a touch screen, so each
   row simply carries its own small square. object-fit keeps a 900x1100
   source from distorting into a square. */
.district-shot {
	flex: none;
	width: 68px;
	height: 68px;
	object-fit: cover;
	border-radius: 0;
	filter: saturate(0.88) contrast(0.98);
}

.district-text { min-width: 0; }
.district-name { margin: 0; font-family: var(--wp--preset--font-family--display); font-weight: 600; font-size: clamp(1.15rem, 1.9vw, 1.45rem); line-height: 1.1; color: var(--wp--preset--color--aubergine); }
.district-note { margin-top: 0.3rem; font-size: 15px; color: var(--wp--preset--color--muted); max-width: 46ch; }

@media (width >= 48rem) {
	.district-shot { width: 84px; height: 84px; }
}

/* DESKTOP: the list becomes the interface. Running down the names swaps a
   single large frame beside them.

   --stage-w is declared once and used twice, for the grid track and for the
   frame that sits in it. They are the same measurement, and drifting them
   apart is how this kind of layout usually breaks. */
@media (width >= 72rem) {
	.map-grid {
		--stage-w: clamp(19rem, 26vw, 25rem);
		position: relative;
		display: grid;
		grid-template-columns: minmax(0, 1fr) var(--stage-w);
		gap: 4.5rem;
		align-items: start;
	}

	/* The row must stay UNTRANSFORMED. A transform on any ancestor makes it
	   the containing block for absolutely positioned descendants, which
	   would drag the stage frame back inside the row. */
	.district {
		display: block;
		position: static;
		padding-block: 1.55rem;
	}

	/*
	  height: 100%, NOT top:0 + bottom:0.

	  An absolutely positioned REPLACED element with height: auto does not
	  stretch between its pinned edges the way a div does. CSS 2.1 10.6.5
	  resolves its height from the intrinsic ratio, then treats the pair of
	  edges as over-constrained and drops `bottom`. The frame came out 250px
	  tall against an 800px list. A percentage height is the only instruction
	  an image actually obeys here.
	*/
	.district-shot {
		position: absolute;
		top: 0;
		right: 0;
		width: var(--stage-w);
		height: 100%;
		aspect-ratio: auto;
		opacity: 0;
		/* opacity: 0 hides a thing without excusing it from hit testing. All
		   six frames stack in one rectangle and the topmost is last in
		   document order, so without this, moving onto the photograph would
		   hover Notting Hill's row whichever district was showing. */
		pointer-events: none;
		scale: 1.02;
		/* Grown from the pinned edge, not the centre: scaled about its centre
		   each hidden frame sat 3.33px past the page margin. Invisible behind
		   the visible one, but a real overflow that becomes a scrollbar the
		   moment something upstream stops clipping. */
		transform-origin: right center;
		transition: opacity 0.35s var(--ease-brand), scale 0.4s var(--ease-brand);
	}

	/* Resting state: the first district is shown, so the stage is never an
	   empty rectangle before the pointer arrives. */
	.districts:not(:hover):not(:focus-within) .district:first-child .district-shot,
	.district:hover .district-shot,
	.district:focus-within .district-shot {
		opacity: 1;
		scale: 1;
	}
}

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

/* ====================================================== SECTION: SAFETY */

.safety-grid { display: grid; gap: 2.5rem; }

@media (width >= 48rem) {
	.safety-grid {
		grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
		gap: 4rem;
	}
}

/* The safety section now sits on the lightest ground, so its hairlines use
   the standard champagne rule token like every other light band. */
.safety-list {
	list-style: none;
	margin: 0;
	padding: 0;
	border-top: 1px solid var(--rule);
}

.safety-item {
	padding-block: 1.35rem;
	border-bottom: 1px solid var(--rule);
	max-width: 54ch;
	color: var(--wp--preset--color--muted);
}

.safety-more { margin-top: 1.5rem; font-size: 15px; font-weight: 550; }

/* On the dark bands the quiet link cannot be wine: wine on aubergine is
   under 2:1. Champagne reads at 5.86:1 and is the correct voice here. */
.band-inverted .link-quiet,
.band-deepest .link-quiet {
	color: var(--wp--preset--color--champagne);
}

.band-inverted .section-lede,
.band-deepest .section-lede {
	color: color-mix(in srgb, var(--wp--preset--color--cream) 82%, transparent);
}

/* ===================================================== SECTION: FOUNDER */

.founder-grid { display: grid; gap: 2.5rem; }

@media (width >= 64rem) {
	.founder-grid {
		grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
		gap: clamp(2.5rem, 5vw, 4.5rem);
		align-items: center;
	}
}

.founder-figure figure { margin: 0; }

.founder-figure img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 4 / 5;
	object-fit: cover;
}

/* The comma and the role sit at body weight, so the name is the thing that
   reads as a name and not as a job title. */
.founder-comma {
	font-family: var(--wp--preset--font-family--sans);
	font-weight: 400;
	font-size: 0.5em;
	letter-spacing: 0.02em;
	color: var(--wp--preset--color--muted);
	white-space: nowrap;
}

.founder-intro { margin-top: 1.25rem; max-width: 52ch; }
.founder-more  { margin-top: 1.5rem; font-size: 15px; font-weight: 550; }

/* ===================================================== SECTION: PRICING

   A LEDGER, NOT CARDS. Three-up pricing cards imply a best-value middle
   option and a decision to agonise over. There are two sessions here and the
   difference between them is time, not tier. */

.ledger {
	list-style: none;
	margin: 2.5rem 0 0;
	padding: 0;
	border-top: 1px solid var(--rule);
}

.ledger-row {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem 2rem;
	padding-block: 1.6rem;
	border-bottom: 1px solid var(--rule);
}

.ledger-main { min-width: 0; flex: 1 1 24ch; }

.ledger-name {
	margin: 0;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(1.15rem, 2vw, 1.5rem);
	line-height: 1.1;
	color: var(--wp--preset--color--aubergine);
}

.ledger-detail {
	margin-top: 0.45rem;
	font-size: 15px;
	line-height: 1.55;
	color: var(--wp--preset--color--muted);
	max-width: 52ch;
}

/* Tabular figures, so £200 and £500 line up at the decimal rather than
   drifting apart by the width of a 2. */
.ledger-price {
	margin: 0;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(1.3rem, 2.4vw, 1.8rem);
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--wine);
}

/* The extras read one step quieter than the sessions: they are additions to
   a choice already made, not a third and fourth option. */
.ledger-row.is-extra .ledger-name { font-size: clamp(1rem, 1.5vw, 1.15rem); }
.ledger-row.is-extra .ledger-price { font-size: clamp(1.05rem, 1.7vw, 1.3rem); }

.ledger-terms { margin-top: 1.5rem; max-width: 56ch; }
.ledger-cta   { margin-top: 2rem; }

/* ========================================================= SECTION: FAQ */

.faqs { margin: 2.5rem 0 0; border-top: 1px solid var(--rule); }

.faq { padding-block: 1.6rem; border-bottom: 1px solid var(--rule); }

.faq-q {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(1.1rem, 1.8vw, 1.35rem);
	line-height: 1.15;
	color: var(--wp--preset--color--aubergine);
}

.faq-a {
	margin: 0.6rem 0 0;
	max-width: 60ch;
	color: var(--wp--preset--color--muted);
}

/* ==================================================== SECTION: BOOKING */

.book-form {
	display: grid;
	gap: 1.75rem;
	margin-top: 2.5rem;
}

@media (width >= 40rem) {
	.book-form { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.75rem 2.5rem; }
	.field-wide { grid-column: 1 / -1; }
}

/* Off-screen rather than display:none: some bots skip anything hidden that
   way, and some browsers will not submit it. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.book-label { display: block; font-size: 14px; font-weight: 560; color: var(--wp--preset--color--soft-black); }
.book-optional { font-weight: 400; color: var(--wp--preset--color--muted); }

/*
  Underline-only fields. A boxed input is the most app-like object a page can
  carry, and the whole design is built to avoid that register. The border is
  --rule-live, not --rule: this is a control someone must find and act on, so
  it uses the 3.25:1 functional hairline rather than the champagne decoration.
*/
.field {
	display: block;
	width: 100%;
	margin-top: 0.5rem;
	padding: 0.65rem 0;
	background: transparent;
	border: 0;
	border-bottom: 1px solid var(--rule-live);
	border-radius: 0;
	font: inherit;
	color: var(--wp--preset--color--soft-black);
}

.field:focus {
	outline: none;
	border-bottom-color: var(--wp--preset--color--wine);
	border-bottom-width: 2px;
}

.field:focus-visible {
	outline: 2px solid var(--wp--preset--color--aubergine);
	outline-offset: 4px;
}

textarea.field { resize: vertical; }

.book-submit { margin-top: 0.5rem; cursor: pointer; border: 0; }

.book-notice {
	margin-top: 1.75rem;
	max-width: 56ch;
	font-size: 14px;
	color: var(--wp--preset--color--muted);
	border-top: 1px solid var(--rule);
	padding-top: 1.25rem;
}

/* Wine, not red. It reads at 9.44:1 on the light grounds, so the message
   works as text rather than relying on being noticed as a colour. */
.book-error {
	margin-top: 2rem;
	padding: 1rem 1.25rem;
	border-left: 3px solid var(--wp--preset--color--wine);
	background: color-mix(in srgb, var(--wp--preset--color--wine) 7%, transparent);
	color: var(--wp--preset--color--wine);
	max-width: 60ch;
}

.book-done { margin-top: 2.5rem; border-top: 1px solid var(--rule); padding-top: 2rem; }
.book-done:focus-visible { outline: 2px solid var(--wp--preset--color--aubergine); outline-offset: 6px; }

.book-done-title {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: var(--wp--preset--font-size--section);
	line-height: 1.1;
	color: var(--wp--preset--color--aubergine);
}

.book-done-body { margin-top: 0.9rem; max-width: 48ch; color: var(--wp--preset--color--muted); }

/* ======================================================== THE MASTHEAD

   Wordmark LEFT, the pages CENTRED, utilities and the booking action RIGHT.

   The split is the point: the middle column is browsing, the right column is
   doing, and the two never sit in the same group. It also gives the right
   column enough weight to balance five links in the middle, which is what
   keeps the row optically even rather than left-heavy.

   NO PILL IN THE HEADER. The chrome is flat text throughout, and a filled
   button here would be the only loud thing in it. "Book a session" is still
   the emphasised item, carrying the wine accent and a champagne rule under
   it, and the filled pill still appears in the hero, in the pricing section
   and in the footer. This is the one that has to be quiet.

   Placed by named grid areas rather than source order: hiding a grid item
   with display:none removes it from the grid entirely and auto-placement
   slides everything after it along, which is how the booking link once ended
   up in the middle of the header. */

.splurge-nav {
	position: sticky;
	top: 0;
	z-index: 50;
	background-color: var(--wp--preset--color--cream);
	background-image: var(--grain);
	background-size: 160px 160px;
	background-blend-mode: multiply;
	border-bottom: 1px solid var(--rule);
	padding-block: 0.85rem;
	transition: translate 300ms var(--ease-brand), background-color 300ms linear;
}

.nav-inner {
	display: grid;
	grid-template-columns: auto 1fr auto;
	grid-template-areas: 'mast menu utils';
	align-items: center;
	gap: 1.5rem;
}

.nav-mast  { grid-area: mast; }
.splurge-menu { grid-area: menu; }
.nav-utils { grid-area: utils; display: flex; align-items: center; gap: 1.5rem; }

.splurge-wordmark a { text-decoration: none; }

.splurge-menu,
.nav-utils {
	font-size: 13px;
	letter-spacing: 0.12em;
	text-transform: uppercase;
}

.splurge-menu { gap: 1.6rem; }

.splurge-menu a,
.nav-util {
	text-decoration: none;
	color: var(--wp--preset--color--soft-black);
	white-space: nowrap;
}

@media (hover: hover) and (pointer: fine) {
	.splurge-menu a:hover,
	.nav-util:hover { color: var(--wp--preset--color--wine); }
}

/* The emphasised item. Wine at 9.44:1 does the speaking; the champagne rule
   underneath is decoration, which is the one job champagne has on cream. */
.nav-book {
	position: relative;
	text-decoration: none;
	color: var(--wp--preset--color--wine);
	font-weight: 560;
	white-space: nowrap;
}

.nav-book::after {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	bottom: -0.35em;
	height: 1px;
	background: var(--wp--preset--color--champagne);
}

/*
  BELOW 64rem the middle column and the two utility links go into the sheet,
  leaving wordmark left and the menu button right. Five links, two utilities
  and a booking action cannot share a row on a phone without every one of
  them becoming too small to hit.
*/
@media (width < 64rem) {
	.nav-inner {
		grid-template-columns: auto 1fr auto;
		grid-template-areas: 'mast utils menu';
		gap: 0.75rem;
	}

	.splurge-menu { justify-self: end; }

	/*
	  THE BOOKING LINK STAYS. Hiding the whole utilities group took the
	  primary action off the header on every phone, which is the one device
	  where a reader is least likely to go hunting for it in a sheet.
	  Instagram and Contact go, since both are repeated in the footer and
	  neither is what someone is on a phone to do.
	*/
	.nav-utils { justify-self: end; gap: 0; }
	.nav-utils .nav-util { display: none; }
	.nav-book { font-size: 12px; }
}

/* A 24px hit area is the WCAG 2.5.8 floor, not a target worth aiming at on a
   phone. 44px is the size a thumb actually finds. The mark inside stays the
   size it was; only the area around it grows. */
.wp-block-navigation__responsive-container-open {
	min-width: 44px;
	min-height: 44px;
	display: grid;
	place-items: center;
}

@media (width < 26rem) {
	/* At the very narrowest the wordmark, the action and the menu stop
	   fitting on one row, so the action gives up its words. */
	.nav-book { font-size: 11px; letter-spacing: 0.08em; }
}

.splurge-nav[data-hidden='true'] { translate: 0 -100%; }

/* Reading progress on the header's own bottom rule. scaleX of a custom
   property: one composited transform per frame, no layout. */
.splurge-nav::after {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	bottom: -1px;
	height: 2px;
	background: var(--wp--preset--color--wine);
	transform: scaleX(var(--progress, 0));
	transform-origin: left;
	pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
	.splurge-nav { transition: none; }
	.splurge-nav[data-hidden='true'] { translate: none; }
}

/* The notice sits on ONE line. It was wrapping its link onto a second row,
   which doubled the height of the very first thing on the page. */
.notice-bar { flex-wrap: nowrap; }
.notice-text { display: inline; }

@media (width < 34rem) {
	.notice-bar { font-size: 12.5px; padding-block: 0.5rem; }
	.notice-link { display: none; }
}

/* ======================================================= MOTION GATING

   The `motion` class is added to <html> by assets/js/motion.js ONLY when
   scripting runs AND prefers-reduced-motion is off. Every hidden initial
   state lives behind that class: no JS, a blocked file, or reduced motion
   all land on a page that is simply visible.

   Motion enhances. It never gates. */

html.motion [data-rise],
html.motion .rise {
	opacity: 0;
	transform: translateY(22px);
}

/*
  Masked line reveal. Structural properties UNCONDITIONAL: an inline element
  ignores overflow entirely, so the clip only exists if .mask-line is
  inline-block, and inline-block is what lets the sentence wrap without JS.
  Only the hidden transform is gated.

  The padding and negative-margin pair is DESCENDER CLEARANCE, and it is the
  reason this device was unusable before. `overflow: clip` cuts at the
  padding edge, and at the tight leading display headings are set in
  (line-height 1.05) that edge falls straight through the tail of every g, y
  and p. Padding pushes the clip box below the descenders; the equal negative
  margin removes that extra height again, so the line box is unchanged and
  nothing below it moves.
*/
.mask-line {
	display: inline-block;
	overflow: clip;
	padding-bottom: 0.16em;
	margin-bottom: -0.16em;
}

.mask-line > span { display: inline-block; }
html.motion .mask-line > span { transform: translateY(110%); }

/*
  Magnetic CTAs get `translate` from JS; this transition smooths it. Scoped
  away from the button link because `transition` is NOT additive: a second
  rule replaces the whole list, and this once silently killed the button's
  own background and colour transitions.
*/
html.motion [data-magnetic],
html.motion .magnetic {
	transition: translate 120ms var(--ease-out-quart, cubic-bezier(0.25, 1, 0.5, 1));
}

/* ------------------------------------------------- THE NAV, WHILE MOVING

   Hidden on the way down, back on the first upward gesture, and never while
   a menu is open. Transform only, so it composites. */
.splurge-nav {
	transition: translate 300ms var(--ease-brand), background-color 300ms linear;
}

.splurge-nav[data-hidden='true'] { translate: 0 -100%; }

/* Reading progress, drawn on the header's own bottom rule rather than as a
   separate bar. scaleX of a custom property: one composited transform per
   frame, no layout. */
.splurge-nav::after {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	bottom: -1px;
	height: 2px;
	background: var(--wp--preset--color--wine);
	transform: scaleX(var(--progress, 0));
	transform-origin: left;
	pointer-events: none;
}

.splurge-nav { position: sticky; }

@media (prefers-reduced-motion: reduce) {
	.splurge-nav { transition: none; }
	.splurge-nav[data-hidden='true'] { translate: none; }
}

/* ================================================== THE CINEMATIC FOOTER

   ONE IN-FLOW BLOCK. The earlier version was a fixed curtain with the
   directory bolted underneath, and it could not work: a fixed element cannot
   be taller than the viewport, so the join between the two landed wherever
   the viewport happened to end. That is what cut a heading in half and left
   a screen of nothing. */

.cinematic-footer {
	position: relative;
	z-index: 10;
	width: 100%;
	overflow: hidden;
	background-color: var(--wp--preset--color--plum-black);
	color: var(--wp--preset--color--cream);
	/* Overlay, not multiply: on plum, multiply shifts about three levels and
	   does nothing, overlay shifts nine. Measured. */
	background-image: var(--grain-inv);
	background-size: 160px 160px;
	background-blend-mode: overlay;
	padding-top: 9rem;

	/*
	  THE PILLS WERE ALMOST INVISIBLE at 30% champagne: roughly a 1.4:1 edge
	  on plum, against the 3:1 WCAG 1.4.11 asks for the boundary of any
	  control a person has to find. These are the two primary actions in the
	  footer. The BORDER does the work rather than the fill: raising the fill
	  would lift the shape off the ground and lose the glass, whereas the
	  outline is what makes a control findable.
	*/
	--pill-bg-1: color-mix(in srgb, var(--wp--preset--color--cream) 14%, transparent);
	--pill-bg-2: color-mix(in srgb, var(--wp--preset--color--cream) 5%, transparent);
	--pill-border: color-mix(in srgb, var(--wp--preset--color--champagne) 62%, transparent);
	--pill-highlight: color-mix(in srgb, var(--wp--preset--color--cream) 16%, transparent);
}

.cf-aurora {
	position: absolute;
	top: 38%;
	left: 50%;
	z-index: 0;
	height: 55vh;
	width: 80vw;
	translate: -50% -50%;
	border-radius: 50%;
	/*
	  48px, down from 80px. The layer underneath is already a radial gradient,
	  so it has no hard edge for a heavy blur to soften; past this the extra
	  radius buys nothing visible and costs real raster time on a surface this
	  large. Promoted to its own layer so the opacity animation never touches
	  the footer behind it.
	*/
	filter: blur(48px);
	will-change: opacity;
	pointer-events: none;
	background: radial-gradient(circle at 50% 50%,
		color-mix(in srgb, var(--wp--preset--color--aubergine) 70%, transparent) 0%,
		color-mix(in srgb, var(--wp--preset--color--wine) 40%, transparent) 45%,
		transparent 70%);
}

.cf-grid {
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background-size: 60px 60px;
	background-image:
		linear-gradient(to right, color-mix(in srgb, var(--wp--preset--color--champagne) 10%, transparent) 1px, transparent 1px),
		linear-gradient(to bottom, color-mix(in srgb, var(--wp--preset--color--champagne) 10%, transparent) 1px, transparent 1px);
	-webkit-mask-image: linear-gradient(to bottom, transparent, #000 30%, #000 70%, transparent);
	mask-image: linear-gradient(to bottom, transparent, #000 30%, #000 70%, transparent);
}

/* Outlined, not filled: a solid 26vw wordmark would be a wall. The stroke
   plus a top-lit gradient reads as embossed into the ground. */
.cf-giant {
	position: absolute;
	bottom: -3vh;
	left: 50%;
	z-index: 0;
	translate: -50%;
	pointer-events: none;
	user-select: none;
	white-space: nowrap;
	font-family: var(--wp--preset--font-family--display);
	font-style: italic;
	font-size: 26vw;
	line-height: 0.75;
	font-weight: 700;
	letter-spacing: -0.03em;
	text-transform: lowercase;
	color: transparent;
	-webkit-text-stroke: 1px color-mix(in srgb, var(--wp--preset--color--champagne) 28%, transparent);
	background: linear-gradient(180deg, color-mix(in srgb, var(--wp--preset--color--cream) 10%, transparent) 0%, transparent 62%);
	-webkit-background-clip: text;
	background-clip: text;
}

.cf-marquee {
	position: absolute;
	top: 2.5rem;
	left: 0;
	z-index: 10;
	width: 100%;
	rotate: -2deg;
	scale: 1.1;
	overflow: hidden;
	border-block: 1px solid color-mix(in srgb, var(--wp--preset--color--champagne) 25%, transparent);
	background: color-mix(in srgb, var(--wp--preset--color--plum-black) 60%, transparent);
	backdrop-filter: blur(12px);
	padding-block: 0.75rem;
}

.cf-marquee-track {
	display: flex;
	width: max-content;
	font-size: 11px;
	font-weight: 500;
	letter-spacing: 0.28em;
	text-transform: uppercase;
	color: color-mix(in srgb, var(--wp--preset--color--cream) 70%, transparent);
	animation: cf-marquee 45s linear infinite;
}

.cf-marquee-item { display: flex; align-items: center; gap: 2.5rem; padding-inline: 1.25rem; }
.cf-star { color: color-mix(in srgb, var(--wp--preset--color--champagne) 70%, transparent); }

@keyframes cf-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/*
  OPACITY ONLY. It used to animate scale as well, and scaling an element
  carrying filter: blur(80px) forces the blur to be re-rastered every single
  frame, across a surface roughly 1000x500 on a laptop. Opacity on an already
  rastered layer is a composite step and costs effectively nothing.

  The breathing still reads: what the eye follows here is the glow rising and
  falling, not the 10% of size that went with it.
*/
@keyframes cf-breathe {
	0%   { opacity: 0.55; }
	100% { opacity: 0.9; }
}

.cf-breathe { animation: cf-breathe 9s ease-in-out infinite alternate; }

/* WCAG 2.2.2: anything moving for more than five seconds needs a way to stop
   it. Hover and focus both pause, and reduced motion stops it outright. */
.cf-marquee:hover .cf-marquee-track,
.cf-marquee:focus-within .cf-marquee-track { animation-play-state: paused; }

.cf-signoff {
	position: relative;
	z-index: 10;
	margin-inline: auto;
	width: 100%;
	max-width: 64rem;
	display: flex;
	flex-direction: column;
	align-items: center;
	padding: 4rem var(--wp--preset--spacing--70) 4rem;
}

.cf-heading {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(2.1rem, 5.5vw, 3.6rem);
	line-height: 1.02;
	text-align: center;
	color: var(--wp--preset--color--cream);
	margin-bottom: 2.25rem;
}

.cf-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem; }

.cf-pill {
	display: inline-flex;
	align-items: center;
	gap: 0.75rem;
	border-radius: 999px;
	padding: 1rem 2.25rem;
	font-size: 12px;
	font-weight: 500;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--wp--preset--color--cream);
	text-decoration: none;
	background: linear-gradient(145deg, var(--pill-bg-1) 0%, var(--pill-bg-2) 100%);
	border: 1px solid var(--pill-border);
	box-shadow: inset 0 1px 1px var(--pill-highlight);
	backdrop-filter: blur(16px);
	transition: background-color 200ms ease, border-color 200ms ease, transform 160ms var(--ease-out-quart, cubic-bezier(0.25, 1, 0.5, 1));
}

@media (hover: hover) and (pointer: fine) {
	.cf-pill:hover { border-color: var(--wp--preset--color--champagne); }
}

/* ---------------------------------------------------------- THE DIRECTORY */

.cf-directory {
	position: relative;
	z-index: 10;
	margin-inline: auto;
	width: 100%;
	max-width: 1360px;
	padding-inline: var(--wp--preset--spacing--70);
	padding-block: 3.5rem;
	display: grid;
	gap: 2.5rem;
	border-top: 1px solid color-mix(in srgb, var(--wp--preset--color--champagne) 22%, transparent);
}

@media (width >= 40rem) { .cf-directory { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (width >= 64rem) { .cf-directory { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 3rem; } }

.cf-col-title {
	font-size: 12px;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--wp--preset--color--champagne);
	margin: 0 0 1rem;
}

.cf-col-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7rem; }

.cf-col-list a,
.cf-col-list span {
	font-size: 15px;
	color: color-mix(in srgb, var(--wp--preset--color--cream) 84%, transparent);
	text-decoration: none;
	/* Long addresses break at the @ and the dots rather than forcing the
	   column wider than its share of the grid. */
	overflow-wrap: anywhere;
}

@media (hover: hover) and (pointer: fine) {
	.cf-col-list a:hover { color: var(--wp--preset--color--champagne); }
}

.cf-baseline {
	position: relative;
	z-index: 10;
	margin-inline: auto;
	max-width: 1360px;
	padding: 1.5rem var(--wp--preset--spacing--70) 2.5rem;
	border-top: 1px solid color-mix(in srgb, var(--wp--preset--color--champagne) 22%, transparent);
	font-size: 13px;
	color: color-mix(in srgb, var(--wp--preset--color--cream) 66%, transparent);
}

@media (prefers-reduced-motion: reduce) {
	.cf-marquee-track,
	.cf-breathe { animation: none; }
}

/* ============================================== SECTION: SCROLL EXPANSION

   One custom property drives the whole thing. --p runs 0 to 1 and CSS derives
   the frame size, the backdrop fade and the title travel from it, so the
   script's only job per frame is writing a single number.

   --p DEFAULTS TO 1, which is the FINISHED state: fully open, title centred,
   backdrop resting behind it. With no scripting or under reduced motion the
   reader gets the completed composition rather than an empty stage. */

.se { --p: 1; position: relative; }

.se-runway {
	position: relative;
	/* The runway's height IS the scroll budget for the expansion. */
	height: 180vh;
}

.se-stage {
	position: sticky;
	top: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	height: 100svh;
	overflow: hidden;
	background-color: var(--wp--preset--color--plum-black);
}

.se-bg { position: absolute; inset: 0; z-index: 0; opacity: calc(1 - var(--p)); }
.se-bg img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }
/*
  52%, up from 25%. The title and caption sit OVER this backdrop, outside the
  media frame, in cream. Against the bright white-brick Shoreditch photograph
  a 25% wash left them barely legible; the media frame has its own separate
  wash and never needed this one to stay light.
*/
.se-bg-wash { position: absolute; inset: 0; background: color-mix(in srgb, var(--wp--preset--color--plum-black) 52%, transparent); }

/* Grows from a 300x400 plate to a near-full-bleed frame. Width and height are
   interpolated separately, so it opens outwards rather than simply scaling,
   which would soften the photograph as it grew. */
.se-media {
	position: absolute;
	top: 50%;
	left: 50%;
	z-index: 1;
	translate: -50% -50%;
	width: calc(300px + var(--p) * 650px);
	height: calc(400px + var(--p) * 200px);
	max-width: 92vw;
}

@media (width >= 48rem) {
	.se-media {
		width: calc(300px + var(--p) * 1250px);
		height: calc(400px + var(--p) * 400px);
	}
}

.se-media-inner { position: relative; width: 100%; height: 100%; overflow: hidden; }
.se-media-inner img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Darkest when closed, lifting as it opens, so the frame reads as coming
   into the light rather than simply getting bigger. */
.se-media-wash {
	position: absolute;
	inset: 0;
	background: var(--wp--preset--color--plum-black);
	opacity: calc(0.55 - var(--p) * 0.3);
}

.se-media-rule {
	position: absolute;
	inset: 0.5rem;
	border: 1px solid color-mix(in srgb, var(--wp--preset--color--champagne) 50%, transparent);
	pointer-events: none;
}

.se-caption {
	position: relative;
	z-index: 2;
	margin-top: 1rem;
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 0.35rem;
}

.se-place {
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.4rem;
	color: var(--wp--preset--color--cream);
	margin: 0;
	translate: calc(var(--p) * -12vw) 0;
}

/* The "Scroll to open" hint that lived here is gone. A reader looking at a
   pinned frame mid-page does not need to be told what scrolling is; the
   frame answering their scroll teaches the mechanic in the first inch. */

/* The title parts as the frame opens. Two halves travelling in opposite
   directions reads as the photograph pushing them aside. */
.se-title {
	position: relative;
	z-index: 2;
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0 0.35em;
	width: 100%;
	text-align: center;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(2.2rem, 6vw, 4rem);
	line-height: 1.06;
	color: var(--wp--preset--color--cream);
	pointer-events: none;
}

/*
  Half a viewport PLUS the half's own width. At plain ±50vw the wide half
  ("Fashion") travelled half a screen and still poked its tail back into
  frame at full open. Percentages in `translate` resolve against the
  element's own box, so adding ±100% guarantees a half always clears by
  exactly as much as it is wide, at every viewport.

  The opacity ramp finishes the job: fully there until a third open, gone by
  four-fifths, so nothing half-travelled ever hangs at the edge.
*/
.se-title-a { translate: calc(var(--p) * -50vw + var(--p) * -100%) 0; opacity: clamp(0, calc(1.4 - var(--p) * 1.75), 1); }
.se-title-b { font-style: italic; translate: calc(var(--p) * 50vw + var(--p) * 100%) 0; opacity: clamp(0, calc(1.4 - var(--p) * 1.75), 1); }

.se-copy { padding-block: var(--wp--preset--spacing--60); }
.se-cta { margin-top: 2rem; }

/* The pill again, on a light ground this time, so it takes the aubergine
   outline rather than the champagne one. */
.cf-pill-light {
	background: transparent;
	border-color: var(--rule-live);
	color: var(--wp--preset--color--aubergine);
	backdrop-filter: none;
	box-shadow: none;
}

@media (hover: hover) and (pointer: fine) {
	.cf-pill-light:hover {
		background: var(--wp--preset--color--aubergine);
		border-color: var(--wp--preset--color--aubergine);
		color: var(--wp--preset--color--cream);
	}
}

/* Under reduced motion the runway collapses to a single screen: there is no
   expansion to scroll through, so budgeting 180vh for it would just be a very
   long scroll past a static picture. */
@media (prefers-reduced-motion: reduce) {
	.se-runway { height: auto; }
	.se-stage { position: relative; }
}

/* ================================================ SECTION: TESTIMONIALS

   Three columns drifting at different speeds, the middle one against the
   other two. Slow enough to read while moving, and it stops the moment a
   reader reaches for it. */

.quote-wall {
	margin-top: 3rem;
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 1.25rem;
	height: 34rem;
	overflow: hidden;
	padding-inline: var(--wp--preset--spacing--70);
	/* Fades top and bottom, so quotes arrive and leave rather than being
	   sliced off at a hard edge. */
	-webkit-mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
	mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
}

@media (width >= 48rem) { .quote-wall { grid-template-columns: repeat(2, minmax(0, 1fr)); height: 38rem; } }
@media (width >= 64rem) { .quote-wall { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.5rem; } }

/* The third column only appears at the widest breakpoint, so below that it
   would sit in the grid as an invisible gap. */
@media (width < 64rem) { .quote-col:nth-child(3) { display: none; } }
@media (width < 48rem) { .quote-col:nth-child(2) { display: none; } }

.quote-col { overflow: hidden; }

.quote-track {
	display: grid;
	gap: 1.25rem;
	animation: quote-drift 60s linear infinite;
	will-change: transform;
}

.quote-col.is-reverse .quote-track { animation-direction: reverse; }

/* Half, because the list is rendered twice: at -50% the second copy sits
   exactly where the first started and the loop is seamless. */
@keyframes quote-drift {
	from { transform: translateY(0); }
	to   { transform: translateY(-50%); }
}

/* WCAG 2.2.2 again: moving content needs a way to stop. Hover and focus
   both pause, so a reader can finish a quote they are halfway through. */
.quote-wall:hover .quote-track,
.quote-wall:focus-within .quote-track { animation-play-state: paused; }

/*
  WHITE CARDS, in the Google-review register: a light plate, a soft edge, an
  avatar, the stars, then the words. It reads as something a person left
  somewhere rather than as pull-quote styling the site applied to itself,
  which is the difference between a testimonial and a review.

  This is the ONE place the brand's square-structure rule bends. A review card
  is a borrowed convention and it is recognisable partly by its corners; set
  square, it stops reading as a review and starts reading as a table cell.
*/
.quote {
	margin: 0;
	padding: 1.4rem 1.35rem 1.5rem;
	background-color: #FFFFFF;
	border: 1px solid color-mix(in srgb, var(--wp--preset--color--soft-black) 10%, transparent);
	border-radius: 10px;
}

.quote-head { display: flex; align-items: center; gap: 0.7rem; }

/* An initial, not a stock face. A fabricated portrait beside a quote is a
   second invented thing, and it is the one people look at first. */
.quote-avatar {
	flex: none;
	display: grid;
	place-items: center;
	width: 38px;
	height: 38px;
	border-radius: 50%;
	background: var(--wp--preset--color--aubergine);
	color: var(--wp--preset--color--cream);
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: 16px;
	line-height: 1;
}

.quote-who { display: grid; min-width: 0; }
.quote-source { margin-left: auto; display: grid; place-items: center; }
.google-mark { width: 18px; height: 18px; display: block; }

.quote-stars {
	margin: 0.85rem 0 0;
	color: #F4B400;
	font-size: 14px;
	letter-spacing: 0.12em;
	line-height: 1;
}

.quote blockquote {
	margin: 0.75rem 0 0;
	font-family: var(--wp--preset--font-family--sans);
	font-size: 15px;
	line-height: 1.55;
	color: var(--wp--preset--color--soft-black);
}

.quote-name { font-weight: 600; font-size: 14.5px; color: var(--wp--preset--color--soft-black); }
.quote-context { font-size: 12.5px; color: var(--wp--preset--color--muted); }

/* The standard visually-hidden pattern: read aloud, never drawn, and it does
   not get skipped the way display:none content does. */
.screen-reader-text {
	position: absolute;
	width: 1px; height: 1px;
	padding: 0; margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* Wine, and unmissable. This banner is the thing standing between a
   placeholder and a consumer-protection problem. */
.quote-warning {
	margin-top: 2rem;
	padding: 1rem 1.25rem;
	border-left: 3px solid var(--wp--preset--color--wine);
	background: color-mix(in srgb, var(--wp--preset--color--wine) 8%, transparent);
	color: var(--wp--preset--color--wine);
	font-size: 14px;
	max-width: 78ch;
}

@media (prefers-reduced-motion: reduce) {
	.quote-track { animation: none; }
	/* Without the drift the wall is just a tall clipped box, so let it be a
	   plain column of quotes instead. */
	.quote-wall { height: auto; -webkit-mask-image: none; mask-image: none; }
}

/* ==================================================== THE PAGE MASTHEAD */

.splurge-masthead { padding-block: clamp(2.5rem, 5vw, 4.5rem) var(--wp--preset--spacing--60); }

/* An inner-page h1 is a title, not a hero: it takes the display serif at
   section scale rather than the wide grotesk, which is reserved for the one
   headline on the homepage. */
.masthead-title {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-stretch: normal;
	letter-spacing: -0.015em;
	line-height: 1.06;
	margin-top: 0.85rem;
	max-width: 20ch;
}

.masthead-title em { font-style: italic; color: var(--wp--preset--color--wine); }

/* ================================================== THE IMMERSIVE GALLERY

   Six frames at different depths. As --p runs 0 to 1 each scales by its own
   factor, so they rush past the reader at different speeds and the nearest
   ones leave first. One line is left behind.

   --p DEFAULTS TO 1, the arrived state, so without scripting the reader gets
   the finished composition rather than a stage waiting to be filled. */

.ig { --p: 1; position: relative; }
.ig-runway { position: relative; height: 240vh; }

.ig-stage {
	position: sticky;
	top: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	height: 100svh;
	overflow: hidden;
	background-color: var(--wp--preset--color--cream);
	background-image: var(--grain);
	background-size: 160px 160px;
	background-blend-mode: multiply;
}

.ig-layer {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* Each layer scales from 1 to its own depth, which is what separates them
   into foreground and distance instead of one flat plane moving. */
.ig-layer-1 { --depth: 2.6; }
.ig-layer-2 { --depth: 3.4; }
.ig-layer-3 { --depth: 2.0; }
.ig-layer-4 { --depth: 2.9; }
.ig-layer-5 { --depth: 1.7; }
.ig-layer-6 { --depth: 2.3; }

.ig-layer { transform: scale(calc(1 + (var(--depth) - 1) * var(--p))); }

/* Percentage offsets, not pixels, so the arrangement holds at 390px. */
.ig-frame { position: relative; overflow: hidden; margin: 0; }
.ig-frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ig-rule { position: absolute; inset: 0.25rem; border: 1px solid color-mix(in srgb, var(--wp--preset--color--champagne) 50%, transparent); pointer-events: none; }

.ig-layer-1 .ig-frame { width: 44%; aspect-ratio: 4/5; }
.ig-layer-2 .ig-frame { width: 34%; aspect-ratio: 4/5; translate: -70% -74%; }
.ig-layer-3 .ig-frame { width: 30%; aspect-ratio: 3/4; translate: 82% -60%; }
.ig-layer-4 .ig-frame { width: 36%; aspect-ratio: 5/4; translate: -84% 68%; }
.ig-layer-5 .ig-frame { width: 28%; aspect-ratio: 4/5; translate: 92% 56%; }
.ig-layer-6 .ig-frame { width: 24%; aspect-ratio: 1/1; translate: 6% 100%; }

@media (width >= 40rem) {
	.ig-layer-1 .ig-frame { width: 24%; }
	.ig-layer-2 .ig-frame { width: 19%; }
	.ig-layer-3 .ig-frame { width: 17%; }
	.ig-layer-4 .ig-frame { width: 21%; }
	.ig-layer-5 .ig-frame { width: 15%; }
	.ig-layer-6 .ig-frame { width: 13%; }
}

/* Fades in over the last third, once the frames are already rushing past, so
   it reads as what is left rather than as a caption. */
.ig-line {
	position: relative;
	z-index: 10;
	margin: 0 auto;
	max-width: 22ch;
	padding-inline: 1.5rem;
	text-align: center;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 400;
	font-size: clamp(1.8rem, 4.2vw, 3.2rem);
	line-height: 1.15;
	color: var(--wp--preset--color--aubergine);
	pointer-events: none;
	opacity: clamp(0, calc((var(--p) - 0.5) / 0.3), 1);
	scale: calc(0.96 + 0.04 * clamp(0, calc((var(--p) - 0.5) / 0.3), 1));
}

@media (prefers-reduced-motion: reduce) {
	.ig-runway { height: auto; }
	.ig-stage { position: relative; }
}

/* ==================================================== SECTION: BUDDIES */

.standard-list {
	list-style: none;
	margin: 3rem 0 0;
	padding: 0;
	display: grid;
	gap: 2rem 3rem;
}

@media (width >= 48rem) { .standard-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (width >= 72rem) { .standard-list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.standard-item { border-top: 1px solid var(--rule); padding-top: 1.25rem; }

.standard-title {
	margin: 0;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(1.1rem, 1.7vw, 1.3rem);
	line-height: 1.12;
	color: var(--wp--preset--color--aubergine);
}

.standard-body { margin-top: 0.6rem; font-size: 15px; line-height: 1.55; color: var(--wp--preset--color--muted); max-width: 42ch; }

/* ==================================================== THE NOTICE BAR */

/* Set before first paint by the inline script in the head, so a dismissed
   bar is never drawn and the page never jumps by its height. */
html.notice-dismissed .notice-bar { display: none; }

.notice-bar {
	position: relative;
	z-index: 60;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 1rem;
	padding: 0.7rem calc(var(--wp--preset--spacing--70) + 2.5rem) 0.7rem var(--wp--preset--spacing--70);
	/*
	  Ivory, not aubergine. The Astro build had already learned this: a solid
	  brand band above the header fights the airiness the whole masthead
	  pattern depends on, and it was one more dark flip on a page that now
	  keeps one theme. A pale strip with a champagne hairline sits back and
	  lets the header breathe.
	*/
	background-color: var(--wp--preset--color--ivory);
	border-bottom: 1px solid var(--rule);
	color: var(--wp--preset--color--soft-black);
	font-size: 13.5px;
	text-align: center;
}

.notice-text { margin: 0; }

.notice-link {
	color: var(--wp--preset--color--wine);
	text-underline-offset: 0.25em;
	margin-left: 0.5rem;
	white-space: nowrap;
}

/*
  The control is 44px square for the touch target (WCAG 2.5.8) while the mark
  itself is drawn at 11px by two pseudo-elements. Drawing it rather than
  setting an x character keeps it optically centred at any font size, and the
  strokes are cream so they carry 10.45:1 rather than relying on a glyph.
*/
.notice-x {
	position: absolute;
	right: calc(var(--wp--preset--spacing--70) - 0.6rem);
	top: 50%;
	translate: 0 -50%;
	width: 44px;
	height: 44px;
	border: 0;
	background: transparent;
	cursor: pointer;
}

.notice-x::before,
.notice-x::after {
	content: '';
	position: absolute;
	left: 50%;
	top: 50%;
	width: 11px;
	height: 1.5px;
	background: var(--wp--preset--color--soft-black);
}

.notice-x::before { translate: -50% -50%; rotate: 45deg; }
.notice-x::after  { translate: -50% -50%; rotate: -45deg; }

.notice-x:focus-visible { outline: 2px solid var(--wp--preset--color--aubergine); outline-offset: -4px; }

/* ======================================================== THE FILM SLOT

   A facade, not an embed. Nothing is fetched from the video host until the
   reader presses play, which keeps roughly half a megabyte of third-party
   script off every page load and keeps them untracked until they opt in. */

.film { position: relative; }
.film img { display: block; width: 100%; height: auto;
	aspect-ratio: 4 / 5; object-fit: cover; }

.film-play {
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
	gap: 0.75rem;
	width: 100%;
	border: 0;
	background: color-mix(in srgb, var(--wp--preset--color--plum-black) 22%, transparent);
	color: var(--wp--preset--color--cream);
	cursor: pointer;
	transition: background-color 300ms var(--ease-brand);
}

@media (hover: hover) and (pointer: fine) {
	.film-play:hover { background: color-mix(in srgb, var(--wp--preset--color--plum-black) 34%, transparent); }
}

/* Drawn, not a glyph: a triangle character sits off-centre in most faces and
   there is no fixing it with padding. */
.film-play-mark {
	width: 0;
	height: 0;
	border-left: 18px solid var(--wp--preset--color--cream);
	border-block: 12px solid transparent;
	margin-left: 5px;
}

.film-play-label {
	font-size: 12px;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	font-weight: 560;
}

.film-play:focus-visible { outline: 2px solid var(--wp--preset--color--champagne); outline-offset: -6px; }

/* :global equivalent. The iframe is created in JS, so it never receives any
   class the markup declares; it is styled by element instead. */
.film iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ==========================================================================
   CORRECTIONS
   ========================================================================== */

/* ------------------------------------------- 1. THE INVISIBLE EMPHASIS

   Wine is the speaking accent on LIGHT grounds, where it measures 9.44:1. On
   aubergine it measures 1.11:1, which is not "low contrast", it is invisible:
   the word "seriously" in the safety heading simply was not there.

   Champagne is the accent on the dark grounds and reaches 5.86:1 on aubergine
   and 7.71:1 on plum. Same job, correct ground. */
.band-inverted .section-title em,
.band-inverted .safety-title em,
.band-inverted h1 em,
.band-inverted h2 em,
.band-inverted h3 em,
.band-deepest .section-title em,
.band-deepest h2 em {
	color: var(--wp--preset--color--champagne);
}

/* ------------------------------------------------ 2. THE VISIBLE STRIPES

   THIS WAS THE CAUSE OF EVERY "LINE" ON THE PAGE.

   theme.json sets a 1.5rem blockGap, and WordPress applies it to the top
   level of the page as `margin-block-start` on every section after the first.
   So between each coloured band there was a 24px gap with the cream plate
   showing through it: a pale stripe between blush and canvas, between sand
   and aubergine, everywhere.

   It read as a gradient edge or a stray border because that is exactly what a
   24px band of a third colour looks like between two others.

   Bands must BUTT. The gap is removed at the top level only, so blockGap
   still does its job inside every section. */
.splurge-plate > * + *,
.wp-site-blocks > main > * + * {
	margin-block-start: 0;
}

/* ------------------------------------------------- 3. NO VISIBLE RULES

   Every hairline that sat ON a section boundary comes off. A rule at a
   colour change is a second answer to a question the colour change has
   already answered, and it is the thing that reads as an unwanted edge.

   Hairlines INSIDE a section stay: they separate ledger rows, FAQ entries
   and safety lines, where they are doing real work. */
.splurge-section,
.splurge-how,
.stack-panel,
.band,
.band-inverted,
.band-deepest {
	border-top: 0;
}

.rule-top { border-top: 0; }

/* ------------------------------------------------ 4. THE CROSS-FADES BACK

   With the stripes gone the light bands are adjacent again, so the
   light-to-light fade can do its job: the previous ground shows through the
   top of the next one and hands over gradually, with no edge to see.

   Dark bands are excluded by construction, since they never carry .band.
   Cream meeting aubergine is a real edge and should stay one. */


/* ------------------------------------------------- 5. ONE BUTTON SYSTEM

   There were four different treatments on one page: filled aubergine, an
   outline, a borderless transparent one that read as floating text, and two
   glass pills with a 16px backdrop blur. The blur in particular sat over the
   footer aurora and made the two most important actions on the page look
   out of focus.

   Two treatments now, everywhere: filled for the primary action, outlined for
   the secondary. No blur anywhere. */
.cf-pill {
	background: color-mix(in srgb, var(--wp--preset--color--cream) 12%, transparent);
	border: 1px solid var(--pill-border, color-mix(in srgb, var(--wp--preset--color--champagne) 62%, transparent));
	backdrop-filter: none;
	-webkit-backdrop-filter: none;
	box-shadow: none;
}

/* The scroll-expansion action sits on a light ground, so it takes the same
   outline as the hero's secondary rather than a footer treatment. */
.cf-pill-light {
	background: transparent;
	border: 1px solid var(--rule-live);
	color: var(--wp--preset--color--aubergine);
}

@media (hover: hover) and (pointer: fine) {
	.cf-pill-light:hover {
		background: var(--wp--preset--color--aubergine);
		border-color: var(--wp--preset--color--aubergine);
		color: var(--wp--preset--color--cream);
	}
}

/* --------------------------------------------- 6. THE REVIEWS, UNSTUCK

   They were clipped to a fixed 34rem box with a mask top and bottom, which
   is what made them read as trapped behind a window rather than as a wall of
   cards. Taller, more of the fade spent at the very edges, and real breathing
   room above and below. */
.quote-wall {
	height: 44rem;
	-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 7%, #000 93%, transparent 100%);
	mask-image: linear-gradient(to bottom, transparent 0, #000 7%, #000 93%, transparent 100%);
	padding-block: 0.5rem;
}

@media (width >= 48rem) { .quote-wall { height: 46rem; } }

/* ------------------------------------- 7. THE RUNWAY HAD NO BACKGROUND

   A sticky stage inside a taller runway only paints where the stage is. The
   expansion's runway is 1620px and its stage is one viewport, so 720px of it
   was showing the cream plate underneath: a pale band appearing below the
   plum stage as it unstuck, which is exactly the "plain background line"
   between the doors section and the expansion.

   The runway carries the ground now, so the whole chapter is one colour and
   the only edge left is the deliberate one where it hands back to cream. */
.se-runway {
	background-color: var(--wp--preset--color--plum-black);
	background-image: var(--grain-inv);
	background-size: 160px 160px;
	background-blend-mode: overlay;
}

/* Same shape of bug, quieter, because the gallery's stage is cream on a cream
   plate. Stated anyway: the grain would otherwise stop and restart. */
.ig-runway {
	background-color: var(--wp--preset--color--cream);
	background-image: var(--grain);
	background-size: 160px 160px;
	background-blend-mode: multiply;
}

/* The stage no longer needs to paint its own ground, and painting it twice
   would double the grain where they overlap. */
.se-stage,
.ig-stage { background-color: transparent; background-image: none; }


/* The strip-based cross-fade that lived here is gone. Its
   .band + .band::before outranked the rebuilt seam system's .band::before on
   specificity, so `top`, `height` and the grain kept winning: every fading
   ground was squashed back into a short grain strip above its section, the
   section body below it fell through to the plate, and the very line the
   strip existed to remove came back at the strip's edge. The authoritative
   seam system is the final block of this file. */

/* Anchor targets clear the sticky header. Without this, following a link to
   #how or #book lands with the heading underneath the header. */
:where([id]) { scroll-margin-top: calc(var(--nav-h, 78px) + 1rem); }

/* ==========================================================================
   THE SEAM SYSTEM, REBUILT. This block is authoritative: it overrides every
   earlier band/fade/grain rule by cascade order, deliberately, so the whole
   story of how boundaries work lives in one place.

   WHY THE LINES KEPT COMING BACK. Every section painted its own grain tile.
   Wherever two grounds met, the textures doubled through the blend zone and
   a 24px fade of doubled grain reads as a smudged grey line. The corner
   blooms made it worse: they lived on the section, so they popped in at the
   boundary instead of fading in with it.

   THE ARCHITECTURE NOW, three layers, bottom to top:

     z0  each band's ::before   flat ground colour + its bloom, full size.
                                On every band after another band, masked
                                transparent-to-opaque over the first
                                --fade of its height, and the section is
                                pulled up by the same distance, so the
                                previous ground shows through and hands
                                over gradually. The bloom fades in WITH
                                the mask, so it can never pop.
     z0  ONE grain overlay      a single tile across the whole plate,
                                painted over every ground and every fade,
                                so the texture is continuous through each
                                boundary. Doubling is impossible: there is
                                only one grain layer to begin with.
     z1  all content            text, photographs, cards. Above the grain,
                                above the fades. A mask can never dim a
                                word and grain can never dirty a photo,
                                by construction rather than by care.
   ========================================================================== */

/* The full fade distance again. The 24px cap existed only to keep the old
   strip out of the text above it; content now sits at z1 where no mask can
   reach it, so the constraint is gone. */
:root { --fade: clamp(56px, 8vw, 130px); }

/* The plate is flat cream. Its grain moves to the single overlay. */
.splurge-plate { background-image: none; }

/* One grain layer for the entire page. It is a positioned pseudo-element
   painted after every section in DOM order, so at the z0 tier it sits over
   all of their grounds; everything at z1 clears it. The dark sections carry
   their own overlay-blend grain because multiply moves aubergine three
   levels out of 255, and they sit at z1 with it. */
.splurge-plate::after {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background-image: var(--grain);
	background-size: 160px 160px;
	background-blend-mode: multiply;
}

/* Bands stop painting themselves. The ground moves to ::before. */
.band {
	position: relative;
	background: none;
}

.band::before {
	content: '';
	position: absolute;
	inset: 0;
	height: auto;
	z-index: 0;
	pointer-events: none;
	background-color: var(--ground, var(--wp--preset--color--cream));
	background-image: var(--bloom, none);
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	background-blend-mode: normal;
	-webkit-mask-image: none;
	mask-image: none;
}

/*
  SECTIONS BUTT. The cross-fade experiment is over, on the client's call:
  the blend zone between two light grounds read as a darker smudge, and
  they preferred an honest hard change. The grounds now meet on a clean
  edge, which the single grain layer crosses without interruption, so the
  boundary is one crisp colour change rather than a gradient trying to
  apologise for itself.
*/

/* The smoothstep fade mask that lived here went with it. */

/* ------------------------------------------------- THE CONTENT TIER (z1)

   Everything that is not a ground outranks the grain overlay and the fades.
   The lift is per surface rather than one broad rule because position:
   relative would silently destroy position: sticky where it already exists. */

.band > * { position: relative; z-index: 1; }

.band-inverted,
.band-deepest { position: relative; }

.band-inverted > *,
.band-deepest > * { position: relative; z-index: 1; }

/* Already sticky, so already positioned: z-index alone, or the pin dies. */
.stack-panel { z-index: 1; }

/* The scroll set-pieces carry their own dark grounds and grain. */
.se-runway,
.ig-runway { position: relative; z-index: 1; }

/* ======================================================= THE MOBILE SHEET

   WordPress's navigation overlay ships as a bare white strip: no height, no
   ground, system styling. It had never been opened during any audit, which
   is exactly how it survived this long. This styles the open state as a
   full-screen sheet of the site's own material. */

/* The ancestor is specificity, not decoration: WordPress prints its own
   white background for the open container from block-supports style tags
   that land after the theme stylesheet, so equal specificity loses on
   order. One extra class outranks it without reaching for !important. */
.splurge-nav .wp-block-navigation__responsive-container.is-menu-open {
	position: fixed;
	inset: 0;
	z-index: 100;
	height: 100dvh;
	padding: 0;
	/*
	  The importants are aimed at WordPress core, which styles this exact
	  container with its own !important rules in block-library CSS, so no
	  amount of ordinary specificity wins the ground back. This is the one
	  narrow case importants are for: two of them, on the ground only,
	  against a rule the theme cannot otherwise reach.
	*/
	background-color: var(--wp--preset--color--cream) !important;
	background-image: var(--grain) !important;
	background-size: 160px 160px;
	background-blend-mode: multiply;
}

/* WordPress nests three wrappers between the container and the links; every
   one of them needs its height stated or the sheet collapses to content. */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__responsive-close,
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__responsive-dialog,
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__responsive-container-content {
	height: 100%;
	width: 100%;
}

.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__responsive-container-content {
	display: flex;
	flex-direction: column;
	justify-content: center;
	padding: 5rem var(--wp--preset--spacing--70) 3rem;
}

.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 0;
	width: 100%;
}

/* The pages, at display scale. A sheet is the one place the menu gets to
   speak at full size: it owns the whole screen while it is open. */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item {
	width: 100%;
	border-bottom: 1px solid var(--rule);
}

.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item a {
	display: block;
	padding: 1.05rem 0;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(1.6rem, 6vw, 2.1rem);
	line-height: 1.1;
	letter-spacing: normal;
	text-transform: none;
	color: var(--wp--preset--color--aubergine);
	text-decoration: none;
}

/* The action reads in wine, the contact rows step down to body scale, and
   the quiet pair sits apart from the pages. */
.wp-block-navigation__responsive-container.is-menu-open .sheet-book a {
	color: var(--wp--preset--color--wine);
}

.wp-block-navigation__responsive-container.is-menu-open .sheet-quiet {
	border-bottom: 0;
}

.wp-block-navigation__responsive-container.is-menu-open .sheet-quiet a {
	font-family: var(--wp--preset--font-family--sans);
	font-weight: 480;
	font-size: 15px;
	color: var(--wp--preset--color--muted);
	padding: 0.5rem 0;
	overflow-wrap: anywhere;
}

.wp-block-navigation__responsive-container.is-menu-open .sheet-quiet:first-of-type,
.wp-block-navigation__responsive-container.is-menu-open .sheet-book + .sheet-quiet {
	margin-top: 1.75rem;
}

/* The close control: a real target where the open button was. */
.wp-block-navigation__responsive-container-close {
	position: absolute;
	top: 0.9rem;
	right: calc(var(--wp--preset--spacing--70) - 10px);
	z-index: 2;
	display: grid;
	place-items: center;
	min-width: 44px;
	min-height: 44px;
	color: var(--wp--preset--color--soft-black);
}

/* Sheet-only rows never appear in the desktop menu: Book lives in the
   utilities column there, and the contact pair lives in the footer. */
@media (width >= 64rem) {
	.splurge-menu .sheet-only { display: none; }
}

/* No scrolling the page underneath an open sheet. */
html:has(.wp-block-navigation__responsive-container.is-menu-open) {
	overflow: hidden;
}

/* Enter: a short rise and fade, from the same motion vocabulary as the rest
   of the page. Feedback-adjacent, so it is short; gated for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
	.wp-block-navigation__responsive-container.is-menu-open {
		animation: sheet-in 240ms var(--ease-brand);
	}
	@keyframes sheet-in {
		from { opacity: 0; transform: translateY(8px); }
		to   { opacity: 1; transform: none; }
	}
}

/* ===================================================== THE ADMIN BAR

   Anyone logged into the dashboard sees the front end with WordPress's
   32px toolbar fixed to the top. The sticky header pinned itself to
   top: 0 underneath it, so for the one person who matters most in a
   review, the site's own chrome sat wedged under WordPress's. */
body.admin-bar .splurge-nav { top: 32px; }

@media (max-width: 782px) {
	body.admin-bar .splurge-nav { top: 46px; }
	/* WP unpins its own bar below 600px; the header follows suit. */
}

body.admin-bar .wp-block-navigation__responsive-container.is-menu-open {
	top: 32px;
	height: calc(100dvh - 32px);
}

@media (max-width: 782px) {
	body.admin-bar .wp-block-navigation__responsive-container.is-menu-open {
		top: 46px;
		height: calc(100dvh - 46px);
	}
}


/* ======================================================== PRESS FEEDBACK

   Every pressable element confirms the press in hand: a 3% scale drop on
   :active, 160ms strong ease-out. Nobody consciously notices it, which is
   the point; its absence is what a page feels like when it is ignoring you.
   Transform only, so it composites, and it is not gated behind html.motion:
   feedback is function, not decoration. */
.wp-block-button__link,
.cf-pill,
.book-submit,
.notice-x {
	transition-property: background-color, border-color, color, transform;
	transition-duration: 200ms, 200ms, 200ms, 160ms;
	transition-timing-function: ease, ease, ease, var(--ease-out-quart, cubic-bezier(0.25, 1, 0.5, 1));
}

.wp-block-button__link:active,
.cf-pill:active,
.book-submit:active {
	transform: scale(0.97);
}

/* Small square control: a deeper dip reads better at 44px. The element is
   centred by the `translate` PROPERTY, which composes with transform, so
   scale alone is correct here. */
.notice-x:active { transform: scale(0.92); }

/* The quiet links get a colour response, not a scale: text that shrinks
   under the finger looks like a misfire. */
.splurge-menu a,
.nav-util,
.nav-book,
.link-quiet {
	transition: color 150ms ease;
}

/* ================================================= REFERENCE PARITY PASS

   Side-by-side against the finished Astro build surfaced drift the port had
   accumulated. These restore the reference's decisions. */

/* THE BUTTON REGISTER. The reference sets every pill in small tracked
   uppercase, which is what keeps a £200 service's buttons reading as
   engraved invitations rather than app controls. The port had drifted to
   large sentence case. One rule, every pill on the site. */
.wp-block-button__link,
.book-submit {
	font-size: 12.5px;
	font-weight: 560;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	padding: 1.05rem 1.9rem;
}

.nav-cta .wp-block-button__link { font-size: 11.5px; padding: 0.6rem 1.3rem; }

/* THE HERO MOUNT. The reference frames the main photograph with an inset
   champagne rule, the same print-mount device the film frame and the
   gallery tiles carry. The port had lost it, leaving the one photograph
   that opens the site as the only unmounted image on the page. */
.hero-frame { position: relative; }

.hero-frame::after {
	content: '';
	position: absolute;
	inset: 0.5rem;
	border: 1px solid color-mix(in srgb, var(--wp--preset--color--champagne) 50%, transparent);
	pointer-events: none;
}


/* ------------------------------------------------ MOBILE, AFTER THE CAPS

   The tracked-uppercase register made every pill wider, and at 390px the
   hero pair overflowed the viewport edge. Wrap is stated explicitly rather
   than assumed from the block layout, and the pills step down a size on
   phones. The header's duplicate pill CTA is gone from the markup; these
   keep the remaining single Book link inside the row. */
.hero-actions,
.wp-block-buttons { flex-wrap: wrap; }

@media (width < 40rem) {
	.wp-block-button__link,
	.book-submit { font-size: 11.5px; padding: 0.9rem 1.5rem; }
	.nav-book { font-size: 11px; letter-spacing: 0.08em; }
}

/* ================================================== CLIENT NOTES, ROUND 1

/* ------------------------------------------------ 1. THE NOTICE, IN ROSE

   Dusty rose is in the client's extended palette and has never been used at
   scale on the site, because at 2.12:1 it cannot carry text ON cream. As a
   GROUND it is a different proposition: soft-black on rose measures well
   past the 4.5:1 floor, so the strip finally gets to be the fashion colour
   in the palette instead of another neutral. */
.notice-bar {
	background-color: var(--wp--preset--color--rose);
	border-bottom: 0;
	color: var(--wp--preset--color--soft-black);
}

.notice-link { color: var(--wp--preset--color--plum-black); }
.notice-x::before,
.notice-x::after { background: var(--wp--preset--color--soft-black); }

/* -------------------------------- 2. THE HEADER IS PART OF THE FIRST SCREEN

   The header carried its own cream ground and a hairline, so the hero read
   as a second section starting below a bar. It is one field now: the header
   is transparent over the hero, and the hero's ground plus bloom paint UP
   behind it, so the champagne light in the top corner runs unbroken from
   the very top edge of the page.

   It gains its ground back the moment the reader scrolls, which is when a
   sticky header actually needs to separate itself from what is passing
   underneath. motion.js already sets data-solid on scroll. */
.splurge-nav {
	background-color: transparent;
	background-image: none;
	border-bottom: 0;
}

.splurge-nav[data-solid='true'] {
	background-color: var(--wp--preset--color--cream);
	background-image: var(--grain);
	background-size: 160px 160px;
	background-blend-mode: multiply;
	border-bottom: 1px solid var(--rule);
}

/* The hero's ground runs up behind the transparent header. Only the hero:
   every other band still starts at its own top edge. */
.splurge-hero::before {
	top: calc((var(--nav-h) + var(--notice-h, 0px)) * -1);
}

/* No scripting means no data-solid, so the header would stay transparent
   over whatever scrolled beneath it. Painted by default there instead. */
html:not(.motion) .splurge-nav {
	background-color: var(--wp--preset--color--cream);
	border-bottom: 1px solid var(--rule);
}

/* ------------------------------------------- 3. THE REVIEWS HEAD, CENTRED

   Centred and set in the display italic, which is the brand's own
   calligraphic register: Fraunces italic at this size has the stroke
   modulation of written letterforms without introducing a third typeface
   against a guide that caps the system at two. */
.quote-head-block {
	max-width: 34ch;
	margin-inline: auto;
	text-align: center;
}

.quote-title {
	font-style: italic;
	font-weight: 500;
	font-size: clamp(2rem, 4.4vw, 3.2rem);
	line-height: 1.08;
	letter-spacing: -0.01em;
}

/* The emphasis is already italic here, so it separates by colour alone. */
.quote-title em { font-style: italic; }

/* ================================================ SESSIONS AS PRODUCTS

   Two cards, each opening its own page. The card is a real link, not a div
   with a click handler: it keyboard-focuses, it opens in a new tab on a
   middle click, and a screen reader announces it as the one destination it
   is. The whole tile is the target, so there is no small "view" link to aim
   at on a phone. */

.tariff {
	margin-top: clamp(2rem, 4vw, 3rem);
	display: grid;
	gap: clamp(1.5rem, 3vw, 2.5rem);
}

@media (width >= 48rem) { .tariff { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.tariff-card {
	display: grid;
	text-decoration: none;
	color: inherit;
	background-color: var(--wp--preset--color--off-white);
	border: 1px solid var(--rule);
	overflow: hidden;
	transition: border-color 200ms ease, transform 200ms var(--ease-out-quart, cubic-bezier(0.25,1,0.5,1));
}

.tariff-shot { display: block; overflow: hidden; }

.tariff-shot img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 5 / 3;
	object-fit: cover;
	transition: scale 600ms var(--ease-brand);
}

@media (hover: hover) and (pointer: fine) {
	.tariff-card:hover { border-color: var(--wp--preset--color--champagne); }
	/* The photograph moves, not the card. A tile that lifts is app furniture;
	   a photograph that breathes inside a fixed frame is editorial. */
	.tariff-card:hover .tariff-shot img { scale: 1.03; }
}

.tariff-card:active { transform: scale(0.995); }

.tariff-card:focus-visible {
	outline: 2px solid var(--wp--preset--color--aubergine);
	outline-offset: 3px;
}

.tariff-body { display: block; padding: 1.5rem 1.5rem 1.75rem; }

.tariff-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem;
	border-bottom: 1px solid var(--rule);
	padding-bottom: 0.9rem;
}

.tariff-name {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(1.3rem, 2.2vw, 1.7rem);
	line-height: 1.05;
	color: var(--wp--preset--color--aubergine);
}

/* Tabular figures so 200 and 500 align on the decimal rather than drifting
   apart by the width of a 2. */
.tariff-price {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(1.3rem, 2.2vw, 1.7rem);
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--wine);
	white-space: nowrap;
}

.tariff-lede {
	display: block;
	margin-top: 0.9rem;
	max-width: 40ch;
	color: var(--wp--preset--color--muted);
	font-size: 15px;
	line-height: 1.55;
}

.tariff-more {
	display: inline-block;
	margin-top: 1.1rem;
	font-size: 12px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	font-weight: 560;
	color: var(--wp--preset--color--wine);
}

.tariff-more::after { content: ' \2192'; }

/* ------------------------------------------------------------- THE EXTRAS */

.extras {
	margin-top: clamp(2.5rem, 4vw, 3.5rem);
	max-width: 52ch;
}

.extras-title {
	font-size: 12px;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--wp--preset--color--wine);
	margin: 0 0 0.9rem;
}

.extras-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }

.extras-list li {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem;
	padding-block: 0.9rem;
	border-bottom: 1px solid var(--rule);
}

.extras-name { color: var(--wp--preset--color--soft-black); }
.extras-price { font-variant-numeric: tabular-nums lining-nums; color: var(--wp--preset--color--wine); white-space: nowrap; }
.extras-note { margin-top: 1.1rem; font-size: 14px; color: var(--wp--preset--color--muted); }

/* ===================================================== THE SESSION PAGE */

.session-grid { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: start; }

@media (width >= 64rem) {
	.session-grid { grid-template-columns: minmax(0, 6fr) minmax(0, 6fr); gap: clamp(3rem, 5vw, 5rem); }
	/* The photograph holds while the copy and the add-ons scroll past it. */
	.session-figure { position: sticky; top: calc(var(--nav-h) + 2rem); }
}

/*
  height: auto is load-bearing, not tidiness. WordPress prints real width and
  height attributes, and without an explicit auto the intrinsic height wins
  over aspect-ratio: the frame rendered 2560px tall inside a 387px column.
*/
.session-figure img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 4 / 5;
	object-fit: cover;
}

.session-title {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-stretch: normal;
	font-size: clamp(2.1rem, 4.4vw, 3.1rem);
	line-height: 1.04;
	letter-spacing: -0.015em;
	margin-top: 0.7rem;
	color: var(--wp--preset--color--aubergine);
}

.session-price {
	margin-top: 0.5rem;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(1.6rem, 3vw, 2.2rem);
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--wine);
}

.session-lede { margin-top: 1.25rem; max-width: 46ch; font-size: clamp(1.02rem, 1.5vw, 1.12rem); }

.session-detail {
	margin-top: 1.25rem;
	padding-top: 1.1rem;
	border-top: 1px solid var(--rule);
	max-width: 50ch;
	font-size: 15px;
	line-height: 1.55;
	color: var(--wp--preset--color--muted);
}

/* ------------------------------------------------------------ THE ADD-ONS */

.session-form { margin-top: 2.25rem; }

.session-addons-title {
	font-size: 12px;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--wp--preset--color--wine);
	margin: 0 0 0.9rem;
}

.session-addons { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }

.addon {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr) auto;
	align-items: start;
	gap: 0.9rem;
	padding-block: 1rem;
	border-bottom: 1px solid var(--rule);
}

/* A real checkbox, restyled rather than replaced: it keeps its keyboard
   behaviour, its focus ring and its announcement, and the label is the
   whole row so the target is generous on a phone. */
.addon-check {
	appearance: none;
	width: 20px;
	height: 20px;
	margin: 0.15rem 0 0;
	border: 1px solid var(--rule-live);
	background: transparent;
	cursor: pointer;
	display: grid;
	place-items: center;
	transition: background-color 160ms ease, border-color 160ms ease;
}

.addon-check::after {
	content: '';
	width: 10px;
	height: 6px;
	border-left: 1.5px solid var(--wp--preset--color--cream);
	border-bottom: 1.5px solid var(--wp--preset--color--cream);
	rotate: -45deg;
	translate: 0 -1px;
	opacity: 0;
	transition: opacity 120ms ease;
}

.addon-check:checked {
	background-color: var(--wp--preset--color--aubergine);
	border-color: var(--wp--preset--color--aubergine);
}

.addon-check:checked::after { opacity: 1; }
.addon-check:focus-visible { outline: 2px solid var(--wp--preset--color--aubergine); outline-offset: 3px; }

.addon-label { cursor: pointer; display: grid; gap: 0.2rem; }
.addon-name { font-weight: 550; color: var(--wp--preset--color--soft-black); }
.addon-note { font-size: 14px; color: var(--wp--preset--color--muted); max-width: 40ch; }

.addon-price {
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--wine);
	white-space: nowrap;
}

.session-total {
	margin-top: 1.4rem;
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem;
	font-size: 12px;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--wp--preset--color--muted);
}

.session-total-figure {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(1.5rem, 2.6vw, 2rem);
	letter-spacing: normal;
	text-transform: none;
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--aubergine);
}

.session-book {
	margin-top: 1.5rem;
	border: 0;
	cursor: pointer;
	border-radius: 999px;
	background: var(--wp--preset--color--aubergine);
	color: var(--wp--preset--color--cream);
	font-family: var(--wp--preset--font-family--sans);
	font-size: 12.5px;
	font-weight: 560;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	padding: 1.05rem 1.9rem;
}

.session-terms { margin-top: 1.1rem; font-size: 14px; color: var(--wp--preset--color--muted); max-width: 50ch; }

.session-includes { list-style: none; margin: 2rem 0 0; padding: 0; border-top: 1px solid var(--rule); }

.session-includes li {
	padding-block: 1.1rem;
	border-bottom: 1px solid var(--rule);
	max-width: 56ch;
	color: var(--wp--preset--color--muted);
}

/* ================================================ THE QUESTIONS, AS AN INDEX

   Not a plus-icon accordion. Each question is a full-width ruled line at
   heading scale, numbered in the margin the way a contents page numbers its
   entries; opening one lets the answer rise into the space beneath it.

   The `name` attribute on <details> makes the group exclusive natively, so
   opening one closes the last with no script at all. Browsers without it
   simply allow several open, which is a fine degradation. */

.index {
	margin-top: clamp(2rem, 4vw, 3rem);
	border-top: 1px solid var(--rule);
}

.index-row { border-bottom: 1px solid var(--rule); }

.index-q {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr) auto;
	align-items: baseline;
	gap: 1.25rem;
	padding-block: clamp(1.1rem, 2vw, 1.6rem);
	cursor: pointer;
	list-style: none;
	transition: color 200ms ease;
}

/* Both are needed: WebKit uses the pseudo-element, everything else honours
   list-style on the summary itself. */
.index-q::-webkit-details-marker { display: none; }

.index-num {
	font-family: var(--wp--preset--font-family--sans);
	font-size: 11px;
	letter-spacing: 0.18em;
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--champagne);
	translate: 0 -0.35em;
}

.index-text {
	font-family: var(--wp--preset--font-family--display);
	font-weight: 500;
	font-size: clamp(1.25rem, 2.6vw, 1.9rem);
	line-height: 1.14;
	color: var(--wp--preset--color--aubergine);
	text-wrap: balance;
}

/* A register mark, not a plus in a box: two hairlines, the vertical one
   rotating away as the row opens, so it settles into a single rule. */
.index-mark {
	position: relative;
	width: 15px;
	height: 15px;
	translate: 0 -0.2em;
	flex: none;
}

.index-mark::before,
.index-mark::after {
	content: '';
	position: absolute;
	inset: 50% 0 auto 0;
	height: 1px;
	background: var(--wp--preset--color--wine);
}

.index-mark::after {
	rotate: 90deg;
	transition: rotate 320ms var(--ease-brand), opacity 220ms ease;
}

.index-row[open] .index-mark::after { rotate: 0deg; opacity: 0; }
.index-row[open] .index-text { font-style: italic; }

@media (hover: hover) and (pointer: fine) {
	.index-q:hover .index-text { color: var(--wp--preset--color--wine); }
}

.index-q:focus-visible {
	outline: 2px solid var(--wp--preset--color--aubergine);
	outline-offset: 4px;
}

.index-a {
	padding-bottom: clamp(1.2rem, 2.4vw, 1.9rem);
	padding-left: calc(11px + 1.25rem);
	max-width: 62ch;
	color: var(--wp--preset--color--muted);
	font-size: clamp(1rem, 1.4vw, 1.08rem);
	line-height: 1.6;
}

.index-a p { margin: 0; }

/* The answer rises rather than appearing. Gated: under reduced motion it is
   simply there when the row opens. */
@media (prefers-reduced-motion: no-preference) {
	.index-row[open] .index-a { animation: index-open 320ms var(--ease-brand); }
	@keyframes index-open {
		from { opacity: 0; transform: translateY(-6px); }
		to   { opacity: 1; transform: none; }
	}
}

/* ============================================ SAFETY, PINNED AGAINST ITS LIST

   The claim holds while the evidence scrolls past it. This section makes one
   assertion, "we take that seriously", and then spends seven lines earning
   it; pinning the claim means the reader is still looking at it when the
   last line lands, which is the whole rhetorical shape of the section.

   Desktop only, and only when the column is genuinely shorter than the list.
   On a phone the two stack and a pinned heading would just eat the screen. */
@media (width >= 64rem) {
	.safety-grid { align-items: start; }

	.safety-claim {
		position: sticky;
		top: calc(var(--nav-h) + 2.5rem);
		/* Never taller than the viewport under the header, or it can never
		   settle and the pin appears to stutter. */
		max-height: calc(100svh - var(--nav-h) - 4rem);
	}

	/* Breathing room so the last item clears the bottom of the pinned claim
	   rather than ending level with it. */
	.safety-list { padding-bottom: 2rem; }
}

@media (prefers-reduced-motion: reduce) {
	.safety-claim { position: static; max-height: none; }
}

/* ================================================== THE HEADER UTILITIES

   THREE BOOKING CONTROLS ON ONE SCREEN IS TWO TOO MANY. The first screen was
   showing the header's "Book a session", the hero's "Book a session" and the
   hero's "How a session runs" all at once, which reads as a page nagging
   rather than inviting.

   The hero owns the first screen: it keeps its pair, one primary and one
   secondary, which is the standard and correct shape. The header's button
   waits until the reader has scrolled past that pair, then takes over as the
   persistent way to book. data-solid is already set by motion.js at the same
   moment the header takes its ground.

   Contact is gone from the chrome entirely. "Contact" and "Book a session"
   were the same intent wearing two labels, and the email is reachable in the
   footer, in the mobile sheet and under the booking form. */

/*
  ALWAYS VISIBLE. It was revealed on scroll, which did solve the crowding but
  bought that with a worse fault: the one action in the chrome was invisible
  at first glance, which is the moment a header CTA exists for.

  The crowding is answered by WEIGHT instead. Three controls on a screen is
  only wrong when all three shout. The hero owns the loud pair, filled and
  outlined at full size; this is a small quiet outline that reads as the
  standing option rather than a third demand. Same label as the hero's
  deliberately: one intent should wear one name everywhere on the site.
*/
.nav-book {
	border: 1px solid var(--rule);
	border-radius: 999px;
	padding: 0.5rem 1.05rem;
	font-size: 11px;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	font-weight: 560;
	color: var(--wp--preset--color--muted);
	transition: color 180ms ease, border-color 180ms ease, background-color 180ms ease;
}

/* The champagne rule under the old text link has no place on a pill. */
.nav-book::after { content: none; }

/* It firms up once the header takes its ground, so the standing action gains
   presence exactly when the hero's own buttons have gone. */
.splurge-nav[data-solid='true'] .nav-book {
	color: var(--wp--preset--color--aubergine);
	border-color: var(--rule-live);
}

@media (hover: hover) and (pointer: fine) {
	.nav-book:hover {
		background: var(--wp--preset--color--aubergine);
		border-color: var(--wp--preset--color--aubergine);
		color: var(--wp--preset--color--cream);
	}
}

/* ---------------------------------------------------------- THE GLYPH */

.nav-ig {
	display: grid;
	place-items: center;
	/* 44px target around an 18px mark: the icon is small because the header
	   is quiet, the target is not because thumbs are not. */
	min-width: 44px;
	min-height: 44px;
	margin-right: -0.4rem;
	color: var(--wp--preset--color--soft-black);
	transition: color 150ms ease;
}

.ig-glyph { width: 18px; height: 18px; display: block; }

@media (hover: hover) and (pointer: fine) {
	.nav-ig:hover { color: var(--wp--preset--color--wine); }
}

.nav-ig:focus-visible {
	outline: 2px solid var(--wp--preset--color--aubergine);
	outline-offset: 2px;
	border-radius: 999px;
}

/* On a phone the glyph goes and the booking pill stays: one action beats one
   social link when the row is 390px wide. The sheet carries both. */
@media (width < 64rem) {
	.nav-utils { display: flex; }
	.nav-ig { display: none; }
}

/* Founder section with no portrait on file: one column, holding the text
   measure, rather than a two-column grid with an empty half. */
@media (width >= 64rem) {
	.founder-grid.is-textonly { grid-template-columns: minmax(0, 1fr); max-width: 62ch; }
}

/* ================================================ BOOK, AND WHAT FOLLOWS */

.book-write {
	margin-top: clamp(2.5rem, 4vw, 3.5rem);
	padding-top: 1.75rem;
	border-top: 1px solid var(--rule);
	max-width: 56ch;
}

.book-write-title {
	font-size: 12px;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--wp--preset--color--wine);
	margin: 0 0 0.7rem;
}

.book-write-body { margin: 0; color: var(--wp--preset--color--muted); }

/* The session action is an anchor now rather than a submit button, so it
   needs the pill drawn rather than inherited from the button element. */
.session-book {
	display: inline-block;
	margin-top: 1.5rem;
	border-radius: 999px;
	background: var(--wp--preset--color--aubergine);
	color: var(--wp--preset--color--cream);
	text-decoration: none;
	font-size: 12.5px;
	font-weight: 560;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	padding: 1.05rem 1.9rem;
}

@media (hover: hover) and (pointer: fine) {
	.session-book:hover { background: var(--wp--preset--color--wine); }
}

/* ------------------------------------------------------ THE CONFIRMATION */

.next-steps {
	list-style: none;
	margin: clamp(2rem, 4vw, 3rem) 0 0;
	padding: 0;
	display: grid;
	gap: 1.5rem;
	border-top: 1px solid var(--rule);
}

.next-steps li {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	gap: 1.25rem;
	padding-top: 1.5rem;
	border-bottom: 1px solid var(--rule);
	padding-bottom: 1.5rem;
}

.next-num {
	font-size: 11px;
	letter-spacing: 0.18em;
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--champagne);
	translate: 0 0.35em;
}

.next-title {
	margin: 0;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: clamp(1.15rem, 2vw, 1.4rem);
	color: var(--wp--preset--color--aubergine);
}

.next-body { margin: 0.5rem 0 0; color: var(--wp--preset--color--muted); max-width: 54ch; }

/* The scheduler embed, when one exists. Aspect-ratio rather than a fixed
   height so it is not a letterbox on a phone. */
.scheduler {
	margin-top: 2rem;
	border: 1px solid var(--rule);
	background: var(--wp--preset--color--off-white);
}

.scheduler iframe { display: block; width: 100%; aspect-ratio: 4 / 3; border: 0; }

@media (width >= 48rem) { .scheduler iframe { aspect-ratio: 16 / 10; } }

.confirmed-terms {
	margin-top: 2rem;
	padding-top: 1.5rem;
	border-top: 1px solid var(--rule);
	font-size: 14px;
	color: var(--wp--preset--color--muted);
	max-width: 56ch;
}

/* The scheduler facade: a control until asked for, an embed afterwards. */
.scheduler { padding: 0; }

.scheduler-open {
	display: inline-block;
	margin: 1.5rem 0 0;
	border: 0;
	cursor: pointer;
	border-radius: 999px;
	background: var(--wp--preset--color--aubergine);
	color: var(--wp--preset--color--cream);
	font-family: var(--wp--preset--font-family--sans);
	font-size: 12.5px;
	font-weight: 560;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	padding: 1.05rem 1.9rem;
}

@media (hover: hover) and (pointer: fine) {
	.scheduler-open:hover { background: var(--wp--preset--color--wine); }
}

.scheduler:has(iframe) { margin-top: 2rem; border: 1px solid var(--rule); background: var(--wp--preset--color--off-white); }

/* ============================================ THE DATE STRIP (demo only)

   A translation of the glass-calendar idea into this brand, built so the
   decision can be looked at rather than argued about. NOT WIRED INTO THE
   BOOKING FLOW: see CALENDAR-SETUP.md for why there is no calendar yet.

   What survives the translation: the horizontal filmstrip of days, which is
   genuinely the good idea in that component. It is compact, it reads like a
   strip of film, and for a date within the next fortnight it beats a month
   grid because the reader never has to parse a week-by-week layout.

   What does not: the dark glass, the pink-to-orange gradient, the 16px
   backdrop blur and the 24px radii. Those are one house style; this is
   another. A cream editorial page with a smoked-glass widget in the middle
   of it reads as a component someone pasted in. */

.strip {
	position: relative;
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scrollbar-width: none;
	padding-block: 0.5rem;
	/* Fades at both ends, so the rail reads as continuing rather than being
	   cut. Same device as the reviews wall. */
	-webkit-mask-image: linear-gradient(to right, transparent, #000 3%, #000 94%, transparent);
	mask-image: linear-gradient(to right, transparent, #000 3%, #000 94%, transparent);
}

.strip::-webkit-scrollbar { display: none; }

.strip-rail {
	display: flex;
	gap: 0.35rem;
	list-style: none;
	margin: 0;
	padding: 0;
	width: max-content;
	/* Days snap under the finger: a filmstrip that stops between two dates
	   is harder to read than one that always lands on one. */
	scroll-snap-type: x proximity;
}

.strip-day {
	display: grid;
	justify-items: center;
	gap: 0.5rem;
	scroll-snap-align: center;
	padding-inline: 0.35rem;
}

.strip-dow {
	font-size: 10.5px;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--wp--preset--color--muted);
}

.strip-num {
	width: 44px;
	height: 44px;
	border-radius: 999px;
	border: 1px solid transparent;
	background: transparent;
	cursor: pointer;
	font-family: var(--wp--preset--font-family--sans);
	font-size: 15px;
	font-weight: 550;
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--soft-black);
	transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
	.strip-num:not(:disabled):hover { border-color: var(--rule-live); }
}

.strip-num:focus-visible { outline: 2px solid var(--wp--preset--color--aubergine); outline-offset: 3px; }

/* Filled aubergine, not a gradient. The site has one accent system and this
   is it; a pink-to-orange fill would be the only gradient on the site. */
.is-picked .strip-num {
	background: var(--wp--preset--color--aubergine);
	border-color: var(--wp--preset--color--aubergine);
	color: var(--wp--preset--color--cream);
}

/* Unavailable days are struck through rather than merely faded: at 40%
   opacity a number still looks like something you can press.

   The rule sits AFTER .is-picked deliberately. A day cannot be both chosen
   and closed, and when the demo data made one, the fill and the strike drew
   on top of each other. Ordering it this way means the closed state always
   wins, so an impossible combination renders as unavailable rather than as
   an invitation. */
.is-off .strip-num {
	color: color-mix(in srgb, var(--wp--preset--color--soft-black) 34%, transparent);
	cursor: not-allowed;
	position: relative;
}

.is-off .strip-num::after {
	content: '';
	position: absolute;
	left: 12px;
	right: 12px;
	top: 50%;
	height: 1px;
	background: currentColor;
}

/* The strip in place on the confirmation page. The demo styles above carry
   the rail itself; these are the block around it. */
/* The date page's email field and its disabled submit. */
.date-email { margin-top: 1.75rem; max-width: 34ch; }

.scheduler-open:disabled {
	background: color-mix(in srgb, var(--wp--preset--color--aubergine) 22%, transparent);
	color: color-mix(in srgb, var(--wp--preset--color--soft-black) 45%, transparent);
	cursor: not-allowed;
}

/* It starts disabled, so it must not look pressable until a day is chosen. */
.scheduler-open:disabled:hover { background: color-mix(in srgb, var(--wp--preset--color--aubergine) 22%, transparent); }

/* ============================================ THE TIME WINDOWS

   Appears after a day is chosen. Real radios, restyled rather than replaced,
   so arrow keys move between them and a screen reader announces the group
   and the selection without a single aria attribute being invented. */

.session-form .session-total { margin-top: 1.75rem; }

.windows-row { display: flex; flex-wrap: wrap; gap: 0.6rem; }

.window { position: relative; }

/* The input is the control, so it keeps focus and keyboard behaviour; it is
   moved out of sight rather than removed from the accessibility tree. */
.window input {
	position: absolute;
	opacity: 0;
	width: 100%;
	height: 100%;
	margin: 0;
	cursor: pointer;
}

.window span {
	display: block;
	padding: 0.75rem 1.25rem;
	border: 1px solid var(--rule-live);
	border-radius: 999px;
	font-size: 13px;
	letter-spacing: 0.06em;
	color: var(--wp--preset--color--soft-black);
	transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}

.window input:checked + span {
	background: var(--wp--preset--color--aubergine);
	border-color: var(--wp--preset--color--aubergine);
	color: var(--wp--preset--color--cream);
}

.window input:focus-visible + span {
	outline: 2px solid var(--wp--preset--color--aubergine);
	outline-offset: 3px;
}

@media (hover: hover) and (pointer: fine) {
	.window input:not(:checked):hover + span { border-color: var(--wp--preset--color--aubergine); }
}

.windows-note {
	margin-top: 0.9rem;
	font-size: 13.5px;
	color: var(--wp--preset--color--muted);
	max-width: 46ch;
}

/* Disabled submit on the session page, matching the date page's. */
.session-book:disabled {
	background: color-mix(in srgb, var(--wp--preset--color--aubergine) 22%, transparent);
	color: color-mix(in srgb, var(--wp--preset--color--soft-black) 45%, transparent);
	cursor: not-allowed;
	border: 0;
}

@media (hover: hover) and (pointer: fine) {
	.session-book:disabled:hover { background: color-mix(in srgb, var(--wp--preset--color--aubergine) 22%, transparent); }
}

/* ==================================================== THE MONTH CALENDAR

   Drawn over a real date input, which stays in the DOM as the form's value.
   Six months of reach, so a visitor planning a trip can pick their actual
   dates rather than whatever falls inside a three week strip. */

.calendar { margin-top: 0.5rem; }

/* Hidden only once the script has replaced it, so a browser without the
   script keeps a working, labelled date field. */
.cal-native.is-enhanced {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.cal {
	max-width: 22rem;
	border: 1px solid var(--rule);
	padding: 1rem 1.1rem 1.25rem;
	background: color-mix(in srgb, var(--wp--preset--color--off-white) 70%, transparent);
}

.cal-head {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr) auto;
	align-items: center;
	gap: 0.5rem;
	margin-bottom: 0.9rem;
}

.cal-title {
	margin: 0;
	text-align: center;
	font-family: var(--wp--preset--font-family--display);
	font-weight: 600;
	font-size: 1.1rem;
	color: var(--wp--preset--color--aubergine);
}

.cal-nav {
	width: 34px;
	height: 34px;
	border: 1px solid transparent;
	border-radius: 999px;
	background: transparent;
	cursor: pointer;
	font-size: 18px;
	line-height: 1;
	color: var(--wp--preset--color--aubergine);
	transition: border-color 160ms ease, color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
	.cal-nav:not(:disabled):hover { border-color: var(--rule-live); }
}

/* Disabled at the ends of the window, so the reader is never taken to a
   month with nothing in it. */
.cal-nav:disabled { color: color-mix(in srgb, var(--wp--preset--color--soft-black) 26%, transparent); cursor: not-allowed; }
.cal-nav:focus-visible { outline: 2px solid var(--wp--preset--color--aubergine); outline-offset: 2px; }

.cal-dow,
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }

.cal-dow {
	margin-bottom: 0.4rem;
	font-size: 10.5px;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--wp--preset--color--muted);
	text-align: center;
}

.cal-day {
	aspect-ratio: 1;
	min-height: 38px;
	border: 1px solid transparent;
	border-radius: 999px;
	background: transparent;
	cursor: pointer;
	font-family: var(--wp--preset--font-family--sans);
	font-size: 14px;
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--soft-black);
	transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
	.cal-day:not(:disabled):not(.is-picked):hover { border-color: var(--rule-live); }
}

.cal-day.is-picked {
	background: var(--wp--preset--color--aubergine);
	border-color: var(--wp--preset--color--aubergine);
	color: var(--wp--preset--color--cream);
}

/* Ruled out rather than dimmed: at low opacity a number still looks
   pressable. */
.cal-day.is-off {
	color: color-mix(in srgb, var(--wp--preset--color--soft-black) 30%, transparent);
	cursor: not-allowed;
	position: relative;
}

.cal-day.is-off::after {
	content: '';
	position: absolute;
	left: 10px;
	right: 10px;
	top: 50%;
	height: 1px;
	background: currentColor;
}

/* Gone rather than out of range: still struck through, but readable, because
   it is a real date somebody may be looking for. */
.cal-day.is-full { color: color-mix(in srgb, var(--wp--preset--color--soft-black) 46%, transparent); }

.cal-day:focus-visible { outline: 2px solid var(--wp--preset--color--aubergine); outline-offset: 2px; }

/* ======================================================= THE BOOKING STEPS

   One question open at a time. The rows stay on screen closed and numbered,
   so the shape of what is being asked is legible before anything is answered;
   what collapses is the answering, not the asking.

   EVERY BODY IS OPEN UNTIL THE SCRIPT SAYS OTHERWISE. Nothing here hides on
   a class the markup ships with, so a browser that never runs the script gets
   the whole form. */

.steps {
	margin-top: 2rem;
	border-top: 1px solid var(--rule);
}

.step { border-bottom: 1px solid var(--rule); }

.step-head {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr) auto auto;
	align-items: center;
	gap: 0.9rem;
	width: 100%;
	padding: 1.05rem 0;
	border: 0;
	background: transparent;
	text-align: left;
	cursor: pointer;
	font-family: var(--wp--preset--font-family--sans);
	color: var(--wp--preset--color--soft-black);
}

.step-n {
	display: grid;
	place-items: center;
	width: 26px;
	height: 26px;
	border: 1px solid var(--rule-live);
	border-radius: 999px;
	font-size: 12px;
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--muted);
	transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}

.step-label { font-size: 15px; font-weight: 560; }

.step-value {
	font-size: 14px;
	color: var(--wp--preset--color--muted);
	text-align: right;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* The chevron, drawn rather than typed, so it can turn. */
.step-mark {
	width: 8px;
	height: 8px;
	border-right: 1.5px solid var(--wp--preset--color--muted);
	border-bottom: 1.5px solid var(--wp--preset--color--muted);
	transform: rotate(45deg) translate(-2px, -2px);
	transition: transform 220ms ease, border-color 160ms ease;
}

.step.is-open .step-mark { transform: rotate(-135deg) translate(-3px, -3px); }

.step.is-done .step-n {
	background: var(--wp--preset--color--aubergine);
	border-color: var(--wp--preset--color--aubergine);
	color: var(--wp--preset--color--cream);
}

.step.is-done .step-value { color: var(--wp--preset--color--aubergine); font-weight: 560; }

/* A step that cannot be answered yet is not pressable, and says so by being
   plainly inert rather than by looking enabled and doing nothing. */
.step.is-locked .step-head { cursor: default; }

/* The LABEL stays readable: the whole reason a locked row is on screen is to
   say what is coming, and at the weight the rest of it is dimmed to it would
   not have said it. The answer slot and the number carry the inertness. */
.step.is-locked .step-label { color: var(--wp--preset--color--muted); font-weight: 500; }
.step.is-locked .step-value,
.step.is-locked .step-n { color: color-mix(in srgb, var(--wp--preset--color--soft-black) 48%, transparent); }
.step.is-locked .step-n { border-color: var(--rule); }
.step.is-locked .step-mark { opacity: 0.35; }

.step-head:focus-visible { outline: 2px solid var(--wp--preset--color--aubergine); outline-offset: 2px; }

@media (hover: hover) and (pointer: fine) {
	.step:not(.is-locked) .step-head:hover .step-n { border-color: var(--wp--preset--color--aubergine); }
	.step:not(.is-locked) .step-head:hover .step-mark { border-color: var(--wp--preset--color--aubergine); }
}

.step-body { padding-bottom: 1.5rem; }

.is-stepped .step-body { animation: step-open 260ms ease both; }

@keyframes step-open {
	from { opacity: 0; transform: translateY(-4px); }
	to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
	.is-stepped .step-body { animation: none; }
	.step-mark { transition: none; }
}

.step-body .calendar { margin-top: 0; }
.step-body .windows-row { margin-top: 0.25rem; }

/* A window somebody else already has. Ruled out on the same principle as the
   calendar's days: struck rather than faded, because a faded label still
   looks like something you may press. */
.window.is-gone span {
	color: color-mix(in srgb, var(--wp--preset--color--soft-black) 34%, transparent);
	border-color: var(--rule);
	text-decoration: line-through;
	cursor: not-allowed;
}

.window.is-gone input { cursor: not-allowed; }

.book-tail { margin-top: 1.75rem; }
.book-tail .date-email { margin-top: 0; }

/* On a narrow screen the row cannot hold a label and a long answer side by
   side, so the answer drops under the label rather than being clipped. */
@media (max-width: 30rem) {
	.step-head {
		grid-template-columns: auto minmax(0, 1fr) auto;
		row-gap: 0.25rem;
	}

	.step-value {
		grid-column: 2 / 3;
		grid-row: 2;
		text-align: left;
		white-space: normal;
	}
}

/* ================================================= THE EDIT MARKERS

   Only ever in the markup for somebody who can edit, so none of this reaches
   a visitor. Absolute inside .band, which is already positioned, so adding
   these moved nothing.

   Quiet until wanted: barely there while she reads the page, solid when the
   pointer is near. A control that shouts on a client's own site makes the
   site feel like a workbench. */

.splurge-editpin {
	position: absolute;
	top: 0.6rem;
	right: 0.6rem;
	z-index: 40;
	padding: 0.3rem 0.7rem;
	border-radius: 999px;
	background: var(--wp--preset--color--aubergine);
	color: var(--wp--preset--color--cream);
	font-family: var(--wp--preset--font-family--sans);
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	text-decoration: none;
	opacity: 0.18;
	transition: opacity 160ms ease;
}

.splurge-editpin:hover,
.splurge-editpin:focus-visible {
	opacity: 1;
	color: var(--wp--preset--color--cream);
}

.splurge-editpin:focus-visible { outline: 2px solid var(--wp--preset--color--cream); outline-offset: 2px; }

/* The whole section brings its own marker up, so she does not have to find a
   20px target to discover the thing exists.

   The footer is not a .band, so it needed saying twice or its own marker
   would have stayed at 0.18 with no way to bring it up. */
.band:hover > .splurge-editpin,
.cinematic-footer:hover > .splurge-editpin { opacity: 0.85; }

/* ON A PHONE THERE IS NO HOVER, so the rule above never fires and every
   marker would have sat at 0.18 forever: present, unreadable and impossible
   to discover. Honey is as likely to fix a typo from her phone as from a
   desk, so on a touch screen they are simply visible.

   They also shrink to the mark alone. At 145px the label was well over a
   third of a phone screen sitting on top of the design. The words stay in
   the markup, clipped rather than removed, so a screen reader still reads
   "Edit these words" rather than a pencil. */
@media (hover: none), (pointer: coarse) {
	.splurge-editpin { opacity: 0.92; }
}

.pin-mark { display: none; }

@media (max-width: 34rem) {
	.splurge-editpin {
		display: grid;
		place-items: center;
		min-width: 34px;
		min-height: 34px;
		padding: 0;
	}

	.pin-mark { display: block; font-size: 15px; line-height: 1; }

	.pin-label {
		position: absolute;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}
}

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

/* The footer is dark and its own world. */
.cinematic-footer .splurge-editpin {
	background: var(--wp--preset--color--cream);
	color: var(--wp--preset--color--aubergine);
}

/* The revealed block is focused when the last step closes, so a keyboard user
   is put where the page just changed rather than at the top of it. It is a
   destination, not a control, so it takes no ring of its own. */
.book-tail:focus { outline: none; }
.book-tail:focus-visible { outline: 2px solid var(--wp--preset--color--aubergine); outline-offset: 6px; }

/* ================================================ FOCUS, EVERYWHERE

   Found in an audit, and it is the kind of gap that only shows up when you
   go looking. Fifteen components in this theme carry a focus ring, and every
   one of them was built late: the calendar, the booking steps, the add-ons,
   the price cards. The things that were here from the start, the navigation,
   the hero buttons, the footer pills and the quiet read-more links, had none
   at all. A keyboard user could cross the entire page, through the nav, the
   two hero buttons and the whole footer, without ever seeing where they were.

   ONE RULE, AT ZERO SPECIFICITY. :where() means every component rule already
   written still wins, so nothing that was deliberately styled changes. This
   only reaches what had nothing.

   :focus-visible rather than :focus, so a mouse click never draws a ring. */

:where(a[href], button, [role="button"], input, select, textarea, summary, [tabindex]:not([tabindex="-1"])):focus-visible {
	outline: 2px solid var(--wp--preset--color--aubergine);
	outline-offset: 3px;
	border-radius: 2px;
}

/* The dark grounds need the light version, or the ring disappears into them. */
:where(.cinematic-footer, .band-inverted, .stack-panel-dark)
	:where(a[href], button, [role="button"], input, [tabindex]:not([tabindex="-1"])):focus-visible {
	outline-color: var(--wp--preset--color--cream);
}

/* The header sits on cream, but the notice bar above it is rose. */
:where(.notice-bar) :where(a[href], button):focus-visible {
	outline-color: var(--wp--preset--color--aubergine);
}

/* Two that the blanket rule could not reach. :where() has zero specificity by
   design, so it yields to anything, and WordPress core's own navigation styles
   outrank it. These need to be said with weight. */
.splurge-menu a:focus-visible,
.splurge-menu .wp-block-navigation-item__content:focus-visible,
.notice-bar .notice-link:focus-visible {
	outline: 2px solid var(--wp--preset--color--aubergine);
	outline-offset: 3px;
	border-radius: 2px;
}

/* ---------------------------------------------------------------------------
   SESSION GALLERY AND PANELS

   The gallery is a scroll-snapping row with ordinary anchor links under it,
   so it pages with the script switched off. With one picture, which is what
   there is today, the track holds a single frame and no thumbnails are drawn:
   the page is exactly what it was before.
--------------------------------------------------------------------------- */

.shot-track {
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: 100%;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	scrollbar-width: none;
}

.shot-track::-webkit-scrollbar { display: none; }

.shot {
	margin: 0;
	scroll-snap-align: start;
	/* The anchor jump lands the frame under the fixed nav without this. */
	scroll-margin-top: calc(var(--nav-h) + 1rem);
}

/* height: auto is load-bearing here for the same reason as .session-figure
   img: WordPress prints real dimensions and the intrinsic height otherwise
   beats aspect-ratio. */
.shot img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 4 / 5;
	object-fit: cover;
}

.shot-thumbs {
	display: flex;
	gap: 0.6rem;
	list-style: none;
	margin: 0.75rem 0 0;
	padding: 0;
}

.shot-thumb {
	display: block;
	width: 4.5rem;
	border: 1px solid transparent;
	opacity: 0.55;
	transition: opacity 220ms cubic-bezier(0.22, 1, 0.36, 1),
	            border-color 220ms cubic-bezier(0.22, 1, 0.36, 1);
}

.shot-thumb img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 1 / 1;
	object-fit: cover;
}

.shot-thumb:hover,
.shot-thumb.is-current {
	opacity: 1;
	border-color: var(--wp--preset--color--aubergine);
}

@media (prefers-reduced-motion: reduce) {
	.shot-track { scroll-behavior: auto; }
	.shot-thumb { transition: none; }
}

/* --- the three panels under the booking form --- */

.panels { margin-top: 0; }

.panel + .panel { margin-top: 2.5rem; }

.panel-title { margin: 0 0 1rem; }

.panel-stages {
	list-style: none;
	margin: 0;
	padding: 0;
	counter-reset: stage;
}

.panel-stages li {
	counter-increment: stage;
	display: grid;
	grid-template-columns: 2rem 1fr;
	gap: 0.2rem 0.9rem;
	padding: 1rem 0;
	border-top: 1px solid var(--rule);
}

.panel-stages li::before {
	content: counter(stage);
	grid-row: 1 / span 2;
	font-family: var(--wp--preset--font-family--display);
	font-variant-numeric: tabular-nums lining-nums;
	color: var(--wp--preset--color--wine);
}

.panel-stages strong { font-weight: 600; }
.panel-stages span { grid-column: 2; }

.panel-note { color: var(--ink-soft, inherit); }

/* --- promoted to tabs, only once the script has said so --- */

.panels[data-tabs] .panel-title { display: none; }

.panels[data-tabs] .tablist {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem 1.6rem;
	margin: 0 0 1.6rem;
	padding: 0 0 0.1rem;
	border-bottom: 1px solid var(--rule);
}

.panels[data-tabs] .tab {
	appearance: none;
	background: none;
	border: 0;
	padding: 0 0 0.7rem;
	margin-bottom: -1px;
	font: inherit;
	font-weight: 600;
	color: inherit;
	opacity: 0.55;
	cursor: pointer;
	border-bottom: 2px solid transparent;
	transition: opacity 200ms cubic-bezier(0.22, 1, 0.36, 1),
	            border-color 200ms cubic-bezier(0.22, 1, 0.36, 1);
}

.panels[data-tabs] .tab[aria-selected="true"] {
	opacity: 1;
	border-bottom-color: var(--wp--preset--color--aubergine);
}

.panels[data-tabs] .panel + .panel { margin-top: 0; }
.panels[data-tabs] .panel[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
	.panels[data-tabs] .tab { transition: none; }
}

/* The tablist already draws the line under itself; the list inside the panel
   drawing its own top border put two rules a few pixels apart. */
.panels[data-tabs] .panel-body > .session-includes { border-top: 0; }
.panels[data-tabs] .panel-body > .panel-stages > li:first-child { border-top: 0; }
