/**
 * Turbo PageSpeed frontend styles.
 *
 * Deliberately self-contained and low-specificity-safe: the markup sits inside
 * whatever theme the page uses, so every rule is namespaced under .tps and no
 * global elements are touched.
 */

.tps {
	--tps-good: #0cce6b;
	--tps-good-bg: #e6f9f0;
	--tps-average: #ffa400;
	--tps-average-bg: #fff4e0;
	--tps-poor: #ff4e42;
	--tps-poor-bg: #ffeceb;
	--tps-none: #9aa0a6;
	--tps-none-bg: #f1f3f4;
	--tps-border: #e3e6ea;
	--tps-text: #1f2328;
	--tps-muted: #656d76;
	--tps-surface: #fff;
	--tps-charcoal: #333333;
	--tps-metric-bg: #ececec;

	/* Dial sizing. The projected dial is drawn at --tps-emphasis times the
	   base so the circle, its score and the logo scale together from one
	   number rather than three that can drift apart. */
	--tps-dial-size: 96px;
	--tps-dial-font: 30px;
	--tps-projected-font: 26px;
	--tps-logo-size: 30px;
	--tps-emphasis: 1.08;
	--tps-arrow-length: 52px;

	max-width: 760px;
	margin: 0 auto;
	color: var(--tps-text);
	font-size: 16px;
	line-height: 1.5;
}

/* Form ------------------------------------------------------------------- */

.tps-form {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
}

.tps-input {
	flex: 1 1 260px;
	min-width: 0;
	padding: 12px 14px;
	font-size: 16px;
	line-height: 1.4;
	color: var(--tps-text);
	background: var(--tps-surface);
	border: 1px solid var(--tps-border);
	border-radius: 8px;
	box-shadow: none;
}

.tps-input:focus {
	outline: 2px solid #1a73e8;
	outline-offset: 1px;
	border-color: #1a73e8;
}

/* Every interactive state is pinned, not just the resting one. Closing the
   modal returns focus to this button, which is correct for keyboard users but
   means any theme rule on button:focus or button:active shows up the moment
   the modal closes — typically as the theme's own accent colour. */
.tps button.tps-submit,
.tps button.tps-submit:link,
.tps button.tps-submit:visited,
.tps button.tps-submit:focus,
.tps button.tps-submit:focus-visible,
.tps button.tps-submit:active {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 12px 22px;
	font-size: 16px;
	font-weight: 600;
	line-height: 1.2;
	text-transform: none !important;
	text-shadow: none;
	color: #fff !important;
	background: #1a73e8 !important;
	border: 0 !important;
	border-radius: 8px !important;
	box-shadow: none !important;
	cursor: pointer;
}

.tps button.tps-submit:hover:not([disabled]),
.tps button.tps-submit:active:not([disabled]) {
	background: #1666d0 !important;
}

/* Focus stays visible as a ring rather than a colour change, so returning
   focus after the modal closes never repaints the button. */
.tps button.tps-submit:focus-visible {
	outline: 3px solid rgba(26, 115, 232, 0.45);
	outline-offset: 2px;
}

.tps button.tps-submit[disabled] {
	opacity: 0.7;
	cursor: default;
}

.tps-spinner {
	display: none;
	width: 15px;
	height: 15px;
	border: 2px solid rgba(255, 255, 255, 0.4);
	border-top-color: #fff;
	border-radius: 50%;
	animation: tps-spin 0.7s linear infinite;
}

.tps.is-loading .tps-spinner {
	display: block;
}

@keyframes tps-spin {
	to {
		transform: rotate(360deg);
	}
}

@media (prefers-reduced-motion: reduce) {
	.tps-spinner {
		animation-duration: 2s;
	}
}

/* Staged progress -------------------------------------------------------- */

.tps-progress {
	margin-top: 20px;
	padding: 20px;
	border: 1px solid var(--tps-border);
	border-radius: 12px;
	background: var(--tps-surface);
}

.tps-progress[hidden] {
	display: none;
}

