/**
 * JoomProof captcha
 *
 * @copyright   (C) 2026 JoomProof
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 *
 * Every rule is scoped under .joomproof so the widget can never leak into a
 * template, and every colour resolves through a custom property so a template
 * (or the plugin settings) can restyle it without overriding selectors.
 */

.joomproof {
	/*
	 * Colours resolve through three layers, cheapest override last:
	 *   --*-user   the plugin settings, written into the page as one inline rule
	 *   --*-theme  the light/dark/inherit theme blocks further down
	 *   literal    the built in default
	 * Keeping the layers in the fallback chain rather than in competing selectors
	 * means an administrator's colour is never lost to a more specific theme rule.
	 */
	--jp-fg: currentColor;
	--jp-muted: color-mix(in srgb, currentColor 62%, transparent);
	--jp-surface: color-mix(in srgb, currentColor 4%, transparent);
	--jp-border: var(--jp-border-user, color-mix(in srgb, currentColor 28%, transparent));
	/* Defaults are a verified-contrast "trust" blue/red/amber family (6.7:1 / 4.8:1 / 5.0:1
	   on white), not arbitrary picks — chosen so an administrator who never touches the
	   colour fields still ships an accessible, professional-looking widget. */
	--jp-accent: var(--jp-accent-user, var(--jp-accent-theme, #1d4ed8));
	/*
	 * Danger and warning are text colours (the status line, read out loud on
	 * failure), so they cannot depend on prefers-color-scheme the way the accent
	 * does: that media query reflects the visitor's OS setting, not this page's
	 * actual background, and a light Joomla template viewed with a dark OS theme
	 * would otherwise render pale "dark mode" red/amber text on a white page at
	 * roughly 2:1 contrast - unreadable and below WCAG AA. Blending a small
	 * amount of currentColor into the base hue instead keys the colour to what the
	 * template actually resolved for body text, which light or dark always gets
	 * right, and comfortably clears 4.5:1 either way.
	 */
	--jp-danger: color-mix(in srgb, currentColor 20%, #dc2626 80%);
	/* Same currentColor blend as danger/warning, same reason: this is the colour of the
	   "correct" confirmation text from instant validation. */
	--jp-success: color-mix(in srgb, currentColor 20%, #15803d 80%);
	--jp-timer-track: var(--jp-timer-track-user, color-mix(in srgb, currentColor 18%, transparent));
	--jp-timer-fill: var(--jp-timer-fill-user, var(--jp-timer-fill-theme, var(--jp-accent)));
	--jp-timer-warn: var(--jp-timer-warn-user, color-mix(in srgb, currentColor 20%, #b45309 80%));

	/* Geometry. Overridden per site by the plugin's inline settings rule. */
	--jp-timer-size: 40px;
	--jp-timer-stroke: 10;
	--jp-timer-c: 283;
	--jp-bar-thickness: 4px;
	--jp-equation-size: 1.15rem;
	--jp-equation-weight: 600;
	--jp-radius: 0.375rem;
	--jp-gap: 0.5rem;
	--jp-input-width: 6rem;

	/* Countdown progress, 1 = full. JavaScript lowers it; without JavaScript it stays full. */
	--jp-progress: 1;

	display: block;
	max-width: 100%;
	color: var(--jp-fg);
}

/* Older engines that do not understand color-mix() get flat fallbacks. */
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) {
	.joomproof {
		--jp-muted: #5c6470;
		--jp-surface: rgba(127, 127, 127, 0.06);
		--jp-border: var(--jp-border-user, rgba(127, 127, 127, 0.4));
		--jp-timer-track: var(--jp-timer-track-user, rgba(127, 127, 127, 0.25));
		/* No color-mix means no currentColor-aware blend either; fall back to the
		   flat, light-page-tuned defaults rather than an invalid custom property. */
		--jp-danger: #dc2626;
		--jp-success: #15803d;
		--jp-timer-warn: var(--jp-timer-warn-user, #b45309);
	}
}

.joomproof *,
.joomproof *::before,
.joomproof *::after {
	box-sizing: border-box;
}

/* ------------------------------------------------------------------ layout */

.joomproof__inner {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--jp-gap);
}

.joomproof__challenge {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--jp-gap);
	min-width: 0;
}

.joomproof--stacked .joomproof__inner,
.joomproof--stacked .joomproof__challenge {
	flex-direction: column;
	align-items: flex-start;
}

.joomproof--card {
	padding: calc(var(--jp-gap) * 1.5);
	border: 1px solid var(--jp-border);
	border-radius: var(--jp-radius);
	background: var(--jp-surface);
}

/*
 * Single line: unlike every other layout, this one is a hard guarantee, not a
 * preference - Inline already wraps gracefully whenever the column is too narrow,
 * which is the right default for a widget with no control over where it is dropped.
 * Single line is for the site owner who has already made sure there is room and wants
 * that promise kept regardless of viewport. If the column turns out to be genuinely too
 * narrow for even the sum's own text, the row scrolls sideways instead of silently
 * wrapping or overlapping - a working escape hatch beats a layout promise broken quietly.
 */
.joomproof--singleline .joomproof__inner,
.joomproof--singleline .joomproof__challenge {
	flex-wrap: nowrap;
}

.joomproof--singleline .joomproof__inner {
	overflow-x: auto;
	overflow-y: hidden;
}

/*
 * A scrollable flex container resolves each child's automatic minimum size to zero
 * instead of its content size, which would let everything below quietly squash into
 * illegible slivers before the scrollbar ever appears. Pinning flex-shrink to 0 keeps
 * every part at its natural size, so overflow - not shrinkage - is what triggers the
 * scrollbar.
 */
.joomproof--singleline .joomproof__challenge,
.joomproof--singleline .joomproof__equation,
.joomproof--singleline .joomproof__input,
.joomproof--singleline .joomproof__timer,
.joomproof--singleline .joomproof__refresh {
	flex-shrink: 0;
}

/* ---------------------------------------------------------------- equation */

.joomproof__equation {
	display: inline-flex;
	align-items: baseline;
	gap: 0.35em;
	padding: 0.35em 0.6em;
	border: 1px solid var(--jp-border);
	border-radius: var(--jp-radius);
	background-color: var(--jp-surface);
	font-size: var(--jp-equation-size);
	font-weight: var(--jp-equation-weight);
	font-variant-numeric: tabular-nums lining-nums;
	line-height: 1.3;
	letter-spacing: 0.01em;
	white-space: nowrap;
	user-select: none;
}

.joomproof__operator,
.joomproof__equals {
	color: var(--jp-muted);
}

/* The equation timer style drains a tint from behind the text, never the text itself. */
.joomproof--timer-equation .joomproof__equation {
	background-image: linear-gradient(
		to right,
		color-mix(in srgb, var(--jp-timer-fill) 22%, transparent),
		color-mix(in srgb, var(--jp-timer-fill) 22%, transparent)
	);
	background-repeat: no-repeat;
	background-size: calc(var(--jp-progress) * 100%) 100%;
	transition: background-size 1s linear;
}

@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) {
	.joomproof--timer-equation .joomproof__equation {
		background-image: linear-gradient(to right, rgba(42, 109, 244, 0.2), rgba(42, 109, 244, 0.2));
	}
}

/* ------------------------------------------------------------------- input */

.joomproof__input {
	width: var(--jp-input-width);
	min-width: 3.5rem;
	min-height: 2.75rem;
	max-width: 100%;
	padding: 0.375rem 0.5rem;
	border: 1px solid var(--jp-border);
	border-radius: var(--jp-radius);
	background-color: transparent;
	color: inherit;
	font: inherit;
	font-variant-numeric: tabular-nums lining-nums;
	text-align: center;
	touch-action: manipulation;
	transition: border-color 0.2s ease;
}

.joomproof__input:focus-visible,
.joomproof__refresh:focus-visible {
	outline: 2px solid var(--jp-accent);
	outline-offset: 2px;
}

/* Templates that suppress :focus-visible still get a ring. */
.joomproof__input:focus,
.joomproof__refresh:focus {
	outline: 2px solid var(--jp-accent);
	outline-offset: 2px;
}

.joomproof.is-expired .joomproof__input {
	border-color: var(--jp-danger);
}

/* Instant validation confirmed the current value before the form was ever submitted.
   The border is a bonus cue only - the status text and icon beside it, not colour
   alone, are what actually say so. */
.joomproof__input.is-verified {
	border-color: var(--jp-success);
}

/* ----------------------------------------------------------------- refresh */

.joomproof__refresh {
	display: none;
	align-items: center;
	justify-content: center;
	/* 44px meets the Apple/Material minimum tap target even though the icon itself is
	   smaller; the button's box, not the glyph, is what a thumb needs to hit. */
	width: 2.75rem;
	height: 2.75rem;
	padding: 0;
	border: 1px solid var(--jp-border);
	border-radius: var(--jp-radius);
	background: transparent;
	color: inherit;
	cursor: pointer;
	touch-action: manipulation;
	transition: border-color 0.2s ease, color 0.2s ease, transform 0.1s ease;
}

/* The button only does something once the script is running. */
.joomproof.is-enhanced .joomproof__refresh {
	display: inline-flex;
}

.joomproof__refresh:hover {
	border-color: var(--jp-accent);
	color: var(--jp-accent);
}

.joomproof__refresh:active {
	transform: scale(0.94);
}

.joomproof__refresh[disabled] {
	opacity: 0.5;
	cursor: default;
	transform: none;
}

.joomproof__refresh-icon {
	width: 1.15rem;
	height: 1.15rem;
}

.joomproof.is-busy .joomproof__refresh-icon {
	animation: joomproof-spin 0.9s linear infinite;
}

@keyframes joomproof-spin {
	to {
		transform: rotate(360deg);
	}
}

/* ------------------------------------------------------------------- timer */

.joomproof__timer {
	position: relative;
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: var(--jp-timer-size);
	height: var(--jp-timer-size);
}

.joomproof--timer-left .joomproof__timer {
	order: -1;
}

.joomproof__timer-svg {
	width: 100%;
	height: 100%;
	/* Start the sweep at twelve o'clock. */
	transform: rotate(-90deg);
}

.joomproof__timer-track,
.joomproof__timer-ring {
	fill: none;
	stroke-width: var(--jp-timer-stroke);
}

.joomproof__timer-track {
	stroke: var(--jp-timer-track);
}

.joomproof__timer-ring {
	stroke: var(--jp-timer-fill);
	stroke-linecap: round;
	stroke-dasharray: var(--jp-timer-c);
	stroke-dashoffset: calc(var(--jp-timer-c) * (1 - var(--jp-progress)));
	transition: stroke-dashoffset 1s linear;
}

.joomproof.is-warning .joomproof__timer-ring {
	stroke: var(--jp-timer-warn);
}

.joomproof.is-expired .joomproof__timer-ring {
	stroke: var(--jp-danger);
}

.joomproof__timer-value {
	position: absolute;
	font-size: calc(var(--jp-timer-size) * 0.32);
	font-variant-numeric: tabular-nums;
	line-height: 1;
}

/* Pie style: a filled wedge instead of a ring outline. */

.joomproof__timer-pie {
	--jp-pie-color: var(--jp-timer-fill);

	display: block;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	border: 1px solid var(--jp-border);
	/*
	 * Sweep clockwise from twelve o'clock, same direction as the ring. This redraws once
	 * a second along with the JavaScript tick rather than sweeping continuously: making a
	 * conic-gradient() ease smoothly needs `transition: --jp-progress` on a registered
	 * (@property) custom property, and that combination does not reliably animate when
	 * the value is inherited from an ancestor's inline style rather than set locally -
	 * confirmed in testing, where the gradient stayed pinned near its start value for the
	 * full transition duration. A once-a-second redraw is a plain, correct fallback; the
	 * ring's own smooth sweep is unaffected, since it animates a native SVG attribute
	 * rather than a custom property.
	 */
	background: conic-gradient(
		from 0deg,
		var(--jp-pie-color) calc(var(--jp-progress) * 360deg),
		var(--jp-timer-track) 0deg
	);
}

.joomproof.is-warning .joomproof__timer-pie {
	--jp-pie-color: var(--jp-timer-warn);
}

.joomproof.is-expired .joomproof__timer-pie {
	--jp-pie-color: var(--jp-danger);
}

/* Dots style: a row of segments, one per timer-quarter, that go out as time runs down. */

.joomproof__timer-dots {
	display: flex;
	align-items: center;
	justify-content: space-between;
	width: 100%;
	height: 100%;
}

.joomproof__timer-dot {
	width: calc(var(--jp-timer-size) / 9);
	height: calc(var(--jp-timer-size) / 9);
	border-radius: 50%;
	background-color: var(--jp-timer-fill);
	transition: background-color 0.3s ease;
}

.joomproof__timer-dot.is-spent {
	background-color: var(--jp-timer-track);
}

.joomproof.is-warning .joomproof__timer-dot:not(.is-spent) {
	background-color: var(--jp-timer-warn);
}

.joomproof.is-expired .joomproof__timer-dot {
	background-color: var(--jp-danger);
}

/* Underline style */

.joomproof__bar {
	display: block;
	width: 100%;
	height: var(--jp-bar-thickness);
	margin-top: calc(var(--jp-gap) * 0.75);
	border-radius: var(--jp-bar-thickness);
	background-color: var(--jp-timer-track);
	overflow: hidden;
}

.joomproof__bar-fill {
	display: block;
	width: calc(var(--jp-progress) * 100%);
	height: 100%;
	border-radius: inherit;
	background-color: var(--jp-timer-fill);
	transition: width 1s linear;
}

.joomproof.is-warning .joomproof__bar-fill {
	background-color: var(--jp-timer-warn);
}

.joomproof.is-expired .joomproof__bar-fill {
	background-color: var(--jp-danger);
}

/* ---------------------------------------------------------- text and hints */

.joomproof__help,
.joomproof__privacy,
.joomproof__status {
	margin: calc(var(--jp-gap) * 0.75) 0 0;
	font-size: 0.85em;
	line-height: 1.4;
	color: var(--jp-muted);
}

/*
 * Reserved at one line tall even with no text node in it at all (an empty element
 * generates no line box on its own, so this needs an explicit min-height, not just
 * leaving :empty unstyled) - the status line sits directly above the form's own
 * submit button, and instant validation can fill it in synchronously, from the same
 * mousedown that is already moving the mouse toward that button. A message that only
 * then starts occupying space pushes the button down between mousedown and mouseup,
 * so the click's mouseup lands past it and nothing happens - the exact "first click
 * does nothing, second click works" reported against this. Reserving the line up
 * front keeps that button still regardless of what the line ends up saying.
 */
.joomproof__status {
	min-height: 1.4em;
}

/*
 * A small decorative glyph in front of a warning/error announcement, so the message
 * can be scanned at a glance instead of read word by word. The text alone already
 * carries the full meaning (and is what a screen reader announces) - this is a bonus
 * for sighted users, never the only signal, and is skipped entirely for neutral
 * messages so it does not cry wolf.
 */
.joomproof__status[data-severity]::before {
	content: '';
	display: inline-block;
	width: 1em;
	height: 1em;
	margin-inline-end: 0.35em;
	vertical-align: -0.15em;
	background-color: currentColor;
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	-webkit-mask-position: center;
	mask-position: center;
	-webkit-mask-size: contain;
	mask-size: contain;
}

.joomproof__status[data-severity='warning']::before {
	-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 3.5 L22 20.5 H2 Z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round' stroke-linecap='round'/%3E%3Cline x1='12' y1='9.5' x2='12' y2='14.5' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3Ccircle cx='12' cy='17.3' r='1.15' fill='black'/%3E%3C/svg%3E");
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 3.5 L22 20.5 H2 Z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round' stroke-linecap='round'/%3E%3Cline x1='12' y1='9.5' x2='12' y2='14.5' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3Ccircle cx='12' cy='17.3' r='1.15' fill='black'/%3E%3C/svg%3E");
}

.joomproof__status[data-severity='error']::before {
	-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke='black' stroke-width='2'/%3E%3Cline x1='12' y1='7.5' x2='12' y2='13' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3Ccircle cx='12' cy='16.3' r='1.15' fill='black'/%3E%3C/svg%3E");
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke='black' stroke-width='2'/%3E%3Cline x1='12' y1='7.5' x2='12' y2='13' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3Ccircle cx='12' cy='16.3' r='1.15' fill='black'/%3E%3C/svg%3E");
}

.joomproof__status[data-severity='success']::before {
	-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke='black' stroke-width='2'/%3E%3Cpath d='M7.5 12.5 L10.5 15.5 L16.5 9' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke='black' stroke-width='2'/%3E%3Cpath d='M7.5 12.5 L10.5 15.5 L16.5 9' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* Status never relies on colour alone: the mask-icon and the text both change too. */
.joomproof__status[data-severity='error'] {
	color: var(--jp-danger);
	font-weight: 600;
}

.joomproof__status[data-severity='success'] {
	color: var(--jp-success);
	font-weight: 600;
}

.joomproof__status[data-severity='warning'] {
	color: var(--jp-timer-warn);
}

/* --------------------------------------------------------------- honeypot */

.joomproof__hp {
	position: absolute !important;
	left: -10000px;
	top: auto;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* ---------------------------------------------------- screen reader helper */

.joomproof__sr-only {
	position: absolute !important;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	border: 0;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	clip-path: inset(50%);
	white-space: nowrap;
}

/* ------------------------------------------------------------------ themes */

.joomproof--theme-dark {
	--jp-accent-theme: #6f9dff;
}

@media (prefers-color-scheme: dark) {
	.joomproof--theme-auto {
		--jp-accent-theme: #6f9dff;
	}
}

/* The inherit theme takes everything from the surrounding template. */
.joomproof--theme-inherit {
	--jp-accent-theme: currentColor;
	--jp-timer-fill-theme: currentColor;
}

/* --------------------------------------------------------- high contrast */

.joomproof--contrast {
	--jp-muted: currentColor;
	--jp-border: currentColor;
	--jp-surface: transparent;
	--jp-timer-track: color-mix(in srgb, currentColor 35%, transparent);
}

.joomproof--contrast .joomproof__equation,
.joomproof--contrast .joomproof__input {
	border-width: 2px;
}

@media (forced-colors: active) {
	.joomproof__equation,
	.joomproof__input,
	.joomproof__refresh,
	.joomproof__timer-pie {
		border: 1px solid CanvasText;
	}

	.joomproof__timer-ring,
	.joomproof__timer-track {
		stroke: CanvasText;
	}

	/* A conic-gradient and a background-colour both disappear under forced colours;
	   an outline is the one cue guaranteed to survive, so give the remaining segments
	   one instead of trying to preserve the fill. */
	.joomproof__timer-dot {
		background-color: transparent;
		outline: 1px solid CanvasText;
	}

	.joomproof__timer-dot.is-spent {
		outline-style: dashed;
	}
}

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

@media (prefers-reduced-motion: reduce) {
	.joomproof__timer-ring,
	.joomproof__timer-dot,
	.joomproof__bar-fill,
	.joomproof--timer-equation .joomproof__equation,
	.joomproof__input,
	.joomproof__refresh {
		transition: none;
	}

	.joomproof.is-busy .joomproof__refresh-icon {
		animation: none;
	}
}

.joomproof.jp-no-motion .joomproof__timer-ring,
.joomproof.jp-no-motion .joomproof__timer-dot,
.joomproof.jp-no-motion .joomproof__bar-fill,
.joomproof.jp-no-motion.joomproof--timer-equation .joomproof__equation,
.joomproof.jp-no-motion .joomproof__input,
.joomproof.jp-no-motion .joomproof__refresh {
	transition: none;
}

.joomproof.jp-no-motion.is-busy .joomproof__refresh-icon {
	animation: none;
}

/*
 * RTL needs no special casing: the bar's fill is a block box, so it hugs the
 * start edge on its own, and the equation itself is forced to dir="ltr" because
 * arithmetic still reads left to right inside a right to left page.
 */

/* --------------------------------------------------------------- narrow */

@media (max-width: 30rem) {
	.joomproof:not(.joomproof--singleline) .joomproof__inner {
		align-items: flex-start;
	}

	/* Single line opts out here too: growing the input to full width is exactly the
	   wrap-to-a-second-line behaviour it exists to prevent. */
	.joomproof:not(.joomproof--singleline) .joomproof__input {
		width: 100%;
	}
}
