/**
 * TiorAI section nav — the canonical shared in-page section navigation.
 *
 * Companion to template-parts/shared/section-nav.php and
 * assets/js/section-nav.js.
 *
 * THE ARGUMENT
 * -----------------------------------------------------------------------------
 * This is navigation utility, not another band. The single prompt page already
 * has a stated hierarchy — the sheet is the one raised object, and guide and
 * related are "flat bands, sectioned by whitespace not by boxes"
 * (assets/css/prompt-single.css) — so a sticky bar with a surface, a border, a
 * radius and a shadow would insert a fifth competing object above the one thing
 * the visitor came for.
 *
 * So it is drawn with three marks and no more:
 *
 *   1. the page's own background, so the bar is a plane and never a panel;
 *   2. the labels, centred on the page measure, with the current one in the
 *      brand colour and a short 2px underline inside the row;
 *   3. one neutral hairline at the foot, and a 2px brand progress line at the
 *      top edge that exists only once the reader has scrolled.
 *
 * Total height 45px: 44 (the touch target) + 1. Deterministic rather than
 * content-derived, which is what lets `scroll-margin-top` be a static calc()
 * instead of a number JavaScript has to keep rewriting.
 *
 * NO BRAND COLOUR UNTIL THE READER MOVES
 * -----------------------------------------------------------------------------
 * The progress line is at the TOP of the bar, and it used to be drawn on a
 * brand-tinted track — `rgba(--color-primary-rgb, 0.12)` — so that it stayed
 * legible under the header's own 1px border rather than compositing with it into
 * one grey band. The cost was that the bar showed a lime line at 0%: the
 * indicator was at its loudest exactly when it had nothing to report, and a
 * reader who had not moved yet saw brand colour anyway.
 *
 * The track is now transparent and the FILL is the whole indicator. Nothing of
 * it exists at the top of the page; the lime grows out of the left edge as the
 * page scrolls. The neutral hairline at the foot is the bar's only permanent
 * mark.
 *
 * The active item is marked inside the row rather than on that hairline, and
 * putting brand ink on a plain surface rather than on a chip is what makes
 * brand-coloured labels possible, which is the point:
 *
 *   brand ink on the brand-tinted chip   4.26:1   FAILS AA
 *   brand ink on the plain bar           4.97:1   passes (12.64:1 in dark)
 *
 * So the chip was the thing preventing `--color-primary` from being used as the
 * active colour at all. Removing it is not a simplification made for its own
 * sake; it is what the contrast measurement required.
 *
 * COLOUR is never the only channel. The underline carries the active state on
 * its own — shape, not hue — so the bar still works in forced-colors and for a
 * visitor who cannot separate lime from slate.
 *
 * Tokens throughout, `--tiorai-` prefixed per CLAUDE.md 9.1. Dark mode is a
 * three-line override, not a second stylesheet, because every value below is a
 * theme token.
 *
 * @package TiorAI
 */

/* ==========================================================================
   Global offsets
   --------------------------------------------------------------------------
   `--tiorai-header-height` is DECLARED here for the first time, and that is a
   repair rather than a new token.

   Two child stylesheets already consume it — assets/css/archive-shell.css:345
   and assets/css/top-100.css:218, both `var(--tiorai-header-height, 65px)` —
   and assets/css/top-100.css says so in a comment: "`--tiorai-header-height` is
   not declared in...". It never was. Both have been running on the fallback.

   65px is measured, not assumed: modules/header/assets/css/header.css sets
   `.header__container { height: 4rem }` with no height override at any
   breakpoint, plus a 1px `border-bottom` on `.header`. Verified at 1440, 768
   and 375 — 65 at every width.

   `--tiorai-secnav-top` is the pinned offset, and it is declared as a token
   rather than written as `top: 65px` for one reason: assets/js/section-nav.js
   MEASURES the header's resting position at runtime and overwrites this
   property on :root. So the admin bar, a future header of a different height,
   and a header that stops being sticky are all handled by the same declaration
   instead of by a stack of media queries that each guess. The value below is
   the no-JS floor, correct for the logged-out case that is 100% of indexed
   traffic.
   ========================================================================== */

