/**
 * Docs Index — the Help Center's filterable index, and the article layout it sits in.
 *
 * Applied via block style: sparklestock/docs-index (see f32_get_block_asset_list).
 *
 * Unlike the sibling category-tabs and product-search files this is NOT a property
 * skin. That block declares no `style` in its block.json and ships no CSS of its
 * own, so the whole presentation is here. The class names are the block's published
 * interface — see its render.php before renaming anything.
 *
 * Not wrapped in a cascade layer, for the reason the sibling files document:
 * WordPress serialises author layout choices as unlayered `.wp-container-…-is-layout-…`
 * rules, and an unlayered rule beats a layered one at any specificity.
 *
 * EVERY rule in this file that competes with core or theme.json is written at two
 * classes or more. That is a house rule here, not decoration, and it was learned the
 * hard way: `.doc-layout > *` at 0,1,0 tied with core's flow-margin rule, core printed
 * last, and the rail sat 24px below the article on staging.
 *
 * The reason is `:root`, not `:where`. theme.json emits its element
 * styles as `:root :where(h2)` and `:root :where(a:where(:not(.wp-element-button)))`;
 * everything inside `:where()` scores zero, so the ONE class in each of those comes
 * from the leading `:root` pseudo-class, and both selectors land at 0,1,0. A single
 * class only TIES with 0,1,0, and a tie is decided by which stylesheet the page
 * printed last — which for a `wp_enqueue_block_style()` asset is not fixed, and which
 * on staging went core's way. Two classes is the difference between a rule that
 * applies and a rule that usually applies. WordPress 7.0 prefixes its LAYOUT rules
 * with `:root` too, so the same arithmetic covers `.is-layout-flow` and friends.
 *
 * Ties BETWEEN rules in this file are fine and are resolved by source order, which
 * one stylesheet does guarantee. Ties across stylesheets are the ones that bite.
 *
 * This file is also pulled into the Site Editor iframe by f32_editor_block_styles(),
 * so nothing here may assume a frontend-only DOM. The two `.doc-layout > …` child
 * selectors simply do not match inside the editor's block list wrappers; the rail
 * stacks under the article there, which is the same thing a phone gets.
 */

/*
 * `hidden` is the block's ONLY filtering mechanism: render.php sets it server-side
 * and the Interactivity API toggles it live. The UA's `[hidden] { display: none }`
 * loses to any author `display`, so the attribute has to be re-asserted at a
 * specificity that clears the layout rules below. Scoped to the block — this is a
 * repair to the block's own markup contract, not a site-wide opinion.
 *
 * INVARIANT, since this rule is only 0,1,1: nothing in this file may set `display`
 * on `.docs-index__group`, `.docs-index__row` or `.docs-index__empty`. Those are the
 * three elements render.php and the runtime mark `[hidden]`, and a two-class
 * `display` rule on any of them would outrank this and un-hide it. Today none of
 * them carries one — every `display` here is on a container that is never hidden.
 */
.docs-index [hidden] {
	display: none;
}

/* The label exists for the field, not for the eye. Defined here rather than leaning
   on core's `.screen-reader-text`, which no rule guarantees is on the page. */
