/*
 * Trade Leads Platform - front-end styles.
 *
 * Until now the plugin shipped no CSS at all: every template was raw
 * HTML with class names attached to nothing.
 *
 * DESIGN POSITION, since this is a decision rather than an omission:
 * this stylesheet imposes structure, rhythm and tap-target size, and
 * deliberately does NOT impose a typeface, a heading scale or a brand
 * colour. It runs inside whatever theme the site uses, and a plugin
 * that overrides the host theme's type is a plugin that makes every
 * site it touches look slightly broken. Font family is inherited
 * throughout; the palette is a set of custom properties a theme can
 * override in one place.
 *
 * The one place with any visual weight is the price-and-buy block on a
 * job card. That's the moment the whole product turns on - a
 * tradesperson standing in a van deciding in four minutes - and
 * everything else is kept quiet so it reads first. Targets are sized
 * for thumbs, not mice.
 */

/*
 * Custom properties live on :root, not on a list of wrapper classes.
 *
 * They were scoped to five wrapper classes, one of which
 * (.tlp-job-submission) does not exist - the template's wrapper is
 * .tlp-job-submission-form. So on the job form, and equally on the
 * registration, verification and login templates, which were never in
 * the list either, every var(--tlp-*) resolved to nothing. Colours
 * silently became transparent. That is why the progress bar was still
 * invisible after being rebuilt on real elements: the elements were
 * there, painted in a colour that did not exist.
 *
 * Every name here is --tlp- prefixed, so there is nothing to collide
 * with at :root and no reason to keep paying for a list that has to be
 * updated every time a template gains a wrapper. This removes the whole
 * class of bug rather than adding the two classes that happened to be
 * missing today.
 */
:root {
	--tlp-ink: #16181d;
	--tlp-ink-soft: #5b6169;
	--tlp-line: #dfe2e6;
	--tlp-line-strong: #b9bec5;
	--tlp-surface: #ffffff;
	--tlp-surface-alt: #f6f7f8;
	--tlp-accent: #14532d;
	--tlp-accent-ink: #ffffff;
	--tlp-urgent: #a5321a;
	--tlp-urgent-bg: #fdf0ec;
	--tlp-error: #8f1d1d;
	--tlp-error-bg: #fcf0f0;
	--tlp-ok: #1c5137;
	--tlp-ok-bg: #eef6f1;
	--tlp-radius: 6px;
	--tlp-gap: 1rem;
}

/* Typography stays scoped - it should not leak to the whole document.
   The list is now the real wrapper classes, all of them. */
.tlp-tradesperson-dashboard,
.tlp-customer-dashboard,
.tlp-available-jobs-page,
.tlp-job-submission-form,
.tlp-tradesperson-registration,
.tlp-verify-listing,
.tlp-inline-login,
.tlp-panel {
	color: var(--tlp-ink);
	line-height: 1.55;
}

/* --- Shared primitives ------------------------------------------------ */

.tlp-panel {
	max-width: 42rem;
	margin: 0 auto;
	padding: 1.5rem;
	background: var(--tlp-surface);
	border: 1px solid var(--tlp-line);
	border-radius: var(--tlp-radius);
}

/*
 * padding rather than margin, deliberately: several of these sit
 * directly under a field with its own margin-bottom, and padding does
 * not collapse against it the way a margin would.
 */
.tlp-muted {
	color: var(--tlp-ink-soft);
	font-size: 0.9em;
	padding-top: 0.5rem;
}

.tlp-right {
	text-align: right;
}

.tlp-empty {
	padding: 1.25rem;
	background: var(--tlp-surface-alt);
	border-radius: var(--tlp-radius);
	color: var(--tlp-ink-soft);
}

.tlp-section-head {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tlp-gap);
	align-items: baseline;
	justify-content: space-between;
}

.tlp-section-head h2 {
	margin-bottom: 0;
}

/* Buttons: one shape, three weights. Minimum 44px high everywhere -
   these get tapped on a phone screen in daylight, often in a hurry. */
/*
 * The :not() exclusions are load-bearing, not tidiness.
 *
 * These blanket "every button inside the dashboard" selectors have a
 * specificity of 0,1,1 - which quietly beat the 0,1,0 of
 * `.tlp-accordion-trigger { display: none }` further down, so the mobile
 * accordion headers were rendering as visible buttons on desktop. That
 * is where the stray "Billing" button above the payment card came from.
 *
 * Excluding them here fixes the cause. Raising the specificity of the
 * hide rule instead would have fixed the symptom and left the next
 * blanket button rule to reintroduce it.
 */
/*
 * .tlp-btn is doubled to raise specificity from 0,1,0 to 0,2,0.
 *
 * Themes routinely style buttons as `.entry-content button` or
 * `.wp-block-post-content button` (0,1,1), which beats a single class
 * and loads after plugin CSS. That is why the "Apply" button on the
 * filter bar was rendering in the theme's purple instead of the
 * platform's own styling - nothing was broken, the theme simply won.
 *
 * Doubling the class is the least invasive way to win that without
 * !important, which would then have to be fought by anything trying to
 * customise these later.
 *
 * .tlp-available-jobs-page was also missing from this group entirely,
 * so every button on the standalone jobs page fell back to the theme.
 */
.tlp-tradesperson-dashboard button.tlp-btn.tlp-btn,
.tlp-customer-dashboard button.tlp-btn.tlp-btn,
.tlp-available-jobs-page button.tlp-btn.tlp-btn,
.tlp-verify-listing button.tlp-btn.tlp-btn,
.tlp-job-submission-form button.tlp-btn.tlp-btn,
.tlp-tradesperson-registration button.tlp-btn.tlp-btn,
.tlp-panel button.tlp-btn.tlp-btn,
.tlp-btn.tlp-btn.tlp-btn,
.tlp-tradesperson-dashboard button:not(.tlp-btn):not(.tlp-accordion-trigger):not(.tlp-btn-link):not([role="tab"]),
.tlp-customer-dashboard button:not(.tlp-btn):not(.tlp-accordion-trigger):not(.tlp-btn-link):not([role="tab"]),
.tlp-available-jobs-page button:not(.tlp-btn):not(.tlp-accordion-trigger):not(.tlp-btn-link):not([role="tab"]),
.tlp-verify-listing button:not(.tlp-btn):not(.tlp-accordion-trigger):not(.tlp-btn-link):not([role="tab"]),
.tlp-job-submission-form button:not(.tlp-btn):not(.tlp-accordion-trigger):not(.tlp-btn-link):not([role="tab"]),
.tlp-tradesperson-registration button:not(.tlp-btn):not(.tlp-accordion-trigger):not(.tlp-btn-link):not([role="tab"]),
.tlp-panel button:not(.tlp-btn):not(.tlp-accordion-trigger):not(.tlp-btn-link):not([role="tab"]) {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 44px;
	padding: 0.55rem 1.1rem;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface);
	color: var(--tlp-ink);
	font: inherit;
	font-weight: 600;
	text-decoration: none;
	cursor: pointer;
}

/*
 * WHY THE SELECTORS ARE WRITTEN OUT PER WRAPPER
 *
 * Every plugin button variant is listed once per page wrapper with the
 * element included - (0,4,1) - and again as a bare repeated class for
 * anywhere outside a wrapper.
 *
 * The bare (0,3,0) form alone was not enough on the live site: the
 * decline link came out white on white, and the filter bar's Apply
 * button lost its text the same way. Themes target buttons by element
 * and by [type="submit"], and those add up faster than they look.
 *
 * Verbose, and deliberately so. The alternative is another round of
 * escalation every time a different button turns out to be losing, which
 * is what the last four releases were.
 */

/*
 * WHY THE CLASS NAMES REPEAT
 *
 * .tlp-btn.tlp-btn.tlp-btn is (0,3,0). That is deliberate and it is
 * stated once here rather than re-derived every time something looks
 * wrong, because counting specificity by eye has now produced three
 * separate visual bugs in this stylesheet:
 *
 *   - themes commonly style `.entry-content button` at (0,1,1), which
 *     beats a single class and loads after plugin CSS;
 *   - the wrapper catch-alls below are (0,3,1), which beat a double
 *     class, so a modifier like .tlp-btn-primary lost to the base;
 *   - :not() arguments and attribute selectors each count as a class,
 *     which is what made the catch-alls that strong in the first place.
 *
 * Repeating the class puts every deliberately-styled button above both,
 * and the variants sit later in the file so at equal specificity the
 * modifier beats the base by source order.
 */

/*
 * :not(.tlp-btn) is load-bearing too.
 *
 * These wrapper selectors are (0,3,1) - an attribute selector and each
 * :not() argument count as a class - so they beat .tlp-btn.tlp-btn-primary
 * at (0,2,0). A catch-all meant as a safety net for unclassed buttons
 * was therefore overriding deliberate styling: "Buy this lead" came out
 * white while "Not for me - pass" came out solid green, exactly the
 * wrong way round. Excluding anything already carrying .tlp-btn leaves
 * the net doing only what it is for.
 */

/*
 * The .tlp-btn-link exclusions are load-bearing.
 *
 * `.tlp-panel button[type="submit"]:not(.tlp-accordion-trigger)` is
 * (0,3,1) - an attribute selector and a :not() argument each count as a
 * class - so it comfortably beat the (0,2,0) rule that was trying to
 * style the "Resend" button as a link, and every submit button in a
 * panel came out as a solid green pill regardless.
 *
 * Fixed by excluding a marker class rather than by naming the resend
 * button, so the next button that should not look like a button is
 * already handled.
 */
.tlp-tradesperson-dashboard button.tlp-btn-primary.tlp-btn-primary,
.tlp-customer-dashboard button.tlp-btn-primary.tlp-btn-primary,
.tlp-available-jobs-page button.tlp-btn-primary.tlp-btn-primary,
.tlp-verify-listing button.tlp-btn-primary.tlp-btn-primary,
.tlp-job-submission-form button.tlp-btn-primary.tlp-btn-primary,
.tlp-tradesperson-registration button.tlp-btn-primary.tlp-btn-primary,
.tlp-panel button.tlp-btn-primary.tlp-btn-primary,
.tlp-btn.tlp-btn-primary.tlp-btn-primary,
.tlp-tradesperson-dashboard button[type="submit"]:not(.tlp-btn):not(.tlp-accordion-trigger):not(.tlp-btn-link),
.tlp-available-jobs-page button[type="submit"]:not(.tlp-btn):not(.tlp-accordion-trigger):not(.tlp-btn-link),
.tlp-panel button[type="submit"]:not(.tlp-btn):not(.tlp-accordion-trigger):not(.tlp-btn-link) {
	background: var(--tlp-accent);
	border-color: var(--tlp-accent);
	color: var(--tlp-accent-ink);
}

/*
 * The hidden attribute is enforced explicitly.
 *
 * [hidden] { display: none } is a user-agent rule with almost no
 * specificity, and the doubled-class button rule above (0,2,0) beats it
 * - so every button the step script hid stayed on screen, which is why
 * "Post my job" appeared on step one alongside Back and Continue. Any
 * element given a display value has to restate this or the attribute
 * silently stops working.
 */
.tlp-btn.tlp-btn.tlp-btn[hidden] {
	display: none;
}

.tlp-tradesperson-dashboard button.tlp-btn-quiet.tlp-btn-quiet,
.tlp-customer-dashboard button.tlp-btn-quiet.tlp-btn-quiet,
.tlp-available-jobs-page button.tlp-btn-quiet.tlp-btn-quiet,
.tlp-verify-listing button.tlp-btn-quiet.tlp-btn-quiet,
.tlp-job-submission-form button.tlp-btn-quiet.tlp-btn-quiet,
.tlp-tradesperson-registration button.tlp-btn-quiet.tlp-btn-quiet,
.tlp-panel button.tlp-btn-quiet.tlp-btn-quiet,
.tlp-btn.tlp-btn-quiet.tlp-btn-quiet {
	background: transparent;
	border-color: transparent;
	color: var(--tlp-ink-soft);
	font-weight: 500;
}

.tlp-btn.tlp-btn[disabled],
.tlp-panel button[disabled],
.tlp-available-jobs-page button[disabled]:not(.tlp-btn):not(.tlp-accordion-trigger),
.tlp-tradesperson-dashboard button[disabled]:not(.tlp-btn):not(.tlp-accordion-trigger) {
	opacity: 0.5;
	cursor: not-allowed;
}

.tlp-btn:focus-visible,
.tlp-panel :focus-visible,
.tlp-tradesperson-dashboard :focus-visible,
.tlp-customer-dashboard :focus-visible,
.tlp-available-jobs-page :focus-visible {
	outline: 3px solid var(--tlp-accent);
	outline-offset: 2px;
}

/* --- Forms ------------------------------------------------------------ */

.tlp-form .tlp-field,
.tlp-job-submission-form p {
	margin-bottom: 1.1rem;
}

.tlp-form label,
.tlp-job-submission-form label {
	display: block;
	margin-bottom: 0.3rem;
	font-weight: 600;
}

.tlp-form input[type="text"],
.tlp-form input[type="email"],
.tlp-form input[type="tel"],
.tlp-form input[type="password"],
.tlp-form input[type="number"],
.tlp-form select,
.tlp-form textarea,
.tlp-job-submission-form input[type="text"],
.tlp-job-submission-form input[type="email"],
.tlp-job-submission-form input[type="tel"],
.tlp-job-submission-form select,
.tlp-job-submission-form textarea {
	width: 100%;
	min-height: 44px;
	padding: 0.55rem 0.7rem;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface);
	font: inherit;
	color: inherit;
}

.tlp-fieldset {
	margin: 0 0 1.25rem;
	padding: 0;
	border: 0;
}

.tlp-fieldset legend {
	margin-bottom: 0.5rem;
	padding: 0;
	font-weight: 600;
}

/* Whole-row hit area for radio choices - a 16px radio dot is not a
   thumb target, and this form is answered on phones. */
.tlp-choice {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	min-height: 44px;
	margin-bottom: 0.4rem;
	padding: 0.4rem 0.7rem;
	border: 1px solid var(--tlp-line);
	border-radius: var(--tlp-radius);
	font-weight: 400;
	cursor: pointer;
}

.tlp-choice:hover {
	border-color: var(--tlp-line-strong);
}

.tlp-rating-scale {
	display: flex;
	gap: 0.5rem;
}

.tlp-rating-option {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.4rem;
	min-height: 48px;
	border: 1px solid var(--tlp-line);
	border-radius: var(--tlp-radius);
	cursor: pointer;
}

.tlp-scale-hint {
	margin-top: 0.5rem;
}

.tlp-consent {
	padding: 0.9rem;
	background: var(--tlp-surface-alt);
	border-radius: var(--tlp-radius);
}

.tlp-consent label {
	display: flex;
	gap: 0.6rem;
	align-items: flex-start;
	font-weight: 400;
}

/*
 * One flex item holding the whole sentence.
 *
 * Without this the links inside the label become flex items of their
 * own and are laid out as separate columns - which is what pushed
 * "privacy notice" to the right-hand edge with the closing bracket
 * stranded beyond it.
 */
.tlp-consent-text {
	flex: 1 1 auto;
	line-height: 1.5;
}

/* The checkbox keeps its size in a flex row, which otherwise shrinks it
   to a sliver on a narrow screen. */
.tlp-consent input[type="checkbox"] {
	flex: 0 0 auto;
	width: 1.15rem;
	height: 1.15rem;
	margin: 0.15rem 0 0;
}

.tlp-consent-text a {
	color: var(--tlp-accent);
}

/* --- Messages --------------------------------------------------------- */

.tlp-form-errors,
.tlp-success-banner,
.tlp-verification-banner {
	margin-bottom: 1.25rem;
	padding: 0.9rem 1.1rem;
	border-left: 4px solid;
	border-radius: 0 var(--tlp-radius) var(--tlp-radius) 0;
}

.tlp-form-errors {
	border-color: var(--tlp-error);
	background: var(--tlp-error-bg);
	color: var(--tlp-error);
}

.tlp-form-errors ul {
	margin: 0;
	padding-left: 1.1rem;
}

.tlp-form-errors:empty {
	display: none;
}

.tlp-success-banner {
	border-color: var(--tlp-ok);
	background: var(--tlp-ok-bg);
	color: var(--tlp-ok);
}

