/**
 * Header, provider coexistence and manual-ad geometry — the authoritative layer.
 *
 * This file loads last, after design-polish, verge-runtime-fixes, site-upgrades,
 * header-reference-parity, overdrive-rebrand and mobile-rpm. Those six layers
 * accumulated 277 rules mentioning `.go-header` between them, each written to
 * correct the one before it. Rather than add a seventh set of corrections, this
 * file states the whole contract in one place, in the order the cascade reads it.
 *
 * It is deliberately not minified and deliberately small. Everything here is a
 * decision, not a patch, and each decision says why.
 *
 * Contents
 *   1. Layer scale (z-index)
 *   2. Sticky header architecture
 *   3. Clever coexistence — the header follows the real measured height
 *   4. Header geometry: one state, one logo
 *   5. Scroll lock
 *   6. Offcanvas
 *   7. Manual ad containers — mobile first
 *   8. Empty and unfilled slots
 */

/* =========================================================================
   1. Layer scale

   One ordered scale instead of values invented per component. The previous
   spread was: header 60 or 100, latest bar 55, search 90/94/95/96/120,
   offcanvas 80/95/96/1200/1201, lightbox 1000 — several of them added purely to
   win an argument with a neighbour.

   Nothing here competes with a provider. Clever's top band and Google's bottom
   anchor are `position: fixed` containers injected into <body> with their own
   z-index, chosen by them and untouched by us. Ours top out at 1300, orders of
   magnitude below what the anchor uses, which is exactly what keeps the anchor
   visible above our chrome. That is a requirement, not an accident: covering an
   anchor is a layout-policy violation.
   ========================================================================= */

:root {
	--go-z-latest-bar: 55;
	--go-z-header: 60;
	--go-z-search: 1100;
	--go-z-offcanvas-backdrop: 1200;
	--go-z-offcanvas: 1210;
	--go-z-lightbox: 1300;

	/*
	 * Published by assets/js/go-ad-geometry.js from the measured height of a
	 * rendered, top-fixed Clever creative. It is REMOVED, not zeroed, when there
	 * is no such creative — so the fallback below is what applies whenever Clever
	 * is closed, absent, blocked or has not loaded yet, and no phantom gap can
	 * survive at the top of the page.
	 */
	--go-clever-top-space: 0px;
}

body.go-verge .go-latest-bar { z-index: var(--go-z-latest-bar); }
body.go-verge .go-search { z-index: var(--go-z-search); }
body.go-verge .go-offcanvas__backdrop { z-index: var(--go-z-offcanvas-backdrop); }
body.go-verge .go-offcanvas { z-index: var(--go-z-offcanvas); }
body.go-verge .go-lightbox { z-index: var(--go-z-lightbox); }

/* =========================================================================
   2. Sticky header architecture

   One strategy: `position: sticky`. Not fixed, not sticky-plus-a-transform, not
   a scroll handler writing `top`, not a class that swaps between them. Those
   were mixed before, and the mixture is what made the failure modes so hard to
   reproduce.

   The two properties that break `position: sticky` are an ancestor with a
   scrolling overflow and an ancestor with a transform/filter/containment. The
   header is a direct child of <body>, so its only ancestors are <body> and
   <html> — which is why section 5 is written the way it is.
   ========================================================================= */

body.go-verge .go-header,
body.go-verge .go-header--editorial {
	position: sticky;
	z-index: var(--go-z-header);

	/*
	 * `contain: paint` was set on the editorial header in design-polish.css. It
	 * makes the header a containing block for fixed-position descendants and
	 * clips anything that overflows it, which is the wrong shape for an element
	 * that hosts a search overlay trigger and a dropdown. Sticky does not need
	 * it, and nothing else here does either.
	 */
	contain: none;

	/*
	 * A stacking context of its own, so the header's internals cannot be
	 * interleaved with page content by an unrelated z-index somewhere below.
	 * This is safe on a sticky element — unlike containment, it does not create
	 * a containing block.
	 */
	isolation: isolate;
}