.tps-progress-track {
	height: 6px;
	border-radius: 999px;
	background: var(--tps-none-bg);
	overflow: hidden;
}

.tps-progress-fill {
	width: 0;
	height: 100%;
	border-radius: 999px;
	background: #1a73e8;
	transition: width 0.4s ease;
}

.tps-progress.is-complete .tps-progress-fill {
	background: var(--tps-good);
}

.tps-stages {
	list-style: none;
	margin: 16px 0 0;
	padding: 0;
	display: grid;
	gap: 2px;
	counter-reset: none;
}

.tps-stage {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 7px 0;
	font-size: 15px;
	color: var(--tps-muted);
	opacity: 0.55;
	transition: opacity 0.3s ease, color 0.3s ease;
}

.tps-stage-marker {
	flex: 0 0 auto;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	border: 2px solid var(--tps-border);
	background: var(--tps-surface);
	position: relative;
}

/* Active: filled dot that breathes, so a long stage still looks alive. */
.tps-stage.is-active {
	opacity: 1;
	color: var(--tps-text);
	font-weight: 600;
}

.tps-stage.is-active .tps-stage-marker {
	border-color: #1a73e8;
}

.tps-stage.is-active .tps-stage-marker::after {
	content: "";
	position: absolute;
	inset: 3px;
	border-radius: 50%;
	background: #1a73e8;
	animation: tps-pulse 1.4s ease-in-out infinite;
}

@keyframes tps-pulse {
	0%,
	100% {
		transform: scale(1);
		opacity: 1;
	}
	50% {
		transform: scale(0.6);
		opacity: 0.5;
	}
}

/* Done: solid green tick. */
.tps-stage.is-done {
	opacity: 1;
	color: var(--tps-text);
}

.tps-stage.is-done .tps-stage-marker {
	border-color: var(--tps-good);
	background: var(--tps-good);
}

.tps-stage.is-done .tps-stage-marker::after {
	content: "";
	position: absolute;
	left: 5px;
	top: 1px;
	width: 4px;
	height: 9px;
	border: solid #fff;
	border-width: 0 2px 2px 0;
	transform: rotate(45deg);
}

.tps-stage-label {
	flex: 1 1 auto;
}

@media (prefers-reduced-motion: reduce) {
	.tps-stage.is-active .tps-stage-marker::after {
		animation: none;
	}

	.tps-progress-fill,
	.tps-stage {
		transition: none;
	}

	/* The widening still happens, just instantly. */
	.tps-modal {
		transition: none;
	}

	.tps-modal[open],
	.tps-modal::backdrop,
	.tps a.tps-cta,
	.tps-modal-body .tps-results:not([hidden]) {
		animation: none;
	}
}

/* Message ---------------------------------------------------------------- */

.tps-message,
.tps-inline-message {
	margin: 12px 0 0;
	min-height: 1.5em;
	font-size: 14px;
	color: var(--tps-muted);
}

.tps-message.is-error,
.tps-inline-message.is-error {
	color: var(--tps-poor);
	font-weight: 600;
}

.tps-inline-message:empty {
	display: none;
}

/* Modal ------------------------------------------------------------------ */

/* A native <dialog> renders in the top layer, so the theme cannot clip it
   with overflow or trap it under a z-index, and the browser supplies focus
   trapping, Esc to close and the backdrop. */