:root {
	--tiorai-header-height: 65px;
	--tiorai-secnav-top: var(--tiorai-header-height, 65px);

	/*
	 * The bar's own height lives on :root, NOT on `.tr-section-nav`, and that is
	 * a bug fix rather than a preference.
	 *
	 * `scroll-margin-top` is applied to `[data-tiorai-section]` — the four or
	 * five page regions the strip points at — and NONE of them is a descendant
	 * of the bar. A custom property declared on the component is not in their
	 * inheritance chain, so `var(--tiorai-secnav-h, 2.875rem)` resolved to the
	 * FALLBACK on every one of them.
	 *
	 * That went unnoticed while the fallback happened to equal the real height.
	 * Moving the progress line to the top of the bar added the 1px rule at the
	 * foot, the bar became 47px, the fallback still said 46px, and every anchor
	 * started landing a pixel high. Declared here, the sum is the same value the
	 * bar is drawn from and the two cannot diverge again.
	 *
	 * 44 + 1: the row (the touch target) and the rule. The progress line no
	 * longer adds to it — it is drawn ON the rule rather than above the row, so
	 * it is out of flow and contributes no height. See "The progress line" below.
	 */
	--tiorai-secnav-row: 2.75rem;
	--tiorai-secnav-track: 2px;
	--tiorai-secnav-rule: 1px;
	--tiorai-secnav-h: calc(
		var(--tiorai-secnav-row) + var(--tiorai-secnav-rule)
	);

	/* Breathing room between the pinned bar and a heading it scrolled to. Not
	   decoration — at 0 the heading's cap-height touches the bar's rule. */
	--tiorai-secnav-anchor-gap: var(--space-5, 1.25rem);
}

/* ==========================================================================
   Component tokens
   ========================================================================== */

