/* =============================================================================
 * llf-forms.css — engine-owned styles for every form the engine renders.
 *
 * Scope note: the visual design of a form lives in the theme's per-template
 * stylesheets. What lives HERE is only what must be true in every context —
 * above all the decoy rule below.
 *
 * No `wpcf7` / `wpcf7-*` selectors, ever. `js/general.js` binds legacy
 * side-channel lead senders to `.wpcf7 form`, so reusing that class for its CSS
 * would make a migrated form deliver twice.
 * ========================================================================== */

/* -----------------------------------------------------------------------------
 * 1. The decoy. UNSCOPED, and the single most important rule in this file.
 *
 * In the engine this replaces, the honeypot was hidden only inside three
 * per-block stylesheets. Any other render context — a new block, a shortcode in
 * a post, a template nobody thought about — therefore printed a visible "Leave
 * this field empty" input, real people filled it in, and the server answered
 * with a fake success: the lead was silently dropped while the visitor was
 * thanked. So: no descendant selector, no block scope, and it applies wherever
 * the markup appears.
 *
 * Offscreen, not `display:none`: a display-hidden input is trivial for a bot to
 * detect and skip, which defeats the whole gate. `!important` because the
 * consequence of one theme rule winning here is lost leads, not a layout bug.
 * -------------------------------------------------------------------------- */
.ll-form__decoy {
	position: absolute !important;
	left: -9999px !important;
	top: auto !important;
	width: 1px !important;
	height: 1px !important;
	overflow: hidden !important;
	opacity: 0 !important;
	pointer-events: none !important;
}

.ll-form__decoy input {
	width: 1px;
	height: 1px;
}

/* -----------------------------------------------------------------------------
 * 2. Structure
 * -------------------------------------------------------------------------- */
.ll-form-shell {
	position: relative;
}

.ll-form {
	position: relative;
}

.ll-form__fields {
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
}

.ll-form__row {
	position: relative;
	display: flex;
	flex-direction: column;
	flex: 1 1 100%;
	min-width: 0;
}

/* Two fields side by side from the tablet breakpoint up; stacked on phones,
 * where a half-width name field is unusable. */
@media (min-width: 768px) {
	.ll-form__row--half {
		flex: 1 1 calc(50% - 8px);
	}
}

/* -----------------------------------------------------------------------------
 * Stand down where the theme owns the grid.
 *
 * A form with `layout => bootstrap` also carries the theme's `.row` / `.col-lg-6`
 * classes, and those two systems disagree: the rule above splits at 768px, while
 * `col-lg-6` is full width until 992px. Production splits at 992px, so on a tablet
 * the engine was halving fields production leaves whole. The `gap` loses too —
 * Bootstrap spaces rows through `.field-input { margin-bottom: … }`, which the
 * theme varies per template, and a gap on top of it double-spaces every row.
 *
 * `flex: 0 0 auto` rather than `initial`: the column still must not grow past the
 * width its `col-*` class gives it.
 * -------------------------------------------------------------------------- */
.ll-form--bs .ll-form__fields {
	/*
	 * BLOCK, not flex. The engine's own container is a wrapping flex row, which is
	 * right when it owns the grid. Under the bootstrap layout it does not: the
	 * renderer emits real `.row.field-row` wrappers, one per pair, and a flex parent
	 * lays those wrappers out SIDE BY SIDE — four fields on one line at 186px each
	 * instead of a 2x2 grid at 430. The `.row` children are flex containers
	 * themselves; the container above them just has to stack them.
	 */
	display: block;
	gap: 0;
}

@media (min-width: 768px) {
	.ll-form--bs .ll-form__row--half {
		flex: 0 0 auto;
	}
}

/*
 * The row is a BLOCK under this layout, because production's column is one.
 *
 * The engine's `.ll-form__row { display: flex }` makes `.field-input` a flex item,
 * and a flex item establishes its own block formatting context — which means the
 * paragraph's 16px bottom margin no longer collapses through its parent the way it
 * does on production. Measured at 768px: `.field-input` 52px tall against
 * production's 36, and the row 100 against 84, on every CF7 family.
 *
 * Margin collapsing is not a detail here. Production's whole vertical rhythm is
 * built on it.
 */
.ll-form--bs .ll-form__row {
	display: block;
}

/*
 * THE SAME STAND-DOWN, FOR THE FAMILY THAT HAS NO `layout`.
 *
 * `landing_lead` (and its alias `fitness_lead`) declares no `layout`, so it never
 * receives `.ll-form--bs` and never got the rule above. Production's rows there
 * are plain blocks — verified by walking every stylesheet rule on the live page
 * and testing `matches()`: nothing on production sets `display` on
 * `.ll-form__row` at all, so they are the initial `block`.
 *
 * What the flex column costs, measured at all three widths and identical at each:
 * name 45 -> 43.5, email 45 -> 43.5, message 51 -> 44, and the phone row 39 -> 45
 * because a flex COLUMN puts the empty `.ll-form__error` span under the control
 * where production's flex ROW puts it beside the `.iti` wrapper, so its
 * `margin-top: 6px` becomes real height. Four pixels net, which is exactly the
 * harness's `h` tolerance and therefore invisible to it at 768.
 *
 * `:not()` is here for specificity, not for taste. At 0-2-0 this rule ties with
 * the theme block's `.block-ll-contact-form-split .ll-form__row--phone
 * { display: flex }` and the winner then depends on enqueue order; at 0-3-0 it
 * does not. Forcing the phone row to `block` was measured too, and it is wrong in
 * the other direction — 45 against production's 39.
 *
 * Scoped to this template so it cannot reach the other three theme-rendered
 * families, which have not been measured for this. `contact_lead` reads zero
 * differences at all three widths today, and a four-pixel change is under the
 * tolerance that would report it — so a blanket rule could move it without
 * anything saying so.
 */