.tps-modal {
	/* Narrow while the test runs: the progress list is a single column and a
	   full-width panel around it would be mostly empty space. It widens to
	   90vw once there are two result panels to hold.

	   Width is held at 90vw and max-width does the moving, so the animated
	   property interpolates between two plain lengths. It also gives the
	   right behaviour on a phone for free: below roughly 510px wide, 90vw is
	   already under the 460px cap, so the panel starts full width and the
	   expansion is a no-op rather than a jolt. */
	width: 90vw;
	max-width: 460px;
	max-height: 90vh;
	padding: 0;
	border: 0;
	border-radius: 14px;
	background: var(--tps-surface);
	color: var(--tps-text);
	box-shadow: 0 24px 60px rgba(0, 0, 0, 0.22);
	overflow: hidden;
	transition: max-width 560ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.tps-modal.is-expanded {
	max-width: 90vw;
}

/* Flex column so the body takes whatever height the header leaves, rather
   than relying on a hard-coded header height that a taller header breaks. */
.tps-modal[open] {
	display: flex;
	flex-direction: column;
	animation: tps-modal-in 260ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.tps-modal::backdrop {
	background: rgba(15, 20, 28, 0.55);
	animation: tps-fade-in 260ms ease;
}

@keyframes tps-modal-in {
	from {
		opacity: 0;
		transform: translateY(10px) scale(0.97);
	}
}

@keyframes tps-fade-in {
	from {
		opacity: 0;
	}
}

@keyframes tps-rise-in {
	from {
		opacity: 0;
		transform: translateY(10px);
	}
}

.tps-modal-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 16px;
	padding: 18px 22px;
	border-bottom: 1px solid var(--tps-border);
	background: var(--tps-surface);
}

.tps-modal-heading {
	min-width: 0;
}

.tps-modal-title {
	margin: 0;
	font-size: 17px;
	font-weight: 700;
	line-height: 1.3;
	color: var(--tps-text);
}

.tps-modal-url {
	margin: 3px 0 0;
	font-size: 13px;
	color: var(--tps-muted);
	overflow-wrap: anywhere;
}

.tps-modal-actions {
	flex: 0 1 auto;
	display: flex;
	align-items: center;
	gap: 14px;
	min-width: 0;
}

/* Sits inline immediately before the button, sharing its baseline. Appears
   and hides with the button, since it is the line that sells it. */
.tps .tps-cta-headline {
	margin: 0;
	font-size: 20px;
	font-weight: 600;
	line-height: 1.25;
	color: var(--tps-charcoal);
	text-align: right;
	text-wrap: balance;
	animation: tps-rise-in 320ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.tps .tps-cta-headline[hidden] {
	display: none;
}

/* The two brand colours are marked important because themes routinely set
   a { color: ... !important }, which normal specificity cannot beat. Without
   it the button renders as the theme's link colour on blue. Everything else
   here is left overridable on purpose. */
.tps a.tps-cta {
	display: inline-flex;
	align-items: center;
	gap: 9px;
	padding: 9px 18px;
	font-size: 14px;
	font-weight: 700;
	line-height: 1.2;
	white-space: nowrap;
	color: #fff !important;
	background: #2e69ff !important;
	border: 0;
	border-radius: 8px;
	text-decoration: none !important;
	box-shadow: none;
	animation: tps-rise-in 320ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.tps a.tps-cta:hover,
.tps a.tps-cta:focus {
	color: #fff !important;
	background: #2455d6 !important;
	text-decoration: none !important;
}

.tps a.tps-cta[hidden] {
	display: none;
}

/* Sized inline from the setting. max-width:none stops a theme's
   img { max-width: 100% } from shrinking it inside the flex row. */
.tps .tps-cta-icon {
	display: block;
	flex: 0 0 auto;
	/* Beats the near-universal img { max-width: 100% } so the icon keeps the
	   size the admin chose instead of being squeezed by the flex row. */
	max-width: none !important;
	object-fit: contain;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	vertical-align: middle;
}

.tps-cta-label {
	display: block;
}

/* Themes style bare <button> elements freely, often with a brand colour and
   always with enough specificity to beat a single class. The neutral grey is
   forced so the close control cannot inherit a coloured button treatment. */
/* Geometry is forced as well as colour. Themes commonly ship
   button { border-radius: 999px; text-transform: uppercase; min-height: 44px;
   padding: ... } with !important, which turns a 34px square close control
   into an oversized uppercase pill. */
.tps button.tps-modal-close {
	flex: 0 0 auto;
	width: 34px !important;
	height: 34px !important;
	min-width: 0 !important;
	min-height: 0 !important;
	max-width: none !important;
	display: grid;
	place-items: center;
	padding: 0 !important;
	margin: 0;
	font-size: 26px !important;
	font-weight: 400 !important;
	line-height: 1 !important;
	letter-spacing: normal !important;
	text-transform: none !important;
	text-shadow: none;
	color: var(--tps-muted) !important;
	background: #f4f5f7 !important;
	border: 1px solid var(--tps-border) !important;
	border-radius: 8px !important;
	box-shadow: none !important;
	cursor: pointer;
}

.tps button.tps-modal-close:hover,
.tps button.tps-modal-close:focus {
	color: var(--tps-text) !important;
	background: #e8eaed !important;
}

/* Only the body scrolls, so the header and its button stay put on a long
   report. min-height:0 is what lets a flex child actually shrink and scroll. */
.tps-modal-body {
	flex: 1 1 auto;
	min-height: 0;
	padding: 20px 22px 24px;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
}

/* Results fade up once the panel has begun widening. */
.tps-modal-body .tps-results:not([hidden]) {
	animation: tps-rise-in 420ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.tps-modal-body .tps-progress {
	margin-top: 0;
}

.tps-modal-body .tps-results {
	margin-top: 0;
}

/* On a phone the heading, the call to action and the close control cannot
   share a row: the title squeezes to two words a line and a long URL breaks
   mid-word. The heading takes its own row and the actions sit beneath it. */
@media (max-width: 600px) {
	.tps-modal-head {
		flex-wrap: wrap;
		gap: 12px;
	}

	.tps-modal-heading {
		flex: 1 1 100%;
		/* Leaves room for the close control, which stays pinned top right. */
		padding-right: 44px;
	}

	.tps-modal-actions {
		flex: 1 1 100%;
		flex-wrap: wrap;
		gap: 10px;
	}

	/* Its own row above the button, and left-aligned with the heading rather
	   than ranged right against nothing. */
	.tps .tps-cta-headline {
		flex: 1 1 100%;
		font-size: 18px;
		text-align: left;
	}

	.tps a.tps-cta {
		flex: 1 1 auto;
		justify-content: center;
	}

	/* Out of the flow so it holds the corner while the heading has the row. */
	.tps-modal-head {
		position: relative;
	}

	.tps button.tps-modal-close {
		position: absolute;
		top: 18px;
		right: 22px;
	}
}

/* Keep the page behind the modal from scrolling under it. */
.tps-modal-open {
	overflow: hidden;
}

/* Fallback for browsers without dialog.showModal(): the panel renders in
   flow beneath the form rather than as an overlay. */
.tps-no-dialog .tps-modal {
	position: static;
	width: auto;
	max-width: none;
	max-height: none;
	margin: 20px 0 0;
	border: 1px solid var(--tps-border);
	box-shadow: none;
}

.tps-no-dialog .tps-modal-body {
	max-height: none;
}

.tps-no-dialog .tps-modal-close {
	display: none;
}

/* Results ---------------------------------------------------------------- */

.tps-results {
	margin-top: 20px;
	display: grid;
	gap: 20px;
}

.tps-results[hidden] {
	display: none;
}

.tps-panel {
	border: 1px solid var(--tps-border);
	border-radius: 12px;
	padding: 20px;
	background: var(--tps-surface);
}

/* A column so the strategy name and tested URL sit above the dials, with the
   gain figures beneath them. Centred, because the dials it heads are centred
   and a left-ranged title above them reads as a misalignment. */
.tps-panel-head {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 14px;
	text-align: center;
}

.tps-panel-heading {
	max-width: 100%;
}

.tps-panel-title {
	margin: 0;
	font-size: 18px;
	font-weight: 700;
}

.tps-panel-url {
	margin: 4px 0 0;
	font-size: 13px;
	color: var(--tps-muted);
	word-break: break-all;
}

/* The score dial mirrors the official Lighthouse presentation: a conic
   gradient filled to the score, with the centre punched out. */
.tps-dial {
	flex: 0 0 auto;
	width: var(--tps-dial-size);
	height: var(--tps-dial-size);
	border-radius: 50%;
	display: grid;
	place-items: center;
	font-size: var(--tps-dial-font);
	font-weight: 700;
	background: var(--tps-none-bg);
	color: var(--tps-none);
}

.tps-dial.is-good {
	color: var(--tps-good);
	background:
		radial-gradient(closest-side, var(--tps-surface) 79%, transparent 80% 100%),
		conic-gradient(var(--tps-good) calc(var(--tps-score) * 1%), var(--tps-good-bg) 0);
}

.tps-dial.is-average {
	color: var(--tps-average);
	background:
		radial-gradient(closest-side, var(--tps-surface) 79%, transparent 80% 100%),
		conic-gradient(var(--tps-average) calc(var(--tps-score) * 1%), var(--tps-average-bg) 0);
}

.tps-dial.is-poor {
	color: var(--tps-poor);
	background:
		radial-gradient(closest-side, var(--tps-surface) 79%, transparent 80% 100%),
		conic-gradient(var(--tps-poor) calc(var(--tps-score) * 1%), var(--tps-poor-bg) 0);
}

.tps-panel-loading {
	color: var(--tps-muted);
	font-size: 14px;
}

/* Projection ------------------------------------------------------------- */

/* Current and projected sit side by side with an arrow between them, so the
   comparison reads left to right without needing a legend. */
/* Takes a full row of the panel head so the pair sits centred in the panel,
   with the title and figures below it.

   Aligned on the bottom edge rather than the middle: the two dials are
   different sizes, so centring them vertically would leave the captions on
   different baselines. Bottom alignment keeps the captions level even when
   one of them wraps to a second line. */
.tps-dials {
	flex: 1 1 100%;
	display: flex;
	align-items: flex-end;
	justify-content: center;
	gap: 12px;
	/* nowrap on purpose: in a wrapping row the projected dial would drop to a
	   second line before the arrow ever gave up any width, which reads as a
	   broken layout. Held on one line, the arrow and the captions absorb the
	   squeeze instead. */
	flex-wrap: nowrap;
}

.tps-dial-group {
	min-width: 0;
}

.tps-dial-group {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: flex-end;
	gap: 6px;
}

.tps-dial-caption {
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.02em;
	color: var(--tps-muted);
	text-align: center;
	max-width: 110px;
}

/* Sits level with the middle of the smaller dial, measured up from the
   caption row that both groups now share. Length is set here rather than by a
   glyph, and the grey is a step darker than the border colour it used to
   borrow, which was close to invisible against the panel. */
.tps-dial-arrow {
	/* Allowed to shrink so a narrow panel compresses the arrow instead of
	   wrapping the projected dial onto its own line. Responds to the space
	   actually available rather than to a viewport breakpoint, since the
	   panel is half the modal on desktop and all of it on a phone. */
	flex: 0 1 auto;
	display: block;
	width: var(--tps-arrow-length);
	min-width: 26px;
	height: 12px;
	line-height: 0;
	color: #b4bac1;
	margin-bottom: calc(var(--tps-dial-size) / 2 + 8px);
}

.tps-dial-arrow svg {
	display: block;
	width: 100%;
	height: 100%;
	overflow: visible;
}

/* The projected dial is always green, whatever figure it shows. It carries
   is-projected instead of a rating class, so there is no specificity fight
   with .is-average or .is-poor. */
.tps-dial.is-projected {
	width: calc(var(--tps-dial-size) * var(--tps-emphasis));
	height: calc(var(--tps-dial-size) * var(--tps-emphasis));
	color: var(--tps-good);
	background:
		radial-gradient(closest-side, var(--tps-surface) 79%, transparent 80% 100%),
		conic-gradient(var(--tps-good) calc(var(--tps-score) * 1%), var(--tps-good-bg) 0);
}

/* Logo above the number, both centred in the punched-out middle. */
.tps-dial-inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 1px;
	line-height: 1;
}

.tps-dial-logo {
	display: block;
	line-height: 0;
}

.tps-dial-logo svg {
	display: block;
	width: calc(var(--tps-logo-size) * var(--tps-emphasis));
	height: calc(var(--tps-logo-size) * var(--tps-emphasis));
}

.tps-dial-score {
	display: block;
	line-height: 1;
}

/* Set below the base size so the logo and the number both clear the inner
   circle, then scaled by the same emphasis factor as the dial itself. */
.tps-dial.is-projected .tps-dial-score {
	font-size: calc(var(--tps-projected-font) * var(--tps-emphasis));
}

.tps-gain {
	display: inline-block;
	margin-top: 10px;
	padding: 4px 12px;
	font-size: 14px;
	font-weight: 700;
	color: var(--tps-good);
	background: var(--tps-good-bg);
	border-radius: 999px;
}

.tps-range {
	margin: 8px 0 0;
	font-size: 13px;
	color: var(--tps-muted);
}

.tps-drivers {
	margin: 0;
	padding: 0;
	list-style: none;
}

.tps-driver {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 14px;
	padding: 8px 0;
	border-bottom: 1px solid var(--tps-border);
	font-size: 14px;
}

.tps-driver:last-child {
	border-bottom: 0;
}

.tps-driver-points {
	flex: 0 0 auto;
	font-weight: 700;
	color: var(--tps-good);
	white-space: nowrap;
}

.tps-scope {
	margin: 0;
	padding: 0;
	list-style: none;
}

.tps-scope-item {
	display: flex;
	justify-content: space-between;
	gap: 14px;
	padding: 6px 0;
	font-size: 13px;
	color: var(--tps-muted);
}

.tps-note {
	margin: 14px 0 0;
	font-size: 12px;
	line-height: 1.45;
	color: var(--tps-muted);
}

/* Metric rows ------------------------------------------------------------ */

.tps-section-title {
	margin: 20px 0 10px;
	font-size: 13px;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--tps-muted);
}

.tps-metrics {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
	gap: 10px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.tps-metric {
	padding: 10px 12px;
	border-radius: 8px;
	background: var(--tps-metric-bg);
}

.tps-metric-label {
	display: block;
	font-size: 12px;
	color: var(--tps-muted);
}

.tps-metric-value {
	display: block;
	font-size: 20px;
	font-weight: 700;
	margin-top: 2px;
}

.tps-metric.is-good .tps-metric-value {
	color: var(--tps-good);
}

.tps-metric.is-average .tps-metric-value {
	color: var(--tps-average);
}

.tps-metric.is-poor .tps-metric-value {
	color: var(--tps-poor);
}

/* Opportunities ---------------------------------------------------------- */

.tps-opps {
	margin: 0;
	padding: 0;
	list-style: none;
	border-top: 1px solid var(--tps-border);
}

.tps-opp {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 14px;
	padding: 10px 0;
	border-bottom: 1px solid var(--tps-border);
	font-size: 14px;
}

.tps-opp-saving {
	flex: 0 0 auto;
	font-weight: 700;
	color: var(--tps-average);
	white-space: nowrap;
}

.tps-empty {
	font-size: 14px;
	color: var(--tps-muted);
	margin: 0;
}

/* Screenshot ------------------------------------------------------------- */

.tps-shot {
	margin-top: 18px;
}

.tps-shot img {
	display: block;
	max-width: 180px;
	height: auto;
	border: 1px solid var(--tps-border);
	border-radius: 8px;
}

.tps-badge {
	display: inline-block;
	margin-left: 8px;
	padding: 2px 8px;
	font-size: 11px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--tps-muted);
	background: var(--tps-none-bg);
	border-radius: 999px;
	vertical-align: middle;
}

/* Two panels side by side once there is room for them. */
@media (min-width: 720px) {
	.tps-results.is-both {
		grid-template-columns: 1fr 1fr;
	}
}
