/*
 * Global header — the floating island and its features dropdown.
 *
 * Markup in template-parts/site-header.php, state classes toggled by
 * assets/js/site-header.js. Four states, one markup tree:
 *
 *   (none)              island: 24px inset, 40px radius, 1200 max
 *   .is-hidden          slid up out of view
 *   .is-pinned          docked: full-bleed surface, content still 1200 max
 *   .is-open            dropdown expanded
 *   .is-pinned.is-open  square top, rounded bottom — a panel hanging off a docked bar
 *
 * THE SURFACE. The visible pill is a ::before layer, not the bar's own
 * background. It goes full-bleed by insetting to 0 against the fixed header,
 * never by a 100vw width — 100vw includes the scrollbar and would give the
 * desktop page a horizontal one.
 *
 * WHERE THE EDGES LINE UP. ::before's inline inset is
 * `max(--wsc-header-inline, (100% - 75rem) / 2)`, which is exactly what the
 * bar's `max-width: 75rem; margin-inline: auto` resolves to inside a wrapper
 * padded by the same variable. At 1440 both land on 120; at 1000, where the
 * container is narrower than 1200, both land on the header's own inline
 * padding. So the two share an edge at every width.
 *
 * The island <-> open transition therefore leaves the logo where it is: only
 * the height changes. DOCKING KEEPS IT STILL TOO — .is-pinned drops the
 * wrapper's inline padding to 0 so the SURFACE runs edge to edge, but the bar
 * keeps its 1200 cap and stays centred, so the logo and nav stay on the
 * container's edges instead of drifting out to the viewport's. Above 1200 the
 * bar and ::before stop sharing an edge in this state — which is exactly why
 * ONE ELEMENT OWNS THE BORDER. It is drawn by ::before, so it travels with the
 * surface it belongs to and no second rect can appear mid-morph.
 *
 * Tokens only; no !important. --wsc-ease is defined at :root in style.css.
 */

:root {
	/*
	 * Published for top-of-page blocks to consume as they are touched. The header
	 * always overlays, so no block offsets its own layout automatically — only
	 * scrolling is offset, by the rule below. This is the static fallback;
	 * site-header.js replaces it with the measured closed height.
	 */
	--wsc-header-offset: 6rem;
}

/*
 * Anchor scrolling clears the header, so a "#section" link does not land behind
 * the bar. Set on the scrollport rather than as scroll-margin on .wsc-section:
 * that rule would match at 0-1-0, and a block stylesheet loads after style.css
 * and matches its own root at 0-1-0 too, so any block setting the property would
 * silently win and lose the offset. This also covers programmatic
 * scrollIntoView(), which is what keeps the features-slider tab scroll aligned
 * with ordinary fragment navigation.
 *
 * DELIBERATELY IN THIS FILE, NOT style.css. style.css is loaded into the editor
 * canvas by add_editor_style(); this file is frontend-only. The canvas has no
 * fixed header, so an offset there would drop every in-canvas scroll ~120px low.
 * For the same reason --wsc-header-offset carries NO var() fallback here: the one
 * context where it is undefined is that canvas, which must have no offset at all.
 */
html {
	scroll-padding-block-start: calc(var(--wsc-header-offset) + var(--wp--preset--spacing--40));
}

/* Root and the morphing surface ------------------------------------------ */

