/**
 * The site's layer order, lowest first. This is the one place it is written down.
 *
 *   raised     (1)  an element lifted above its own siblings inside one component
 *   toolbar    (10) in-page sticky chrome, which slides under the site header
 *   header     (20) the site header template part - persistent chrome, and it has
 *                   to beat page content
 *   scrim      (30) the modal scrim
 *   modal      (40) the filter drawer, and anything else that takes over the screen
 *   skip-link  (100000) the skip link, which has to beat every one of the above -
 *                   and also WordPress core's admin bar, which is fixed at 99999 and
 *                   is not ours to renumber. That is why this one number is not on
 *                   the same decimal ladder as the rest: the ladder describes layers
 *                   this theme owns, and the skip link has to clear one it does not.
 *
 * The numbers live in theme.json under `settings.custom.zIndex`, which WordPress
 * emits as `--wp--custom--z-index--<key>`. Every cross-component rule reads them from
 * there and carries the raw number as a fallback, so a stylesheet still orders itself
 * correctly if theme.json is not loaded, and no number in this scale is authored twice.
 *
 * The rule the scale exists to state, because this is exactly what was broken: the
 * header outranks in-page chrome, the drawer outranks the header, and the skip link
 * outranks everything. The archive toolbar was sitting at 20 against a header at 10,
 * so a bar that is positioned to slide UNDER the header - it sticks at
 * `--f32-header-height` - was painting a band straight through the open category
 * panel instead.
 *
 * A z-index that only orders siblings inside a single component is NOT part of this
 * scale and does not take a token. Reach for one only when two different components
 * have to be ordered against each other.
 *
 * NOTHING ON THE SITE ESCAPES THIS SCALE, and one thing used to. A `<dialog>` opened
 * with showModal() is promoted to the browser's TOP LAYER, which paints after the
 * whole root stacking context - after the skip link, and after the trapped
 * view-transition context described further down this file. The phone search panel
 * was opened that way and so carried a number for one of its two open states and not
 * for the other.
 *
 * It is opened with show() now (assets/css/mobile-bar.css), because it became a sheet
 * that sits ON the mobile bottom bar and a modal dialog would have made that bar
 * inert. Both of its open states are ordinary fixed elements and both take `modal`
 * (40), which is above the bar's `header` (20) and is all the sheet ever needed.
 */

/**
 * Site header — two rows: the search row, and the category browse strip.
 *
 * It was three. The first was a black utility bar carrying a tagline, Help and Request
 * Presets; it was removed deliberately to simplify the header, and its two links live
 * in the footer, which already carried them. Its going is why the sticky rule below
 * pins at `top: 0` instead of offsetting past it.
 *
 * This is the theme's FIRST unconditional stylesheet; every other one is loaded on a
 * predicate (checkout, download archive, hero, forms). That is deliberate here and
 * not an oversight: the header renders on every request, so a conditional load would
 * be a condition that is always true, written out at the cost of a hook.
 *
 * Nothing here is wrapped in a cascade layer. WordPress serialises the layout choices
 * in patterns/site-header.php as unlayered `.wp-container-…-is-layout-…` rules, and an
 * unlayered rule beats a layered one at any specificity, so the flex declarations
 * below have to stand in the same origin to be reachable at all.
 *
 * @package F32
 */

/* === Skip link =========================================================== */

/*
 * The first focusable thing in the document, printed on `wp_body_open` from
 * functions.php.
 *
 * It is moved off-screen rather than hidden. `display: none` and `visibility: hidden`
 * both remove an element from the tab order, so a skip link hidden either way can
 * never receive the focus that is supposed to reveal it - which is the single most
 * common way this control is shipped broken.
 *
 * `position: fixed` and the top layer of the scale at the head of this file, because
 * the header it skips over is itself sticky at the `header` layer and would otherwise
 * cover the link the moment the page is scrolled.
 *
 * It does NOT use core's `.screen-reader-text`. That class is shipped by the block
 * library, not by this theme, and a control whose entire job is to appear on focus
 * should not depend on a stylesheet the theme does not own being present and
 * unmodified.
 */