.tlp-verification-banner {
	border-color: var(--tlp-urgent);
	background: var(--tlp-urgent-bg);
}

.tlp-form-errors p:last-child,
.tlp-success-banner p:last-child,
.tlp-verification-banner p:last-child {
	margin-bottom: 0;
}

/* --- Job board -------------------------------------------------------- */

.tlp-job-board-head h2 {
	margin-bottom: 0.2rem;
}

.tlp-job-filters {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	/* end, not center: every control in this bar has a label above it
	   of differing height, and aligning to the bottom is what keeps the
	   inputs themselves on one line. */
	align-items: flex-end;
	margin: 1.25rem 0;
	padding: 1rem;
	background: var(--tlp-surface-alt);
	border-radius: var(--tlp-radius);
}

.tlp-filter {
	flex: 1 1 10rem;
}

/*
 * Child combinator, deliberately. This rule styles the small label above
 * each dropdown, and those are direct children of .tlp-filter - but as a
 * descendant selector it also matched the toggle pills nested inside
 * .tlp-toggle-group, where its 0,1,1 specificity beat .tlp-toggle (0,1,0)
 * and forced them back to display: block. That is what kept the
 * alignment broken through three attempted fixes, none of which were
 * ever going to work while this rule was winning.
 *
 * Scoping it to what it was always meant to target is the fix; raising
 * .tlp-toggle's specificity would have left the same trap for the next
 * label added inside a filter.
 */
.tlp-filter > label {
	display: block;
	margin-bottom: 0.25rem;
	font-size: 0.85em;
	font-weight: 600;
	color: var(--tlp-ink-soft);
}

.tlp-filter select {
	width: 100%;
	min-height: 44px;
	padding: 0.4rem 0.6rem;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface);
	font: inherit;
}

.tlp-filter-toggles {
	flex: 0 1 auto;
}

/* Matches the <label> styling on the three dropdowns, so all four
   controls sit on one baseline instead of the checkboxes floating
   somewhere between the label row and the input row. */
.tlp-filter-legend {
	display: block;
	margin-bottom: 0.25rem;
	font-size: 0.85em;
	font-weight: 600;
	color: var(--tlp-ink-soft);
}

.tlp-toggle-group {
	display: flex;
	gap: 0.4rem;
}

/*
 * Toggle pills rather than bare checkboxes. A checkbox with a label
 * beside it gives a ~16px hit target and no visible state at a glance;
 * the pill is the full 44px tall the rest of the bar is, and its
 * checked state is legible from across the room.
 *
 * The native checkbox is kept and merely restyled rather than hidden
 * behind a fake control - it stays keyboard-operable, announces itself
 * correctly, and works if the :has() rule below is unsupported.
 */
.tlp-toggle {
	/* Block-level, not inline-flex. As an inline-level box the pill sat
	   on the parent's text baseline, so its contents were being nudged by
	   the surrounding line-height no matter how the checkbox inside was
	   sized - which is why the two previous attempts at this, both aimed
	   at the checkbox, did not hold. Verified in the real theme. */
	display: flex;
	align-items: center;
	gap: 0.45rem;
	min-height: 44px;
	margin: 0;
	padding: 0 0.85rem;
	border: 1px solid var(--tlp-line-strong);
	border-radius: 999px;
	background: var(--tlp-surface);
	font-size: 0.9em;
	font-weight: 500;
	white-space: nowrap;
	cursor: pointer;
}

/*
 * The checkbox is drawn from scratch rather than restyled.
 *
 * Sizing the native control was not enough: themes commonly render the
 * checked state with their own ::before or ::after tick, sized in their
 * own units, which overflows whatever box we set - which is why ticking
 * one made the mark sit proud of the pill and shunt the label sideways.
 * You cannot reliably constrain a pseudo-element you did not author.
 *
 * appearance: none removes the theme's rendering entirely and the tick
 * below is ours, so the result is identical whatever theme this runs in.
 * The input itself is still a real checkbox - keyboard operable, correct
 * to assistive tech, submitted normally. Only the paint changed.
 */
.tlp-toggle input[type="checkbox"] {
	appearance: none;
	-webkit-appearance: none;
	box-sizing: border-box;
	position: relative;
	flex: 0 0 auto;
	width: 1.05rem;
	height: 1.05rem;
	min-width: 0;
	min-height: 0;
	margin: 0;
	padding: 0;
	border: 1.5px solid var(--tlp-line-strong);
	border-radius: 4px;
	background: #fff;
	box-shadow: none;
	align-self: center;
	cursor: pointer;
}

/* Neutralises any tick the theme was drawing, so it cannot show through
   alongside ours. */
.tlp-toggle input[type="checkbox"]::before {
	content: none;
}

.tlp-toggle input[type="checkbox"]:checked {
	background: var(--tlp-accent);
	border-color: var(--tlp-accent);
}

/* Our tick: absolutely positioned, so it cannot affect the input's box
   and therefore cannot move the label beside it. That coupling was the
   second half of the reported fault. */
.tlp-toggle input[type="checkbox"]:checked::after {
	content: "";
	position: absolute;
	left: 50%;
	top: 50%;
	width: 0.28rem;
	height: 0.55rem;
	border: solid #fff;
	border-width: 0 2px 2px 0;
	transform: translate(-50%, -60%) rotate(45deg);
}

/* Label text: 1rem line-height against a 1.05rem box, both centred by
   the pill's align-items, so neither can drift. */
.tlp-toggle > span {
	line-height: 1;
}

.tlp-toggle:hover {
	border-color: var(--tlp-ink-soft);
}

/* Progressive: without :has() support the tick alone still shows the
   state, which is exactly what a plain checkbox would have done. */
.tlp-toggle:has(input:checked) {
	background: var(--tlp-ok-bg);
	border-color: var(--tlp-accent);
	color: var(--tlp-accent);
	font-weight: 600;
}

.tlp-toggle:has(input:focus-visible) {
	outline: 3px solid var(--tlp-accent);
	outline-offset: 2px;
}

@media (max-width: 520px) {
	/* Full-width stacked below the dropdowns rather than squeezed onto
	   a shared line at half the tap size. */
	.tlp-toggle-group {
		flex-wrap: wrap;
	}

	.tlp-toggle {
		flex: 1 1 auto;
		justify-content: center;
	}
}

.tlp-filter-actions {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	flex: 0 1 auto;
	margin-left: auto;
}

.tlp-job-list {
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * Two columns: what the job is, and what it costs / what you can do.
 *
 * The action column is a fixed track rather than content-sized, so the
 * prices line up down the page and can be compared by scanning rather
 * than by reading each row. Previously the price floated to wherever the
 * text ended.
 */
/* Boxed cards, matching the rest of the dashboard: a rule between long
   rows was not enough to tell where one job ended and the next began. */
.tlp-job-card {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 12rem;
	gap: 1rem 1.5rem;
	align-items: start;
	padding: 1.25rem;
	border: 1px solid var(--tlp-line);
	border-radius: var(--tlp-radius);
}

.tlp-job-card + .tlp-job-card {
	margin-top: 1rem;
}

@media (max-width: 600px) {
	.tlp-job-card {
		grid-template-columns: 1fr;
		gap: 0.75rem;
	}
}

.tlp-job-card-mine {
	border: 2px solid var(--tlp-accent);
}

/* Shown only when explicitly asked for, so dimmed rather than hidden -
   readable, but visibly not something to act on. */
/*
 * Dimmed, but still readable. 0.55 took body text well below a usable
 * contrast ratio - someone who deliberately ticked "include taken"
 * wants to read these, not squint at them.
 */
.tlp-job-card-dead {
	opacity: 0.72;
}

.tlp-job-card-main {
	min-width: 0;
}

/* The customer's own words, at reading size. This is the line that tells
   a tradesperson whether the job is worth buying, and it used to be the
   smallest thing on the card. */
.tlp-job-title {
	margin: 0 0 0.4rem;
	font-size: 1.05rem;
	line-height: 1.4;
	max-width: 52ch;
}

.tlp-job-tags {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem;
	margin: 0 0 0.4rem;
}

/*
 * REGRESSION FIX. The base .tlp-tag rule and the emergency/yours
 * variants were destroyed when the job-card block was rewritten in
 * v0.7.2 - a whole-block replacement that reached further than intended.
 * Nothing errored, the markup still had the classes, and the result was
 * a square grey box for the spec tag and completely unstyled text for
 * "Emergency". Restored here, and the pill radius is now in the base
 * rule where every variant inherits it.
 */
.tlp-tag {
	display: inline-flex;
	align-items: center;
	padding: 0.2rem 0.65rem;
	border-radius: 999px;
	background: var(--tlp-surface-alt);
	color: var(--tlp-ink-soft);
	font-size: 0.75rem;
	font-weight: 600;
	line-height: 1.5;
	white-space: nowrap;
}

/* Emergency earns the one loud colour on the card. It is the single
   piece of information that changes how fast someone should act, and
   it was reduced to plain grey text by the regression above. */
.tlp-tag-emergency {
	background: var(--tlp-urgent-bg);
	color: var(--tlp-urgent);
}

.tlp-tag-yours {
	background: var(--tlp-ok-bg);
	color: var(--tlp-ok);
}

/*
 * The spec tag was transparent with a 1px border, which at this size
 * reads as an empty text input rather than as a label - "Boiler Refit"
 * looked like a broken form field. Filled, no border, and quieter than
 * the emergency tag so the two do not compete.
 */
.tlp-tag-spec {
	background: transparent;
	border: 1px solid var(--tlp-line-strong);
	color: var(--tlp-ink-soft);
	text-transform: uppercase;
	letter-spacing: 0.05em;
	font-size: 0.68rem;
	padding: 0.15rem 0.65rem;
}

.tlp-job-meta {
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem 0.9rem;
	margin: 0;
	color: var(--tlp-ink-soft);
	font-size: 0.88em;
}

/* Dot separators via CSS rather than markup, so they never end up read
   aloud or copied along with the text. */
.tlp-job-meta span + span::before {
	content: "·";
	margin-right: 0.9rem;
	color: var(--tlp-line-strong);
}

/*
 * A distinct panel rather than text floating at the right-hand edge.
 * This column answers one question - what does it cost and can I have
 * it - and giving it an edge lets the eye run straight down the prices
 * without reading the job text on the way past.
 */
.tlp-job-card-action {
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	gap: 0.6rem;
	padding-left: 1.25rem;
	border-left: 1px solid var(--tlp-line);
	text-align: right;
}

@media (max-width: 600px) {
	/* Stacked on a phone, a left border and right alignment both stop
	   making sense - it becomes a row under the job instead. */
	.tlp-job-card-action {
		flex-direction: row;
		flex-wrap: wrap;
		align-items: center;
		justify-content: space-between;
		padding: 0.75rem 0 0;
		border-left: 0;
		border-top: 1px solid var(--tlp-line);
		text-align: left;
	}
}

.tlp-job-price {
	margin: 0;
	line-height: 1.2;
}

/* The label is what stops a bold "£2" being read as the job's value when
   the customer's budget is sitting a few centimetres to the left. */
.tlp-job-price-label {
	display: block;
	font-size: 0.65rem;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--tlp-ink-soft);
	margin-bottom: 0.1rem;
}

.tlp-job-price-value {
	display: block;
	font-size: 1.6rem;
	font-weight: 700;
	line-height: 1;
	font-variant-numeric: tabular-nums;
}

/*
 * Status as a chip rather than as grey text. "Taken" in muted type sat
 * directly beneath a bold price and read as a caption belonging to it;
 * as a chip it is plainly a state, and the two stop merging into one
 * ambiguous block.
 */
.tlp-job-status {
	display: inline-flex;
	align-items: center;
	padding: 0.2rem 0.6rem;
	border-radius: 999px;
	font-size: 0.75rem;
	font-weight: 600;
}

.tlp-job-status-gone {
	background: var(--tlp-surface-alt);
	color: var(--tlp-ink-soft);
}

.tlp-job-status-blocked {
	background: var(--tlp-urgent-bg);
	color: var(--tlp-urgent);
}

/* Legacy state text, kept for anywhere still using it. */
.tlp-job-state {
	color: var(--tlp-ink-soft);
	font-size: 0.88em;
}

.tlp-board-note {
	margin: 0.35rem 0 0;
}

/* Hint text under a Buy button: narrow, right-aligned with the column,
   and small enough that it reads as a footnote to the button rather
   than as another thing to act on. */
.tlp-job-card-action .tlp-muted {
	max-width: 11rem;
	font-size: 0.78rem;
	line-height: 1.35;
}

@media (max-width: 600px) {
	.tlp-job-card-action .tlp-muted {
		max-width: none;
		flex-basis: 100%;
	}
}

.tlp-pagination {
	display: flex;
	gap: var(--tlp-gap);
	align-items: center;
	justify-content: center;
	margin-top: 1.25rem;
}

/* --- Tables ----------------------------------------------------------- */

.tlp-table,
.tlp-listing-table {
	width: 100%;
	border-collapse: collapse;
	margin: 1rem 0;
}

.tlp-table th,
.tlp-table td,
.tlp-listing-table th,
.tlp-listing-table td {
	padding: 0.65rem 0.5rem;
	border-bottom: 1px solid var(--tlp-line);
	text-align: left;
	vertical-align: top;
}

.tlp-table th,
.tlp-listing-table th {
	font-size: 0.85em;
	color: var(--tlp-ink-soft);
}

.tlp-row-muted td {
	color: var(--tlp-ink-soft);
}

/* Tables become stacked rows on a phone rather than scrolling sideways,
   which nobody discovers they can do. */
@media (max-width: 640px) {
	.tlp-table thead,
	.tlp-listing-table thead {
		position: absolute;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip: rect(0 0 0 0);
		white-space: nowrap;
	}

	.tlp-table tr,
	.tlp-listing-table tr {
		display: block;
		padding: 0.75rem 0;
		border-bottom: 1px solid var(--tlp-line);
	}

	.tlp-table td,
	.tlp-listing-table td {
		display: block;
		padding: 0.15rem 0;
		border: 0;
		text-align: left;
	}

	.tlp-right {
		text-align: left;
	}
}

/* --- Purchase history / card setup ------------------------------------ */

.tlp-purchase-history {
	margin: 0;
	padding: 0;
	list-style: none;
}

.tlp-card-setup {
	padding: 1.1rem;
	background: var(--tlp-surface-alt);
	border-radius: var(--tlp-radius);
}

#tlp-card-element {
	padding: 0.75rem;
	margin-bottom: 0.75rem;
	background: var(--tlp-surface);
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
}

.tlp-offer-card {
	padding: 1.25rem;
	border: 2px solid var(--tlp-accent);
	border-radius: var(--tlp-radius);
}

/* --- Receipt ---------------------------------------------------------- */

.tlp-receipt-head {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tlp-gap);
	justify-content: space-between;
	padding-bottom: 1rem;
	border-bottom: 2px solid var(--tlp-ink);
}

.tlp-receipt-head h2 {
	margin: 0;
}