.ll-form--legacy-landing-lead .ll-form__row:not(.ll-form__row--phone) {
	display: block;
}

.ll-form--legacy-landing-lead .ll-form__row--phone {
	flex-direction: row;
}

.ll-form__label {
	display: block;
	margin-bottom: 6px;
}

.ll-form__help {
	display: block;
	margin-top: 4px;
	font-size: 12px;
	opacity: 0.75;
}

.ll-form__control {
	width: 100%;
	max-width: 100%;
	box-sizing: border-box;
}

.ll-form__textarea {
	resize: vertical;
}

.ll-form__row--choices {
	gap: 8px;
}

.ll-form__choice,
.ll-form__consent {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	position: relative;
	cursor: pointer;
}

.ll-form__note {
	margin: 8px 0;
	font-size: 13px;
}

.ll-form__note--info {
	opacity: 0.8;
}

/* -----------------------------------------------------------------------------
 * 3. Errors. The slot is always in the DOM (empty), so filling it in cannot
 *    reflow the page around it — the error text appears in space that was
 *    already reserved.
 * -------------------------------------------------------------------------- */
.ll-form__error {
	display: block;
	min-height: 1em;
	margin-top: 4px;
	font-size: 12px;
	line-height: 1.3;
	color: #d63638;
}

/*
 * `min-height: 0` is the useful half — an error slot with nothing in it should not
 * reserve a line of text.
 *
 * `margin-top: 0` used to be here too and it was quietly costing 6px per row on
 * every skinned form. The block skins declare `.block-… .ll-form__error
 * { margin-top: 6px }` at the same specificity as `.ll-form__error:empty`, so the
 * winner is whichever sheet loads later — and this one does. The rhythm loss
 * accumulated down the form: 12px by the third row, 45px by the consent line.
 * Leave the margin to whoever is styling the form.
 */
.ll-form__error:empty {
	min-height: 0;
}

.has-error .ll-form__control,
.has-error input[type="checkbox"],
.has-error input[type="file"] {
	border-color: #d63638;
	outline-color: #d63638;
}

.ll-form__message {
	margin-top: 12px;
	min-height: 1em;
}

.ll-form__message.is-error {
	color: #d63638;
}

.ll-form__message.is-success {
	color: #1a7f37;
}

/* -----------------------------------------------------------------------------
 * 4. Submit + pending state
 * -------------------------------------------------------------------------- */
.ll-form__submit {
	position: relative;
	display: inline-flex;
	align-items: center;
	/*
	 * `justify-content` is not decoration here. Production's submit is an
	 * `<input type="submit">`, which centres its own value text; ours is a flex
	 * container holding a label, an icon and a spinner, and with no
	 * `justify-content` they pack to the start. Invisible while the button hugs
	 * its content — and at 390px, where several families make it full-width, the
	 * label sat 69px left of centre (measured on cf7_no_consent_tel: label centre
	 * 109.9 against production's 179).
	 *
	 * The parity harness compares the button's BOX and never looked inside it, so
	 * this was a defect no row reported. Plugin-wide, because the cause is the
	 * plugin's own flex container and not any one family.
	 */
	justify-content: center;
	gap: 8px;
	cursor: pointer;
}

.ll-form__submit[disabled] {
	cursor: progress;
	opacity: 0.7;
}

.ll-form__spinner {
	display: none;
	width: 14px;
	height: 14px;
	border: 2px solid currentColor;
	border-right-color: transparent;
	border-radius: 50%;
}

.ll-form.is-loading .ll-form__spinner {
	display: inline-block;
	animation: llf-spin 0.7s linear infinite;
}

.ll-form.is-loading .ll-form__submit-icon {
	display: none;
}

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

/* Respect a reduced-motion preference: the spinner still shows, it just does
 * not rotate. */
@media (prefers-reduced-motion: reduce) {
	.ll-form.is-loading .ll-form__spinner {
		animation: none;
	}

	.ll-form__success {
		transition: none;
	}
}

/* -----------------------------------------------------------------------------
 * 5. Dropzone
 * -------------------------------------------------------------------------- */
.ll-form__dropzone {
	position: relative;
	flex: 1 1 100%;
}

.ll-form__dropzone.is-drag {
	outline: 2px dashed currentColor;
	outline-offset: 4px;
}

.ll-form__dropzone-ui {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
}

.ll-form__filelist {
	margin: 8px 0 0;
	padding: 0;
	list-style: none;
}

.ll-form__fileitem {
	display: flex;
	align-items: center;
	gap: 8px;
}

.ll-form__fileremove {
	border: 0;
	background: none;
	cursor: pointer;
	line-height: 1;
}

/* -----------------------------------------------------------------------------
 * 6. Success panel. `hidden` until the client swaps it in, then faded — the
 *    `[hidden]` attribute is honoured so a non-JS visitor never sees a "thank
 *    you" for a submission that did not happen.
 * -------------------------------------------------------------------------- */
.ll-form__success[hidden] {
	display: none;
}

.ll-form__success {
	opacity: 0;
	transition: opacity 0.25s ease;
}

.ll-form__success.is-visible {
	opacity: 1;
}