.wsc-header {
	/*
	 * THE ISLAND'S DEFAULT SURFACE IS THE LIGHT-PAGE ONE, and the frosted white
	 * that used to be the only treatment is now opted into. The island floats
	 * over whichever section opens the page, so a translucent white pill with a
	 * white border vanished entirely on the pages that start on the page's own
	 * white — every legal page. Three surfaces, each one step off the page:
	 *
	 *   white page   grey island   (this default)
	 *   grey band    white island  (--surface-muted below)
	 *   dark slab    frosted white (--surface-dark below)
	 *
	 * SAFE DIRECTION BY DESIGN. If detection misses, a page falls back to this
	 * grey island, which is visible on every background. The old default failed
	 * the other way — invisible exactly where it was wrong.
	 */
	--wsc-header-surface: var(--wp--preset--color--surface); /* #f4f4f4 */
	--wsc-header-border: var(--wp--preset--color--border); /* #ddd */

	--wsc-header-panel-surface: rgb(250 250 248 / 0.5);
	--wsc-header-shadow-pinned: 0 10px 30px rgb(0 0 0 / 0.4);
	--wsc-header-shadow-open: 0 2px 8px rgb(0 0 0 / 0.06), 0 20px 40px rgb(0 0 0 / 0.12);

	/*
	 * ONE radius for every state that has one. A pill radius closed and 46px open
	 * meant the corners visibly jumped as the panel expanded; at a single 40px the
	 * only thing that changes is the height.
	 */
	--wsc-header-radius: 2.5rem; /* 40px */

	/*
	 * The corner the CURRENT state wants. Both ::before and the bar read it, so
	 * the two borders cannot disagree — they are the same rect, and duplicating
	 * the state machine onto a second element is how that drifts.
	 *
	 * border-radius still animates through the var: substitution happens at
	 * computed-value time, and transitions run on computed values.
	 */
	--wsc-header-corner: var(--wsc-header-radius);

	/*
	 * The header's own inset. Read by BOTH padding-inline below and ::before's
	 * inline inset — they have to move together or the bar and the surface stop
	 * sharing an edge, which is what keeps the logo still through the morph.
	 * Overridden on the compact layout; never change one without the other.
	 */
	--wsc-header-inline: var(--wp--custom--section--padding-inline);

	position: fixed;
	inset-inline: 0;
	top: 0;
	z-index: 100; /* the theme's highest existing z-index is 1 */
	padding-top: var(--wp--preset--spacing--40); /* 24px island inset */
	padding-inline: var(--wsc-header-inline);
	color: var(--wp--preset--color--ink);
	transition:
		transform 0.45s var(--wsc-ease),
		padding-top 0.45s var(--wsc-ease),
		padding-inline 0.45s var(--wsc-ease);
}

.wsc-header::before {
	content: "";
	position: absolute;
	/* top | inline | bottom */
	inset: var(--wp--preset--spacing--40)
		max(var(--wsc-header-inline), calc((100% - 75rem) / 2)) 0;
	border-radius: var(--wsc-header-corner);
	background-color: var(--wsc-header-surface);
	backdrop-filter: blur(4px);
	border: 1px solid var(--wsc-header-border);
	/*
	 * Colour joins the transition because the island and the docked bar no longer
	 * share a surface: on a light page the island is opaque grey and the docked
	 * bar is frosted white, and without this the swap snaps mid-way through the
	 * 0.45s dock movement.
	 */
	transition:
		inset 0.45s var(--wsc-ease),
		border-radius 0.45s var(--wsc-ease),
		box-shadow 0.45s var(--wsc-ease),
		background-color 0.45s var(--wsc-ease),
		border-color 0.45s var(--wsc-ease);
}

/* Page-surface detection -------------------------------------------------- */

/*
 * The island reads the FIRST section on the page and picks a treatment that
 * stays visible against it. inc/section-frame.php puts the marker class there;
 * wsc_section_surface_map() is the curated list of which block paints what.
 *
 * WHY :has() AND NOT A BODY CLASS. This reads the DOM that actually rendered, so
 * a first block whose fields are all empty — which renders nothing, per the
 * theme's empty-state discipline — is handled for free. A server-side body class
 * would have to predict that, which means a second source of truth about how
 * blocks render.
 *
 * EACH RULE MUST STAND ALONE, never joined into a selector list with a
 * non-:has() selector: one unsupported selector invalidates a whole list. Kept
 * separate, a browser without :has() simply skips the override and keeps the
 * grey default, which is visible on every background.
 *
 * .is-pinned is excluded because the docked bar is not over the first section
 * any more — it is over arbitrary page content, where the frosted white below
 * is correct and is what shipped before this existed.
 */
body:has(.wsc-main > :first-child.wsc-section--surface-muted) .wsc-header:not(.is-pinned) {
	/* A grey band would swallow the grey island; white keeps it a step off. */
	--wsc-header-surface: var(--wp--preset--color--bg);
}