/* =========================================================================
   3. Clever coexistence

   The whole mechanism is one custom property, and the header reads it directly.
   No magic numbers, no breakpoint tables, no `if (mobile) header.style.top`.

   State A  Clever open       --go-clever-top-space = measured height
                              header sits immediately below the creative
   State B  Clever shrinks    ResizeObserver fires, the value drops, the header
                              follows in one transition
   State C  Clever closed     the runtime removes the property, the fallback 0px
                              applies, the header returns to the top edge
   State D  Clever never loads the property was never set; 0px from first paint,
                              so there is no reserved gap to collapse
   State E  scrolling with    `top` is a sticky offset, not a position. It is
            Clever open       recomputed by the compositor on every frame; there
                              is no JavaScript in the scroll path at all, which
                              is why the header can no longer be left behind
   State F  Clever closes     the value changes mid-scroll; only `top` animates,
            during a scroll   and the sticky offset stays authoritative
   State G  rotate / resize   the runtime re-measures on resize and
                              orientationchange and republishes
   ========================================================================= */

body.go-verge .go-header,
body.go-verge .go-header--editorial {
	top: var(--go-clever-top-space, 0px);

	/*
	 * Only `top` transitions, and only briefly. Transitioning the height or a
	 * transform here would fight the compositor on every scroll frame; `top`
	 * changes only when the provider's box actually changes, which is rare.
	 */
	transition: top 200ms cubic-bezier(.16, 1, .3, 1), box-shadow 180ms ease;
}

@media (prefers-reduced-motion: reduce) {
	body.go-verge .go-header,
	body.go-verge .go-header--editorial {
		transition: none;
	}
}

/*
 * The EM ALTA rail sits directly under the header, and it is sticky too, so its
 * offset is the header's real height plus whatever Clever is occupying.
 *
 * This is the fix for the gap between the header and the rail. Two layers pin
 * the rail with `top: var(--go-header-expanded-height) !important` — 94px, the
 * height of the header's old EXPANDED state. The header is now always 58px, so
 * the rail was sticking 36px lower than the header's bottom edge and the page
 * showed a band of canvas between them.
 *
 * `!important` is needed here only because header-reference-parity.css uses it;
 * the value itself is derived, never a magic number.
 */
body.go-verge .go-header--editorial + .go-latest-bar,
body.go-verge .go-latest-bar {
	/*
	 * `--go-header-height` is the header's measured border-box height, published
	 * by theme.js. The fallback is the content height, which is 1px short because
	 * it excludes the bottom border — fine as a pre-JavaScript approximation,
	 * wrong as a permanent value, which is why it is only the fallback.
	 */
	top: calc(var(--go-clever-top-space, 0px) + var(--go-header-height, var(--go-header-compact-height, 58px))) !important;
	margin-block-start: 0 !important;
}

/*
 * Scrolling hides the rail.
 *
 * It is a front-page orientation device: useful in the first screen, noise once
 * the reader has committed to scrolling. The old two-state header did this by
 * hiding the rail whenever the masthead compacted, which is why removing that
 * state took the behaviour with it.
 *
 * It is restored through `.is-hidden` on the rail itself — the class the runtime
 * already owns and that the legacy layers already style — and NOT through
 * `.is-compact` on the header, which those same layers also read as "hide the
 * logo".
 */
body.go-verge .go-latest-bar {
	transition:
		max-height 280ms cubic-bezier(.16, 1, .3, 1),
		opacity 160ms ease,
		visibility 0s linear 0s,
		top 200ms cubic-bezier(.16, 1, .3, 1);
}

body.go-verge .go-header--editorial + .go-latest-bar.is-hidden {
	max-height: 0 !important;
	overflow: hidden !important;
	opacity: 0 !important;
	visibility: hidden !important;
	pointer-events: none !important;
	border-block-end-color: transparent !important;
	transition:
		max-height 260ms cubic-bezier(.16, 1, .3, 1),
		opacity 140ms ease,
		visibility 0s linear 260ms;
}

@media (prefers-reduced-motion: reduce) {
	body.go-verge .go-latest-bar,
	body.go-verge .go-header--editorial + .go-latest-bar.is-hidden {
		transition: none;
	}
}

/* =========================================================================
   4. Header geometry — one state, one logo

   The header no longer has an expanded state, so `--go-header-expanded-height`
   is not used by anything and the bar height is constant. A masthead that does
   not change height cannot contribute to CLS, and it does not need a second,
   larger logo — which is where the duplicate came from.
   ========================================================================= */