.tr-section-nav {
	/* The row, the track and the rule are declared on :root — see the block
	   above for why the anchor targets could not otherwise reach them. Only the
	   bar's own visual tokens are declared here. */

	/* The active indicator and the fade that brings it in. 220ms is long enough
	   to read as a transition and short enough not to lag a scroll that crosses
	   two boundaries quickly. */
	--tiorai-secnav-fade: 220ms;

	/* Measure and gutter are the CONSUMER's, inherited rather than restated, so
	   the strip lines up with whatever container the page already uses. On the
	   prompt page `.tr-promptpage` declares both, and they are the archive's
	   values, so this bar shares an edge with the breadcrumb above it and the
	   related grid below it. The literals are only reached when a consumer
	   declares neither. */
	--tiorai-secnav-measure: var(--tiorai-archive-measure, 1280px);
	--tiorai-secnav-gutter: var(--tiorai-archive-gutter, clamp(1rem, 4vw, 2rem));

	/* The bar's surface is the PAGE's, by the same token the page paints itself
	   with — `.tr-promptpage` uses `background: var(--color-bg-primary, #fff)`.
	   Using `--surface-base` instead would look right in light (both #ffffff)
	   and wrong in dark: `--surface-base` is #070B12 against the article's
	   #0B0F17, so the unpinned bar would be a darker band across the page.
	   Same token in, no seam out. */
	--tiorai-secnav-surface: var(--color-bg-primary, #fff);

	/* Ten under `--z-sticky` (200), which is the site header's. The header must
	   win any overlap: at 320px with a long label set, this bar and the header
	   are the two sticky things on screen and only one of them can be on top. */
	--tiorai-secnav-z: 190;

	/* Breathing room between the pinned bar and a heading it scrolled to. Not
	   decoration — at 0 the heading's cap-height touches the progress line. */
	--tiorai-secnav-anchor-gap: var(--space-5, 1.25rem);

	--tiorai-secnav-ease: var(--tiorai-archive-ease, cubic-bezier(0.22, 1, 0.36, 1));

	position: sticky;
	top: var(--tiorai-secnav-top, 65px);
	z-index: var(--tiorai-secnav-z);

	/* Full-bleed. The STRIP inside is what respects the page measure; the
	   surface and the progress line run edge to edge, because a reading-progress
	   line that stops 80px short of the viewport edge reads as a component with
	   a bug rather than as a page-level indicator. */
	background: var(--tiorai-secnav-surface);

	/*
	 * The bar's one horizontal edge — a neutral hairline, always drawn.
	 *
	 * It is also the track the progress line runs on: the fill is laid OVER this
	 * border rather than given a tinted rail of its own, so at rest there is no
	 * brand colour anywhere on the bar and the reader sees one ordinary rule. The
	 * lime appears only as they scroll, growing along this same edge.
	 */
	border-bottom: var(--tiorai-secnav-rule) solid var(--border-default);

	/*
	 * The sticky RANGE is its parent's box, and on the prompt page the parent is
	 * `<article class="tr-promptpage">` — deliberately, and it is the whole
	 * reason no JavaScript unsticks this bar at the footer.
	 *
	 * Measured: the article runs 65 -> 2289 and `.site-footer` begins at 2289.
	 * So the bar pins when the intro scrolls past and releases at exactly the
	 * pixel the footer starts, with no scroll listener, no IntersectionObserver
	 * sentinel and no `is-pinned` class to get out of sync. The brief's "the
	 * footer must not be part of this" is a DOM placement decision, not a
	 * runtime one.
	 */
}

/* ==========================================================================
   The strip
   ========================================================================== */

.tr-section-nav__strip {
	max-width: var(--tiorai-secnav-measure);
	margin-inline: auto;
	padding-inline: var(--tiorai-secnav-gutter);

	/* Horizontal overflow rather than shrinking labels. "How to use" at 12px is
	   not a smaller version of "How to use" at 14px, it is a worse one. */
	overflow-x: auto;
	overflow-y: hidden;
	overscroll-behavior-x: contain;

	/* The scrollbar is chrome on a 44px strip. The strip stays keyboard- and
	   wheel-scrollable without it, and `ensureVisible()` in the script keeps the
	   active chip on screen, so nothing depends on the visitor seeing a bar. */
	scrollbar-width: none;
	-ms-overflow-style: none;
}

.tr-section-nav__strip::-webkit-scrollbar {
	display: none;
}

/*
 * The edge fade, applied only when the strip actually overflows.
 *
 * `is-overflow-start` / `is-overflow-end` are set by the script from the
 * strip's own scroll position, so the fade is on the side that has more content
 * and never on a side that does not. A permanent both-sides mask would dim the
 * first and last chip on a desktop strip that fits, which is a fade advertising
 * scroll that is not there.
 */
.tr-section-nav__strip.is-overflow-end {
	mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent 100%);
}