body:has(.wsc-main > :first-child.wsc-section--surface-dark) .wsc-header:not(.is-pinned) {
	--wsc-header-surface: rgb(255 255 255 / 0.8);
	--wsc-header-border: var(--wp--preset--color--bg);
}

/*
 * Docked, on every page. Higher specificity than the .wsc-header default above
 * and disjoint from the :not(.is-pinned) rules, so the two never contend.
 */
.wsc-header.is-pinned {
	--wsc-header-surface: rgb(255 255 255 / 0.8);
	--wsc-header-border: var(--wp--preset--color--bg);
}

/* States ------------------------------------------------------------------ */

.wsc-header.is-hidden {
	transform: translateY(-100%);
}

/*
 * Docked: the wrapper gives up its inline padding so ::before — the visible
 * surface — runs edge to edge. The bar keeps its 1200 cap and its centring, so
 * the content stays inside the container: the logo holds its 152px x-position
 * at 1440 and the nav stops at the container's right edge rather than the
 * viewport's. Below 1200 the cap does not bind and the bar fills the surface,
 * which is the same rect either way.
 */
.wsc-header.is-pinned {
	padding-top: 0;
	padding-inline: 0;
	--wsc-header-corner: 0;
}

.wsc-header.is-pinned::before {
	inset: 0;
	box-shadow: var(--wsc-header-shadow-pinned);
}

.wsc-header.is-open::before {
	box-shadow: var(--wsc-header-shadow-open);
}

/*
 * A hidden header casts no shadow.
 *
 * translateY(-100%) moves the BOX by exactly its own height, which leaves its
 * bottom edge on y=0 — but a shadow is painted outside the box and does not
 * leave with it. The docked shadow is 10px down with a 30px blur, so about 40px
 * of it at 0.4 alpha kept bleeding across the top of the viewport while the bar
 * that cast it was completely off screen.
 *
 * The two states always arrive together — site-header.js adds is-hidden and
 * is-pinned in the same frame — so every hidden header had one.
 *
 * Must stay below the .is-pinned and .is-open rules: all three match at 0-2-0
 * and only source order separates them. The shadow fades rather than snaps,
 * because ::before already transitions box-shadow over the same 0.45s and
 * easing as the transform, so it leaves with the bar and returns with it.
 */
.wsc-header.is-hidden::before {
	box-shadow: none;
}

/*
 * Pinned AND open — no design frame covers it, but it is one scroll and one
 * click away. The surface keeps its full-bleed square top and takes the rounded
 * corners at the bottom, so it reads as a dropdown hanging off a docked toolbar.
 */
.wsc-header.is-pinned.is-open {
	--wsc-header-corner: 0 0 var(--wsc-header-radius) var(--wsc-header-radius);
}

/* Bar and row ------------------------------------------------------------- */

/*
 * position: relative only to lift the content above ::before — both are in the
 * header's stacking context and ::before comes first in paint order.
 */
.wsc-header__bar {
	position: relative;
	max-width: var(--wp--style--global--content-size); /* 75rem — 1200px */
	margin-inline: auto;
	/*
	 * ONE PADDING FOR EVERY STATE. Opening used to tighten the bar to a uniform
	 * 24px, which moved the logo 8px inward and 8px down at the moment the panel
	 * expanded — the row visibly jumped while the panel was still growing. The bar
	 * keeps this padding open, so the only thing opening changes is the panel's
	 * height.
	 *
	 * It also keeps the measured header offset honest: site-header.js derives
	 * --wsc-header-offset from root height minus panel height, which is the closed
	 * height only if the bar around the panel is the same in both states.
	 */
	padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--30)
		var(--wp--preset--spacing--30) var(--wp--preset--spacing--50); /* 16 16 16 32 */
	/*
	 * NO BORDER HERE — see ::before, which owns the only one. The bar carried a
	 * second, hardcoded-white one for as long as white was the only surface. Once
	 * the island became page-aware it started overpainting ::before's grey on
	 * every light page, and it could not follow the surface through the dock
	 * morph: mid-move the two are different rects, so the bar's border read as a
	 * stray 1200-wide box drawn inside a full-bleed one.
	 */
	border-radius: var(--wsc-header-corner);
	transition: border-radius 0.45s var(--wsc-ease);
}