body.go-verge .go-header--editorial {
	--go-header-compact-height: 58px;
	height: auto;
}

body.go-verge .go-header--editorial .go-header__bar {
	height: var(--go-header-compact-height);
	min-height: var(--go-header-compact-height);
	/* Nothing animates the bar's height any more; there is only one height. */
	transition: none !important;
}

/*
 * Safety net for `.is-compact`.
 *
 * The markup no longer emits it and theme.js actively removes it, so these rules
 * should never match. They exist because the failure mode is silent and severe:
 * five stylesheets read `.is-compact` as the old "scrolled" state and use it to
 * hide the wordmark and collapse the EM ALTA bar, several with `!important`, and
 * those stylesheets ship minified with no build step in the repository — they
 * cannot simply be corrected at source.
 *
 * If anything ever re-adds the class — a cached script, a plugin, a future edit
 * that assumes "compact header" means "add is-compact" — the header keeps its
 * logo and the front page keeps its rail.
 */
body.go-verge .go-header--editorial.is-compact .go-header__logo-full {
	display: inline-flex !important;
	opacity: 1 !important;
	visibility: visible !important;
	transform: none !important;
}

body.go-verge .go-header--editorial.is-compact + .go-latest-bar:not(.is-hidden) {
	max-height: 46px !important;
	overflow: visible !important;
	opacity: 1 !important;
	visibility: visible !important;
	pointer-events: auto !important;
	transform: none !important;
}

/*
 * The single header logo.
 *
 * `.go-header__logo-icon` no longer exists in the document — header.php renders
 * one lockup, not two — so every rule targeting it across the older layers is
 * inert by construction rather than by being overridden here. That is the point
 * of fixing it structurally: there is nothing left to hide.
 */
body.go-verge .go-header--editorial .go-header__logo-full {
	display: flex;
	align-items: center;
	/* No absolute positioning: there is nothing to stack it against. */
	position: static;
	opacity: 1;
	visibility: visible;
	transform: none;
}

/*
 * Logo HEIGHT is deliberately not set here. assets/css/overdrive-rebrand.css is
 * the brand layer and already owns it with `!important` (34px desktop, 32px and
 * 28px at its breakpoints). Declaring a competing height would either lose
 * silently or start another override war. This layer only guarantees the box
 * cannot overflow a narrow header.
 */
body.go-verge .go-header--editorial .go-header__logo-full .go-logo__img {
	width: auto;
	max-width: min(190px, 42vw);
	object-fit: contain;
	object-position: left center;
}

body.go-verge .go-header--editorial .go-header__brand {
	display: inline-flex;
	align-items: center;
	min-width: 0;
	/* The brand link no longer stacks two logos, so it must not clip one. */
	overflow: visible;
}

/*
 * Full wordmark is the only masthead identity at every viewport and scroll
 * state.  Older brand layers still contain the historical mobile rule that
 * shrinks `.go-header__brand` to 42px when `.is-compact` appears; keep that
 * stale class harmless as well.  The compact symbol is reserved for the
 * offcanvas and is never allowed to become a second masthead logo.
 */
body.go-verge .go-header--editorial .go-header__logo-icon {
	display: none !important;
}

body.go-verge .go-header--editorial.is-compact .go-header__brand {
	width: auto !important;
	min-width: 0 !important;
}

body.go-verge .go-header--editorial.is-compact .go-header__logo-full .go-logo__img {
	width: auto !important;
	height: 34px !important;
	max-width: min(190px, 42vw) !important;
}

@media (max-width: 767px) {
	/*
	 * Mobile header: logo, light/dark, search, menu. The X shortcut is the one
	 * action that does not earn its width on a phone; it stays in the offcanvas,
	 * which is where the rest of the social links already live.
	 */
	body.go-verge .go-header--editorial .go-header__nav,
	body.go-verge .go-header--editorial .go-header__x {
		display: none;
	}

	body.go-verge .go-header--editorial .go-header__logo-full .go-logo__img,
	body.go-verge .go-header--editorial.is-compact .go-header__logo-full .go-logo__img {
		width: auto !important;
		height: 28px !important;
		max-width: min(150px, 46vw) !important;
	}

	body.go-verge .go-header--editorial .go-header__brand,
	body.go-verge .go-header--editorial.is-compact .go-header__brand {
		width: auto !important;
		min-width: 0 !important;
		height: auto !important;
		overflow: visible !important;
	}

	body.go-verge .go-header--editorial .go-header__bar {
		gap: 8px;
	}

	body.go-verge .go-header--editorial .go-header__actions {
		margin-inline-start: auto;
		gap: 2px;
	}
}

