/*
	Timed ESAT module runner.

	Loaded by includes/lr-mock-test.php on the twenty compiled paper pages that
	carry a test, and nowhere else. It is its own file rather than a block in
	style.css for two reasons: it is needed on twenty pages out of a hundred and
	change, and keeping it out of the shared sheet means this feature never
	forces a cache-busting Version bump on every other page of the site.

	Everything is inside @layer components, the same layer the .lr-diagnostic
	block sits in, so the two runners cascade identically. style.css declares the
	layer order and loads first, so this file joins that layer rather than
	creating a new one after it. Nothing here is !important: the .lr-btn-* fills
	are, and this file has no business fighting them.

	Tokens come from style.css. Two do not exist there and are declared locally
	on the block: a correct/incorrect pair. They are scoped to .lr-mock-test so
	adding them cannot change a single pixel anywhere else on the site.
*/

@layer components {

	/* Put on the page's question index and solutions section while an attempt is
	   running. It has to be !important and it has to be a class: the runner used
	   the `hidden` attribute alone at first, and `.lr-card { display: flex }`
	   later in this same layer beat the UA rule, so the index stayed visible for
	   the whole timed test with its 27 links pointing into a section that had
	   correctly disappeared. Anything that hides a themed element has to outrank
	   the theme, not the browser. */
	.lr-mock-test-concealed {
		display: none !important;
	}

	.lr-mock-test {
		/* #1f7a3d measures 5.37:1 on white and 5.11:1 on the wash below, so it
		   carries the "correct" meaning as text rather than only as colour.
		   The incorrect pair is the site's existing danger token. */
		--lr-mock-correct: #1f7a3d;
		--lr-mock-correct-wash: #f1f8f3;
		--lr-mock-wrong: var(--lr-color-danger, #c63838);
		--lr-mock-wrong-wash: var(--lr-color-danger-wash, #fef2f2);

		max-width: var(--lr-max-width-centered, 900px);
		margin-inline: auto;
		margin-bottom: var(--lr-gap-card, 32px);
	}

	[data-theme="dark"] .lr-mock-test {
		/* Nothing sets data-theme yet; kept in step with the dark engine in
		   style.css so the block is not the one thing that breaks when it does. */
		--lr-mock-correct: #4ade80;
		--lr-mock-correct-wash: rgba(74, 222, 128, 0.12);
		--lr-mock-wrong-wash: rgba(198, 56, 56, 0.15);
	}

	/* ---------------------------------------------------------------- launch */

	/* The invitation. This is the first thing under the page heading, so it is
	   deliberately loud: accent rule down the leading edge, a heading in the
	   display face, and one primary action with nothing competing with it. */
	.lr-mock-test__launch {
		padding: var(--lr-padding-card, 32px);
		border: 1px solid var(--lr-color-border-card);
		border-left: 4px solid var(--lr-color-accent);
		border-radius: var(--lr-radius-large, 12px);
		background-color: var(--lr-color-bg-card);
	}

	.lr-mock-test__eyebrow {
		margin-top: 0;
	}

	.lr-mock-test__heading {
		font-family: var(--lr-font-heading);
		font-size: var(--lr-font-size-h3);
		line-height: var(--lr-line-height-heading);
		margin: var(--lr-gap-tight, 8px) 0 var(--lr-gap-small);
	}

	.lr-mock-test__lead {
		margin: 0;
		color: var(--lr-color-text-main);
		line-height: var(--lr-line-height-body);
	}

	.lr-mock-test__actions {
		margin-top: var(--lr-gap-medium);
		margin-bottom: 0;
		display: flex;
		flex-wrap: wrap;
		gap: var(--lr-gap-button);
	}

	/* Larger than a stock primary button, because it is the only thing on the
	   page asking to be pressed. Fill, label colour and radius are left to the
	   .lr-btn-primary primitive, which sets them !important. */
	.lr-mock-test__start {
		font-size: var(--lr-font-size-lead);
		padding: 16px 32px;
	}

	.lr-mock-test__facts {
		list-style: none;
		margin: var(--lr-gap-medium) 0 0;
		padding: 0;
		display: flex;
		flex-wrap: wrap;
		gap: var(--lr-gap-tight, 8px) var(--lr-gap-medium);
		font-size: var(--lr-font-size-small);
		color: var(--lr-color-text-muted);
	}

	.lr-mock-test__facts li {
		margin: 0;
		position: relative;
		padding-left: 1.1em;
	}

	.lr-mock-test__facts li::before {
		content: "";
		position: absolute;
		left: 0;
		top: 0.55em;
		width: 5px;
		height: 5px;
		border-radius: 50%;
		background-color: var(--lr-color-accent);
	}

	.lr-mock-test__resume,
	.lr-mock-test__noscript {
		margin: var(--lr-gap-medium) 0 0;
		font-size: var(--lr-font-size-small);
		color: var(--lr-color-text-muted);
	}

	.lr-mock-test__resume button + button {
		margin-left: var(--lr-gap-small);
	}

	/* ---------------------------------------------------------------- runner */

	.lr-mock-test__runner,
	.lr-mock-test__result {
		padding: var(--lr-padding-card, 32px);
		border: 1px solid var(--lr-color-border-card);
		border-radius: var(--lr-radius-large, 12px);
		background-color: var(--lr-color-bg-card);
	}

	.lr-mock-test__bar {
		display: flex;
		flex-wrap: wrap;
		justify-content: space-between;
		align-items: baseline;
		gap: var(--lr-gap-small);
		padding-bottom: var(--lr-gap-small);
		border-bottom: 1px solid var(--lr-color-border-subtle);
	}

	.lr-mock-test__progress {
		margin: 0;
		font-size: var(--lr-font-size-small);
		color: var(--lr-color-text-muted);
	}

	/* The clock is the loudest number on the screen while the test runs, and it
	   changes meaning twice: amber under five minutes, red under one. Colour is
	   not the only cue, because the digits themselves are the message. */
	.lr-mock-test__clock {
		margin: 0;
		font-family: var(--lr-font-mono);
		font-size: var(--lr-font-size-h4);
		letter-spacing: var(--lr-letter-spacing-mono);
		font-variant-numeric: tabular-nums;
		color: var(--lr-color-accent);
	}

	.lr-mock-test__clock.is-low {
		color: #8a5a00;
	}

	.lr-mock-test__clock.is-critical {
		color: var(--lr-mock-wrong);
		font-weight: 700;
	}

	/* ------------------------------------------------------------- navigator */

	/* Twenty-seven questions need a way to move that is not twenty-six presses of
	   Next, so the grid is the primary navigation and the Back/Next pair is the
	   fallback. 44px is the touch target, not a guess at one: on a 375px screen
	   the grid settles at six columns and the height is what is left to choose. */
	.lr-mock-test__navigator {
		display: grid;
		grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));
		gap: 6px;
		margin: var(--lr-gap-medium) 0;
	}

	.lr-mock-test__jump {
		appearance: none;
		min-height: 44px;
		padding: 0;
		font-family: var(--lr-font-mono);
		font-size: var(--lr-font-size-small);
		line-height: 1;
		color: var(--lr-color-text-muted);
		background-color: var(--lr-color-bg-card);
		border: 1px solid var(--lr-color-border-subtle);
		border-radius: var(--lr-radius-small, 4px);
		cursor: pointer;
		font-variant-numeric: tabular-nums;
		transition:
			border-color var(--lr-duration-fast, 120ms) var(--lr-easing-mechanical, linear),
			background-color var(--lr-duration-fast, 120ms) var(--lr-easing-standard, ease);
	}

	.lr-mock-test__jump:focus-visible {
		outline: 2px solid var(--lr-color-focus-ring);
		outline-offset: 2px;
	}

	.lr-mock-test__jump.is-answered {
		color: var(--lr-color-accent);
		border-color: var(--lr-color-accent);
		background-color: var(--lr-color-accent-wash);
	}

	/* A flag is a note to come back, so it reads as an edge marker rather than
	   as another state of the answer. Kept distinct from the answered fill so a
	   flagged-and-answered question shows both. */
	.lr-mock-test__jump.is-flagged {
		box-shadow: inset 0 -3px 0 0 #8a5a00;
	}

	/* Drawn as a ring rather than an outline so it cannot compete with the focus
	   ring. Both were outlines at equal specificity and this rule came second,
	   so the current question's button looked identical focused and unfocused,
	   which is the one button a keyboard user most needs to locate. */
	.lr-mock-test__jump.is-current {
		box-shadow: 0 0 0 2px var(--lr-color-accent);
		font-weight: 700;
	}

	/* box-shadow is one property, so the current-question ring would otherwise
	   erase the flag's edge marker on the question the candidate is looking at,
	   which is exactly when they need to see it. */
	.lr-mock-test__jump.is-flagged.is-current {
		box-shadow:
			inset 0 -3px 0 0 #8a5a00,
			0 0 0 2px var(--lr-color-accent);
	}

	.lr-mock-test__jump.is-correct {
		color: var(--lr-mock-correct);
		border-color: var(--lr-mock-correct);
		background-color: var(--lr-mock-correct-wash);
	}

	.lr-mock-test__jump.is-wrong {
		color: var(--lr-mock-wrong);
		border-color: var(--lr-mock-wrong);
		background-color: var(--lr-mock-wrong-wash);
	}

	/* -------------------------------------------------------------- question */

	.lr-mock-test__question:focus {
		outline: none;
	}

	.lr-mock-test__question:focus-visible {
		outline: 2px solid var(--lr-color-focus-ring);
		outline-offset: 4px;
	}

	.lr-mock-test__fieldset {
		border: 0;
		margin: 0;
		padding: 0;
	}

	.lr-mock-test__legend {
		display: block;
		width: 100%;
		padding: 0;
	}

	.lr-mock-test__prompt {
		display: block;
		margin-top: var(--lr-gap-small);
		font-size: var(--lr-font-size-lead);
		line-height: var(--lr-line-height-body);
		color: var(--lr-color-text-main);
	}

	/* A wide equation scrolls inside its own row rather than pushing the page
	   sideways. Same reasoning as the solution pages these prompts come from.

	   overflow-y is set explicitly, and that is not tidiness. Per the CSS
	   overflow spec, `visible` on one axis computes to `auto` when the other
	   axis is not visible, so `overflow-x: auto` alone silently makes the box
	   scroll vertically too. A MathJax container rounds about a pixel taller
	   than its line box, which was enough: every single option row drew a
	   full-height vertical scrollbar and lost 15px of width to it. The 2px of
	   block padding gives that rounding somewhere to go, so `hidden` never
	   actually clips anything. */
	.lr-mock-test__prompt,
	.lr-mock-test__answer {
		overflow-x: auto;
		overflow-y: hidden;
		padding-block: 2px;
		max-width: 100%;
		-webkit-overflow-scrolling: touch;
	}

	.lr-mock-test__figure {
		margin: var(--lr-gap-medium) 0 0;
		text-align: center;
	}

	.lr-mock-test__figure img {
		max-width: 100%;
		height: auto;
		border: 1px solid var(--lr-color-border-subtle);
		border-radius: var(--lr-radius-medium, 8px);
	}

	.lr-mock-test__options {
		list-style: none;
		margin: var(--lr-gap-medium) 0 0;
		padding: 0;
		display: grid;
		gap: var(--lr-gap-small);
	}

	.lr-mock-test__option {
		margin: 0;
	}

	/* The radio is visually replaced by the lettered badge but stays in the
	   accessibility tree and the tab order; :focus-visible on it paints the ring
	   on the label, so keyboard position is never invisible. */
	.lr-mock-test__option input[type="radio"] {
		position: absolute;
		width: 1px;
		height: 1px;
		padding: 0;
		margin: -1px;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
		border: 0;
	}

	.lr-mock-test__option label {
		display: flex;
		align-items: flex-start;
		gap: var(--lr-gap-small);
		padding: 14px 18px;
		border: 1px solid var(--lr-color-border-card);
		border-radius: var(--lr-radius-medium, 8px);
		background-color: var(--lr-color-bg-card);
		cursor: pointer;
		transition:
			border-color var(--lr-duration-moderate, 200ms) var(--lr-easing-mechanical, linear),
			background-color var(--lr-duration-moderate, 200ms) var(--lr-easing-standard, ease);
	}

	@media (hover: hover) and (pointer: fine) {
		.lr-mock-test__option label:hover {
			border-color: rgba(var(--lr-color-accent-rgb), 0.3);
		}
	}

	.lr-mock-test__option input[type="radio"]:focus-visible + label {
		outline: 2px solid var(--lr-color-focus-ring);
		outline-offset: 2px;
	}

	.lr-mock-test__option input[type="radio"]:checked + label {
		border-color: var(--lr-color-accent);
		background-color: var(--lr-color-accent-wash);
	}

	/* A disabled fieldset is the review pass, not a broken form: keep the
	   options fully legible rather than letting the browser grey them out. */
	.lr-mock-test__fieldset[disabled] label {
		cursor: default;
		opacity: 1;
	}

	.lr-mock-test__option.is-answer label {
		border-color: var(--lr-mock-correct);
		background-color: var(--lr-mock-correct-wash);
	}

	.lr-mock-test__option.is-chosen-wrong label {
		border-color: var(--lr-mock-wrong);
		background-color: var(--lr-mock-wrong-wash);
	}

	.lr-mock-test__letter {
		flex: 0 0 auto;
		font-family: var(--lr-font-mono);
		font-size: var(--lr-font-size-small);
		font-weight: 600;
		letter-spacing: var(--lr-letter-spacing-mono);
		color: var(--lr-color-accent);
		min-width: 1.5em;
	}

	.lr-mock-test__answer {
		flex: 1 1 auto;
		min-width: 0;
	}

	.lr-mock-test__verdict {
		margin-top: var(--lr-gap-medium);
		padding: var(--lr-gap-small) var(--lr-gap-medium);
		border-left: 3px solid var(--lr-color-border-subtle);
		border-radius: var(--lr-radius-small, 4px);
		font-size: var(--lr-font-size-small);
	}

	.lr-mock-test__verdict.is-correct {
		border-left-color: var(--lr-mock-correct);
		background-color: var(--lr-mock-correct-wash);
	}

	.lr-mock-test__verdict.is-wrong {
		border-left-color: var(--lr-mock-wrong);
		background-color: var(--lr-mock-wrong-wash);
	}

	/* ------------------------------------------------------------ navigation */

	.lr-mock-test__nav {
		display: flex;
		gap: var(--lr-gap-button);
		margin-top: var(--lr-gap-medium);
		flex-wrap: wrap;
	}

	.lr-mock-test__nav button {
		flex: 1 1 auto;
		min-width: 8rem;
	}

	.lr-mock-test__flag[aria-pressed="true"] {
		border-color: #8a5a00;
		color: #8a5a00;
	}

	.lr-mock-test__hint {
		margin-top: var(--lr-gap-small);
		margin-bottom: 0;
		font-size: var(--lr-font-size-small);
	}

	.lr-mock-test__hint button {
		margin-left: var(--lr-gap-small);
	}

	/* Shown once, before marking a paper that still has blanks on it. Accent
	   edge rather than the danger colour: leaving a blank is a habit worth
	   correcting, not an error state. */
	.lr-mock-test__warning {
		margin-top: var(--lr-gap-medium);
		padding: var(--lr-gap-small) var(--lr-gap-medium);
		border-left: 3px solid var(--lr-color-accent);
		background-color: var(--lr-color-accent-wash);
		border-radius: var(--lr-radius-small, 4px);
		font-size: var(--lr-font-size-small);
	}

	.lr-mock-test__warning p:last-child {
		margin-bottom: 0;
	}

	/* ---------------------------------------------------------------- result */

	.lr-mock-test__result:focus {
		outline: none;
	}

	.lr-mock-test__score {
		text-align: center;
		padding-bottom: var(--lr-gap-medium);
		border-bottom: 1px solid var(--lr-color-border-subtle);
	}

	.lr-mock-test__headline {
		font-family: var(--lr-font-heading);
		font-size: var(--lr-font-size-h1);
		line-height: 1;
		margin: var(--lr-gap-small) 0 0;
		font-variant-numeric: tabular-nums;
	}

	.lr-mock-test__headline strong {
		color: var(--lr-color-accent);
	}

	.lr-mock-test__percent {
		margin: var(--lr-gap-tight, 8px) 0 0;
		font-size: var(--lr-font-size-small);
		color: var(--lr-color-text-muted);
	}

	.lr-mock-test__stat-grid {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
		gap: var(--lr-gap-small);
		margin: var(--lr-gap-large) 0;
	}

	.lr-mock-test__stat {
		padding: var(--lr-gap-medium);
		border: 1px solid var(--lr-color-border-subtle);
		border-radius: var(--lr-radius-medium, 8px);
		text-align: center;
	}

	.lr-mock-test__stat-value {
		display: block;
		font-family: var(--lr-font-mono);
		font-size: var(--lr-font-size-h4);
		color: var(--lr-color-accent);
		font-variant-numeric: tabular-nums;
	}

	.lr-mock-test__stat-label {
		display: block;
		margin-top: 4px;
		font-size: var(--lr-font-size-small);
		color: var(--lr-color-text-muted);
		line-height: var(--lr-line-height-small);
	}

	.lr-mock-test__note,
	.lr-mock-test__privacy {
		font-size: var(--lr-font-size-small);
		line-height: var(--lr-line-height-body);
	}

	.lr-mock-test__privacy {
		margin-top: var(--lr-gap-large);
	}

	/* -------------------------------------------------------- reduced motion */

	/* style.css has three reduced-motion blocks and every one is scoped by
	   selector rather than being a universal kill switch, so none of them
	   reaches into this file. The theme has already shipped a defect of exactly
	   this shape once, recorded at version 1.8.01, so the transitions declared
	   above are turned off here by name rather than assumed to be covered. */
	@media (prefers-reduced-motion: reduce) {

		.lr-mock-test__jump,
		.lr-mock-test__option label {
			transition: none;
		}
	}

	/* ----------------------------------------------------------------- print */

	/* The solutions page is written to be printed and worked on paper. An
	   interactive runner has nothing to contribute to that, and the launch
	   panel would print as a button nobody can press. */
	@media print {
		.lr-mock-test {
			display: none;
		}
	}
}