.wsc-header__row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--40);
}

.wsc-header__logo {
	display: flex;
	flex: none;
	text-decoration: none;
}

/*
 * The design width, pinned in CSS rather than left to the markup's width
 * attribute. The attribute reserves space before the file loads, but only an SVG
 * gets one (see inc/site-logo.php) — a raster fallback would otherwise render at
 * whatever size it was uploaded at.
 */
.wsc-header__logo-image {
	display: block;
	width: 11.375rem; /* 182px */
	max-width: 100%;
	height: auto;
}

/* Desktop nav ------------------------------------------------------------- */

/*
 * THE WIDTH BUDGET, and why the nav breakpoint is 1024 and not 768.
 *
 * At 1024 the row gets 1024 − 72.2 (header padding-inline, from the clamp) − 48
 * (bar padding) = ~904px, against 182 (logo) + 24 (row gap) + ~548 (nav) ≈ 754px
 * of content. ~150px of slack, so the gap can sit flat at the design's 40px and
 * a longer label has somewhere to go.
 *
 * The nav figure includes the market switcher: ~38px for a two-letter code
 * plus its chevron, and the 40px gap before it. It spent a third of the slack
 * this budget used to carry, so a further row item now needs re-costing rather
 * than assuming there is room.
 *
 * At 768 the same sum gives 659px available against the same ~676px needed — a
 * ~17px DEFICIT. It was originally costed at 20px of slack, but that count used
 * 768 − 48 and omitted the header's own padding-inline. Worth knowing why that
 * mattered: .wsc-header is fixed, so an overflow here produces no scrollbar at
 * all. The row is clipped by the viewport edge and the CTA silently stops being
 * clickable, rather than looking broken.
 */
.wsc-header__nav {
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--60); /* 40px */
}

.wsc-header__trigger,
.wsc-header__link,
.wsc-header__panel-link,
.wsc-header__card-link,
.wsc-header__market-trigger {
	font-family: var(--wp--preset--font-family--mono);
	font-size: var(--wp--preset--font-size--mono); /* 13px */
	letter-spacing: 0; /* The Mono token is 0; the inherited body default is -1%. */
	line-height: 1.25;
	text-transform: uppercase;
	color: var(--wp--preset--color--ink);
	white-space: nowrap;
}

/*
 * Both the trigger and the burger do nothing without JS — they open a panel that
 * only the script can expand. So they appear only once html.wsc-js says scripts
 * run, rather than sitting there as focusable dead controls advertising
 * aria-expanded="false". The no-JS page keeps the desktop row and loses the
 * dropdown, which is the trade the brainstorm already accepted.
 *
 * html.wsc-js AND NOT .is-enhanced, which this used to key off. site-header.js
 * is enqueued in the footer, so .is-enhanced only lands after the whole document
 * has parsed and these controls visibly popped into a header that had already
 * painted without them. The inline head script in inc/site-chrome-assets.php
 * sets html.wsc-js before the first frame instead.
 *
 * The weaker signal is the deliberate trade: wsc-js promises scripts run, not
 * that this controller initialised. If the file itself fails to load these turn
 * into dead controls — the same bargain every above-the-fold enhancement makes,
 * and cheaper than showing every visitor the wrong header first.
 */
/*
 * background-color, not the `background` shorthand: this button also carries
 * .wsc-link, whose underline IS a background-image. The shorthand would reset it
 * and, at equal specificity from a stylesheet that prints later, win — leaving
 * the one control in the nav without the hover the links beside it have.
 *
 * margin/padding are likewise stated per axis, so .wsc-link's own 2px sits
 * under the label instead of being zeroed here.
 */
.wsc-header__trigger {
	display: none;
	align-items: center;
	gap: var(--wp--preset--spacing--10); /* 8px */
	margin-inline: 0;
	padding-inline: 0;
	padding-block-start: 0;
	border: 0;
	background-color: transparent;
	cursor: pointer;
}

.wsc-js .wsc-header__trigger {
	display: inline-flex;
}

.wsc-header__chevron {
	flex: none;
	transition: transform 0.3s var(--wsc-ease);
}

.wsc-header.is-open .wsc-header__chevron {
	transform: rotate(180deg);
}