.docs-index .docs-index__label {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* ---------------------------------------------------------------- Filter row -- */

/*
 * The field, then the count, over one hairline. No card: the index is the page's
 * content, not an object sitting on it, so there is no border, background, radius
 * or shadow anywhere in this file except on the field itself.
 */
.docs-index .docs-index__filter {
	display: flex;
	align-items: center;
	padding-bottom: var(--wp--preset--spacing--30);
	margin-bottom: var(--wp--preset--spacing--50);
	border-bottom: 1px solid var(--wp--preset--color--border-subtle, #e0e0e0);
}

.docs-index .docs-index__field {
	flex: 1;
	/* A flex item's automatic minimum size is its content width; without this the
	   field refuses to shrink and pushes the count out of a 236px rail. */
	min-width: 0;
	max-width: 420px;
	padding: 6px 14px;
	border: 1px solid var(--wp--preset--color--border-strong, #d1d5db);
	border-radius: var(--wp--custom--border-radius--full, 9999px);
	background: var(--wp--preset--color--contrast, #fff);
	color: var(--wp--preset--color--primary, #000);
	font-size: 13.5px;
	line-height: 1.45;
	/* Safari draws `type=search` with its own rounded skin that ignores the border. */
	appearance: none;
}

.docs-index .docs-index__field::placeholder {
	color: var(--wp--preset--color--muted, #6b7280);
}

.docs-index .docs-index__field:focus-visible {
	outline: 2px solid var(--wp--preset--color--accent, #1473e6);
	outline-offset: 1px;
}

/*
 * The count COLLAPSES, it does not merely fade. An invisible "27 of 27" still holds
 * roughly 49px, and in the rail it would take that width straight out of the 236px
 * field beside it — so the resting state has to be zero-width, not transparent.
 */
.docs-index .docs-index__count {
	flex: 0 0 auto;
	max-width: 0;
	overflow: hidden;
	opacity: 0;
	white-space: nowrap;
	color: var(--wp--preset--color--muted, #6b7280);
	font-size: 11px;
	font-variant-numeric: tabular-nums;
	transition: max-width 0.18s ease, margin-left 0.18s ease, opacity 0.18s ease;
}

/* `is-filtering` is bound on the block root, so both variants expand from here. */
.docs-index.is-filtering .docs-index__count {
	max-width: 80px;
	margin-left: 8px;
	opacity: 1;
}

/* ------------------------------------------------------------------- Groups -- */

.docs-index .docs-index__heading {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 8px;
	margin: 0 0 var(--wp--preset--spacing--30);
	color: var(--wp--preset--color--muted, #6b7280);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.1em;
	line-height: 1.4;
	text-transform: uppercase;
}

.docs-index .docs-index__heading-text {
	min-width: 0;
}

.docs-index .docs-index__n {
	flex: 0 0 auto;
	font-weight: 400;
	font-variant-numeric: tabular-nums;
}

.docs-index .docs-index__list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.docs-index .docs-index__empty {
	margin: 0;
	color: var(--wp--preset--color--muted, #6b7280);
	font-size: 13.5px;
	line-height: 1.45;
}

/* --------------------------------------------------------------------- Rows -- */

/*
 * No gap between rows, deliberately: the 2px left rail is the only vertical
 * structure a row has, and a gap would break it into ticks.
 */
.docs-index .docs-index__row {
	padding: 4px 0 4px 11px;
	border-left: 2px solid transparent;
	font-size: 13.5px;
	line-height: 1.45;
}

.docs-index .docs-index__link {
	display: block;
	color: var(--wp--preset--color--muted, #6b7280);
}

.docs-index .docs-index__row:not(.is-current):hover {
	border-left-color: var(--wp--preset--color--border-strong, #d1d5db);
}

/*
 * Hover already speaks twice — muted ink goes to full ink, and the 2px rule
 * appears on the left edge. theme.json's global `a:hover { text-decoration:
 * underline }` would be a third simultaneous signal on a 27-row list, and no
 * documentation sidebar underlines on hover. Cancelled here and nowhere else.
 *
 * That global rule is `:root :where(a:where(:not(.wp-element-button))):hover`,
 * where `:root` and `:hover` each score a class and everything inside `:where()`
 * scores nothing — 0,2,0. A single-class `.docs-index__link:hover` would only
 * tie with it; this selector is 0,4,0 and settles it outright.
 */
.docs-index .docs-index__row:hover .docs-index__link {
	color: var(--wp--preset--color--primary, #000);
	text-decoration: none;
}

.docs-index .docs-index__link:focus-visible {
	outline: 2px solid var(--wp--preset--color--accent, #1473e6);
	outline-offset: 2px;
}

/*
 * The current row is marked by weight and a rule, never by a fill. A filled chip
 * would be the loudest mark on the surface, and it would be spent on the one page
 * the reader is already reading.
 */
.docs-index .docs-index__row.is-current {
	border-left-color: var(--wp--preset--color--primary, #000);
}

.docs-index .docs-index__link.is-current {
	color: var(--wp--preset--color--primary, #000);
	font-weight: 600;
}

/* ------------------------------------------------------- Variant: the archive -- */

/*
 * The archive's `main` is `alignwide` over a flow layout, so without a cap the
 * index inherits theme.json's 1420px wide-size. Four columns of one- and two-word
 * labels across 1420px reads as a stretched table rather than an index — the
 * hairlines end up further apart than the text they separate.
 *
 * 980px is not a new number: it is the measure `.doc-layout` uses below, so a
 * document and the index of documents sit on the same column width and the Help
 * Center reads as one surface. The archive's `h1` is centre-aligned, so centring
 * the index under it keeps the two agreeing rather than exposing a ragged edge.
 */
.wp-block-sparklestock-docs-index.docs-index--grid {
	max-width: 980px;
	margin-inline: auto;
}

/*
 * Flex, not grid. A `display: none` group is not a flex item at all, so when the
 * filter empties two of the four the survivors pack from the left at a sane width
 * instead of sitting in quarter-width tracks with half the row blank — which is a
 * grid track's behaviour, and it would break the layout at precisely the moment a
 * reader is using it. Wrapping also means no column-count breakpoint has to exist:
 * the wrap point moves with how many groups are visible, so no fixed breakpoint
 * could have been right for every filter state anyway.
 *
 * `overflow: clip` is here for the divider below — see it for why. `clip` rather
 * than `hidden` so this never silently becomes a scroll container, and the clip
 * margin leaves the row links' 2px focus ring room to sit outside the first
 * column's edge instead of being shaved off.
 */
.docs-index--grid .docs-index__groups {
	display: flex;
	flex-wrap: wrap;
	column-gap: var(--wp--preset--spacing--50);
	row-gap: var(--wp--preset--spacing--60);
	overflow: clip;
	overflow-clip-margin: 3px;
}

.docs-index--grid .docs-index__group {
	flex: 1 1 200px;
	max-width: 245px;
}

/*
 * The hairline is a pseudo-element parked in the middle of the column gap, not a
 * `border-inline-start` on the group, and that is the whole reason it survives
 * wrapping. A border sits on the group's own edge, so the first group of a second
 * row would draw one down the index's left edge; a pseudo half a gap further out
 * lands outside the container and the `overflow: clip` above erases it. Nothing
 * has to guess where the rows break.
 *
 * `:not([hidden]) ~ :not([hidden])` keeps it off the first VISIBLE group rather
 * than the first child: filtering hides groups in place, so `:first-child` would
 * leave a rule standing on whichever column has just become the leftmost.
 */
.docs-index--grid .docs-index__group:not([hidden]) ~ .docs-index__group:not([hidden]) {
	position: relative;
}

.docs-index--grid
	.docs-index__group:not([hidden])
	~ .docs-index__group:not([hidden])::before {
	content: "";
	position: absolute;
	inset-block: 0;
	inset-inline-start: calc(var(--wp--preset--spacing--50) / -2);
	border-inline-start: 1px solid var(--wp--preset--color--border-subtle, #e0e0e0);
}

/* ---------------------------------------------------------- Variant: the rail -- */

/* One column, always. */
.docs-index--rail .docs-index__groups {
	display: block;
}

.docs-index--rail .docs-index__group:not([hidden]) ~ .docs-index__group:not([hidden]) {
	margin-block-start: var(--wp--preset--spacing--50);
}

/* The rail's field owns the full 236px; 420px is an archive figure. */
.docs-index.docs-index--rail .docs-index__field {
	max-width: none;
}

/*
 * The group counts collapse in the rail for the same reason the total does — 236px
 * has no width to spare. Scoped through `.docs-index__group` on purpose: the nearby
 * section's count labels a fixed neighbourhood that the filter never touches, so
 * hiding it until someone types would be simply wrong.
 */
.docs-index--rail .docs-index__group .docs-index__n {
	max-width: 0;
	overflow: hidden;
	opacity: 0;
	transition: max-width 0.18s ease, margin-left 0.18s ease, opacity 0.18s ease;
}

.docs-index--rail.is-filtering .docs-index__group .docs-index__n {
	max-width: 80px;
	margin-left: 8px;
	opacity: 1;
}

/*
 * The neighbourhood now follows the sibling group instead of preceding the whole
 * rail, so the gap it needs is above it — and only when there is something above
 * it to be separated from. A document in the catch-all group renders no sibling
 * section at all, and a bare `margin-block-start` would then push the rail's first
 * line down for no reason. The sibling combinator is the rule and the condition in
 * one, at two classes, which is the specificity this file requires of anything that
 * competes with core's flow margins.
 */
.docs-index__groups + .docs-index__nearby {
	margin-block-start: var(--wp--preset--spacing--50);
}

/*
 * The way out of the rail, and the only place the size of the Help Center is now
 * stated. It reads as a footer to the rail rather than as a row in it: the rule
 * above it is the same hairline the group headings sit under, and it is the one
 * link here that is not a document.
 */
.docs-index .docs-index__more {
	display: block;
	margin-block-start: var(--wp--preset--spacing--40);
	padding-block-start: var(--wp--preset--spacing--30);
	border-top: 1px solid var(--wp--preset--color--border-subtle, #e0e0e0);
	font-size: 0.84375rem;
	font-weight: 500;
	color: var(--wp--preset--color--accent, #1473e6);
	text-decoration: none;
}

/* theme.json underlines every hovered anchor at 0,2,0 via `:root :where(a…):hover`.
   Three classes clears it without touching the colour, which stays the accent. */
.docs-index .docs-index__nav .docs-index__more:hover {
	text-decoration: underline;
}

.docs-index .docs-index__more:focus-visible {
	outline: 2px solid var(--wp--preset--color--accent, #1473e6);
	outline-offset: 2px;
}

.docs-index .docs-index__disclosure {
	border-top: 1px solid var(--wp--preset--color--border-subtle, #e0e0e0);
}

/*
 * `display: flex` replaces the disclosure triangle with the block's own chevron;
 * the marker resets below are what stop both from being drawn.
 */
.docs-index .docs-index__summary {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 12px 0;
	list-style: none;
	color: var(--wp--preset--color--primary, #000);
	font-size: 13.5px;
	font-weight: 600;
	line-height: 1.45;
	cursor: pointer;
}

.docs-index .docs-index__summary::-webkit-details-marker {
	display: none;
}

.docs-index .docs-index__summary:focus-visible {
	outline: 2px solid var(--wp--preset--color--accent, #1473e6);
	outline-offset: 2px;
}

.docs-index .docs-index__chev {
	width: 7px;
	height: 7px;
	margin-inline-start: auto;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: rotate(45deg);
	transition: transform 0.15s ease;
}

.docs-index .docs-index__disclosure[open] .docs-index__chev {
	transform: rotate(-135deg);
}

.docs-index .docs-index__nav {
	padding-block-end: var(--wp--preset--spacing--40);
}

/* ------------------------------------------------------- The article layout -- */

/*
 * Core's flow layout emits its vertical rhythm as
 * `:root :where(.is-layout-flow) > * { margin-block-start: 24px }`. Inside the grid
 * below that becomes a phantom gap on the rail, which is placed in its own column
 * and has nothing to be rhythmic against — it put the rail 24px lower than the
 * article, which is the one thing this layout exists to prevent.
 *
 * That selector is 0,1,0, NOT 0,0,0. The `:where()` contributes nothing, but the
 * leading `:root` is a pseudo-class and contributes a full class on its own, and
 * WordPress 7.0 prefixes its layout rules that way. A single-class reset only ties
 * with it, core prints last, and core wins the tie — measured on staging. Two
 * classes settles it, and `.wp-block-group.doc-layout` is honest about what the
 * element actually is rather than doubling a class to buy weight. Still no
 * `!important`.
 */
.wp-block-group.doc-layout > * {
	margin-block-start: 0;
}

/*
 * `main` is a constrained group, which hands `.alignwide` children theme.json's
 * 1420px wide-size. The article column is 696px. One wide image or wide group in a
 * doc would overflow its own column by more than the column's width, so wide
 * alignment is clamped to the column — but ONLY inside `.doc-layout`, so every
 * other template keeps real wide alignment.
 *
 * Two classes plus a type, deliberately. Core states this two ways: the global
 * `:where(.wp-block-group.is-layout-constrained) > .alignwide`, which is only 0,1,0
 * because the `:where()` scores nothing and just the trailing `.alignwide` counts,
 * and the per-block `.wp-container-… > .alignwide` it serialises whenever the group
 * carries layout values of its own, which is 0,2,0. `.wp-block-group.doc-layout`
 * puts this at 0,3,1 and clears both.
 */
.wp-block-group.doc-layout main .alignwide,
.wp-block-group.doc-layout main .alignfull {
	max-width: 100%;
}

/*
 * Media, tables and preformatted blocks clamped to the column.
 *
 * NOT my mess and not a new rule of the theme's: the theme constrains images
 * nowhere — no `styles.elements.img` in theme.json, no `img { max-width: 100% }` in
 * any stylesheet — and these documents carry raw `<img>` rather than `wp:image`, so
 * core's `.wp-block-image img` never reaches them. Eight images on a single doc
 * overflow, the worst by 711px. A 696px column merely hid it less badly than a
 * 632px one does.
 *
 * Scoped to `.doc-layout` on purpose. The unconstrained `<img>` is a real theme-wide
 * defect, but fixing it site-wide would touch the homepage, the product gallery and
 * hero-floating.css, and that deserves its own pass with its own verification.
 *
 * `height: auto` is not optional: these images carry `width`/`height` attributes, so
 * clamping the width alone squashes every screenshot on the page. `table` and `pre`
 * are in the list because this is a corpus of error messages and file paths, and
 * they run past the column exactly the same way.
 */
.wp-block-group.doc-layout main :is(img, video, iframe, table, pre) {
	max-width: 100%;
}

.wp-block-group.doc-layout main img {
	height: auto;
}

/*
 * 1040px is the ONE place this breakpoint is written down. view.js derives it by
 * testing whether `.docs-index__summary` is visible (`offsetParent !== null`), so
 * the summary must genuinely be `display: none` above this width and genuinely
 * rendered below it — do not swap either rule for `visibility` or a clip.
 *
 * The rail's sticky scroll box lives in here too: below 1040px the index is a
 * full-width section under the article, and a nested scroller with its own
 * `max-height` there would trap a phone's scroll inside 27 rows.
 */
@media (min-width: 1040px) {
	/*
	 * The cap has to describe the CONTENT box, not the border box. 980px is
	 * 236 + 48 + 696, but this element also carries 32px of gutter padding a side,
	 * and a plain `max-width: 980px` lets that padding eat the article column —
	 * measured 632px on staging where 696px was designed. Adding the gutter back
	 * lands `main` on exactly 696px, which is also `--wp--style--global--content-size`,
	 * so core's constrained layout INSIDE `main` and this grid track finally agree
	 * instead of disagreeing by 64px.
	 *
	 * At 1040px the gutter is still short of 32px, so the column is a few px under
	 * 696 there. The media clamp below is what makes that harmless.
	 */
	.wp-block-group.doc-layout {
		display: grid;
		grid-template-columns: 236px 1fr;
		gap: 48px;
		align-items: start;
		max-width: calc(980px + var(--wp--preset--spacing--gutter) * 2);
		margin-inline: auto;
	}

	/*
	 * The index is AFTER `<main>` in the source and is pulled left by explicit
	 * placement. That way a phone reads article, then the neighbourhood, then the
	 * rest — the order a reader wants — with no markup duplicated to get it.
	 */
	.wp-block-group.doc-layout > main {
		grid-column: 2;
		grid-row: 1;
	}

	.wp-block-group.doc-layout > .docs-index {
		grid-column: 1;
		grid-row: 1;
	}

	/*
	 * A grid item's `min-width` defaults to `auto`, which floors at its content's
	 * minimum size — so one unbreakable string pushes its track wider than the
	 * track's own definition. In a Help Center that string is not hypothetical: it
	 * is a file path, a registry key, a `<pre>` line or an error message. Both
	 * children need the guard, or a doc widens the grid past 980px and takes the
	 * rail with it.
	 */
	.wp-block-group.doc-layout > main,
	.wp-block-group.doc-layout > .docs-index {
		min-width: 0;
	}

	/*
	 * 27 rows outrun a short viewport, and a sticky rail that cannot scroll hides
	 * its own last group.
	 */
	.wp-block-sparklestock-docs-index.docs-index--rail {
		position: sticky;
		top: 16px;
		max-height: calc(100vh - 32px);
		overflow-y: auto;
		overscroll-behavior: contain;
	}

	/* Sticky INSIDE that scroll box, and opaque, so rows do not run under the
	   field. The ground here is the page background, not white. */
	.docs-index.docs-index--rail .docs-index__filter {
		position: sticky;
		top: 0;
		z-index: var(--wp--custom--z-index--raised, 1);
		background: var(--wp--preset--color--page, #f9fafb);
	}

	/*
	 * The summary is the phone affordance, and hiding it here is not decoration:
	 * view.js reads this breakpoint by testing whether the element is visible
	 * (`offsetParent !== null`), so this must stay a genuine `display: none` and
	 * must not become `visibility` or a clip.
	 *
	 * The neighbourhood used to be hidden alongside it, on the grounds that it was a
	 * subset of a full index sitting right there in the column. The column no longer
	 * holds the full index — it holds one group — so on desktop the neighbourhood is
	 * the only thing offering the rest of the category, and it stays.
	 */
	.docs-index.docs-index--rail .docs-index__summary {
		display: none;
	}

	.docs-index.docs-index--rail .docs-index__disclosure {
		border-top: 0;
	}
}

@media (prefers-reduced-motion: reduce) {
	.docs-index .docs-index__count,
	.docs-index .docs-index__n,
	.docs-index .docs-index__chev {
		transition: none;
	}
}