/* =========================================================================
   4b. Reading progress follows geometry, not the header's viewport rect

   The reading meter is `position: fixed`.  Its old inline runtime sampled
   `header.getBoundingClientRect().bottom` and wrote that coordinate back as a
   custom property.  If Clever arrived while sticky geometry was being
   recalculated, one bad sample could become a large fixed `top` value and leave
   the meter stranded halfway down the viewport.

   Both numbers we actually need are already authoritative and independent:
   `--go-clever-top-space` is the measured provider height and
   `--go-header-height` is the measured masthead border-box.  Compose them in
   CSS, so no transient document coordinate can ever position this fixed bar.
   ========================================================================= */

body.single-post.go-verge .go-reading-meter {
	top: calc(
		var(--go-clever-top-space, 0px) +
		var(--go-header-height, var(--go-header-compact-height, 58px))
	) !important;
}

/* =========================================================================
   5. Scroll lock

   On <html>, never on <body> — and the reason is subtler than it looks, which
   is why this bug was intermittent rather than constant.

   A sticky element sticks inside its nearest scrolling ancestor. `overflow:
   hidden` on <body> would make <body> exactly that, except that viewport
   propagation normally rescues it: while <html> computes to `overflow: visible`,
   the UA propagates <body>'s overflow to the viewport and <body> keeps behaving
   as `visible`. That is why `body.go-lock { overflow: hidden }` seemed fine.

   Propagation stops the moment anything else sets an overflow on <html> — a
   lightbox, a consent modal, a provider script, a second scroll lock. Then
   <body> really does become a scroll container and the sticky header re-anchors
   to it. Measured: with `html { overflow: auto }` present, opening the drawer
   2.000px down left the header at -2000px, scrolled away with the document.

   Two harmless declarations, one broken header, only in combination.

   Locking <html> removes the first half. The rule below removes the second: even
   if something re-adds `go-lock` to <body>, <body> is explicitly kept out of the
   scroll-container role. Verified in tests/header-states.html: under the exact
   hostile combination, with the legacy verge.css declaration still in the sheet,
   the header holds at 0.
   ========================================================================= */

html.go-lock {
	overflow: hidden;
	/* Stop a scroll gesture inside the drawer from chaining to the document. */
	overscroll-behavior: none;
}

html.go-lock body {
	/*
	 * Explicit: whatever else happens, <body> must not become a scroll container
	 * while the drawer is open, or the header goes back to being stranded.
	 */
	overflow: visible;
}

/* =========================================================================
   6. Offcanvas

   Full viewport height and independent of the header. It does not read
   --go-clever-top-space for its own position: the drawer covers the screen, so
   starting it below Clever would leave a transparent strip showing the article
   behind it.

   Its CONTENT is padded instead, so the logo and the close button are never
   underneath Clever's fixed band.
   ========================================================================= */

body.go-verge .go-offcanvas {
	top: 0;
	bottom: 0;
	height: 100%;
	/* Not affected by the header's offset, in either direction. */
	margin-block-start: 0;
}

body.go-verge .go-offcanvas__head {
	padding-block-start: calc(20px + var(--go-clever-top-space, 0px));
	transition: padding-block-start 200ms cubic-bezier(.16, 1, .3, 1);
}

@media (prefers-reduced-motion: reduce) {
	body.go-verge .go-offcanvas__head { transition: none; }
}

body.go-verge .go-offcanvas__backdrop {
	top: 0;
	height: 100%;
}

/*
 * The compact symbol, and only here. Explicit geometry because the icon artwork
 * is 237x265 — nearly square — while the header lockup is 1226x178. Inheriting
 * the header's sizing would render it as a tall block.
 */
body.go-verge .go-offcanvas__head .go-logo--icon .go-logo__img {
	height: 34px;
	width: auto;
	max-width: 34px;
	object-fit: contain;
}