.tr-section-nav__strip.is-overflow-start {
	mask-image: linear-gradient(to right, transparent 0, #000 2.5rem);
}

.tr-section-nav__strip.is-overflow-start.is-overflow-end {
	mask-image: linear-gradient(to right, transparent 0, #000 2.5rem, #000 calc(100% - 2.5rem), transparent 100%);
}

.tr-section-nav__items {
	display: flex;
	align-items: stretch;

	/*
	 * Centred.
	 *
	 * `min-width: 100%` is what makes this work rather than `justify-content`
	 * alone: the row is `max-content` wide, so without the minimum it would
	 * shrink-wrap the links and there would be no free space for `center` to
	 * distribute. With it, the row is at least as wide as the strip on desktop
	 * and the items sit in the middle of the page measure.
	 *
	 * When the labels overflow — 320 and 375, measured — `max-content` exceeds
	 * `min-width` and wins, so the row goes back to its natural width and
	 * `center` has no free space to act on. The strip scrolls from its start,
	 * which is the correct behaviour: a centred row that is wider than its
	 * container would open scrolled to the middle with the first item already
	 * out of view.
	 */
	justify-content: center;

	/* No `gap`. The separation is the link's own `padding-inline`, so the space
	   between two labels is still inside a target. See `__link`. */
	gap: 0;
	height: var(--tiorai-secnav-row);
	margin: 0;
	padding: 0;
	list-style: none;

	/* The row must not compress its items to fit; overflow is the designed
	   outcome. */
	width: max-content;
	min-width: 100%;
}

.tr-section-nav__item {
	display: flex;
	margin: 0;
}

/* ==========================================================================
   The link
   ========================================================================== */

/*
 * The anchor is the full 44px row, so the touch target is the row rather than
 * the text. The label span carries the type; the anchor carries the underline.
 */
.tr-section-nav__link {
	position: relative;
	display: inline-flex;
	align-items: center;
	height: 100%;

	/* The separation between labels, expressed as the target's own padding rather
	   than as `gap` on the list, so the dead space between two labels still
	   belongs to one of them and a thumb landing there hits a link. */
	padding-inline: var(--space-3, 0.75rem);

	text-decoration: none;
	color: var(--color-text-secondary);
	transition: color var(--tiorai-secnav-fade) var(--tiorai-secnav-ease);
}

.tr-section-nav__label {
	display: inline-flex;
	align-items: center;
	font-size: var(--font-size-sm, 0.875rem);

	/*
	 * ONE weight, in every state, and that is a layout decision rather than a
	 * typographic one.
	 *
	 * Stepping the active item to 600 is the obvious move and it is wrong here:
	 * the row is `max-content` wide, so a label that gets wider re-lays the whole
	 * row out and every label to its right shifts sideways — while the visitor is
	 * scrolling, five times per page. Worse now the row is CENTRED, because a
	 * width change there moves the items to the LEFT of it as well.
	 *
	 * The underline below is the non-colour channel instead. It is drawn on a
	 * pseudo-element and animated with `transform`, so it has no effect on layout
	 * at all.
	 */
	font-weight: 500;
	line-height: 1;
	white-space: nowrap;
}

/*
 * THE ACTIVE UNDERLINE, and the fade.
 *
 * It sits ON the bar's 1px bottom rule — a 2px brand segment laid over the
 * hairline rather than a mark floating inside the row — so the bar still reads
 * as having exactly one horizontal edge at its foot, with the current label's
 * share of that edge lit.
 *
 * The position is available because the progress line is at the TOP of the bar.
 * With both at the foot they would be the same 2px of lime on the same pixel
 * row, and wherever the fill had already passed the active item its mark would
 * disappear into it.
 *
 * Fades in by `opacity` and grows by `scaleX` from 40%. Both are
 * compositor-only, so the transition triggers no layout and no paint.
 * `transform-origin: center` opens it outward from the middle of the label,
 * which reads as the indicator ARRIVING at that item rather than sliding in
 * from whichever side the page happens to be scrolling.
 */
.tr-section-nav__link::after {
	content: "";
	position: absolute;
	inset-inline: var(--space-3, 0.75rem);
	bottom: calc(-1 * var(--tiorai-secnav-rule));
	height: 2px;
	border-radius: 2px;
	background: var(--color-primary);

	opacity: 0;
	transform: scaleX(0.4);
	transform-origin: center;
	transition:
		opacity var(--tiorai-secnav-fade) var(--tiorai-secnav-ease),
		transform var(--tiorai-secnav-fade) var(--tiorai-secnav-ease);
}

.tr-section-nav__link:hover {
	color: var(--color-text-primary);
}

/*
 * Hover previews the indicator at a third of its strength, on the same element
 * and the same transition — so running the pointer along the row reads as one
 * continuous fade rather than five separate marks appearing and vanishing.
 */
.tr-section-nav__link:hover::after {
	opacity: 0.3;
	transform: scaleX(0.75);
}

/*
 * Active — the brand colour, on the label itself.
 *
 * `--color-primary` is theme-paired (#5A7A10 light, #B4E227 dark) and this is
 * exactly the role CLAUDE.md records it carrying: a foreground as well as a
 * fill. MEASURED on this bar: **4.97:1 in light, 12.64:1 in dark**. Both clear
 * AA for 14px text.
 *
 * It only clears it because the label sits on the PLAIN bar surface. The
 * previous revision put brand ink on a brand-tinted chip, and that composite
 * measures **4.26:1 — a fail**. Removing the chip is what makes brand ink
 * legible here, so the two are a single decision.
 *
 * Colour is never the only channel: the underline carries the state on its own,
 * which is what keeps this readable in forced-colors and for a visitor who
 * cannot separate lime from slate.
 */
.tr-section-nav__link[aria-current] {
	color: var(--color-primary);
}

.tr-section-nav__link[aria-current]::after,
.tr-section-nav__link[aria-current]:hover::after {
	opacity: 1;
	transform: scaleX(1);
}

/*
 * The ring traces the LABEL rather than the 44px row: an outline around the
 * full row height would collide with the progress line above it and the
 * underline below it, and read as a third rule on a bar that has one. The
 * anchor's own outline is suppressed only because a clearly visible
 * replacement is drawn one element down.
 */
.tr-section-nav__link:focus-visible {
	outline: none;
}

.tr-section-nav__link:focus-visible .tr-section-nav__label {
	outline: var(--tiorai-focus-ring-width, 2px) solid var(--tiorai-focus-ring-color);
	outline-offset: 4px;
	border-radius: var(--radius-sm, 0.25rem);
}

/* ==========================================================================
   The progress line — the TOP edge of the bar, and OUT OF FLOW
   --------------------------------------------------------------------------
   Back at the top, where the reading position belongs: it is the first thing
   under the site header, so the page's own progress reads as the header's line
   rather than as a mark inside a strip of links.

   Two things about it are NOT what they were before, and both matter:

   1. **No track.** It used to sit on `rgba(--color-primary-rgb, 0.12)`, so the
      bar showed a lime line at 0% — the indicator was at its loudest exactly
      when it had nothing to report. The track is now transparent, which means
      there is no brand colour anywhere on this component until the reader
      actually scrolls, and the fill is the only thing that ever appears.
   2. **Absolutely positioned**, so it adds no height. The bar is 44 + 1, and
      `--tiorai-secnav-h` — which feeds every anchor's `scroll-margin-top` — is
      that sum. A 2px line in flow would push the whole strip down and put every
      anchor landing two pixels out.

   First in the DOM still, because the reading order of a decorative element is
   irrelevant and moving it visually costs nothing.
   ========================================================================== */

.tr-section-nav__progress {
	position: absolute;
	inset-inline: 0;
	top: 0;
	height: var(--tiorai-secnav-track);
	background: transparent;
	pointer-events: none;
}

.tr-section-nav__progress-fill {
	display: block;
	height: 100%;
	background: var(--color-primary);

	/*
	 * `transform: scaleX()`, never `width`.
	 *
	 * Width is a layout property: animating it from a scroll handler runs layout
	 * and paint on the whole bar every frame. A transform is composited, so the
	 * per-frame cost is a matrix update on one element and the main thread does
	 * no work at all.
	 *
	 * No `transition`. The fill tracks the scroll position directly, so easing
	 * it would make the line lag the page it is reporting on — the one place
	 * where a smooth animation is less honest than an instant one.
	 */
	transform-origin: left center;
	transform: scaleX(var(--tiorai-secnav-progress, 0));
	will-change: transform;
}

[dir="rtl"] .tr-section-nav__progress-fill {
	transform-origin: right center;
}

/* ==========================================================================
   Anchor targets
   --------------------------------------------------------------------------
   The consumer marks each destination with `data-tiorai-section`, and this one
   rule is the entire scroll-offset solution: it covers the click, the keyboard
   activation, the pasted deep link and the reload-with-hash, in every browser,
   with no script running.

   The brief's "do not implement fragile JavaScript pixel subtraction if CSS can
   handle the offset more robustly" is exactly this rule. `scroll-margin-top` is
   applied by the browser at scroll time, so it is correct even when the page
   height changes between load and click — which JavaScript that computed an
   offset once would not be.

   Deliberately NOT reusing `--tiorai-anchor-offset` (assets/css/unified/toc.css,
   96px). That token means "clear the header", and it is consumed by five other
   templates that have no second sticky bar. A page carrying this component
   needs to clear the header AND 46px more, so overwriting the shared token here
   would move the anchor landing point on blog, resource, collection and
   comparison pages that never render this nav.
   ========================================================================== */

[data-tiorai-section] {
	scroll-margin-top: calc(
		var(--tiorai-secnav-top, 65px) +
		var(--tiorai-secnav-h, 2.875rem) +
		var(--tiorai-secnav-anchor-gap, 1.25rem)
	);
}

/*
 * SMOOTH SCROLLING AND REDUCED MOTION ARE NOT DECLARED HERE, deliberately.
 *
 * Both are already owned by the parent theme, and a copy in this file would be
 * a second declaration of a decision that is already made:
 *
 *   assets/css/theme-unified.css:1272   html { scroll-behavior: smooth }
 *   assets/css/theme-unified.css:1281   @media (prefers-reduced-motion: reduce)
 *                                       html { scroll-behavior: auto }
 *   assets/css/theme-unified.css:1190   @media (prefers-reduced-motion: reduce)
 *                                       *, *::before, *::after {
 *                                         transition-duration: 0.01ms !important;
 *                                         scroll-behavior: auto !important; }
 *
 * The `!important` global reset means a `transition: none` block here could not
 * change the outcome even if it disagreed, and an `html:has(.tr-section-nav) {
 * scroll-behavior: smooth }` rule would re-state a property that is already
 * smooth on every page. Verified at runtime rather than read off: the anchor
 * glides with motion allowed and jumps with `prefers-reduced-motion: reduce`,
 * and the label's computed `transition-duration` is 0.01ms under reduce — all
 * from the parent, with nothing declared in this file.
 *
 * What this component DOES owe reduced motion is the one animation the parent
 * cannot see, because it is issued from script rather than from CSS: the
 * horizontal nudge that keeps the current chip in view on a narrow strip. That
 * is handled in assets/js/section-nav.js, which reads the media query and
 * passes `behavior: 'auto'`.
 */

/* ==========================================================================
   Forced colors
   --------------------------------------------------------------------------
   In forced-colors the chip's tint is dropped by the UA, so the active item
   would fall back to the weight step alone. A border restores a second channel
   using a system colour, and the progress fill gets one too — without it the
   track and the fill both resolve to the same forced background and the line
   disappears entirely.
   ========================================================================== */

@media (forced-colors: active) {
	/* `background` on a pseudo-element is dropped by the UA, so the underline —
	   the only non-colour channel the active state has — would vanish. A system
	   colour restores it. */
	.tr-section-nav__link::after {
		background: Highlight;
		forced-color-adjust: none;
	}

	.tr-section-nav__link[aria-current] {
		color: Highlight;
	}

	/* The bar's edge is a border, which forced-colors keeps — so only the fill
	   needs a system colour to stay visible against it. The track is transparent
	   by design and stays that way. */
	.tr-section-nav__progress-fill {
		background: Highlight;
		forced-color-adjust: none;
	}
}

/* ==========================================================================
   Print
   --------------------------------------------------------------------------
   A sticky strip of in-page anchors is meaningless on paper, and a 2px accent
   rule across the top of the printed page is worse than meaningless.
   ========================================================================== */

@media print {
	.tr-section-nav {
		display: none;
	}
}