/* 16px inline, against the shared button's default 24 — the header row is tight. */
.wsc-header__cta {
	flex: none;
	padding-inline: var(--wp--preset--spacing--30);
}

/* Market switcher ---------------------------------------------------------- */

/*
 * The inverse of the trigger/burger deal above, and deliberately so.
 *
 * Those two are hidden without JS because the panel they open cannot expand
 * without it, and the links inside are a convenience. Here the links ARE the
 * feature: WPML's own switchers are suppressed site-wide, so if this collapsed
 * to nothing neither the translated site nor any other market would have an
 * entry point at all. The menu is therefore open in the markup and only
 * collapses once html.wsc-js says a script is present to reopen it — a no-JS
 * visitor gets every market and language as plain links instead of a dead
 * button.
 *
 * Collapsing on wsc-js rather than .is-enhanced is what stops the swap: keyed off
 * the footer script's class, this list painted its open fallback for the whole
 * parse and the visitor watched the full market list collapse to "US".
 */
/*
 * The end margin restores the design's 32px inset for THIS item only.
 *
 * The bar's padding is 16px on the right against 32 on the left, because the row
 * used to end in the filled CTA pill, whose own inset carries the rest of the
 * optical gap. Plain text does not, so at 16px the code sits visibly tighter to
 * the edge than the logo does on the other side. Adding the 16 here rather than
 * widening the bar leaves the CTA-last case — a site with WPML inactive, where
 * this element never renders — exactly as it was.
 */
.wsc-header__market {
	position: relative;
	/*
	 * Stretched to the nav's full height so this box's bottom edge IS the row's
	 * bottom edge. The dropdown hangs off that edge, and the trigger is only
	 * ~17px tall and optically centred — measuring from it would start the
	 * dropdown 11px above where the row ends, which is not a distance any token
	 * describes. The trigger stays centred inside the stretched box via
	 * align-items below, so nothing moves visually.
	 *
	 * The ROW rather than the bar on purpose: the bar grows by the whole panel
	 * when the features dropdown opens, and anchoring to it would fling this card
	 * downward mid-fade on the way out.
	 */
	align-self: stretch;
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--30);
	margin-inline-end: var(--wp--preset--spacing--30); /* 16px + the bar's 16 = the design's 32 */
}

.wsc-header__market-trigger {
	display: none;
	align-items: center;
	gap: var(--wp--preset--spacing--10); /* 8px — matches the features trigger */
	margin-inline: 0;
	padding-inline: 0;
	padding-block-start: 0;
	border: 0;
	background-color: transparent;
	cursor: pointer;
}

.wsc-js .wsc-header__market-trigger {
	display: inline-flex;
}

/*
 * Names the control for assistive tech without showing a second word in a row
 * that has no space for one. Not `display: none` — that would take it out of the
 * accessibility tree along with the layout.
 */
.wsc-header__market-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	border: 0;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* Its own class, not __chevron: that one rotates off the root's .is-open, which
 * belongs to the features panel and would spin this one at the same time. */
.wsc-header__market-chevron {
	flex: none;
	transition: transform 0.3s var(--wsc-ease);
}

.wsc-header__market.is-open .wsc-header__market-chevron {
	transform: rotate(180deg);
}

/*
 * Grid, not the flex row this used to be. The list now stacks one group per
 * market, each with its own row of language pills, so the markets read down and
 * the languages read across.
 */