.f32-skip-link {
	position: fixed;
	top: 8px;
	left: 8px;
	z-index: var(--wp--custom--z-index--skip-link, 100000);
	padding: 0.7rem 1.1rem;
	border-radius: 8px;
	background: var(--wp--preset--color--primary, #000);
	color: var(--wp--preset--color--contrast, #fff);
	font-size: 0.9rem;
	font-weight: 600;
	line-height: 1.2;
	text-decoration: none;
	transform: translateY(calc(-100% - 16px));
	transition: transform 0.16s ease;
}

.f32-skip-link:focus {
	transform: translateY(0);
	outline: 2px solid var(--wp--preset--color--accent, #1473e6);
	outline-offset: 2px;
	color: var(--wp--preset--color--contrast, #fff);
	text-decoration: none;
}

@media (prefers-reduced-motion: reduce) {
	.f32-skip-link {
		transition: none;
	}
}

/* --------------------------------------------------------------------------
   The header must own a stacking context that wins against page content.

   Something on this site puts `view-transition-name: header` on the header
   template part - the stylesheet handle is `plvt-view-transitions-inline-css`,
   i.e. the View Transitions plugin, not core and not this theme (grep the theme
   for `view-transition`: the only hits are this comment). A
   view-transition-name other than `none` CREATES A STACKING CONTEXT, so
   everything the header draws - including the submenu, which core positions
   absolutely at z-index 2 - is trapped inside a context that is itself
   `position: static; z-index: auto` and therefore paints in step 3 of the root
   stacking order. Any `position: relative` section further down the page paints
   in step 8, i.e. later, i.e. on top. The homepage hero
   (`.wp-block-group.home-hero`, position: relative, z-index auto) is one such
   section, and it swallowed the bottom two thirds of the Downloads panel.

   THIS IS WHY THE BUG LOOKS INTERMITTENT. The plugin only assigns the name
   around a navigation, so a tab that reached the page by clicking through the
   site has it and a tab that loaded the URL cold computes `none` and looks
   fine. Measured both ways on the same page in two tabs. Anyone trying to
   reproduce it from a fresh tab will conclude there is no bug.

   Raising the submenu's own z-index to 100 does nothing (it is competing inside
   the trapped context). Raising `.site-header` - the group one level in, which
   is already position: relative - does nothing, for the same reason. Setting
   `position: relative; z-index` on the element that carries the
   view-transition-name is the only thing that moves it, because that is the
   context that was losing. Harmless when the name is absent.

   The selector is keyed on `.site-header`, a class this theme puts on the
   group inside the part, rather than on `header` as a tag: the tag comes from
   `tagName` on the template's `wp:template-part`, which lives in another file.

   RETAINED after the mega-menu was removed, and re-homed here from
   assets/blocks/core-navigation.css when that file was deleted. The submenu this
   was written for is gone, but the cause is not: the plugin still assigns
   `view-transition-name` around the navigation, the trapped context is still
   trapped, and the header still draws things that must win against page content -
   the category strip's absolutely positioned scroll arrows, and focus rings on the
   strip's first and last items.

   The context this opens is also a ceiling: everything the header draws is clamped
   into whatever layer this rule takes, including the category panel and the search
   suggestions, which the sparklestock plugin paints at 20 and 30 of their own. So the
   layer here is the header's layer for the whole site, and it has to sit above the
   in-page chrome that is positioned to slide under it.
   -------------------------------------------------------------------------- */
.wp-block-template-part:has(> .site-header) {
	position: relative;
	z-index: var(--wp--custom--z-index--header, 20);
}

/* --------------------------------------------------------------------------
   Sticky, and only where it pays for itself.

   The search is the point of this header, and on a 1,082-product archive it leaves
   the screen within one flick of the wheel. So the header follows - but a sticky
   header is a permanent tax on the viewport, and this one is only affordable because
   the header is now two rows rather than three.

   The element that sticks has to be the template part, not the search row inside it.
   A sticky box is confined to its own containing block, and `.site-header-main`'s
   containing block is the header group - so it would pin for exactly the height of
   the strip below it and then leave. Measured before this rule was written.

   `top: 0` rather than the negative offset this used to carry. The offset existed to
   slide a black utility bar off the top on the way past; that bar is gone, and what
   pins is the whole of what is left - the search and the category strip, the two
   things a visitor mid-browse reaches for. Measured stuck: 134px.

   NOT on phones. The mobile header stacks logo, search and strip, so pinning it would
   cost about 150px of a 667px screen. Below 900px the header scrolls away like
   everything else, which is also what the browser's own collapsing address bar
   assumes.
   -------------------------------------------------------------------------- */
@media (min-width: 900px) {
	.wp-block-template-part:has(> .site-header) {
		position: sticky;
		top: 0;
	}

	/*
	 * Published so a second sticky element can sit under this one. The archive
	 * filter toolbar is the first consumer: it pins too, and without an offset
	 * it pins *behind* the header and the twelve colour swatches disappear
	 * under the category strip. 134px is the measured stuck height quoted
	 * above - keep the two numbers together, because a header row added later
	 * has to move both.
	 */
	:root {
		--f32-header-height: 134px;
	}

	/*
	 * THE HEADER IS NOT ALWAYS TWO ROWS ANY MORE, so the height it publishes
	 * cannot be one constant.
	 *
	 * The `plain` header variant renders the top row alone - see
	 * f32_header_variant() in functions.php for which surfaces take it, and
	 * patterns/site-header.php for the markup. A page with no browse row is 88px, not
	 * 134px - measured on staging at 1400px, the same way the 134 above was -
	 * and every consumer of this variable offsets by whatever it says:
	 * the account rail pins at `calc(var(--f32-header-height) + 1.5rem)`
	 * (assets/css/account.css), so a stale 134 would park the rail 59px lower
	 * than the header it is clearing, with the gap sitting there on every
	 * scroll.
	 *
	 * Keyed on the presence of the browse row rather than on a body class,
	 * because `:has()` is already this file's mechanism one rule up - the
	 * sticky hook itself is `:has(> .site-header)` - so no new dependency is
	 * introduced, and a header part added later publishes the right number by
	 * having (or not having) the row, with nothing to remember to update.
	 */
	:root:not(:has(.site-header-browse)) {
		--f32-header-height: 88px;
	}
}

/* === Row 1: logo, search, account, cart ================================== */

/*
 * Three tracks, not a flex row, and neither half of that is taste.
 *
 * The search is the point of this header, so it belongs in a track centred on the
 * PAGE rather than sitting in whatever space is left over after the logo. Equal side
 * tracks also mean a second action later - a wishlist, a login - does not drag the
 * search off centre.
 *
 * Grid rather than flex with `margin-left: auto` on the actions, because that is what
 * this file used to say and it never applied. Core prints
 * `.is-layout-flex > :is(*, div) { margin: 0 }`, which is 0,1,1 - `:is()` takes the
 * weight of its heaviest argument, and `div` is 0,0,1 - so a single-class margin rule
 * on a flex child loses silently, with no warning and no error. Measured: the cart sat
 * at x=840 in a 1637px row, 732px short of the right edge. `justify-self` is not a
 * margin, so that reset has nothing to override and this cannot fail the same way.
 *
 * The group is declared `layout: default` in patterns/site-header.php so core sets no
 * `display` on it at all, and this file owns the row outright instead of racing the
 * serialised `.wp-container-...-is-layout-flex` rule for it.
 */
.site-header-main {
	display: grid;
	/*
	 * The side tracks carry a floor, and it is not decoration. A bare `1fr` track is
	 * floored at its content's min-content size, and the logo's min-content is ZERO -
	 * core sizes `.wp-block-site-logo img` from its container, so the container asks
	 * the track how wide it is and the track asks back. With free space to distribute
	 * the fr expansion hides that; without it the loop resolves at 0 and the logo
	 * disappears. Measured at a 768px viewport before this floor existed:
	 * `0px 621px 44px`.
	 *
	 * Both side tracks take the SAME floor, so the centre track stays centred on the
	 * page whichever one is binding.
	 */
	/*
	 * The centre track is fluid, not a fixed 660px: it takes everything the two side
	 * tracks and the gaps do not need, between a floor that keeps the placeholder
	 * readable and a ceiling that stops a 2560px monitor turning the search into a
	 * runway. Raise --f32-header-search to let it run wider; there is nothing else to
	 * change.
	 *
	 * `100%` and NOT `100vw`, and that is the second time this file has had to say so.
	 * A viewport unit includes the scrollbar, so `100vw - 288px` asks for a track about
	 * fifteen pixels wider than the row it sits in - and since the side tracks have a
	 * floor, that surplus has nowhere to go but out the right edge. A percentage in a
	 * track size resolves against the grid container's content box, which is the number
	 * this actually wants.
	 *
	 * The subtraction names the same two variables the tracks and the gap use, so the
	 * three cannot be edited out of agreement with each other.
	 */
	grid-template-columns:
		minmax(var(--f32-header-side, 120px), 1fr)
		minmax(
			0,
			var(
				--f32-header-search,
				clamp(
					20rem,
					100% - 2 * (var(--f32-header-side, 120px) + var(--f32-header-gap, 24px)),
					60rem
				)
			)
		)
		minmax(var(--f32-header-side, 120px), 1fr);
	align-items: center;
	gap: var(--f32-header-gap, 24px);
	background: var(--wp--preset--color--contrast, #fff);
}

/*
 * THE ROW HOLDS TWO THINGS ON EVERY PAGE THAT IS NOT THE STORE, and until this rule the
 * grid did not know it.
 *
 * patterns/site-header.php prints the search for the `browse` variant only. The docs
 * header and the plain header therefore have two children, the logo and the actions,
 * and the three tracks above are still three. Grid auto-placement puts a second child in
 * the SECOND track - the search track - so the account link and the cart stop where the
 * search would have ended instead of at the edge of the row.
 *
 * Measured on staging at a 1914px viewport, on /docs/ and again on /account/: tracks
 * `421px 960px 421px`, two children, and the cart's right edge at 1437px against a row
 * edge of 1914px. It is 477px short, and it is short on every surface except the store.
 *
 * The fix is to state the row that actually rendered. Two things means two tracks: the
 * logo takes the fluid one, the actions take what they need at the end.
 *
 * `:has()` AND NOT A VARIANT CLASS from f32_header_variant(). This file already asks the
 * same question twice - the sticky hook is `:has(> .site-header)` and the published
 * header height is `:root:not(:has(.site-header-browse))` - and the reason is the same
 * one each time: a header that gains or loses a part lays itself out correctly by HAVING
 * or not having that part, with nothing to remember to update. A class printed by the
 * pattern would put one fact in two files and let the two disagree.
 *
 * `grid-column: 3` ON THE ACTIONS IS THE OTHER CANDIDATE AND IS REJECTED. It pins the
 * right edge, but it leaves a 960px track reserved for a block that is not there. The
 * row then looks correct and reads wrong, and the next item added to this header is
 * auto-placed into the dead track.
 *
 * ABOVE 900px ONLY. Below that width the mobile block at the foot of this file already
 * declares two columns and places both items explicitly, which is why the phone header
 * never showed this fault. The scope is also a guard: this selector is 0,2,0 and the
 * mobile one is 0,1,0, so an unscoped rule here would silently outweigh it, and the next
 * person to edit the mobile columns would watch the edit do nothing.
 */
@media (min-width: 900px) {
	.site-header-main:not(:has(.site-header-main__search)) {
		grid-template-columns: minmax(var(--f32-header-side, 120px), 1fr) auto;
	}
}

.site-header-main__logo {
	justify-self: start;
}

/*
 * `min-width: 0` is load-bearing here too: a grid item's automatic minimum size is its
 * content, so without it the search refuses to shrink below the placeholder and pushes
 * the cart off the row. Same rule, same reason, as assets/blocks/core-post-template.css.
 */
.site-header-main__search {
	min-width: 0;
}

/*
 * A flex group holding the account link and the cart, not a single block. `justify-self`
 * still does the pushing - core's `.is-layout-flex > :is(*, div) { margin: 0 }` reset
 * would eat an auto margin here exactly as it did before, and it is not a margin.
 */
.site-header-main__actions {
	justify-self: end;
}

/* === Row 2: category browse strip ======================================== */

.site-header-browse {
	background: var(--wp--preset--color--contrast, #fff);
	border-bottom: 1px solid var(--wp--preset--color--border-subtle, #e0e0e0);
}

/*
 * The strip is a browse row, so it starts at the content edge rather than centring
 * itself. Left alone it would inherit the block's shrink-to-fit centring, which is
 * right for a standalone strip and wrong directly under a left-aligned logo.
 */
/*
 * A STRIP WITH A BROWSE CONTROL STARTS LEFT; a strip without one is centred.
 *
 * The store's strip ends in the "All categories" panel, which is pinned to the right
 * of the row. Tabs starting at the content edge and a control at the far edge read as
 * one band with two ends. The Help Center's strip is built with `showBrowse: false`,
 * so it has no right-hand end - and the same rule left eight docs categories huddled
 * against the left margin of an otherwise empty row, under a hero whose search and
 * heading are centred.
 *
 * Keyed on the presence of the control rather than on which header rendered, because
 * there is only ONE header now (patterns/site-header.php) and the strip is the thing
 * that differs. `:has()` is already this file's mechanism twice over.
 *
 * `safe center` and NOT `center`. The scroller is `overflow-x: auto`, and a centred
 * flex line that overflows its container overflows in BOTH directions - the first tabs
 * end up before the scroll origin, where no gesture and no scrollbar can reach them.
 * The `safe` keyword falls back to `flex-start` at exactly that point.
 */
.site-header-browse
	.wp-block-sparklestock-category-tabs:has(
		.wp-block-sparklestock-category-tabs__browse
	)
	.wp-block-sparklestock-category-tabs__scroller {
	margin-inline: 0;
	justify-content: flex-start;
}

.site-header-browse
	.wp-block-sparklestock-category-tabs:not(
		:has(.wp-block-sparklestock-category-tabs__browse)
	)
	.wp-block-sparklestock-category-tabs__scroller {
	margin-inline: 0;
	justify-content: safe center;
}

/* === Mobile ============================================================== */

/*
 * ONE breakpoint, not two, and it is a deliberate simplification of what was here
 * before.
 *
 * Below this width the search takes a line of its own, leaving logo + account + cart
 * above it and the strip below. This used to happen at 781px, and the utility bar's
 * disappearance at 599px was a second breakpoint, which left a band around 600-780px
 * where the search was still sharing a row with two 120px side tracks: measured at a
 * 600px viewport, the search field itself was 99px wide. A search that narrow is the
 * thing this header exists to avoid.
 *
 * 899 and not 781, which is the second correction to the same number. The three-column
 * row needs about 290px for the side tracks and the gaps before the search gets
 * anything, and a tablet does not have it to spare: measured at 782px the field was
 * 225px and at 900px it was 287px, against roughly 700px for the same widths stacked.
 * iPad portrait is 810 to 834 CSS pixels, i.e. squarely inside the band this used to
 * hand the worse layout to.
 *
 * The search deliberately does NOT collapse behind an icon. A hidden search is a
 * search that visitors do not use, which would defeat the purpose of this header.
 */
@media (max-width: 899px) {
	/*
	 * Every item is placed explicitly. Auto-placement would put the logo in row 1,
	 * find the search cannot fit beside it once it spans both columns, and push the
	 * cart down to a third row of its own.
	 */
	.site-header-main {
		grid-template-columns: 1fr auto;
		row-gap: 14px;
	}

	.site-header-main__logo {
		grid-area: 1 / 1;
	}

	.site-header-main__actions {
		grid-area: 1 / 2;
	}
}

/*
 * THE TABLET BAND, 783px to 899px, and it is a band rather than "everything below
 * 899px" because two different things collapse at two different widths.
 *
 * The header stops fitting three columns at 899px and stacks the search onto a row of
 * its own. The bottom tab bar - which is what makes the header's search redundant -
 * only appears at 782px. Between the two there is no tab bar, so the header keeps its
 * search and its account link and this is the only layout that shows them.
 *
 * Written as a band and not as a `max-width: 899px` rule that a later
 * `max-width: 782px` rule undoes, because the second form leaves a grid-area assigned
 * to an element that is no longer displayed, and the next reader has to hold both
 * rules in their head to know what row 2 is for.
 */
@media (min-width: 783px) and (max-width: 899px) {
	.site-header-main__search {
		grid-area: 2 / 1 / 3 / 3;
	}

	/*
	 * The scope select goes, and this is the fix for the mobile squeeze rather than
	 * the width caps in the block's own stylesheet, which are the safety net.
	 *
	 * A select is as wide as its WIDEST option, so a taxonomy containing "Photoshop
	 * Elements" charges every tablet about 190px for a control most visitors never
	 * touch. Nothing is lost: the row below is a category strip, and the browse panel
	 * on the end of it reaches all twenty-seven terms in one tap.
	 *
	 * `display: none` rather than removing it from the markup, and the difference
	 * matters. A hidden select still SUBMITS, and on a term archive it is prefilled
	 * with the term being browsed - so a tablet visitor searching from inside LUTs
	 * still searches inside LUTs, which is what the same search does on a desktop.
	 * Disabling it would have quietly changed the behaviour by viewport width.
	 */
	.site-header-main__search .wp-block-sparklestock-product-search__scope {
		display: none;
	}
}

/* === Phones ============================================================== */

/*
 * WHAT THE BOTTOM BAR TAKES OVER. At 782px assets/css/mobile-bar.css puts four fixed
 * tabs at the foot of the screen, and two of them are things this header is also
 * showing. Two search fields and two account links on a 375px screen is not a choice
 * the visitor benefits from being offered; it is the header saying the same thing
 * twice in the scarcest space the site has.
 *
 * So the header gives up both, and keeps the logo and the CART. The cart is not
 * duplicated - the bar has no cart tab - and it is the one action that has to stay
 * reachable from every page.
 *
 * 782px and not 899px. This rule exists BECAUSE of the bar, so it has to start at the
 * width the bar starts at; hiding the search at 899px would take it away from tablets
 * that get no bar to replace it, and leave them with no search at all.
 *
 * `display: none` rather than not rendering it. The part renders at every width - the
 * server has no viewport to test - and the same argument the bar's stylesheet makes
 * applies here.
 *
 * THE SEARCH IS NOT LOST, it moved: the bar's Search tab opens the same block in a
 * dialog. See patterns/mobile-bar.php for why that is a second instance of the block
 * rather than this one relocated.
 */
@media (max-width: 782px) {
	.site-header-main__search,
	.site-header-main__actions .wp-block-sparklestock-account-link {
		display: none;
	}
}
