/*
 * MF Reading Rail — in-margin reading-progress rail for single posts.
 *
 * Built against the field-test prototype in docs/12-headline-rail_2.html
 * ("Feldversuch Nº 02"). Proportions, spacing, the muted→signal state change and
 * the transition timings are taken from there; see docs/modules/reading-rail.md
 * for the two places this deliberately departs from it, both because the
 * prototype is a dark lab page and the blog is light.
 *
 * Phase 1 ships the "label" variant only. The prototype's rules for numbered /
 * dots / ticks land with their markup in Phase 2 — no dead CSS ahead of it.
 *
 * Two structural rules this file must keep:
 *
 * 1. The rail is `position: fixed` and never participates in document flow, so
 *    it can neither displace nor narrow the content column.
 * 2. Everything horizontal uses logical properties (inset-inline-*, padding-inline).
 *    RTL is out of scope for v1, but the positioning maths then needs no
 *    revisiting — see docs/PLAN-reading-rail.md 0.8.
 *
 * The JS owns four custom properties: --mfrr-inset and --mfrr-width place the
 * rail beside the column, --mfrr-progress (0..1) drives the fill, and per node
 * --mfrr-ratio (0..1) sets its vertical position.
 */

.mfrr {
	--mfrr-signal: #ffb13c;
	--mfrr-signal-cool: #4fd8c4;
	--mfrr-on-signal: #10131a;
	--mfrr-surface: var(--wp--preset--color--base, #fff);

	/*
	 * The single vertical axis everything lines up on: the track, and the centre of
	 * every mark whatever its size. Marks are absolutely positioned onto it rather
	 * than laid out in flow, because a dot's *rendered* width includes its border
	 * (box-sizing here is the page's, not ours) and a 12px dot with a 2px border
	 * would otherwise sit 2px off the line — and the 19px numbered dot 5.5px off.
	 */
	--mfrr-axis: 6px;
	--mfrr-line-width: 2px;
	--mfrr-dot-size: 12px;

	position: fixed;
	inset-block-start: calc(var(--mfrr-band-top, 0.16) * 100vh);
	inset-inline-start: var(--mfrr-inset, 0);
	z-index: 60;
	width: var(--mfrr-width, 0);
	height: calc(var(--mfrr-band-height, 0.7) * 100vh);

	/*
	 * How present the rail is, 0..1, written by the script from where the article
	 * sits: the rail is fixed, so without this it would hang over the post header
	 * above the article and over Next Read below it. It fades in and out with the
	 * article instead.
	 */
	opacity: var(--mfrr-reveal, 1);
	transition: opacity 250ms ease, visibility 250ms ease;

	/*
	 * The rail is a 220px-wide fixed box over the margin. Only its buttons may
	 * catch the pointer, or the empty band would swallow clicks and text
	 * selection beside the article.
	 */
	pointer-events: none;
}

/*
 * Hidden by the space gate. A class, not the `hidden` attribute, so the rail
 * fades the way the prototype does instead of vanishing between frames.
 */
.mfrr.is-hidden {
	opacity: 0;
	pointer-events: none;
}

/*
 * Fully faded out, because the reader is in the post header or already past the
 * article. `visibility: hidden` rather than opacity alone: it also takes the nodes
 * out of the tab order and the accessibility tree, so nobody tabs onto a button
 * they cannot see.
 */
.mfrr.is-away,
.mfrr-bar.is-away {
	opacity: 0;
	visibility: hidden;
}

.mfrr-track {
	position: relative;
	height: 100%;
}

/* The unfilled track, centred on the axis. */
.mfrr-track::before {
	content: "";
	position: absolute;
	inset-block: 0;
	inset-inline-start: calc(var(--mfrr-axis) - var(--mfrr-line-width) / 2);
	width: var(--mfrr-line-width);
	background: currentcolor;
	opacity: 0.14;
}

.mfrr-fill {
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: calc(var(--mfrr-axis) - var(--mfrr-line-width) / 2);
	width: var(--mfrr-line-width);
	height: 100%;
	background: linear-gradient(var(--mfrr-signal-cool), var(--mfrr-signal));
	transform: scaleY(var(--mfrr-progress, 0));
	transform-origin: top;
}

.mfrr-nodes {
	position: absolute;
	inset: 0;
	margin: 0;
	padding: 0;
	list-style: none;
}

.mfrr-node {
	position: absolute;
	inset-block-start: calc(var(--mfrr-ratio, 0) * 100%);
	inset-inline: 0;
	transform: translateY(-50%);
}

/*
 * The clickable node: a real <button>, so Enter and Space work natively and it is
 * announced as a button. Reset to look like the mark it wraps.
 */
.mfrr-hit {
	position: relative;
	display: block;
	width: 100%;

	/* Clear of the mark sitting on the axis, plus the gap to the text. */
	padding-block: 0;
	padding-inline: calc(var(--mfrr-axis) + var(--mfrr-dot-size) / 2 + 0.7rem) 0;
	margin: 0;
	border: 0;
	background: none;
	color: inherit;
	font: inherit;
	text-align: start;
	cursor: pointer;
	pointer-events: auto;
}

.mfrr-hit:hover .mfrr-dot,
.mfrr-hit:focus-visible .mfrr-dot {
	border-color: var(--mfrr-signal-cool);
	opacity: 1;
}

.mfrr-hit:hover .mfrr-label,
.mfrr-hit:focus-visible .mfrr-label {
	opacity: 1;
}

.mfrr-hit:hover .mfrr-tick,
.mfrr-hit:focus-visible .mfrr-tick {
	background: var(--mfrr-signal-cool);
	opacity: 1;
}

/*
 * The prototype removed the focus outline and signalled focus with a border
 * colour alone. Keeping a real ring: a 2px colour shift on a 12px dot is not a
 * dependable focus indicator, and this is the only keyboard affordance the rail
 * has.
 */
.mfrr-hit:focus-visible {
	outline: 2px solid var(--mfrr-signal);
	outline-offset: 3px;
	border-radius: 4px;
}

/*
 * The mark, centred on the axis in both directions. `box-sizing: border-box` makes
 * the declared size the rendered size, so the border cannot push the circle off
 * centre.
 */
.mfrr-dot {
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: var(--mfrr-axis);
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	width: var(--mfrr-dot-size);
	height: var(--mfrr-dot-size);
	border: 2px solid currentcolor;
	border-radius: 50%;
	background: var(--mfrr-surface);
	font-family: var(--mf-mono, ui-monospace, "SF Mono", menlo, consolas, monospace);
	font-size: 0.58rem;
	line-height: 1;
	opacity: 0.55;
	transform: translate(-50%, -50%);
	transition:
		background-color 250ms ease,
		border-color 250ms ease,
		box-shadow 250ms ease,
		opacity 250ms ease;
}

.mfrr-label {
	font-family: var(--mf-mono, ui-monospace, "SF Mono", menlo, consolas, monospace);
	font-size: 0.68rem;
	line-height: 1.25;
	opacity: 0.55;
	transition: opacity 250ms ease;

	/*
	 * A long heading wraps rather than being cut off with an ellipsis — the node
	 * spacing is computed from the *measured* height, so a two-line label pushes its
	 * neighbours apart instead of colliding with them. Three lines is the ceiling;
	 * past that the full text is still on the `title`.
	 */
	display: -webkit-box;
	overflow: hidden;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	overflow-wrap: break-word;
}

/*
 * h3 is a sub-step: its mark is smaller and sits one step in from the line, which
 * is the hierarchy cue. Shifting the axis moves the dot and the text together.
 */
.mfrr-node--h3 {
	--mfrr-axis: 20px;
	--mfrr-dot-size: 7px;
}

/*
 * Passed state. The dot takes the signal colour with the prototype's 4px halo.
 * The label only gains contrast — see the module doc for why it does not turn
 * teal the way the prototype's does.
 */
.mfrr-node.is-passed .mfrr-dot {
	border-color: var(--mfrr-signal);
	background: var(--mfrr-signal);
	box-shadow: 0 0 0 4px rgb(255 177 60 / 16%);
	color: var(--mfrr-on-signal);
	opacity: 1;
}

.mfrr-node.is-passed .mfrr-label {
	opacity: 1;
}

/* --- variant: numbered ------------------------------------------------------
 * A wider mark so a one- or two-digit section number fits inside it. The axis is
 * unchanged, so the bigger circle still sits centred on the line.
 */
.mfrr--numbered .mfrr-node--h2 {
	--mfrr-dot-size: 19px;
}

/* --- variant: ticks ---------------------------------------------------------
 * No dots at all: a perpendicular mark spanning the rail, h3 shorter and fainter.
 */
.mfrr--ticks .mfrr-hit {
	padding-inline: 0;
}

.mfrr-tick {
	display: block;
	width: 100%;
	height: 2px;
	border-radius: 2px;
	background: currentcolor;
	opacity: 0.55;
	transition: background-color 250ms ease, opacity 250ms ease;
}

.mfrr-node--h3 .mfrr-tick {
	width: 60%;
	opacity: 0.3;
}

.mfrr-node.is-passed .mfrr-tick {
	background: var(--mfrr-signal);
	opacity: 1;
}

/*
 * The tooltip on marks that show no text of their own. Positioned outside the rail
 * towards the text, since a 22px-wide rail has no room for it; the rail sets no
 * overflow, so it is free to extend past its box. Hidden with visibility rather
 * than display so it can fade.
 */
.mfrr-tip {
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: calc(var(--mfrr-axis) + var(--mfrr-dot-size) / 2 + 0.6rem);
	z-index: 1;

	/*
	 * `max-content` is load-bearing: the tip is absolutely positioned inside a rail
	 * that is only 22–46px wide in these variants, so without it the containing
	 * block squeezes the text into a sliver and it wraps after two or three
	 * characters. It floats over the start of the article while shown, which is what
	 * a tooltip does — it is transient and ignores the pointer.
	 */
	width: max-content;
	max-width: 28ch;
	padding: 0.3rem 0.5rem;
	border-radius: 4px;
	background: var(--mfrr-on-signal);
	color: var(--mfrr-surface);
	font-family: var(--mf-mono, ui-monospace, "SF Mono", menlo, consolas, monospace);
	font-size: 0.68rem;
	line-height: 1.3;
	text-align: start;
	opacity: 0;
	visibility: hidden;
	transform: translateY(-50%);
	transition: opacity 150ms ease, visibility 150ms ease;
	pointer-events: none;
}

.mfrr-hit:hover .mfrr-tip,
.mfrr-hit:focus-visible .mfrr-tip {
	opacity: 1;
	visibility: visible;
}

/*
 * The slim fallback bar, shown only when the rail does not fit and no other
 * module already owns a page-level progress bar (post-header's `sticky`
 * variant ships one — see docs/modules/reading-rail.md).
 */
.mfrr-bar {
	--mfrr-signal: #ffb13c;
	--mfrr-signal-cool: #4fd8c4;

	position: fixed;
	inset-block-start: 0;
	inset-inline: 0;
	z-index: 90;
	height: 3px;
	opacity: 0;
	transition: opacity 250ms ease, visibility 250ms ease;
	pointer-events: none;
}

/* Shown when the rail does not fit — and then only while the article is in view. */
.mfrr-bar.is-visible {
	opacity: var(--mfrr-reveal, 1);
}

.mfrr-bar__fill {
	width: 100%;
	height: 100%;
	background: linear-gradient(90deg, var(--mfrr-signal-cool), var(--mfrr-signal));

	/* Physical `left` on purpose: RTL is out of scope for v1 (audit 0.8). */
	transform: scaleX(var(--mfrr-progress, 0));
	transform-origin: left;
}

/*
 * Give the headings enough scroll-margin that a click lands them clear of a
 * sticky site header rather than under it. Scoped to .mfrr-has-rail, a class the
 * script adds to the content container only once a rail is actually built — so a
 * post below the heading threshold, or one where the rail bailed out, keeps the
 * theme's own scroll behaviour untouched.
 */
.mfrr-has-rail :is(h2, h3) {
	scroll-margin-top: var(--mfrr-scroll-margin, 5rem);
}

@media (prefers-reduced-motion: reduce) {

	.mfrr,
	.mfrr-fill,
	.mfrr-dot,
	.mfrr-label,
	.mfrr-tick,
	.mfrr-tip,
	.mfrr-bar,
	.mfrr-bar__fill {
		transition-duration: 1ms;
	}
}