.wsc-header__market-list {
	display: grid;
	gap: var(--wp--preset--spacing--30);
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * The hairline belongs to the SECOND and later groups, not to every group, so a
 * one-market network draws no stray rule and the list never ends in one. The
 * gap above the border and the padding below it are equal, so the line sits
 * optically centred between two markets.
 */
.wsc-header__market-group + .wsc-header__market-group {
	padding-block-start: var(--wp--preset--spacing--30);
	border-block-start: 1px solid var(--wp--preset--color--border);
}

/*
 * Sentence case, body font — deliberately NOT the mono uppercase treatment the
 * nav row and the trigger share. These are place names, and the design sets them
 * as prose; running "United States" through the nav's text-transform would read
 * as a label rather than a destination.
 */
.wsc-header__market-name {
	margin: 0 0 var(--wp--preset--spacing--10);
	font-size: var(--wp--preset--font-size--body);
	line-height: 1.3;
	color: var(--wp--preset--color--ink);
}

/*
 * The code carries no colour of its own: the design sets "US United States" in
 * one weight and one ink, so the span exists to let the code be omitted when a
 * market has not been given one, not to tint it.
 */

.wsc-header__market-langs {
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--10);
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * text-decoration is cleared explicitly because these links deliberately do NOT
 * carry .wsc-link. That class swaps theme.json's global underline for a hover
 * sweep, which is right for a text link and wrong inside a pill — without this
 * rule the global underline would run through every language.
 */
.wsc-header__market-lang {
	display: inline-block;
	padding: 0.25rem var(--wp--preset--spacing--20);
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--wp--custom--radius--pill);
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.4;
	text-decoration: none;
	color: var(--wp--preset--color--ink);
	white-space: nowrap;
	transition:
		border-color 0.2s var(--wsc-ease),
		background-color 0.2s var(--wsc-ease);
}

.wsc-header__market-lang:hover,
.wsc-header__market-lang:focus-visible {
	border-color: var(--wp--preset--color--ink);
}

/*
 * The language being read, in the market being read. Same fill/text pairing as
 * the primary button so the two greens cannot drift apart.
 */
.wsc-header__market-lang.is-current {
	border-color: var(--wp--preset--color--primary);
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--bg);
}

/*
 * THE NO-JS BAR, and why it gets its own layout rather than the stacked one.
 *
 * The card rules below are scoped to .wsc-js, so without a script this list is
 * a plain block sitting IN the header row. Stacked, that is one tall column per
 * market inside a bar sized for a single line — the row grows several times its
 * height before a visitor has done anything.
 *
 * The commitment at the top of this section still stands: nothing collapses to
 * nothing without JS, because WPML's own switchers are suppressed and this is
 * the only way out of the market. So the fallback flows inline and wraps
 * instead, trading the design's grouping for a row that stays a row. It is
 * wider and plainer than the dropdown and is meant to be — it is the fallback,
 * not the design.
 *
 * Scoped to the bar: the mobile panel renders the same list with no
 * __market wrapper, and wants the stacked layout in every case because it has
 * the height for it.
 */
html:not( .wsc-js ) .wsc-header__market .wsc-header__market-list {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--wp--preset--spacing--30);
}

html:not( .wsc-js ) .wsc-header__market .wsc-header__market-group {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--wp--preset--spacing--10);
	padding-block-start: 0;
	border-block-start: 0;
}

html:not( .wsc-js ) .wsc-header__market .wsc-header__market-name {
	margin-block-end: 0;
}

/*
 * Opaque white, unlike .wsc-header__card and the features panel it used to
 * mirror. That divergence is the design's, and it earns itself: this card sits
 * over the hero image and now carries two markets' worth of small text, where
 * the translucent surface let the photo through behind the language pills.
 * The border therefore has to be a real hairline — against a white card the
 * panel's own white border would be invisible.
 *
 * Radius stays sm: the card's md is 1.5rem, which on a menu this narrow curves
 * into a blob.
 */
.wsc-js .wsc-header__market-menu {
	position: absolute;
	/*
	 * Both offsets clear the bar's padding to land on the ISLAND's edges, which
	 * is what the design aligns to — this element sits inside the row, and the
	 * row sits 16px inside the island on every side.
	 *
	 * Down: the row's bottom + the bar's 16px padding = the island's bottom,
	 * then the design's 8px gap.
	 * Out: back over this element's own 16px end margin AND the bar's 16px
	 * padding, so the card's right edge is flush with the island's, as drawn.
	 */
	inset-block-start: calc(100% + var(--wp--preset--spacing--30) + var(--wp--preset--spacing--10));
	inset-inline-end: calc(-2 * var(--wp--preset--spacing--30));
	z-index: 1;
	/*
	 * width: max-content is load-bearing, not a tidy-up.
	 *
	 * This card's containing block is .wsc-header__market — the trigger, about
	 * 38px wide. With inset-inline-end and width:auto, shrink-to-fit resolves
	 * against THAT as the available width, so the card collapses to its
	 * min-content and every language row wraps one pill per line. max-content
	 * sizes it to its widest line instead and lets it extend leftward from the
	 * right edge it is pinned to.
	 *
	 * min-width keeps a one-language market from rendering as a sliver;
	 * max-width stops a long market name pushing the card off the left of a
	 * narrow window, at which point wrapping is the correct answer.
	 */
	width: max-content;
	min-width: 11rem;
	max-width: min( 20rem, calc( 100vw - 3rem ) );
	padding: var(--wp--preset--spacing--30);
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--wp--custom--radius--sm);
	background-color: var(--wp--preset--color--bg);
	opacity: 0;
	visibility: hidden;
	transform: translateY(-4px);
	transition:
		opacity 0.2s var(--wsc-ease),
		transform 0.2s var(--wsc-ease),
		visibility 0s linear 0.2s;
}