.tlp-receipt-number {
	margin: 0.2rem 0 0;
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

.tlp-receipt-issuer {
	text-align: right;
	font-size: 0.9em;
}

.tlp-receipt-issuer p,
.tlp-receipt-billed p {
	margin: 0 0 0.15rem;
}

.tlp-receipt-billed {
	margin: 1.25rem 0;
}

.tlp-receipt-table {
	width: 100%;
	border-collapse: collapse;
	margin: 1rem 0;
	font-variant-numeric: tabular-nums;
}

.tlp-receipt-table th,
.tlp-receipt-table td {
	padding: 0.6rem 0.4rem;
	border-bottom: 1px solid var(--tlp-line);
	text-align: left;
}

.tlp-receipt-total td {
	border-top: 2px solid var(--tlp-ink);
	border-bottom: 0;
	font-size: 1.1em;
	font-weight: 700;
}

.tlp-receipt-credited {
	padding: 0.9rem;
	background: var(--tlp-urgent-bg);
	border-radius: var(--tlp-radius);
}

@media print {
	.tlp-no-print,
	.tlp-receipt ~ *,
	header,
	footer,
	nav {
		display: none !important;
	}

	.tlp-receipt {
		max-width: none;
		border: 0;
		padding: 0;
	}
}

/* --- Landing pages ---------------------------------------------------- */

.tlp-related-areas {
	margin-top: 2.5rem;
	padding-top: 1.5rem;
	border-top: 1px solid var(--tlp-line, #dfe2e6);
}

.tlp-related-areas h3 {
	font-size: 1rem;
}

.tlp-related-areas ul {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem 1.25rem;
	margin: 0 0 1.5rem;
	padding: 0;
	list-style: none;
}

@media (prefers-reduced-motion: reduce) {
	* {
		animation-duration: 0.01ms !important;
		transition-duration: 0.01ms !important;
	}
}

/* --- Tabs (desktop) / accordion (mobile) ------------------------------ */

/*
 * Every hiding rule below is scoped under .tlp-tabs-ready, which only the
 * tab script adds. Without JavaScript nothing is hidden and the dashboard
 * renders as one long stacked page - longer, but entirely usable.
 */

.tlp-tabs {
	margin-top: 2rem;
}

.tlp-tablist {
	display: none;
}

.tlp-tabs-ready .tlp-tablist {
	display: flex;
	flex-wrap: wrap;
	gap: 0.25rem;
	border-bottom: 1px solid var(--tlp-line);
}

.tlp-tabs-ready .tlp-tablist [role="tab"] {
	min-height: 44px;
	padding: 0.65rem 1.1rem;
	margin-bottom: -1px;
	/* No box and no fill. A border on each made four tabs look like four
	   buttons in a row, which reads as four actions rather than as four
	   places you can already be. */
	border: 0;
	border-bottom: 3px solid transparent;
	border-radius: 0;
	background: none;
	font: inherit;
	font-weight: 600;
	color: var(--tlp-ink-soft);
	cursor: pointer;
}

.tlp-tabs-ready .tlp-tablist [role="tab"]:hover {
	color: var(--tlp-ink);
	border-bottom-color: var(--tlp-line-strong);
}

/* The active tab is marked by weight, colour and a solid rule - three
   signals, not colour alone, so it survives a greyscale screen and any
   theme that overrides the accent. */
/*
 * Marked three ways - weight, colour and a solid accent underline - so
 * it survives a greyscale screen, a theme overriding the accent, and
 * anyone who cannot separate the two greens. One signal would not.
 */
.tlp-tabs-ready .tlp-tablist [role="tab"][aria-selected="true"] {
	color: var(--tlp-accent);
	border-bottom-color: var(--tlp-accent);
	font-weight: 700;
}

.tlp-accordion-trigger {
	display: none;
}

.tlp-tabs-ready .tlp-tabpanel {
	display: none;
	padding-top: 1.5rem;
}

.tlp-tabs-ready .tlp-tabpanel.is-active {
	display: block;
}

@media (max-width: 680px) {
	.tlp-tabs-ready .tlp-tablist {
		display: none;
	}

	/* Below the breakpoint every panel is present as an accordion row;
	   the same is-active state decides which body is open, so there is
	   one piece of state rather than two widgets to keep in sync. */
	.tlp-tabs-ready .tlp-tabpanel {
		display: block;
		padding-top: 0;
		border-bottom: 1px solid var(--tlp-line);
	}

	.tlp-tabs-ready .tlp-accordion-trigger {
		display: flex;
		align-items: center;
		justify-content: space-between;
		width: 100%;
		min-height: 52px;
		padding: 0.9rem 0;
		border: 0;
		background: none;
		font: inherit;
		font-weight: 600;
		text-align: left;
		color: var(--tlp-ink);
		cursor: pointer;
	}

	.tlp-tabs-ready .tlp-accordion-trigger::after {
		content: "+";
		font-size: 1.4em;
		font-weight: 400;
		line-height: 1;
		color: var(--tlp-ink-soft);
	}

	.tlp-tabs-ready .tlp-accordion-trigger[aria-expanded="true"]::after {
		content: "−";
	}

	.tlp-tabs-ready .tlp-tabpanel .tlp-tabpanel-inner {
		display: none;
		padding-bottom: 1.25rem;
	}

	.tlp-tabs-ready .tlp-tabpanel.is-active .tlp-tabpanel-inner {
		display: block;
	}
}

.tlp-live-offer {
	margin-bottom: 2rem;
}

/* --- Payment card graphic --------------------------------------------- */

.tlp-card-graphic-wrap {
	display: flex;
	flex-wrap: wrap;
	gap: 1.5rem;
	align-items: center;
	margin-bottom: 1.25rem;
}

.tlp-card-graphic {
	position: relative;
	overflow: hidden;
	flex: 0 0 auto;
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	width: 20rem;
	max-width: 100%;
	aspect-ratio: 1.7;
	padding: 1.1rem 1.35rem 1.15rem;
	border: 2px solid var(--tlp-card-edge, rgba(255, 255, 255, 0.18));
	border-radius: 14px;
	background: var(--tlp-card-bg, linear-gradient(135deg, #2a3242 0%, #151a22 100%));
	color: #ffffff;
	box-shadow: 0 6px 18px rgba(16, 20, 26, 0.28);
}

/* A diagonal sheen across the card - the one flourish, and it is what
   makes the flat gradient read as plastic rather than as a panel. */
.tlp-card-graphic::before {
	content: '';
	position: absolute;
	inset: 0;
	background: linear-gradient(115deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.04) 38%, rgba(255, 255, 255, 0) 60%);
	pointer-events: none;
}

.tlp-card-graphic > * {
	position: relative;
}

.tlp-card-graphic-top {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: center;
	gap: 0.75rem;
}

.tlp-card-chip {
	display: block;
	width: 2.4rem;
	height: 1.75rem;
	border-radius: 5px;
	background: linear-gradient(150deg, #e8c66e 0%, #c9a24a 55%, #a9832f 100%);
	box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.18);
}

.tlp-card-brand-name {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
	justify-self: end;
	font-size: 1rem;
	font-weight: 800;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	font-style: italic;
}

.tlp-card-brand-mastercard .tlp-card-brand-name {
	font-style: normal;
	text-transform: none;
	font-weight: 700;
}

.tlp-card-mc-circles {
	display: inline-flex;
}

.tlp-card-mc-circles i {
	display: block;
	width: 1.1rem;
	height: 1.1rem;
	border-radius: 50%;
	background: #eb001b;
}

.tlp-card-mc-circles i + i {
	margin-left: -0.45rem;
	background: #f79e1b;
	mix-blend-mode: screen;
}

.tlp-card-graphic-tag {
	justify-self: center;
	padding: 0.3rem 0.85rem;
	border-radius: 999px;
	background: #ffffff;
	color: #1c2333;
	font-size: 0.68rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.tlp-card-graphic-number {
	display: flex;
	align-items: baseline;
	gap: 0.85rem;
	margin: 0.25rem 0;
	font-size: 1.25rem;
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.08em;
}

.tlp-card-last4 {
	font-size: 1.45rem;
	font-weight: 700;
	letter-spacing: 0.12em;
}

.tlp-card-graphic-foot {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 1rem;
	font-size: 0.85rem;
}

.tlp-card-graphic-name {
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.tlp-card-graphic-expiry {
	text-align: right;
	font-variant-numeric: tabular-nums;
	font-weight: 600;
}

.tlp-card-graphic-expiry-label {
	display: block;
	margin-bottom: 0.1rem;
	font-size: 0.6rem;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, 0.7);
}

/* Brand palettes. Each sets the gradient and the edge glow; everything
   else on the card is shared. */
.tlp-card-brand-visa {
	--tlp-card-bg: linear-gradient(135deg, #3d4ec4 0%, #1f2a8f 55%, #131a6a 100%);
	--tlp-card-edge: #5cc27a;
}

.tlp-card-brand-mastercard {
	--tlp-card-bg: linear-gradient(135deg, #3a2c2a 0%, #1f1614 55%, #120c0b 100%);
	--tlp-card-edge: #f79e1b;
}

.tlp-card-brand-amex {
	--tlp-card-bg: linear-gradient(135deg, #2aa1b8 0%, #12707f 55%, #0b4c57 100%);
	--tlp-card-edge: #bfe9f0;
}

.tlp-card-brand-discover {
	--tlp-card-bg: linear-gradient(135deg, #5a5f6a 0%, #2f333b 55%, #1d2026 100%);
	--tlp-card-edge: #ff6f00;
}

.tlp-card-brand-unknown,
.tlp-card-brand-diners,
.tlp-card-brand-jcb,
.tlp-card-brand-unionpay {
	--tlp-card-bg: linear-gradient(135deg, #2a3242 0%, #151a22 100%);
	--tlp-card-edge: rgba(255, 255, 255, 0.25);
}

.tlp-card-graphic-empty {
	--tlp-card-bg: var(--tlp-surface-alt);
	--tlp-card-edge: var(--tlp-line-strong);
	border-style: dashed;
	color: var(--tlp-ink-soft);
	box-shadow: none;
}

.tlp-card-graphic-empty::before {
	display: none;
}

.tlp-card-graphic-empty .tlp-card-chip {
	background: var(--tlp-line-strong);
	box-shadow: none;
}

.tlp-card-graphic-expired {
	--tlp-card-bg: linear-gradient(135deg, #4a2a2a 0%, #2a1616 100%);
	--tlp-card-edge: #c0392b;
	filter: saturate(0.5);
}

.tlp-card-graphic-expired .tlp-card-graphic-tag {
	background: #c0392b;
	color: #ffffff;
}

.tlp-card-graphic-side {
	flex: 1 1 16rem;
}

.tlp-card-graphic-side p {
	margin: 0 0 0.5rem;
}

.tlp-card-alert {
	padding: 0.75rem 0.9rem;
	border-left: 4px solid var(--tlp-urgent);
	border-radius: 0 var(--tlp-radius) var(--tlp-radius) 0;
	background: var(--tlp-urgent-bg);
}

.tlp-card-alert-bad {
	border-color: var(--tlp-error);
	background: var(--tlp-error-bg);
	color: var(--tlp-error);
}

.tlp-tag-market {
	background: #eef2fb;
	color: #274690;
}

/* --- Setup checklist -------------------------------------------------- */

/*
 * Only rendered when something is actually blocking the tradesperson, so
 * it is styled as a to-do rather than a warning - a working account never
 * sees it, and the ones that do are new rather than in trouble.
 */
.tlp-setup {
	margin-bottom: 2rem;
	padding: 1.25rem;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface-alt);
}

.tlp-setup-heading {
	margin: 0 0 0.75rem;
	font-size: 1.05rem;
}

.tlp-setup-list {
	margin: 0;
	padding: 0;
	list-style: none;
}

.tlp-setup-item {
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
	align-items: center;
	justify-content: space-between;
	padding: 0.85rem 0;
	border-top: 1px solid var(--tlp-line);
}

.tlp-setup-item:first-child {
	border-top: 0;
	padding-top: 0;
}

.tlp-setup-item > div {
	flex: 1 1 18rem;
}

.tlp-setup-title {
	margin: 0 0 0.15rem;
	font-weight: 600;
}

.tlp-setup-item p:last-child {
	margin-bottom: 0;
}

/* A left rule marks the items the person can act on, distinguishing them
   from "we're checking your details", which needs nothing from them. */
.tlp-setup-item-urgent {
	padding-left: 0.85rem;
	border-left: 3px solid var(--tlp-urgent);
}

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

/* Pairs of related fields sit side by side above a phone screen and stack
   below it. Postcode/radius and quiet-hours from/until are each a single
   idea split across two inputs, and reading them as one line is the point. */
.tlp-field-row {
	display: flex;
	flex-wrap: wrap;
	gap: 0 1rem;
}

.tlp-field-row .tlp-field {
	flex: 1 1 12rem;
}

.tlp-check-grid {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-bottom: 1rem;
}

.tlp-check-grid .tlp-choice {
	flex: 0 1 auto;
	margin-bottom: 0;
}

.tlp-profile-form .tlp-fieldset,
.tlp-boxed.tlp-fieldset {
	padding: 1.1rem 1.25rem;
	margin-bottom: 1.25rem;
	border: 1px solid var(--tlp-line);
	border-radius: var(--tlp-radius);
}

.tlp-profile-form legend,
.tlp-boxed.tlp-fieldset legend {
	padding: 0 0.4rem;
	font-size: 0.95rem;
}

/* Receipts box: the table runs edge to edge inside the border, and the
   heading row with the CSV button stays outside it. */
.tlp-receipts-box {
	padding: 0.5rem 1rem 0.25rem;
}

/* No heading in the row now - the CSV button sits alone, right-aligned,
   with room beneath it before the box. */
.tlp-section-head-actions {
	justify-content: flex-end;
	margin-bottom: 0.75rem;
}

.tlp-receipts-box .tlp-table {
	margin: 0;
}

.tlp-receipts-box .tlp-table tbody tr:last-child td {
	border-bottom: 0;
}

.tlp-inline-form {
	display: inline;
}

/* --- Billing: main column plus card sidebar --------------------------- */

/*
 * Source order is card-then-receipts so the stacked phone view leads with
 * "which card am I paying with". `order` flips it on desktop, where the
 * receipts deserve the reading width and the card is reference material.
 * Doing it this way round keeps the DOM order right for screen readers
 * and keyboard users on the layout where it matters most.
 */
.tlp-billing-layout {
	display: grid;
	gap: 2rem;
}

@media (min-width: 860px) {
	.tlp-billing-layout {
		grid-template-columns: minmax(0, 1fr) 22rem;
		align-items: start;
	}

	.tlp-billing-main {
		order: 1;
	}

	.tlp-billing-aside {
		order: 2;
		position: sticky;
		top: 1.5rem;
	}
}

.tlp-billing-aside h3 {
	margin-top: 0;
}

.tlp-billing-aside .tlp-card-graphic-wrap {
	display: block;
}

.tlp-billing-aside .tlp-card-graphic {
	width: 100%;
}

.tlp-billing-aside .tlp-card-graphic-side {
	margin-top: 1rem;
}

/* The card-setup block was a large grey panel wrapping a single button.
   In a sidebar it just needs to be the button until it has a form in it. */
.tlp-billing-aside .tlp-card-setup {
	padding: 0;
	background: none;
}

/* --- Headings inside plugin views ------------------------------------- */

/*
 * Themes commonly style h2/h3 at display sizes intended for article
 * headings. Inside a dashboard that puts "Payment card" at the same
 * visual weight as the page title and makes the whole screen shout.
 * These are capped relative to the surrounding text rather than set in
 * pixels, so a theme's own type scale still comes through.
 */
.tlp-tradesperson-dashboard h2,
.tlp-customer-dashboard h2,
.tlp-available-jobs-page h2 {
	font-size: 1.3rem;
	line-height: 1.3;
	margin-bottom: 0.5rem;
}

.tlp-tradesperson-dashboard h3,
.tlp-customer-dashboard h3,
.tlp-available-jobs-page h3 {
	font-size: 1.05rem;
	line-height: 1.35;
	margin-bottom: 0.5rem;
}

/* Collapses to nothing until the purchase handler writes to it. */
.tlp-status-line:empty {
	display: none;
}

.tlp-status-line {
	padding: 0.75rem 1rem;
	border-left: 4px solid var(--tlp-accent, #14532d);
	border-radius: 0 6px 6px 0;
	background: var(--tlp-surface-alt, #f6f7f8);
	font-weight: 600;
}

/* --- Receipt dialog --------------------------------------------------- */

.tlp-receipt-dialog {
	width: min(42rem, calc(100vw - 2rem));
	padding: 0;
	border: 0;
	border-radius: 8px;
	box-shadow: 0 12px 40px rgba(16, 20, 26, 0.28);
}

.tlp-receipt-dialog::backdrop {
	background: rgba(16, 20, 26, 0.5);
}

.tlp-receipt-dialog-chrome {
	display: flex;
	gap: 0.5rem;
	justify-content: flex-end;
	padding: 0.75rem 1rem;
	border-bottom: 1px solid var(--tlp-line, #dfe2e6);
	background: var(--tlp-surface-alt, #f6f7f8);
}

.tlp-receipt-dialog-body {
	padding: 1.25rem;
	max-height: 70vh;
	overflow-y: auto;
}

/* The receipt inside the dialog is already framed by the dialog. */
.tlp-receipt-dialog-body .tlp-panel {
	max-width: none;
	margin: 0;
	padding: 0;
	border: 0;
}

@media print {
	/* Printing with the dialog open should print the receipt, not the
	   dashboard behind it. */
	body:has(.tlp-receipt-dialog[open]) > *:not(.tlp-receipt-dialog) {
		display: none !important;
	}

	.tlp-receipt-dialog[open] {
		position: static;
		box-shadow: none;
		width: auto;
	}

	.tlp-receipt-dialog-body {
		max-height: none;
		overflow: visible;
		padding: 0;
	}
}

/* --- Purchased lead cards --------------------------------------------- */

/*
 * Each bought lead is a boxed card with its title sitting on the top
 * border, the same shape as the Profile fieldsets and the receipts box
 * - a single rule between long cards was not enough to tell where one
 * ended and the next began.
 */
.tlp-purchase-history > li {
	position: relative;
	margin: 1.6rem 0 0;
	padding: 1.5rem 1.25rem 1.25rem;
	border: 1px solid var(--tlp-line);
	border-radius: var(--tlp-radius);
}

.tlp-purchase-history > li:first-child {
	margin-top: 1rem;
}

.tlp-lead-head {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem 1rem;
	align-items: flex-start;
	justify-content: space-between;
	margin-bottom: 0.9rem;
}

/* The legend: lifted onto the border, backed with the surface colour
   so the line breaks behind it. */
.tlp-lead-title {
	position: absolute;
	top: -0.75em;
	left: 0.85rem;
	max-width: calc(100% - 1.7rem);
	margin: 0;
	padding: 0 0.4rem;
	background: var(--tlp-surface);
	font-size: 0.95rem;
	font-weight: 600;
	line-height: 1.4;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* Date and price are provenance, not the headline - the job is. They
   were previously bold and above the job title, which put the least
   useful line of the card first. */
.tlp-lead-sub {
	margin: 0;
}

.tlp-contact-block {
	padding: 1.1rem 1.25rem;
	border: 1px solid #cfe3d6;
	border-radius: var(--tlp-radius);
	background: var(--tlp-ok-bg);
}

.tlp-contact-name {
	margin: 0 0 0.75rem;
	font-size: 1.05rem;
	font-weight: 700;
}

/* The three things this block exists for, as targets rather than as
   underlined text sitting among other underlined text. */
.tlp-contact-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-bottom: 1rem;
}

.tlp-contact-actions .tlp-btn {
	flex: 0 1 auto;
}

@media (max-width: 480px) {
	/* Full-width stacked buttons on a phone: this is the screen where
	   someone actually taps Call, usually one-handed. */
	.tlp-contact-actions {
		flex-direction: column;
		align-items: stretch;
	}
}

/* Definition list rather than paragraphs, because these genuinely are
   label/value pairs - which also gives screen readers the relationship
   for free instead of a run of unlabelled lines. */
.tlp-contact-details {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	gap: 0.2rem 1rem;
	margin: 0;
	font-size: 0.92em;
}

.tlp-contact-details dt {
	color: var(--tlp-ink-soft);
	font-weight: 600;
}

.tlp-contact-details dd {
	margin: 0;
	overflow-wrap: anywhere;
}

.tlp-lead-outcome {
	margin: 0.9rem 0 0;
	color: var(--tlp-ink-soft);
	font-size: 0.92em;
}

/* --- Dispute disclosure ------------------------------------------------ */

/*
 * The gap the screenshot was missing. The <details> sat flush against
 * the bottom edge of the green contact box, so the summary read as part
 * of it - and when opened, the form fields ran straight into the box
 * above with no separation at all.
 */
.tlp-dispute-form {
	margin-top: 1.25rem;
	border: 1px solid var(--tlp-line);
	border-radius: var(--tlp-radius);
}

.tlp-dispute-form summary {
	padding: 0.75rem 1rem;
	min-height: 44px;
	display: flex;
	align-items: center;
	cursor: pointer;
	color: var(--tlp-ink-soft);
	font-size: 0.92em;
	font-weight: 600;
}

.tlp-dispute-form summary:hover {
	color: var(--tlp-ink);
}

.tlp-dispute-form[open] summary {
	border-bottom: 1px solid var(--tlp-line);
}

/* Bottom padding was 0.25rem, which put the submit button hard against
   the bottom edge of the disclosure box. Even padding all round. */
.tlp-dispute-fields {
	padding: 1.25rem;
	max-width: 34rem;
}

.tlp-dispute-fields > p:last-child {
	margin-bottom: 0;
}

.tlp-dispute-fields textarea {
	min-height: 5rem;
	resize: vertical;
}

.tlp-relist-offer {
	margin: 1.5rem 0;
	padding: 1.25rem;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface-alt);
}

.tlp-relist-offer h3 {
	margin-top: 0;
}

/* --- Purchase confirmation ------------------------------------------- */

.tlp-confirm-dialog {
	width: min(26rem, calc(100vw - 2rem));
	padding: 1.5rem;
	border: 0;
	border-radius: 10px;
	box-shadow: 0 12px 40px rgba(16, 20, 26, 0.28);
}

.tlp-confirm-dialog::backdrop {
	background: rgba(16, 20, 26, 0.5);
}

.tlp-confirm-dialog h2 {
	margin: 0 0 0.5rem;
	font-size: 1.25rem;
	line-height: 1.3;
}

.tlp-confirm-job {
	margin: 0 0 0.75rem;
	font-weight: 600;
}

.tlp-confirm-actions {
	display: flex;
	gap: 0.5rem;
	margin-top: 1.5rem;
}

/*
 * Cancel first in source order and given equal width. The confirm
 * button is the coloured one, so it is still findable - but this is a
 * dialog that appears under someone's thumb on a phone, and the
 * destructive-by-default layout (big primary button where the last tap
 * landed) is exactly what caused the mis-taps it exists to prevent.
 */
.tlp-confirm-actions .tlp-btn {
	flex: 1 1 0;
}

@media (max-width: 380px) {
	.tlp-confirm-actions {
		flex-direction: column-reverse;
	}
}

/* --- Multi-step job form ---------------------------------------------- */

/*
 * Hiding is scoped under .tlp-steps-ready, added only by the step
 * script. Without JavaScript every step renders at once and the form
 * submits normally - a form that posts a job is the last thing that
 * should depend on JS.
 */
.tlp-step-progress {
	display: flex;
	margin: 0 0 2rem;
	padding: 0;
	list-style: none;
	counter-reset: none;
}

.tlp-step-progress li {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	flex: 1 1 0;
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: 0.78rem;
	font-weight: 600;
	color: var(--tlp-ink-soft);
	text-align: center;
}

/*
 * The line leading INTO a step, drawn behind its marker and reaching
 * back to the previous one. The first step has none in the markup -
 * there is nothing before it to connect to.
 */
.tlp-step-rail {
	position: absolute;
	top: 0.85rem;
	right: 50%;
	left: -50%;
	height: 2px;
	background: var(--tlp-line);
	overflow: hidden;
}

.tlp-step-rail-fill {
	display: block;
	height: 100%;
	background: var(--tlp-accent);
	transform: scaleX(0);
	transform-origin: left center;
	/* scaleX rather than width: transforms are composited, width is
	   not, which on a mid-range phone is the difference between a smooth
	   line and a stutter. */
	transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1);
}

/*
 * A rail fills once its step has been REACHED. No half states: a step
 * you are standing on has a line behind you that is complete, and the
 * bar in front of you that is not. Half-filling the current segment
 * made step one look half done before anything had been typed.
 */
.tlp-step-progress li.is-current .tlp-step-rail-fill,
.tlp-step-progress li.is-done .tlp-step-rail-fill {
	transform: scaleX(1);
}

.tlp-step-marker {
	position: relative;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	width: 1.75rem;
	height: 1.75rem;
	margin-bottom: 0.5rem;
	border: 2px solid var(--tlp-line);
	border-radius: 50%;
	background: var(--tlp-surface);
	font-size: 0.78rem;
	font-variant-numeric: tabular-nums;
	transition: border-color 200ms, background-color 200ms, color 200ms;
}

/*
 * Three states, told apart by more than colour: an upcoming step is
 * outlined in grey, the current one is ringed in the accent with its
 * label darkened, and a completed one is filled solid. Somebody who
 * cannot separate the two greens still sees an empty circle, a ringed
 * circle and a filled one.
 */
.tlp-step-progress li.is-current .tlp-step-marker {
	border-color: var(--tlp-accent);
	color: var(--tlp-accent);
}

.tlp-step-progress li.is-current {
	color: var(--tlp-ink);
}

.tlp-step-progress li.is-done .tlp-step-marker {
	border-color: var(--tlp-accent);
	background: var(--tlp-accent);
	color: var(--tlp-accent-ink);
}

.tlp-step-label {
	display: block;
	line-height: 1.3;
}

@media (max-width: 480px) {
	/* The labels are the first thing to go: four of them across a phone
	   wrap to two lines each and turn a progress bar into a paragraph.
	   The numbered markers still say where you are. */
	.tlp-step-progress {
		margin-bottom: 1.5rem;
	}

	.tlp-step-label {
		font-size: 0.68rem;
	}
}

.tlp-steps-ready .tlp-step {
	display: none;
}

.tlp-steps-ready .tlp-step.is-active {
	display: block;
}

/* The heading receives focus on each step change; the outline would
   otherwise sit oddly around a full-width block. */
.tlp-step-heading:focus {
	outline: none;
}

.tlp-step-heading {
	margin-top: 0;
}

.tlp-step-nav {
	display: flex;
	gap: 0.5rem;
	align-items: center;
	margin-top: 1.5rem;
}

.tlp-step-nav .tlp-btn {
	flex: 0 1 auto;
}

/* Back stays left, forward motion goes right - the direction of travel
   matches where the buttons sit, which is worth more than it sounds on a
   form people fill in on a phone without reading labels closely. */
.tlp-step-nav .tlp-step-next,
.tlp-step-nav .tlp-step-submit {
	margin-left: auto;
}

/* Without the script both nav buttons would show; the Continue button
   is meaningless when every step is already visible. */
/* Continue is hidden in the markup and revealed by the script, so this
   only has to cover the case where the script ran on a different form. */
.tlp-job-submission-form form:not(.tlp-steps-ready) .tlp-step-next {
	display: none;
}

@media (max-width: 480px) {
	.tlp-step-nav {
		flex-direction: column-reverse;
	}

	.tlp-step-nav .tlp-btn {
		width: 100%;
	}

	.tlp-step-progress li {
		font-size: 0.68rem;
	}
}

/* Visually redundant next to the progress bar, but it is what a screen
   reader announces on each step change. */
.tlp-step-status {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

/* --- Verify your details ---------------------------------------------- */

/*
 * The narrowest page in the plugin, and the one with a single job: get
 * six digits typed in. Everything is sized around that field.
 */
.tlp-verify-panel {
	max-width: 30rem;
	text-align: center;
}

.tlp-verify-panel h2 {
	margin-top: 0;
	font-size: 1.4rem;
}

.tlp-verify-lead {
	margin-bottom: 0.4rem;
}

.tlp-verify-targets {
	margin: 0 0 1.75rem;
	padding: 0;
	list-style: none;
	font-weight: 600;
	/* Masked phone numbers and emails are strings of asterisks; tabular
	   digits stop them shifting about between the two lines. */
	font-variant-numeric: tabular-nums;
}

/*
 * The code input, sized like the thing it holds. A six-digit code in a
 * full-width text box looks like a comment field - this reads as a code
 * entry before anybody has read the label.
 */
.tlp-code-field {
	margin-bottom: 1.25rem;
}

/*
 * display: block, because the verify form does not carry .tlp-form and
 * therefore never picked up the label rule that every other form in the
 * plugin relies on - the label was sitting inline beside the input.
 */
.tlp-code-field label {
	display: block;
	text-align: center;
}

/*
 * Sized in ch, not rem.
 *
 * The previous 11rem cap was a guess at how wide six digits plus
 * letter-spacing would be, and it was wrong - the sixth digit was cut
 * off. ch is the width of a "0" in the current font, so 6ch of digits
 * plus 6 × 0.25em of tracking plus padding is arithmetic rather than
 * estimation, and it survives a theme changing the font.
 */
.tlp-verify-panel .tlp-code-field input {
	box-sizing: border-box;
	width: calc( 6ch + 6 * 0.25em + 1.6rem );
	max-width: 100%;
	margin: 0 auto;
	padding: 0.6rem 0.8rem;
	font-size: 1.75rem;
	font-weight: 700;
	line-height: 1.2;
	text-align: center;
	letter-spacing: 0.25em;
	/* Trailing letter-spacing sits after the last digit and pushes the
	   visual centre left; the indent puts it back. */
	text-indent: 0.25em;
	font-variant-numeric: tabular-nums;
}

.tlp-verify-panel form {
	margin-bottom: 0.75rem;
}

.tlp-verify-submit {
	width: 100%;
}

/*
 * A <button> that reads as a link.
 *
 * Used where an action must be a POST - because it changes state or
 * costs money - but should not compete visually with the real action on
 * the page. Resending a code is the case in hand: it sends an SMS, so it
 * cannot be an <a> (a GET that any prefetcher could fire, which is what
 * broke the magic-login links in v0.8.5), but two solid buttons stacked
 * read as two equal choices.
 */
/*
 * Written at (0,4,1) - wrapper, element, class three times.
 *
 * (0,3,0) was not enough against the real theme: the decline link came
 * out white text on white, visible only on hover, because the theme was
 * still supplying the colour. The stand-in theme used in testing styled
 * `.entry-content button` at (0,1,1), which this cleared easily - so
 * the test passed and the site did not. The stand-in has been raised to
 * match.
 *
 * Including the element (`button`) matters as much as the wrapper here:
 * themes target buttons by element, and an element in the selector
 * costs nothing while breaking ties against anything that does the same.
 */
.tlp-tradesperson-dashboard button.tlp-btn-link.tlp-btn-link,
.tlp-customer-dashboard button.tlp-btn-link.tlp-btn-link,
.tlp-available-jobs-page button.tlp-btn-link.tlp-btn-link,
.tlp-verify-listing button.tlp-btn-link.tlp-btn-link,
.tlp-job-submission-form button.tlp-btn-link.tlp-btn-link,
.tlp-tradesperson-registration button.tlp-btn-link.tlp-btn-link,
.tlp-panel button.tlp-btn-link.tlp-btn-link,
.tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-verify-panel .tlp-btn-link {
	display: inline;
	width: auto;
	min-height: 0;
	padding: 0.35rem 0.25rem;
	border: 0;
	border-radius: 0;
	background: none;
	color: var(--tlp-ink-soft);
	font: inherit;
	font-size: 0.9rem;
	font-weight: 500;
	text-decoration: underline;
	cursor: pointer;
}

.tlp-tradesperson-dashboard button.tlp-btn-link.tlp-btn-link:hover,
.tlp-customer-dashboard button.tlp-btn-link.tlp-btn-link:hover,
.tlp-available-jobs-page button.tlp-btn-link.tlp-btn-link:hover,
.tlp-verify-listing button.tlp-btn-link.tlp-btn-link:hover,
.tlp-panel button.tlp-btn-link.tlp-btn-link:hover,
.tlp-btn-link.tlp-btn-link.tlp-btn-link:hover {
	color: var(--tlp-ink);
}

/*
 * Resend is a link, not a button.
 *
 * Two solid buttons stacked reads as two equal choices, and this is not
 * one - it is the fallback for the small number of people whose code did
 * not arrive. Stripped back to text so Verify is unambiguously the
 * action on this page.
 *
 * It is still a real <button> inside a real form, because resending has
 * to be a POST - it changes state and costs an SMS. Making it an <a>
 * would mean a GET that any prefetcher could trigger, which is the same
 * trap the magic-login links fell into.
 *
 * Specificity has to beat .tlp-btn.tlp-btn (0,2,0), hence the panel
 * prefix.
 */
.tlp-verify-panel .tlp-resend-btn {
	display: inline;
	width: auto;
	min-height: 0;
	padding: 0.35rem 0.25rem;
	border: 0;
	background: none;
	color: var(--tlp-ink-soft);
	font-size: 0.9rem;
	font-weight: 500;
	text-decoration: underline;
	cursor: pointer;
}

.tlp-verify-panel .tlp-resend-btn:hover {
	color: var(--tlp-ink);
}

.tlp-verify-panel .tlp-form-errors,
.tlp-verify-panel .tlp-verification-banner {
	text-align: left;
}

/* --- Job photos -------------------------------------------------------- */

.tlp-job-photos {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem;
	margin: 0.75rem 0 0;
	padding: 0;
	list-style: none;
}

.tlp-job-photos li {
	margin: 0;
	padding: 0;
	list-style: none;
	line-height: 0;
}

.tlp-job-photos img {
	display: block;
	width: 64px;
	height: 64px;
	/* object-fit rather than letting the browser letterbox: a phone
	   photo of a boiler is portrait and a photo of a roof is landscape,
	   and a row of mismatched shapes reads as broken. */
	object-fit: cover;
	border-radius: var(--tlp-radius);
	border: 1px solid var(--tlp-line);
}

.tlp-job-photos-large img {
	width: 108px;
	height: 108px;
}

/*
 * Sized to match a thumbnail exactly.
 *
 * It was inheriting .tlp-photo-link's display:block and line-height:0
 * (correct for an <img>, wrong for text) and had no dimensions of its
 * own, so it collapsed to the height of its own label and sat as an odd
 * short box in a row of squares. The dimensions are shared with the
 * thumbnails below rather than repeated, so the two cannot drift.
 */
.tlp-job-photos .tlp-job-photos-more {
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	width: 64px;
	height: 64px;
	padding: 0 0.4rem;
	border: 1px dashed var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	color: var(--tlp-ink-soft);
	font-size: 0.78rem;
	font-weight: 600;
	line-height: 1.2;
	text-align: center;
	text-decoration: none;
}

.tlp-job-photos-large .tlp-job-photos-more {
	width: 108px;
	height: 108px;
	font-size: 0.85rem;
}

.tlp-job-photos .tlp-job-photos-more:hover {
	border-color: var(--tlp-ink-soft);
	color: var(--tlp-ink);
}

.tlp-job-photos-large a:focus-visible img {
	outline: 3px solid var(--tlp-accent);
	outline-offset: 2px;
}

/* --- Photo lightbox ---------------------------------------------------- */

.tlp-photo-link {
	display: block;
	line-height: 0;
	border-radius: var(--tlp-radius);
}

.tlp-photo-link:focus-visible {
	outline: 3px solid var(--tlp-accent);
	outline-offset: 2px;
}

.tlp-photo-dialog {
	position: fixed; /* positioning context for the overlaid arrows */
	width: min(56rem, calc(100vw - 1.5rem));
	max-width: none;
	padding: 0;
	border: 0;
	border-radius: 10px;
	background: #14171a;
	color: #f4f6f8;
	overflow: hidden;
}

.tlp-photo-dialog::backdrop {
	background: rgba(10, 12, 15, 0.82);
}

.tlp-photo-chrome {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0.75rem 1rem;
	font-size: 0.85rem;
	font-variant-numeric: tabular-nums;
}

.tlp-photo-close {
	min-width: 44px;
	min-height: 44px;
	border: 0;
	background: none;
	color: #f4f6f8;
	font-size: 1.1rem;
	line-height: 1;
	cursor: pointer;
}

.tlp-photo-stage {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 40vh;
	/* Caps the image at the viewport rather than the other way round -
	   a 4000px phone photo should not make the dialog scroll. */
	max-height: 78vh;
	padding: 0 1rem 1rem;
}

.tlp-photo-stage img {
	max-width: 100%;
	max-height: 72vh;
	object-fit: contain;
	border-radius: 4px;
}

/*
 * The arrows sit over the image, vertically centred, and are 48px so
 * they are usable with a thumb. Positioned rather than in a row beneath,
 * because on a phone the useful place for "next" is where the thumb
 * already is.
 */
.tlp-photo-nav {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: 48px;
	height: 48px;
	border: 0;
	border-radius: 50%;
	background: rgba(20, 23, 26, 0.72);
	color: #ffffff;
	font-size: 1.6rem;
	line-height: 1;
	cursor: pointer;
}

.tlp-photo-nav:hover {
	background: rgba(20, 23, 26, 0.9);
}

.tlp-photo-prev {
	left: 0.75rem;
}

.tlp-photo-next {
	right: 0.75rem;
}

.tlp-photo-nav[hidden] {
	display: none;
}

/*
 * Defined here rather than assumed from the theme. Most themes ship a
 * .screen-reader-text rule, but "most" is not a guarantee - and where it
 * is missing, every visually-hidden label in this plugin renders as
 * stray visible text instead. Scoped to the plugin so it cannot conflict
 * with a theme's own version.
 */
.tlp-job-photos .screen-reader-text,
.tlp-panel .screen-reader-text,
.tlp-receipt .screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
	border: 0;
}

.tlp-postcode-note {
	display: block;
	margin-top: 0.35rem;
	font-size: 0.85rem;
	font-weight: 600;
	min-height: 1.2em; /* reserves the line so the form does not jump */
}

.tlp-postcode-note.is-checking {
	color: var(--tlp-ink-soft);
	font-weight: 400;
}

.tlp-postcode-note.is-found {
	color: var(--tlp-ok);
}

.tlp-postcode-note.is-error {
	color: var(--tlp-error);
}

/* --- Address lookup ---------------------------------------------------- */

/*
 * Sits under the postcode field rather than beside it: on a phone a
 * button next to an input either shrinks the input or wraps anyway, and
 * a full-width target is easier to hit than a narrow one.
 */
.tlp-find-address {
	margin-top: 0.5rem;
}

.tlp-address-picker select {
	width: 100%;
	min-height: 44px;
	padding: 0.55rem 0.7rem;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface);
	font: inherit;
}

.tlp-address-picker[hidden] {
	display: none;
}

/* --- Job cards on a phone --------------------------------------------- */

@media (max-width: 600px) {
	/*
	 * The dot separators go.
	 *
	 * They are drawn with `span + span::before`, which is fine on one
	 * line and wrong the moment the row wraps: the dot belongs to the
	 * item AFTER it, so every wrapped line starts with a stray "·"
	 * hanging in the left margin. Stacking the items one per line and
	 * dropping the dots is both cleaner and shorter to read than trying
	 * to make an inline separator survive wrapping.
	 */
	.tlp-job-meta {
		display: block;
		font-size: 0.82rem;
		line-height: 1.5;
	}

	.tlp-job-meta span {
		display: block;
	}

	.tlp-job-meta span + span::before {
		content: none;
	}

	/* Postcode and distance read as one fact, so they stay together
	   rather than taking two of the four lines. */
	.tlp-job-meta span:first-child {
		display: inline;
	}

	.tlp-job-meta span:first-child + span {
		display: inline;
	}

	.tlp-job-meta span:first-child + span::before {
		content: "·";
		margin: 0 0.4rem;
		color: var(--tlp-line-strong);
	}

	.tlp-job-card {
		gap: 0.6rem;
		padding: 1rem;
	}

	.tlp-job-title {
		font-size: 1rem;
		line-height: 1.35;
	}

	.tlp-job-tags {
		gap: 0.3rem;
		margin-bottom: 0.3rem;
	}

	.tlp-tag {
		padding: 0.15rem 0.5rem;
		font-size: 0.7rem;
	}

	.tlp-job-photos {
		gap: 0.3rem;
		margin-top: 0.6rem;
	}

	.tlp-job-photos img,
	.tlp-job-photos .tlp-job-photos-more {
		width: 56px;
		height: 56px;
	}

	/* "+2 more" wrapped onto two lines inside the smaller tile. */
	.tlp-job-photos .tlp-job-photos-more {
		padding: 0 0.2rem;
		font-size: 0.68rem;
	}

	/*
	 * Price and button on one line, hint underneath. The action block
	 * was taking four lines of a small screen to say "£2" and "Buy",
	 * which pushed the next job most of a scroll away.
	 */
	.tlp-job-card-action {
		gap: 0.4rem 0.75rem;
		padding-top: 0.6rem;
	}

	.tlp-job-price {
		display: flex;
		align-items: baseline;
		gap: 0.5rem;
	}

	.tlp-job-price-label {
		margin-bottom: 0;
		font-size: 0.65rem;
	}

	.tlp-job-price-value {
		font-size: 1.25rem;
	}

	.tlp-job-card-action .tlp-btn {
		/* A primary action worth a full-width target at this size. */
		flex: 1 1 100%;
		order: 3;
	}

	.tlp-job-card-action .tlp-muted {
		order: 4;
		flex-basis: 100%;
		margin-top: -0.1rem;
	}

	.tlp-job-board-head h2 {
		font-size: 1.15rem;
	}
}

/* --- Title row and meta icons ------------------------------------------ */

.tlp-job-titlerow {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0.4rem 0.9rem;
	margin-bottom: 0.4rem;
}

/* 0 1 auto, not 1 1 auto: growing the title pushed the age to the far
   edge of the column, which reads as an unrelated column rather than as
   "posted an hour ago" belonging to this job. It should sit against the
   title, wrapping beneath only when the title is long enough to need
   the room. */
.tlp-job-titlerow .tlp-job-title {
	margin: 0;
	flex: 0 1 auto;
}

/* Pushed to the right of the title, and quieter than it - freshness is
   context for the headline, not a competing headline. */
.tlp-job-age {
	flex: 0 0 auto;
	color: var(--tlp-ink-soft);
	font-size: 0.82rem;
	white-space: nowrap;
}

/*
 * Icons are decorative: every one sits beside text that already says
 * the same thing. Fixed width so the labels line up down the column
 * even though the glyphs differ in width, and inherited colour so they
 * never draw more attention than the text they belong to.
 */
.tlp-job-meta i,
.tlp-job-age i {
	width: 1em;
	margin-right: 0.3rem;
	text-align: center;
	color: var(--tlp-line-strong);
	font-size: 0.9em;
}

@media (max-width: 600px) {
	/* Stacked, the age no longer has a right-hand side to sit on - it
	   goes under the title as its own line. */
	.tlp-job-titlerow {
		gap: 0.15rem;
	}

	.tlp-job-titlerow .tlp-job-title {
		flex-basis: 100%;
	}
}

/* Live character count under a capped field. maxlength on its own just
   stops accepting keystrokes, which reads as a fault rather than a
   limit; this is what makes the cap explain itself. */
.tlp-char-count {
	display: block;
	margin-top: 0.3rem;
	font-size: 0.8rem;
}

.tlp-char-count.is-low {
	color: var(--tlp-urgent);
	font-weight: 600;
}

.tlp-lead-description {
	margin: 0 0 0.9rem;
	max-width: 60ch;
}

/* --- Live offer actions ------------------------------------------------ */

.tlp-offer-card .tlp-buy-lead-btn {
	margin-top: 0.75rem;
}

/* Sits beside Buy as text, not as a second button. Passing on a lead is
   a legitimate choice but it is not the one the card is asking for, and
   two filled buttons side by side read as two equal options. */
.tlp-offer-card .tlp-decline-lead-btn {
	margin-left: 0.9rem;
}

.tlp-decline-note {
	/* Was sitting hard against the buttons, reading as a label on the
	   one next to it rather than as a note about the choice. */
	margin-top: 0.9rem;
	max-width: 60ch;
}

.tlp-verify-why {
	margin-top: 1.75rem;
	padding-top: 1.25rem;
	border-top: 1px solid var(--tlp-line);
	text-align: left;
	font-size: 0.9rem;
}

.tlp-verify-why p {
	margin: 0 0 0.6rem;
}

.tlp-verify-why p:last-child {
	margin-bottom: 0;
}

/* --- Emergency staleness caution --------------------------------------- */

/*
 * Amber rather than red. Red on a card that also carries a red
 * "Emergency" tag would read as the same signal repeated, and this is a
 * caution about age, not about urgency.
 */
.tlp-job-caution {
	display: flex;
	align-items: flex-start;
	gap: 0.5rem;
	margin: 0.75rem 0 0;
	padding: 0.6rem 0.75rem;
	border-radius: var(--tlp-radius);
	background: #fdf6e7;
	color: #6b4e12;
	font-size: 0.85rem;
	line-height: 1.45;
	max-width: 56ch;
}

.tlp-job-caution i {
	margin-top: 0.15rem;
	color: #b7861a;
}

/* --- Outcome form ------------------------------------------------------ */

.tlp-outcome-form .tlp-field {
	margin-top: 1.5rem;
}

/*
 * The comment box was a bare textarea hanging under a run of boxed
 * radio options, so it read as unrelated to the questions above it.
 */
.tlp-outcome-form textarea {
	width: 100%;
	box-sizing: border-box;
	min-height: 7rem;
	padding: 0.75rem;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface);
	font: inherit;
	line-height: 1.5;
	resize: vertical;
}

.tlp-outcome-form textarea:focus {
	outline: 3px solid var(--tlp-accent);
	outline-offset: 1px;
}

.tlp-outcome-form label[for] {
	display: block;
	margin-bottom: 0.4rem;
	font-weight: 600;
}

/* --- Trade pages -------------------------------------------------------- */

.tlp-trade-intro {
	max-width: 68ch;
}

.tlp-trade-faqs {
	margin-top: 2rem;
}

/* The FAQ accordion now uses the theme's own markup, so the theme
   styles it. The <details> rules that lived here were removed in
   v0.22.0 rather than left to fight it. */

.tlp-trade-block {
	max-width: 68ch;
	margin-bottom: 1.75rem;
}

.tlp-trade-block img {
	max-width: 100%;
	height: auto;
}

/* --- Trades index ------------------------------------------------------- */

.tlp-trade-index-list {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
	gap: 0.75rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.tlp-trade-index-item {
	margin: 0;
	padding: 0;
	list-style: none;
}

.tlp-trade-index-item a {
	display: block;
	height: 100%;
	box-sizing: border-box;
	padding: 1rem;
	border: 1px solid var(--tlp-line);
	border-radius: var(--tlp-radius);
	text-decoration: none;
	color: inherit;
}

.tlp-trade-index-item a:hover {
	border-color: var(--tlp-accent);
}

.tlp-trade-index-name {
	display: block;
	font-weight: 700;
}

.tlp-trade-index-summary {
	display: block;
	margin-top: 0.3rem;
	color: var(--tlp-ink-soft);
	font-size: 0.88rem;
	line-height: 1.45;
}

.tlp-trade-image img {
	max-width: 100%;
	height: auto;
	border-radius: var(--tlp-radius);
}

.tlp-trade-index-image {
	display: block;
	width: 100%;
	height: 140px;
	/* Cover, because trade photos will be a mix of shapes and a grid of
	   mismatched heights reads as broken. */
	object-fit: cover;
	border-radius: var(--tlp-radius);
	margin-bottom: 0.6rem;
}

/* --- Trade images as backgrounds ---------------------------------------- */

/*
 * Ready-made classes for a page builder section, so a background image
 * can be wired up without the builder needing to know the field exists.
 * Add the class to the section; the plugin supplies the image.
 *
 * The variables are only emitted when an image is actually set, so a
 * section using one falls back to whatever the builder configured
 * rather than to url() pointing at nothing.
 */
.tlp-trade-hero {
	background-image: var(--tlp-trade-image);
	background-size: cover;
	background-position: center;
}

.tlp-trade-bg-1 {
	background-image: var(--tlp-trade-block-1-image);
	background-size: cover;
	background-position: center;
}

.tlp-trade-bg-2 {
	background-image: var(--tlp-trade-block-2-image);
	background-size: cover;
	background-position: center;
}

.tlp-trade-bg-3 {
	background-image: var(--tlp-trade-block-3-image);
	background-size: cover;
	background-position: center;
}

.tlp-trade-icon {
	display: inline-flex;
	align-items: center;
}

.tlp-trade-icon img {
	width: auto;
	height: 2.5em;
	max-width: 100%;
}

/* On the index the icon replaces the photo rather than sitting with it -
   a card carrying both reads as two competing images. */
.tlp-trade-index-icon {
	display: block;
	width: 48px;
	height: 48px;
	object-fit: contain;
	margin-bottom: 0.6rem;
}

/* --- Icons inside a term grid ------------------------------------------- */

/*
 * For use inside a grid item, where a shortcode cannot know which term
 * it is on but the item's own term-<slug> class can supply the image by
 * inheritance. Drop an empty element with this class into a Custom HTML
 * element:
 *
 *     <span class="tlp-trade-icon-bg"></span>
 *
 * contain, not cover: an icon cropped to fill its box is no longer the
 * icon somebody drew.
 */
.tlp-trade-icon-bg {
	display: block;
	width: var(--tlp-icon-size, 64px);
	height: var(--tlp-icon-size, 64px);
	margin: 0 auto;
	background-image: var(--tlp-trade-icon);
	background-repeat: no-repeat;
	background-position: center;
	background-size: contain;
}

/* Nothing is drawn for a trade with no icon, rather than an empty box
   holding space in the grid. */
.tlp-trade-icon-bg:not([style*="--tlp-trade-icon"]) {
	background-image: var(--tlp-trade-icon, none);
}

/*
 * SVGs carry no intrinsic width or height in WordPress's attachment
 * metadata, so wp_get_attachment_image() renders them at 0x0 unless a
 * size is imposed. This is why an SVG icon can upload fine, appear in
 * the media library, and then render as nothing.
 */
.tlp-trade-icon img[src$=".svg"],
.tlp-trade-index-icon[src$=".svg"] {
	width: 100%;
	height: auto;
	max-height: 3em;
}

/* --- Quick start -------------------------------------------------------- */

.tlp-quick-start-fields {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
	align-items: flex-end;
}

.tlp-quick-field-service {
	/* Takes the room, because it is the harder of the two questions and
	   its option labels are long. */
	flex: 3 1 16rem;
	margin: 0;
}

.tlp-quick-field-postcode {
	flex: 1 1 9rem;
	margin: 0;
}

.tlp-quick-submit {
	flex: 0 0 auto;
	margin: 0;
}

.tlp-quick-start-fields select,
.tlp-quick-start-fields input[type="text"] {
	width: 100%;
	box-sizing: border-box;
	min-height: 48px;
	padding: 0.6rem 0.8rem;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface);
	font: inherit;
}

.tlp-quick-submit .tlp-btn {
	min-height: 48px;
}

.tlp-quick-note {
	margin: 0.75rem 0 0;
	font-size: 0.88rem;
}

@media (max-width: 640px) {
	/* Stacked, the button is the only thing that should be full width -
	   two half-width fields side by side at this size are unusable. */
	.tlp-quick-field-service,
	.tlp-quick-field-postcode,
	.tlp-quick-submit {
		flex-basis: 100%;
	}

	.tlp-quick-submit .tlp-btn {
		width: 100%;
	}
}

/* --- Photo picker -------------------------------------------------------- */

/*
 * The input is hidden but stays in the form and stays reachable from its
 * label, so clicking the label opens the picker exactly as before.
 * display:none would take it out of the accessibility tree and break
 * that; this keeps it focusable.
 */
.tlp-file-input-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	opacity: 0;
	overflow: hidden;
}

.tlp-dropzone {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0.3rem;
	min-height: 9rem;
	padding: 1.5rem 1rem;
	border: 2px dashed var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface-alt);
	text-align: center;
	cursor: pointer;
}

.tlp-dropzone:hover,
.tlp-dropzone.is-over {
	border-color: var(--tlp-accent);
	background: var(--tlp-ok-bg);
}

.tlp-dropzone:focus-visible {
	outline: 3px solid var(--tlp-accent);
	outline-offset: 2px;
}

/* Drawn in CSS rather than pulled from an icon font: this sits inside a
   form that must work before anything external has loaded. */
.tlp-dropzone-icon {
	width: 2.25rem;
	height: 1.75rem;
	border: 2px solid var(--tlp-ink-soft);
	border-radius: 3px;
	border-top-width: 6px;
	opacity: 0.65;
}

.tlp-dropzone-text {
	font-weight: 600;
}

.tlp-dropzone-hint {
	font-size: 0.85rem;
	color: var(--tlp-ink-soft);
}

.tlp-file-list {
	margin: 0.75rem 0 0;
	padding: 0;
	list-style: none;
}

.tlp-file-item {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	margin: 0 0 0.5rem;
	padding: 0.5rem;
	border: 1px solid var(--tlp-line);
	border-radius: var(--tlp-radius);
	list-style: none;
}

.tlp-file-thumb {
	flex: 0 0 auto;
	width: 48px;
	height: 48px;
	object-fit: cover;
	border-radius: 4px;
}

.tlp-file-meta {
	flex: 1 1 auto;
	min-width: 0; /* lets a long file name ellipsis rather than push the button off */
}

.tlp-file-name {
	display: block;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-weight: 600;
	font-size: 0.9rem;
}

.tlp-file-size {
	display: block;
	font-size: 0.8rem;
}

.tlp-file-status {
	margin: 0.5rem 0 0;
	font-size: 0.85rem;
	min-height: 1.2em; /* holds the line so the form does not jump */
}

.tlp-file-status.is-error {
	color: var(--tlp-error);
	font-weight: 600;
}

/* --- Emergency choice ---------------------------------------------------- */

.tlp-emergency-choice {
	margin: 1.25rem 0;
}

/*
 * A card, not a tickbox in a list. This is the only choice on the form
 * that changes what actually happens - dispatch window, chase time,
 * quiet hours, expiry and price all turn on it - so it is sized like a
 * decision rather than a preference.
 */
/*
 * Doubled class, (0,2,0).
 *
 * The form's own `.tlp-job-submission-form label` rule is (0,1,1) and
 * sets display:block - so a single class here lost, and the checkbox
 * stacked above the text instead of sitting beside it. Same counting
 * trap as the buttons in v0.17.4, recorded there at length.
 */
.tlp-emergency-card.tlp-emergency-card {
	display: flex;
	gap: 0.85rem;
	align-items: flex-start;
	padding: 1rem 1.1rem;
	border: 2px solid var(--tlp-line);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface);
	cursor: pointer;
	transition: border-color 150ms, background-color 150ms;
}

.tlp-emergency-card:hover {
	border-color: var(--tlp-line-strong);
}

/*
 * Drawn rather than restyled, for the reason learned in v0.8.7: themes
 * render a checked checkbox with their own pseudo-element, sized in
 * their own units, which overflows whatever box we set and shoves the
 * label sideways.
 */
.tlp-emergency-card input[type="checkbox"] {
	appearance: none;
	-webkit-appearance: none;
	box-sizing: border-box;
	position: relative;
	flex: 0 0 auto;
	width: 1.35rem;
	height: 1.35rem;
	margin: 0.15rem 0 0;
	padding: 0;
	border: 2px solid var(--tlp-line-strong);
	border-radius: 4px;
	background: #fff;
	box-shadow: none;
	cursor: pointer;
}

.tlp-emergency-card input[type="checkbox"]::before {
	content: none;
}

.tlp-emergency-card input[type="checkbox"]:checked {
	background: var(--tlp-urgent);
	border-color: var(--tlp-urgent);
}

.tlp-emergency-card input[type="checkbox"]:checked::after {
	content: "";
	position: absolute;
	left: 50%;
	top: 50%;
	width: 0.35rem;
	height: 0.7rem;
	border: solid #fff;
	border-width: 0 2px 2px 0;
	transform: translate(-50%, -60%) rotate(45deg);
}

.tlp-emergency-card:focus-within {
	outline: 3px solid var(--tlp-accent);
	outline-offset: 2px;
}

/* Ticked, the whole card changes - not just a 20px box somewhere in it.
   This is a state worth noticing from across the room. */
.tlp-emergency-card:has(input:checked) {
	border-color: var(--tlp-urgent);
	background: var(--tlp-urgent-bg);
}

.tlp-emergency-body {
	display: block;
}

.tlp-emergency-title {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-weight: 700;
	font-size: 1.02rem;
	color: var(--tlp-ink);
}

.tlp-emergency-title i {
	color: var(--tlp-urgent);
}

.tlp-emergency-detail {
	display: block;
	margin-top: 0.35rem;
	font-size: 0.88rem;
	line-height: 1.45;
	color: var(--tlp-ink-soft);
}

/* The restraint line is quieter than the offer above it, deliberately -
   it is a caution, not a second sales pitch. */
.tlp-emergency-caveat {
	font-style: italic;
}

/* Explains why a quick-start "Something else" lands back on step one
   rather than jumping ahead like every other handover does. */
.tlp-prefill-note {
	padding: 0.75rem 0.9rem;
	border-radius: var(--tlp-radius);
	background: var(--tlp-ok-bg);
	color: var(--tlp-ok);
	font-weight: 600;
}

/* Trade selector on the quick start, only present once the job list has
   grown past the point where one dropdown is scannable. */
.tlp-quick-field-trade {
	flex: 2 1 11rem;
	margin: 0;
}

/* --- Address suggestions ------------------------------------------------ */

/*
 * A plain list under the field rather than a floating dropdown. It
 * pushes the form down as it appears, which is the honest behaviour on
 * a phone: an overlay would cover the next field and, at the bottom of
 * a viewport, open off screen.
 */
.tlp-address-suggestions {
	margin: 0.4rem 0 0;
	padding: 0;
	list-style: none;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	overflow: hidden;
}

.tlp-address-suggestions li {
	margin: 0;
	padding: 0;
	list-style: none;
	border-bottom: 1px solid var(--tlp-line);
}

.tlp-address-suggestions li:last-child {
	border-bottom: 0;
}

.tlp-btn-link.tlp-address-suggestion,
.tlp-address-suggestion {
	display: block;
	width: 100%;
	min-height: 44px;
	padding: 0.6rem 0.8rem;
	border: 0;
	border-radius: 0;
	background: var(--tlp-surface);
	font: inherit;
	text-align: left;
	text-decoration: none;
	color: var(--tlp-ink);
	cursor: pointer;
}

.tlp-address-suggestion:hover,
.tlp-address-suggestion:focus-visible {
	background: var(--tlp-ok-bg);
}

/* --- Liveness ----------------------------------------------------------- */

.tlp-job-liveness {
	margin: 0.6rem 0 0;
	padding: 0.45rem 0.7rem;
	border-radius: var(--tlp-radius);
	font-size: 0.85rem;
	font-weight: 600;
	max-width: 56ch;
}

/*
 * Green for confirmed, amber for unanswered. Not red: an unanswered
 * lead is not a bad lead, it is one with less evidence behind it, and
 * red would say "do not buy" about jobs that are perfectly real.
 */
.tlp-job-liveness.is-confirmed {
	background: var(--tlp-ok-bg);
	color: var(--tlp-ok);
}

.tlp-job-liveness.is-unanswered {
	background: #fdf6e7;
	color: #6b4e12;
	font-weight: 500;
}

/* --- Credited receipts -------------------------------------------------- */

.tlp-receipt-refunded {
	display: inline-block;
	margin: 0.3rem 0 0;
	padding: 0.2rem 0.6rem;
	border-radius: var(--tlp-radius);
	background: var(--tlp-ok-bg);
	color: var(--tlp-ok);
	font-size: 0.85rem;
	font-weight: 600;
}

/* Muted rather than red: a credit note is a correction, not a problem,
   and the accountant reading it wants it legible rather than alarming. */
.tlp-receipt-credit td {
	color: var(--tlp-ink-soft);
}

.tlp-buy-assurance {
	display: block;
	margin-top: 0.4rem;
	font-size: 0.8rem;
	line-height: 1.35;
	max-width: 22ch;
}

/* Quieter than the figure it qualifies, but never hidden - it is the
   difference between the price shown and the price charged. */
.tlp-vat-note {
	font-size: 0.6em;
	font-weight: 500;
	color: var(--tlp-ink-soft);
	white-space: nowrap;
}

/* The struck-through original sits before the price, smaller and
   quieter, so the figure being charged is still the one read first. */
.tlp-job-price-was {
	margin-right: 0.4rem;
	font-size: 0.75em;
	font-weight: 500;
	color: var(--tlp-ink-soft);
	text-decoration: line-through;
}

/* Neutral rather than amber: a customer gathering prices has done
   nothing wrong, and this is information rather than a caution. */
.tlp-job-liveness.is-browsing {
	background: var(--tlp-surface-alt);
	color: var(--tlp-ink-soft);
	font-weight: 500;
}

/* --- Timescale cards ---------------------------------------------------- */

.tlp-timescale {
	border: 0;
	padding: 0;
	margin: 0 0 1.4rem;
}

.tlp-timescale legend {
	padding: 0;
	margin-bottom: 0.6rem;
	font-weight: 600;
}

.tlp-timescale-card.tlp-timescale-card {
	display: flex;
	gap: 0.7rem;
	align-items: flex-start;
	padding: 0.85rem 1rem;
	margin-bottom: 0.5rem;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface);
	font-weight: 400;
	cursor: pointer;
}

.tlp-timescale-card:hover {
	border-color: var(--tlp-accent);
}

/* Chosen state carried by more than colour, for the same reason the
   step markers are: colour alone is not a state anybody can rely on. */
.tlp-timescale-card:has( input:checked ) {
	border-color: var(--tlp-accent);
	border-width: 2px;
	padding: calc( 0.85rem - 1px ) calc( 1rem - 1px );
	background: var(--tlp-ok-bg);
}

.tlp-timescale-card input {
	flex: 0 0 auto;
	width: 1.15rem;
	height: 1.15rem;
	margin: 0.15rem 0 0;
}

.tlp-timescale-body {
	display: block;
}

.tlp-timescale-title {
	display: block;
	font-weight: 600;
	margin-bottom: 0.15rem;
}

.tlp-timescale-detail {
	display: block;
	font-size: 0.85rem;
	line-height: 1.4;
	color: var(--tlp-ink-soft);
}

/*
 * Guidance, not instruction.
 *
 * Set apart from the field's own help text, which otherwise ran
 * straight into it and made both read as one long caveat. A rule down
 * the left rather than a filled box or a border all round: enough to
 * separate it, not so much that it competes with the question above.
 *
 * Never styled as a warning. A customer with a small budget is telling
 * a tradesperson something true, and colouring this amber would suggest
 * they had got something wrong.
 */
.tlp-cost-guide {
	display: block;
	margin: 0.55rem 0 0.65rem;
	padding: 0.1rem 0 0.1rem 0.75rem;
	border-left: 3px solid var(--tlp-line-strong);
	max-width: 52ch;
}

.tlp-cost-guide-figure {
	display: block;
	font-size: 0.9rem;
	line-height: 1.45;
	color: var(--tlp-ink);
}

/* The provenance matters - published ranges disagree wildly - but it is
   small print, and sizing it as such is what stops it competing with
   the figure. */
.tlp-cost-guide-source {
	display: block;
	margin-top: 0.15rem;
	font-size: 0.78rem;
	line-height: 1.4;
	color: var(--tlp-ink-soft);
}

/* A pound sign that sits inside the field's own box, so the input does
   not read as a bare number the customer has to interpret. */
.tlp-input-prefixed {
	display: flex;
	align-items: stretch;
	border: 1px solid var(--tlp-line-strong);
	border-radius: var(--tlp-radius);
	overflow: hidden;
	max-width: 12rem;
}

.tlp-input-prefixed > span {
	display: flex;
	align-items: center;
	padding: 0 0.6rem;
	background: var(--tlp-surface-alt);
	color: var(--tlp-ink-soft);
	border-right: 1px solid var(--tlp-line);
}

.tlp-input-prefixed input {
	border: 0;
	border-radius: 0;
	flex: 1 1 auto;
	min-width: 0;
}

/* v0.46.0 - tradesperson mobile confirmation panel */
.tlp-phone-confirm .tlp-inline-form { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-top: 0.75rem; }
.tlp-phone-confirm .tlp-inline-form label { flex-basis: 100%; }
.tlp-phone-confirm input[type="text"] { max-width: 9rem; letter-spacing: 0.15em; font-size: 1.1rem; color: var(--tlp-ink); background: var(--tlp-surface); }
.tlp-phone-confirm label { color: var(--tlp-ink); }

/* --- Your account tab (v0.47.2) ---------------------------------------- */

.tlp-account-facts {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
	gap: 1rem 1.5rem;
	margin: 0 0 1rem;
}

.tlp-account-facts dt {
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--tlp-ink-soft);
}

.tlp-account-facts dd {
	margin: 0.15rem 0 0;
	font-weight: 500;
	color: var(--tlp-ink);
}

/*
 * The close-account block is collapsed behind a <details> so it is
 * never the first thing on the tab, and never mistaken for a form
 * that wants filling in.
 */
.tlp-danger-zone {
	margin-top: 2rem;
	border: 1px solid #ecd4d4;
	border-radius: var(--tlp-radius);
	background: #fff8f8;
}

.tlp-danger-zone summary {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.25rem 0.75rem;
	padding: 0.9rem 1.1rem;
	line-height: 1.3;
	cursor: pointer;
	list-style: none;
}

.tlp-danger-zone summary > * {
	margin: 0;
	line-height: 1.3;
}

/* .tlp-muted carries a padding-top for the field-hint case; in the
   summary row it just pushes the subtitle off-centre. */
.tlp-danger-zone summary .tlp-muted {
	padding-top: 0;
}

.tlp-danger-zone summary::-webkit-details-marker {
	display: none;
}

.tlp-danger-zone summary::after {
	content: '+';
	margin-left: auto;
	font-size: 1.25rem;
	font-weight: 600;
	line-height: 1;
	color: #991b1b;
}

.tlp-danger-zone[open] summary::after {
	content: '\2013';
}

.tlp-danger-zone-title {
	font-weight: 600;
	color: #991b1b;
}

.tlp-danger-zone-body {
	padding: 0 1.1rem 1.1rem;
	border-top: 1px solid #ecd4d4;
}

.tlp-danger-zone-body p:first-child {
	margin-top: 1rem;
}

.tlp-delete-account-confirm {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: 0.75rem 1rem;
}

.tlp-delete-account-confirm .tlp-field {
	flex: 0 1 14rem;
	margin: 0;
}

.tlp-delete-account-form input[type="text"] {
	text-transform: uppercase;
	letter-spacing: 0.08em;
}

/*
 * Same doubled-class weight as the primary button, for the same
 * reason: the theme styles <button> with enough specificity to turn
 * a single-class rule into white-on-white.
 */
.tlp-btn.tlp-btn-danger.tlp-btn-danger,
.tlp-tradesperson-dashboard button.tlp-btn.tlp-btn-danger {
	background: #991b1b;
	border-color: #991b1b;
	color: #ffffff;
}

.tlp-btn.tlp-btn-danger.tlp-btn-danger:hover,
.tlp-tradesperson-dashboard button.tlp-btn.tlp-btn-danger:hover {
	background: #7f1d1d;
	border-color: #7f1d1d;
	color: #ffffff;
}

/* v0.47.3 - remove card */
.tlp-card-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1rem; margin-bottom: 0.75rem; }
.tlp-remove-card-form { display: inline; margin: 0; }

/* --- Theme override backstop (v0.47.6) ---------------------------------
 *
 * The theme (Impreza) restyles <button>, <input> and <textarea> inside
 * content areas - with !important and selectors like
 * `.l-main button[type="submit"]` (0,2,1) - so a plain scoped rule, even
 * with !important, still loses on specificity. The symptom is white
 * text on a white control: the "Remove" link, the DELETE box, the SMS
 * code box.
 *
 * Every selector below is therefore built to out-weigh the theme's:
 * wrapper + element + attribute + the class repeated three times, which
 * lands at (0,5,1) or better. Scoped to plugin wrappers only, pins only
 * colour/background/border, and this is the one place !important is
 * used - these are the plugin's own controls in its own markup.
 */
.tlp-tradesperson-dashboard button.tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-tradesperson-dashboard button[type="submit"].tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-customer-dashboard button.tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-customer-dashboard button[type="submit"].tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-available-jobs-page button.tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-available-jobs-page button[type="submit"].tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-verify-listing button.tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-verify-listing button[type="submit"].tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-job-submission-form button.tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-job-submission-form button[type="submit"].tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-tradesperson-registration button.tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-tradesperson-registration button[type="submit"].tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-panel button.tlp-btn-link.tlp-btn-link.tlp-btn-link,
.tlp-panel button[type="submit"].tlp-btn-link.tlp-btn-link.tlp-btn-link {
	background: none !important;
	border: 0 !important;
	box-shadow: none !important;
	color: var(--tlp-ink-soft) !important;
	text-decoration: underline !important;
	text-shadow: none !important;
	padding: 0.35rem 0.25rem !important;
	min-height: 0 !important;
	font-size: 0.9rem !important;
	font-weight: 500 !important;
}

.tlp-tradesperson-dashboard button.tlp-btn-link.tlp-btn-link.tlp-btn-link:hover,
.tlp-tradesperson-dashboard button[type="submit"].tlp-btn-link.tlp-btn-link.tlp-btn-link:hover,
.tlp-customer-dashboard button.tlp-btn-link.tlp-btn-link.tlp-btn-link:hover,
.tlp-available-jobs-page button.tlp-btn-link.tlp-btn-link.tlp-btn-link:hover,
.tlp-verify-listing button.tlp-btn-link.tlp-btn-link.tlp-btn-link:hover,
.tlp-job-submission-form button.tlp-btn-link.tlp-btn-link.tlp-btn-link:hover,
.tlp-tradesperson-registration button.tlp-btn-link.tlp-btn-link.tlp-btn-link:hover,
.tlp-panel button.tlp-btn-link.tlp-btn-link.tlp-btn-link:hover {
	color: var(--tlp-ink) !important;
}

.tlp-tradesperson-dashboard button.tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-tradesperson-dashboard button[type="submit"].tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-customer-dashboard button.tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-customer-dashboard button[type="submit"].tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-available-jobs-page button.tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-verify-listing button.tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-job-submission-form button.tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-tradesperson-registration button.tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-panel button.tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-panel button[type="submit"].tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-tradesperson-dashboard a.tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger),
.tlp-panel a.tlp-btn.tlp-btn.tlp-btn:not(.tlp-btn-primary):not(.tlp-btn-danger) {
	background: var(--tlp-surface) !important;
	border-color: var(--tlp-line-strong) !important;
	color: var(--tlp-ink) !important;
	text-shadow: none !important;
}

.tlp-tradesperson-dashboard button.tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-tradesperson-dashboard button[type="submit"].tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-customer-dashboard button.tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-customer-dashboard button[type="submit"].tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-available-jobs-page button.tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-verify-listing button.tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-verify-listing button[type="submit"].tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-job-submission-form button.tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-job-submission-form button[type="submit"].tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-tradesperson-registration button.tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-tradesperson-registration button[type="submit"].tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-panel button.tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-panel button[type="submit"].tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-tradesperson-dashboard a.tlp-btn-primary.tlp-btn-primary.tlp-btn-primary,
.tlp-panel a.tlp-btn-primary.tlp-btn-primary.tlp-btn-primary {
	background: var(--tlp-accent) !important;
	border-color: var(--tlp-accent) !important;
	color: var(--tlp-accent-ink) !important;
}

.tlp-tradesperson-dashboard button.tlp-btn-danger.tlp-btn-danger.tlp-btn-danger,
.tlp-tradesperson-dashboard button[type="submit"].tlp-btn-danger.tlp-btn-danger.tlp-btn-danger,
.tlp-panel button[type="submit"].tlp-btn-danger.tlp-btn-danger.tlp-btn-danger {
	background: #991b1b !important;
	border-color: #991b1b !important;
	color: #ffffff !important;
}

.tlp-tradesperson-dashboard form input[type="text"],
.tlp-tradesperson-dashboard form input[type="tel"],
.tlp-tradesperson-dashboard form input[type="email"],
.tlp-tradesperson-dashboard form input[type="number"],
.tlp-tradesperson-dashboard form input[type="time"],
.tlp-tradesperson-dashboard form input[type="password"],
.tlp-tradesperson-dashboard form textarea,
.tlp-tradesperson-dashboard form select,
.tlp-customer-dashboard form input[type="text"],
.tlp-customer-dashboard form input[type="password"],
.tlp-customer-dashboard form textarea,
.tlp-verify-listing form input[type="text"],
.tlp-job-submission-form form input[type="text"],
.tlp-job-submission-form form input[type="tel"],
.tlp-job-submission-form form input[type="email"],
.tlp-job-submission-form form textarea,
.tlp-job-submission-form form select,
.tlp-tradesperson-registration form input[type="text"],
.tlp-tradesperson-registration form input[type="tel"],
.tlp-tradesperson-registration form input[type="email"],
.tlp-tradesperson-registration form input[type="password"],
.tlp-tradesperson-registration form input[type="date"],
.tlp-tradesperson-registration form textarea,
.tlp-tradesperson-registration form select,
.tlp-panel form input[type="text"],
.tlp-panel form input[type="tel"],
.tlp-panel form input[type="email"],
.tlp-panel form input[type="number"],
.tlp-panel form input[type="time"],
.tlp-panel form input[type="password"],
.tlp-panel form textarea,
.tlp-panel form select,
.tlp-job-submission-form input[type="text"],
.tlp-job-submission-form input[type="tel"],
.tlp-job-submission-form input[type="email"],
.tlp-job-submission-form textarea,
.tlp-job-submission-form select {
	color: var(--tlp-ink) !important;
	background-color: var(--tlp-surface) !important;
	-webkit-text-fill-color: var(--tlp-ink) !important;
}

.tlp-tradesperson-dashboard form label,
.tlp-customer-dashboard form label,
.tlp-verify-listing form label,
.tlp-job-submission-form label,
.tlp-tradesperson-registration form label,
.tlp-panel form label {
	color: var(--tlp-ink) !important;
}

/* --- Board card vertical rhythm (v0.47.13) ------------------------------
 * The left column was packed to about half the height of the action
 * column beside it. Rows now sit on a consistent 0.85rem gap so the two
 * columns read as one card. Later in the sheet so it wins over the
 * per-row margins above without touching them.
 */
.tlp-job-card-main {
	display: flex;
	flex-direction: column;
	gap: 0.85rem;
}

.tlp-job-card-main > .tlp-job-titlerow,
.tlp-job-card-main > .tlp-job-title,
.tlp-job-card-main > .tlp-job-tags,
.tlp-job-card-main > .tlp-job-meta,
.tlp-job-card-main > .tlp-job-liveness,
.tlp-job-card-main > .tlp-job-photos {
	margin: 0;
}

.tlp-job-card-main > .tlp-job-liveness:first-of-type {
	margin-top: 0.1rem;
}

/* v0.48.0 - dispute status on a bought lead */
.tlp-dispute-status {
	margin-top: 1rem;
	padding: 0.85rem 1rem;
	border: 1px solid var(--tlp-line);
	border-left-width: 4px;
	border-radius: var(--tlp-radius);
	background: var(--tlp-surface-alt);
}
.tlp-dispute-status p { margin: 0; }
.tlp-dispute-status p + p { margin-top: 0.35rem; }
/* Inline, not flex: on a phone the flex row split "Problem reported:"
   and the status into two stacked columns. The dot is an inline block
   that sits on the text baseline and wraps with it. */
.tlp-dispute-status-label { line-height: 1.45; }
.tlp-dispute-status-label strong { white-space: normal; }
.tlp-dispute-status-dot { display: inline-block; width: 0.6rem; height: 0.6rem; margin-right: 0.45rem; border-radius: 50%; background: var(--tlp-ink-soft); vertical-align: 0.05em; }
.tlp-dispute-status-waiting { border-left-color: #d97706; }
.tlp-dispute-status-waiting .tlp-dispute-status-dot { background: #d97706; }
.tlp-dispute-status-review { border-left-color: #2563eb; }
.tlp-dispute-status-review .tlp-dispute-status-dot { background: #2563eb; }
.tlp-dispute-status-refunded { border-left-color: var(--tlp-accent); }
.tlp-dispute-status-refunded .tlp-dispute-status-dot { background: var(--tlp-accent); }
.tlp-dispute-status-rejected { border-left-color: #991b1b; }
.tlp-dispute-status-rejected .tlp-dispute-status-dot { background: #991b1b; }

/* v0.49.0 - quote field on the outcome form */
.tlp-input-prefix { display: flex; align-items: center; gap: 0.4rem; }
.tlp-input-prefix > span { font-weight: 600; color: var(--tlp-ink-soft); }
.tlp-input-prefix input { max-width: 10rem; }

/* v0.50.0 - lead progress, pause, status page */
.tlp-lead-progress { margin-top: 1rem; padding: 0.9rem 1rem; border: 1px solid var(--tlp-line); border-radius: var(--tlp-radius); }
.tlp-lead-progress-label { margin: 0 0 0.5rem; font-size: 0.8rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--tlp-ink-soft); }
.tlp-progress-track { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.5rem; }
.tlp-progress-step { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.35rem 0.75rem; border-radius: 999px; font-size: 0.85rem; font-weight: 600; }
.tlp-progress-done { background: var(--tlp-ok-bg); border: 1px solid #cfe3d6; }
.tlp-progress-todo { background: var(--tlp-surface-alt); border: 1px dashed var(--tlp-line-strong); color: var(--tlp-ink-soft); font-weight: 500; }
.tlp-progress-arrow { color: var(--tlp-ink-soft); font-size: 1.2rem; line-height: 1; }
.tlp-progress-form { display: inline-flex; margin: 0; }
.tlp-progress-quote-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; margin: 0.75rem 0 0; }
.tlp-progress-quote-row label { font-size: 0.85rem; color: var(--tlp-ink-soft); margin: 0; }
.tlp-progress-quote { max-width: 8rem; font-size: 0.9rem; }
.tlp-progress-skip { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin: 0.6rem 0 0; font-size: 0.85rem; color: var(--tlp-ink-soft); }
.tlp-progress-cant { margin: 0.5rem 0 0; }
.tlp-progress-cant .tlp-btn-link { font-size: 0.85rem !important; padding: 0 !important; }
.tlp-btn-small { padding: 0.35rem 0.8rem !important; font-size: 0.85rem !important; min-height: 0 !important; }
.tlp-paused-banner { background: #fff8e6; border-color: #f0d28a; }
.tlp-job-status h1 { margin-top: 0.25rem; }
.tlp-status-state { margin: 1rem 0; padding: 1rem 1.1rem; border-radius: var(--tlp-radius); background: var(--tlp-surface-alt); }
.tlp-status-taken { background: var(--tlp-ok-bg); border: 1px solid #cfe3d6; }
.tlp-status-state strong { display: block; font-size: 1.05rem; }
.tlp-who-is-coming { margin: 1rem 0; padding: 1rem 1.1rem; border: 1px solid var(--tlp-line); border-radius: var(--tlp-radius); }
.tlp-who-is-coming > p { margin: 0; }
.tlp-who-name { font-size: 1.15rem; font-weight: 600; }
.tlp-who-facts { margin: 0.5rem 0 0.75rem 1.1rem; padding: 0; }
.tlp-who-facts li { margin: 0.2rem 0; }
.tlp-status-answers { margin-top: 1.25rem; display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.tlp-status-answers > p { flex-basis: 100%; margin: 0 0 0.25rem; }
.tlp-status-answers .tlp-inline-form { margin: 0; }

.tlp-budget-note { display: block; margin-top: 0.4rem; padding: 0.5rem 0.75rem; border-radius: var(--tlp-radius); background: #fff8e6; border: 1px solid #f0d28a; font-size: 0.9rem; }

.tlp-reset-request { margin-top: 1rem; }
.tlp-reset-request summary { cursor: pointer; color: var(--tlp-ink-soft); text-decoration: underline; }
.tlp-reset-request form { margin-top: 0.75rem; }

/* v0.51.0 - job (spec) pages and trade cost table */
.tlp-spec-cost { display: inline-block; margin: 1rem 0; padding: 1rem 1.25rem; border: 1px solid var(--tlp-line); border-radius: var(--tlp-radius); background: var(--tlp-surface-alt); }
.tlp-spec-cost p { margin: 0; }
.tlp-spec-cost-label { font-size: 0.8rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--tlp-ink-soft); }
.tlp-spec-cost-figure { font-size: 1.8rem; font-weight: 700; line-height: 1.2; }
.tlp-spec-cost-source { font-size: 0.85rem; color: var(--tlp-ink-soft); margin-top: 0.25rem !important; }
.tlp-spec-cost .tlp-muted { font-size: 0.8rem; margin-top: 0.5rem !important; }
.tlp-cost-table { width: 100%; border-collapse: collapse; margin: 0.5rem 0; }
.tlp-cost-table th, .tlp-cost-table td { padding: 0.6rem 0.75rem; text-align: left; border-bottom: 1px solid var(--tlp-line); }
.tlp-cost-table th { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--tlp-ink-soft); }
.tlp-spec-index { list-style: none; padding: 0; margin: 0; columns: 2; column-gap: 2rem; }
.tlp-spec-index li { margin: 0.35rem 0; break-inside: avoid; }
@media (max-width: 640px) { .tlp-spec-index { columns: 1; } }

/* v0.52.0 */
.tlp-oncall-note { margin: 0.5rem 0 0; padding: 0.5rem 0.75rem; border-radius: var(--tlp-radius); font-size: 0.9rem; }
.tlp-oncall-ok { background: var(--tlp-ok-bg); border: 1px solid #cfe3d6; }
.tlp-oncall-none { background: #fff8e6; border: 1px solid #f0d28a; }
.tlp-auto-buy .tlp-form-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: 0.75rem 1rem; margin: 0.75rem 0; }
.tlp-auto-buy .tlp-choice { margin: 0.35rem 0; }

/* v0.53.0 - wait list stat */
.tlp-gap-stat { margin: 0 0 1.5rem; padding: 1rem 1.25rem; border-left: 4px solid var(--tlp-accent); background: var(--tlp-ok-bg); border-radius: var(--tlp-radius); }
.tlp-gap-stat p { margin: 0; }
.tlp-gap-stat p + p { margin-top: 0.35rem; }
.tlp-gap-stat-headline { font-size: 1.05rem; font-weight: 600; }

/* v0.53.3 - progress track: vertical steps on phones */
@media (max-width: 640px) {
	.tlp-progress-track { display: grid; grid-template-columns: 1fr; gap: 0.5rem; }
	.tlp-progress-arrow { display: none; }
	.tlp-progress-track .tlp-progress-step,
	.tlp-progress-track .tlp-progress-form,
	.tlp-progress-track .tlp-progress-form .tlp-btn { display: flex; width: 100%; box-sizing: border-box; }
	/* Chips and buttons share one height and one padding so the rows
	   line up as a column; label and date are centred on the same axis. */
	.tlp-progress-track .tlp-progress-step { min-height: 2.75rem; padding: 0 0.9rem; align-items: center; justify-content: space-between; line-height: 1.2; }
	.tlp-progress-track .tlp-progress-step .tlp-muted { font-size: 0.85rem; line-height: 1; }
	.tlp-progress-track .tlp-progress-form .tlp-btn { min-height: 2.75rem !important; padding: 0 0.9rem !important; align-items: center; justify-content: center; line-height: 1.2; }
	.tlp-progress-track .tlp-progress-form + .tlp-progress-form { margin-top: -0.15rem; }
	.tlp-progress-step { border-radius: var(--tlp-radius); }
	.tlp-progress-quote-row { flex-direction: column; align-items: stretch; }
	.tlp-progress-quote-row .tlp-input-prefix input { max-width: none; flex: 1; }
	.tlp-progress-skip { flex-direction: column; align-items: stretch; }
	.tlp-progress-skip .tlp-progress-form,
	.tlp-progress-skip .tlp-progress-form .tlp-btn { width: 100%; }
}

/* v0.53.4 - contact details stack on phones */
@media (max-width: 640px) {
	.tlp-contact-details { display: block; }
	.tlp-contact-details dt { margin: 0.6rem 0 0.1rem; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--tlp-ink-soft); }
	.tlp-contact-details dd { margin: 0; overflow-wrap: anywhere; }
}

/* v0.53.5 - choice rows: box stays put, text wraps beside it. The theme
   restyles checkbox inputs; pin the row layout so a wrapped label never
   drops under the box. */
.tlp-tradesperson-dashboard label.tlp-choice,
.tlp-job-submission-form label.tlp-choice,
.tlp-tradesperson-registration label.tlp-choice,
.tlp-outcome-form label.tlp-choice {
	display: flex !important;
	flex-wrap: nowrap;
	align-items: flex-start;
	gap: 0.7rem;
	line-height: 1.35;
}
.tlp-tradesperson-dashboard label.tlp-choice input[type="checkbox"],
.tlp-tradesperson-dashboard label.tlp-choice input[type="radio"],
.tlp-job-submission-form label.tlp-choice input[type="checkbox"],
.tlp-job-submission-form label.tlp-choice input[type="radio"],
.tlp-tradesperson-registration label.tlp-choice input[type="checkbox"],
.tlp-tradesperson-registration label.tlp-choice input[type="radio"],
.tlp-outcome-form label.tlp-choice input[type="radio"] {
	flex: 0 0 auto;
	margin: 0.2rem 0 0;
	float: none;
}
.tlp-choice-text { flex: 1 1 0; min-width: 0; }

/* v0.53.8 - .tlp-muted carries a padding-top for field hints; inside a
   progress chip it pushed the date down. Zeroed, both layouts. */
.tlp-progress-step .tlp-muted,
.tlp-progress-step span.tlp-muted { padding-top: 0 !important; margin: 0 !important; display: inline; line-height: inherit; }

/* v0.53.9 - the hidden attribute must win over any display rule. The
   budget note is inserted hidden and only shown when a band sits below
   the guide, but `.tlp-budget-note { display:block }` was overriding
   [hidden] and drawing an empty amber box. */
.tlp-job-submission-form [hidden],
.tlp-tradesperson-dashboard [hidden],
.tlp-outcome-form [hidden],
.tlp-outcome-page [hidden],
.tlp-tradesperson-registration [hidden],
.tlp-verify-listing [hidden] { display: none !important; }

/* v0.55.0 - trust stats */
.tlp-trust-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 1rem; margin: 1rem 0 0.5rem; }
.tlp-trust-stat { padding: 1rem 1.1rem; border: 1px solid var(--tlp-line); border-radius: var(--tlp-radius); background: var(--tlp-surface-alt); }
.tlp-trust-num { display: block; font-size: 2rem; font-weight: 700; line-height: 1.1; }
.tlp-trust-label { display: block; margin-top: 0.25rem; font-size: 0.9rem; color: var(--tlp-ink-soft); }

/* v0.55.1 - on-call line inside the emergency card */
.tlp-oncall-inline { display: block; margin: 0.5rem 0 0; padding: 0.4rem 0.65rem; font-size: 0.85rem; font-weight: 600; }
.tlp-timescale-card .tlp-oncall-ok { color: var(--tlp-accent); }

/* v0.55.5 - trade page: job spec list / grid */
.tlp-spec-list { columns: 2; column-gap: 2rem; padding-left: 1.2rem; }
.tlp-spec-list li { margin: 0.3rem 0; break-inside: avoid; }
.tlp-spec-price { font-size: 0.9em; color: var(--tlp-ink-soft); }
.tlp-spec-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); gap: 0.75rem; }
.tlp-spec-card { display: flex; flex-direction: column; gap: 0.25rem; padding: 0.9rem 1rem; border: 1px solid var(--tlp-line); border-radius: var(--tlp-radius); background: var(--tlp-surface); }
.tlp-spec-card:hover { border-color: var(--tlp-accent); }
.tlp-spec-card-name { font-weight: 600; color: inherit; text-decoration: none; }
a.tlp-spec-card-name:hover { text-decoration: underline; }
.tlp-spec-post { margin-top: 0.35rem; font-size: 0.85rem; font-weight: 600; color: var(--tlp-accent); text-decoration: none; }
.tlp-spec-post:hover { text-decoration: underline; }

/* Tag cloud: chips flow and wrap like a masonry of words; size by how
   often the job is posted. */
.tlp-spec-cloud { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.6rem; }
.tlp-spec-chip { display: inline-flex; align-items: baseline; gap: 0.4rem; padding: 0.4rem 0.85rem; border: 1px solid var(--tlp-line); border-radius: 999px; background: var(--tlp-surface-alt); color: inherit; text-decoration: none; line-height: 1.2; }
a.tlp-spec-chip:hover { border-color: var(--tlp-accent); background: var(--tlp-ok-bg); }
.tlp-spec-chip em { font-style: normal; font-size: 0.75em; color: var(--tlp-ink-soft); }
.tlp-spec-chip-1 { font-size: 0.9rem; }
.tlp-spec-chip-2 { font-size: 1.05rem; }
.tlp-spec-chip-3 { font-size: 1.25rem; font-weight: 600; }
.tlp-spec-chip-4 { font-size: 1.5rem; font-weight: 700; padding: 0.5rem 1.1rem; }
@media (max-width: 640px) { .tlp-spec-list { columns: 1; } }

/* v0.55.9 - align="center" on the spec list / grid / cloud */
.tlp-trade-specs.tlp-align-center { text-align: center; }
.tlp-trade-specs.tlp-align-center .tlp-spec-cloud { justify-content: center; }
.tlp-trade-specs.tlp-align-center .tlp-spec-grid { justify-content: center; }
.tlp-trade-specs.tlp-align-center .tlp-spec-list { columns: 1; padding: 0; list-style: none; }

/* v0.56.0 - breadcrumb on spec pages */
.tlp-breadcrumb ol { display: flex; flex-wrap: wrap; gap: 0.35rem; list-style: none; margin: 0 0 1rem; padding: 0; font-size: 0.9rem; color: var(--tlp-ink-soft); }
.tlp-breadcrumb li + li::before { content: '\203A'; margin-right: 0.35rem; }
.tlp-breadcrumb a { color: inherit; }

/* v0.57.0 - header account links */
.tlp-account-links { display: inline-flex; align-items: center; gap: 0.35rem; white-space: nowrap; }
.tlp-account-link { color: inherit; text-decoration: none; font-weight: 600; }
.tlp-account-link:hover { text-decoration: underline; }

/* v0.57.2 - login page card */
.tlp-login-card { max-width: 30rem; margin: 2rem auto; padding: 2rem 2.25rem 1.75rem; border: 1px solid var(--tlp-line); border-radius: 16px; background: var(--tlp-surface); box-shadow: 0 10px 30px rgba(16, 20, 26, 0.06); }
.tlp-login-title { margin: 0 0 0.25rem; font-size: 1.75rem; }
.tlp-login-sub { margin: 0 0 1.25rem; padding-top: 0; }
.tlp-login-fields .tlp-field { margin-bottom: 1rem; }
.tlp-login-fields input[type="text"], .tlp-login-fields input[type="password"] { width: 100%; padding: 0.8rem 0.9rem; font-size: 1rem; }
.tlp-login-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem; margin-top: 0.5rem; }
.tlp-login-card .tlp-login-submit { min-width: 9rem; padding: 0.8rem 1.4rem !important; }
.tlp-login-forgot { font-size: 0.9rem !important; padding: 0 !important; }
.tlp-reset-request { margin-top: 1rem; }
.tlp-reset-request summary { display: none; }
.tlp-reset-request:not([open]) { display: none; }
.tlp-reset-request form { margin: 0; padding: 1rem 1.1rem; border: 1px solid var(--tlp-line); border-radius: var(--tlp-radius); background: var(--tlp-surface-alt); }
.tlp-login-register { margin: 1.5rem 0 0; padding-top: 1.25rem; border-top: 1px solid var(--tlp-line); font-weight: 500; }
.tlp-login-register a { color: var(--tlp-accent); text-decoration: none; font-weight: 600; }
.tlp-login-register a:hover { text-decoration: underline; }
.tlp-login-customer { margin: 0.5rem 0 0; padding-top: 0; font-size: 0.85rem; }
.tlp-inline-login .tlp-success-banner { margin: 0 0 1rem; }
/* inline (dashboard) prompt keeps its compact look; the forgot link still works there */
.tlp-inline-login:not(.tlp-login-card) .tlp-reset-request form { border: 0; padding: 0.5rem 0 0; background: none; }

/* v0.57.3 - registration form: card, same field treatment as the job form */
.tlp-tradesperson-registration { max-width: 46rem; margin: 0 auto; }
.tlp-register-intro { margin-bottom: 1.25rem; }
.tlp-register-title { margin: 0 0 0.35rem; font-size: 1.75rem; }
.tlp-register-intro .tlp-muted { padding-top: 0; }
.tlp-register-form { padding: 1.75rem 2rem 1.5rem; border: 1px solid var(--tlp-line); border-radius: 16px; background: var(--tlp-surface); box-shadow: 0 10px 30px rgba(16, 20, 26, 0.05); }
.tlp-register-form .tlp-step-progress { margin: 0 0 1.5rem; }
.tlp-register-form .tlp-step-heading { font-size: 1.35rem; margin-bottom: 1rem; }
.tlp-register-form .tlp-field { margin-bottom: 1.1rem; }
.tlp-register-form .tlp-field label { display: block; margin-bottom: 0.35rem; font-weight: 600; }
.tlp-register-form .tlp-field .tlp-muted, .tlp-register-form .tlp-field .description { display: block; margin-top: 0.35rem; padding-top: 0; font-size: 0.85rem; color: var(--tlp-ink-soft); }
.tlp-register-form input[type="text"], .tlp-register-form input[type="email"], .tlp-register-form input[type="tel"], .tlp-register-form input[type="password"], .tlp-register-form input[type="number"], .tlp-register-form input[type="date"], .tlp-register-form select { width: 100%; min-height: 46px; padding: 0.65rem 0.85rem; border: 1px solid var(--tlp-line-strong) !important; border-radius: var(--tlp-radius) !important; background: var(--tlp-surface) !important; box-shadow: none !important; }
.tlp-register-form input[type="file"] { display: block; width: 100%; padding: 0.6rem; border: 1px dashed var(--tlp-line-strong); border-radius: var(--tlp-radius); background: var(--tlp-surface-alt); }
.tlp-register-form .tlp-fieldset { padding: 1rem 1.1rem; margin: 0 0 1.1rem; border: 1px solid var(--tlp-line); border-radius: var(--tlp-radius); }
.tlp-register-form .tlp-fieldset legend { padding: 0 0.4rem; font-weight: 600; }
.tlp-register-form .tlp-choice { margin-bottom: 0.5rem; }
.tlp-register-form .tlp-step-nav { justify-content: space-between; padding-top: 1.25rem; border-top: 1px solid var(--tlp-line); }
.tlp-register-form .tlp-step-nav .tlp-btn-primary { margin-left: auto; min-width: 9rem; }
@media (max-width: 640px) {
	.tlp-register-form { padding: 1.25rem 1.1rem; border-radius: 12px; }
	.tlp-register-form .tlp-step-nav { flex-direction: column-reverse; align-items: stretch; }
	.tlp-register-form .tlp-step-nav .tlp-btn { width: 100%; }
}

/* v0.57.4 - toggle pills: pin the checkbox on the pill's centre line.
   The theme's checkbox rules (top offset / margin) were winning on
   order; same weight as the other backstops. */
.tlp-tradesperson-registration label.tlp-toggle,
.tlp-tradesperson-dashboard label.tlp-toggle,
.tlp-job-submission-form label.tlp-toggle {
	display: inline-flex !important;
	align-items: center !important;
	line-height: 1 !important;
}
.tlp-tradesperson-registration label.tlp-toggle input[type="checkbox"],
.tlp-tradesperson-dashboard label.tlp-toggle input[type="checkbox"],
.tlp-job-submission-form label.tlp-toggle input[type="checkbox"] {
	position: relative !important;
	top: auto !important;
	margin: 0 !important;
	transform: none !important;
	vertical-align: middle !important;
	align-self: center !important;
	float: none !important;
}
.tlp-toggle-group { flex-wrap: wrap; }

/* v0.57.6 - live social proof */
.tlp-live-stat { display: inline-flex; align-items: center; gap: 0.5rem; font-weight: 600; }
.tlp-live-dot { width: 0.6rem; height: 0.6rem; border-radius: 50%; background: #16a34a; box-shadow: 0 0 0 0 rgba(22, 163, 74, 0.5); animation: tlp-pulse 2s infinite; }
@keyframes tlp-pulse { 0% { box-shadow: 0 0 0 0 rgba(22, 163, 74, 0.45); } 70% { box-shadow: 0 0 0 0.5rem rgba(22, 163, 74, 0); } 100% { box-shadow: 0 0 0 0 rgba(22, 163, 74, 0); } }
@media (prefers-reduced-motion: reduce) { .tlp-live-dot { animation: none; } }
.tlp-live-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 1.5rem; text-align: center; }
.tlp-live-num { display: block; font-size: 2.4rem; font-weight: 700; line-height: 1.1; }
.tlp-live-label { display: block; margin-top: 0.3rem; font-size: 0.9rem; opacity: 0.8; }