/* =========================================================================
   7. Manual ad containers — mobile first

   Rules for the OUTER container only. Nothing here sets a width or a height on
   `ins.adsbygoogle` or on the iframe Google renders inside it: the creative's
   size is the auction's answer, and forcing it is how a publisher ends up with
   cropped or letterboxed ads. We control the box; Google fills it.
   ========================================================================= */

body.go-verge .go-ad-slot {
	/* A fluid container can never be the source of horizontal overflow. */
	max-width: 100%;
	box-sizing: border-box;
	margin-inline: auto;
	/* Centre the creative whatever shape the auction returns. */
	text-align: center;
	/*
	 * `overflow` is deliberately NOT declared here.
	 *
	 * It is per-state, and go-ads-component.css already gets it right: a slot
	 * that has not been requested, or that came back unfilled, is fully collapsed
	 * with `overflow: hidden !important`, while `.is-ad-filled` is
	 * `overflow: visible` so a creative is never cropped. A blanket declaration
	 * at this layer loses to the collapsed states (they carry `!important`) and
	 * wins only in the states where it does not matter — all cost, no effect.
	 */
}

body.go-verge .go-ad-slot > ins {
	/* Centres the inline-block <ins> inside the centred container. */
	margin-inline: auto;
	max-width: 100%;
}

/*
 * Post-article billboard — edge to edge.
 *
 * No `100vw`, and that is the whole point.
 *
 * The obvious full-bleed idiom is `inline-size: 100vw` with
 * `margin-inline: calc(50% - 50vw)`, and go-ads-component.css uses it for the
 * mobile full-width units. It is wrong here, and measurably so: `100vw` includes
 * the classic scrollbar, so on desktop Windows the unit came out 947px wide in a
 * 932px viewport — 15px too wide, roughly 7px of creative cut off each side, and
 * only invisible because a clip on the wrapper hid the overflow.
 *
 * `.go-post-mgid` is a plain section inside `.go-site` and is already exactly the
 * viewport's content width. Measured: section 932px, left 0, in a 932px
 * viewport. So the unit needs no trick at all — its natural width IS full bleed,
 * and it stays correct whether or not a scrollbar is present.
 */
body.go-verge .go-ad-slot.go-post-billboard-ad {
	inline-size: auto;
	max-inline-size: 100%;
	margin-inline: 0;
	margin-block: clamp(20px, 3vw, 36px);
	padding-inline: 0;
	/* The creative centres itself whatever width the auction returns. */
	text-align: center;
}

body.go-verge .go-ad-slot.go-post-billboard-ad > ins {
	inline-size: 100%;
	max-inline-size: 100%;
}

@media (max-width: 767px) {
	body.go-verge .go-ad-slot.go-post-billboard-ad {
		margin-block: 20px;
	}
}

@media (max-width: 767px) {
	body.go-verge .go-ad-slot--in-article {
		/*
		 * Editorial distance. Close enough to the preceding paragraph to read as
		 * a break in the page rather than a floating object, far enough that it
		 * is never mistaken for part of the prose.
		 */
		margin-block: 28px;
		padding-inline: 0;
	}

	body.go-verge .go-site-masthead-ad {
		margin-block: 12px 16px;
	}
}

/* =========================================================================
   8. Empty and unfilled slots

   A slot that has not answered yet holds its reserved height — that is what
   keeps CLS down and it must not be shortened. A slot that has definitively not
   filled collapses completely: no box, no border, no background, no label.

   `is-ad-not-requested` deliberately keeps its reserve. Collapsing on a timer
   would turn a slow fill into a lost impression, which is the expensive
   direction of this trade.
   ========================================================================= */

body.go-verge .go-ad-slot.is-ad-unfilled:not(.go-ad-slot--has-fallback),
body.go-verge .go-ad-slot.is-ad-failed:not(.go-ad-slot--has-fallback) {
	display: none !important;
	block-size: 0 !important;
	min-block-size: 0 !important;
	margin-block: 0 !important;
	padding-block: 0 !important;
	border: 0 !important;
	background: none !important;
}

/*
 * A slot that is waiting shows nothing but the space it reserved — no skeleton,
 * no placeholder frame, no "advertisement" chrome around emptiness.
 */
body.go-verge .go-ad-slot.is-ad-not-requested,
body.go-verge .go-ad-slot.is-ad-preparing {
	background: none;
	border: 0;
}