.wsc-js .wsc-header__market.is-open .wsc-header__market-menu {
	opacity: 1;
	visibility: visible;
	transform: none;
	transition-delay: 0s;
}

/*
 * Inside the mobile panel the same list renders without the card: the panel is
 * already a surface, and nesting a second bordered, blurred box inside it reads
 * as a dialog within a dialog. Only the bar context emits the __market-menu
 * wrapper the card rules above are scoped to, so this needs no counter-rule —
 * it simply lays the groups out against the panel's own left edge.
 */
.wsc-header__panel-nav .wsc-header__market-list {
	gap: var(--wp--preset--spacing--20);
}

/* Burger ------------------------------------------------------------------ */

/*
 * 14px tall: three 2px bars with two 4px gaps, so the bar centres sit at 1, 7
 * and 13. Converging the outer two on the middle is therefore exactly 6px —
 * which is where the X transform's offset comes from.
 */
.wsc-header__burger {
	display: none;
	flex: none;
	flex-direction: column;
	justify-content: space-between;
	width: 1.5rem; /* 24px */
	height: 0.875rem; /* 14px */
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
	color: inherit;
}

.wsc-header__burger span {
	display: block;
	width: 100%;
	height: 2px;
	background: currentColor;
	transition:
		transform 0.3s var(--wsc-ease),
		opacity 0.3s var(--wsc-ease);
}

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

.wsc-header.is-open .wsc-header__burger span:nth-child(2) {
	opacity: 0;
	transform: scaleX(0);
}

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

/* Dropdown panel ---------------------------------------------------------- */

/*
 * grid-template-rows 0fr → 1fr rather than height auto: it works everywhere,
 * needs no measured height, and survives a repeater of any length.
 * (Transitioning to height: auto still needs interpolate-size, Chrome only.)
 */
.wsc-header__panel {
	display: grid;
	grid-template-rows: 0fr;
	transition: grid-template-rows 0.4s var(--wsc-ease);
}

.wsc-header.is-open .wsc-header__panel {
	grid-template-rows: 1fr;
}

/*
 * The clipper, and NOTHING else. It carries no padding on purpose.
 *
 * box-sizing is border-box theme-wide, and a border box floors at padding +
 * border — so an element the grid has sized to 0 still renders its own padding,
 * and overflow: hidden does not help because it clips the CONTENT, not the box.
 * Putting the 24px gap here made the closed island 24px too tall. It belongs on
 * __panel-content, inside the clip.
 *
 * visibility keeps the collapsed panel's links out of the tab order — a zero
 * height clipped by overflow is still focusable, so without this a keyboard user
 * tabs into invisible links. Delayed on close so it does not blank the content
 * mid-collapse.
 */
.wsc-header__panel-inner {
	min-height: 0;
	overflow: hidden;
	visibility: hidden;
	transition: visibility 0s linear 0.4s;
}

.wsc-header.is-open .wsc-header__panel-inner {
	visibility: visible;
	transition-delay: 0s;
}

.wsc-header__panel-content {
	display: flex;
	gap: var(--wp--preset--spacing--40);
	padding-top: var(--wp--preset--spacing--40); /* 24px — inside the clip */
}

.wsc-header__card {
	flex: 1;
	min-width: 0;
	padding: var(--wp--preset--spacing--40);
	border: 1px solid var(--wp--preset--color--bg);
	border-radius: var(--wp--custom--radius--md);
	background-color: var(--wsc-header-panel-surface);
}

/*
 * One repeater, two balanced columns, no editor decision: 5 rows split 3/2,
 * 6 split 3/3, 4 split 2/2.
 */
.wsc-header__card-list {
	column-count: 2;
	column-fill: balance;
	column-gap: var(--wp--preset--spacing--40);
	margin: 0;
	padding: 0;
	list-style: none;
}

.wsc-header__card-list li {
	break-inside: avoid;
	margin-bottom: var(--wp--preset--spacing--30); /* 16px */
}

.wsc-header__card-list li:last-child {
	margin-bottom: 0;
}

.wsc-header__promo {
	display: flex;
	flex: none;
	flex-direction: column;
	align-items: stretch; /* heading and button both run full width */
	justify-content: space-between;
	gap: var(--wp--preset--spacing--40);
	width: 20rem; /* 320px */
	padding: var(--wp--preset--spacing--40);
	border: 1px solid var(--wp--preset--color--bg);
	border-radius: var(--wp--custom--radius--md);
	background-color: var(--wsc-header-panel-surface);
}

/* The theme's H5 metrics, which is exactly what the design names this style. */
.wsc-header__promo-heading {
	margin: 0;
	font-family: var(--wp--preset--font-family--heading);
	font-weight: 600;
	font-size: 1rem; /* 16px */
	line-height: 1.25; /* 20px */
	letter-spacing: 0.02em; /* 0.32px */
	/* The editor's line break is honoured via nl2br(); this catches the rest. */
	overflow-wrap: break-word;
}

/* Full-width in the design, unlike the header row's hug-width CTA. */
.wsc-header__promo-button {
	width: 100%;
}

/* Hidden on desktop — this nav is the compact copy of .wsc-header__nav. */
.wsc-header__panel-nav {
	display: none;
}

/* Compact layout — burger and panel, below the 1024 nav breakpoint ---------- */

/*
 * 1024, not 768: see the width budget at .wsc-header__nav. Tablets get the
 * burger treatment rather than a desktop row that cannot fit.
 */
@media (max-width: 1023px) {
	/*
	 * A flat 32px rather than the section token, which clamps down to 24px on a
	 * phone. ::before reads the same variable, so both edges move together.
	 */
	.wsc-header {
		--wsc-header-inline: 2rem; /* 32px */
	}

	/*
	 * Held through open as well — the state-specific override that used to
	 * re-assert this inline padding is gone with the desktop one it corrected.
	 */
	.wsc-header__bar {
		padding: var(--wp--preset--spacing--40) var(--wp--preset--spacing--50); /* 24 / 32 */
	}

	.wsc-header__nav,
	.wsc-header__promo {
		display: none;
	}

	/* Only once JS can actually open the panel — see the trigger rule above. */
	.wsc-js .wsc-header__burger {
		display: flex;
	}

	.wsc-header__panel-content {
		flex-direction: column;
		gap: var(--wp--preset--spacing--60); /* 40px */
		padding-top: var(--wp--preset--spacing--60);
	}

	/* Flat list on mobile — the card's frame is a desktop affordance. */
	.wsc-header__card {
		padding: 0;
		border: 0;
		background: none;
	}

	.wsc-header__card-list {
		column-count: 1;
	}

	.wsc-header__panel-nav {
		display: flex;
		flex-direction: column;
		align-items: stretch;
		gap: var(--wp--preset--spacing--30); /* 16px */
	}

	.wsc-header__panel-link {
		white-space: normal;
	}

	.wsc-header__panel-cta {
		width: 100%;
	}

	/* The CTA sits in its own group below the links, not inside the 16px rhythm. */
	.wsc-header__panel-cta:not(:first-child) {
		margin-top: var(--wp--preset--spacing--40); /* 24px */
	}
}

/* Reduced motion ---------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	.wsc-header,
	.wsc-header::before,
	.wsc-header__bar,
	.wsc-header__panel,
	.wsc-header__panel-inner,
	.wsc-header__chevron,
	.wsc-header__burger span {
		transition: none;
	}
}
