/*
 * Composition and rhythm.
 *
 * Every value here reads from theme.json — no colours, sizes, or spacing are
 * defined in this file. If you find yourself typing a hex code or a pixel
 * value below, it belongs in theme.json instead.
 *
 * A note on !important: WordPress's constrained layout sets margin-left/right
 * to auto with !important on every block narrower than its container, which
 * centres things. LANDING-SPEC §2 is left-aligned throughout, so the rules that
 * undo that centring have to match it. Those are the only ones.
 */

/* --- The metric-matched fallback face ------------------------------------
 * The one @font-face in this theme that is NOT in theme.json, and the only
 * declaration in this file that is a measurement rather than a token.
 *
 * WHY IT EXISTS. `font-display: swap` (LANDING-SPEC §3, and correct) means the
 * H1 paints in a fallback first and repaints in Space Grotesk when it arrives.
 * The two faces are not the same width, so on a phone the headline wraps to a
 * different number of lines before and after the swap, the whole of <main>
 * moves, and mobile CLS measured 0.258 against a 0.1 budget. It is a
 * pre-existing defect, not a new one — measured at both the old 72px H1 scale
 * and the current 80px, mobile CLS was 0.258 either way.
 *
 * WHY 104.28%. Measured on the running page, not looked up: the H1 string set
 * in Space Grotesk is 2425.5px wide where Arial is 2325.84px at the same size,
 * a ratio of 1.0428. Arial is the target because it is present on Windows and
 * macOS and Liberation Sans is metric-compatible with it on Linux, which makes
 * one number right nearly everywhere; `ui-sans-serif` resolves to something
 * different on every platform and cannot be matched with a single figure.
 *
 * `line-height` is a unitless ratio everywhere in this theme, so it is computed
 * from the specified font-size and size-adjust does not change the line box —
 * only the wrap, which is the thing that was moving.
 *
 * WHY NOT theme.json. WordPress rewrites every `fontFace` entry to its parent
 * family name and discards any `src` that is not a `file:` path. Declared
 * there, this emitted a second, src-less `@font-face { font-family: "Space
 * Grotesk"; size-adjust: 104.28% }` that overrode the real face. It was tried;
 * do not try it again. The font STACK is still a token and still lives in
 * theme.json — only this definition is here.
 *
 * If none of the local faces exist the rule simply does not apply and the stack
 * falls through exactly as it did before.
 */

@font-face {
	font-family: "Space Grotesk Fallback";
	font-style: normal;
	font-weight: 700;
	size-adjust: 104.28%;
	src: local("Arial"), local("Helvetica Neue"), local("Segoe UI"), local("Roboto"), local("Liberation Sans");
}

/*
 * And the same for the body face, which is the one Lighthouse actually named.
 * The first attempt matched Space Grotesk on the assumption that the H1 drove
 * the shift; Lighthouse's own attribution says the shifting resource is
 * inter-latin.woff2, so the body copy is what reflows and takes <main> with it.
 * Worth recording: the assumption was wrong and only measurement caught it.
 *
 * Measured the same way, on a real paragraph from the page rather than lorem,
 * because wrapping is the thing that moves: Inter 400 is 106.78% of Arial's
 * width for that string, Inter 600 is 101.08%. One face covers the range and
 * takes the 400 figure — nearly all body copy is 400, and bold runs are short
 * enough that a 5% difference across them does not change a wrap.
 */
@font-face {
	font-family: "Inter Fallback";
	font-style: normal;
	font-weight: 400 700;
	size-adjust: 106.78%;
	src: local("Arial"), local("Helvetica Neue"), local("Segoe UI"), local("Roboto"), local("Liberation Sans");
}

/* --- Rhythm -------------------------------------------------------------
 * One value, from theme.json, governs every gap between sections.
 *
 * Sections carry no vertical margin at all, so the space between any two of
 * them is exactly twice --wp--custom--rhythm--section and cannot drift: there
 * is no second contributor to add to it. Change that one token and the whole
 * page re-spaces.
 *
 * The margin reset needs !important because WordPress's blockGap applies
 * margin-block-start to every child of the content container, which would
 * otherwise add an invisible extra gap on top of the padding.
 */

.perceva-section {
	padding-block: var(--wp--custom--rhythm--section);
	margin-block: 0 !important;
}

/* The first section follows the header, which brings its own padding — a full
   section gap there reads as a hole rather than a break. */
.perceva-section--first {
	padding-block-start: calc(var(--wp--custom--rhythm--section) / 2);
}

/* Inside a section: heading to content is a tighter relationship than section
   to section, and the rule fragment belongs to the heading it introduces. */
.perceva-section > * + * {
	margin-block-start: var(--wp--custom--rhythm--stack) !important;
}

.perceva-section > .perceva-rule + * {
	margin-block-start: var(--wp--custom--rhythm--tick) !important;
}

/*
 * The full-bleed band puts one extra wrapper between the section and its
 * content — see .perceva-band-inner below — so the same two rhythm rules have
 * to reach one level deeper for it. They are restated rather than loosened to
 * `.perceva-section *`, which would also catch every block inside a column.
 */
.perceva-band-inner > * + * {
	margin-block-start: var(--wp--custom--rhythm--stack) !important;
}

.perceva-band-inner > .perceva-rule + * {
	margin-block-start: var(--wp--custom--rhythm--tick) !important;
}

/* --- The inspector's tick ------------------------------------------------
 * A short left-aligned rule fragment, not a full-width divider and not a
 * centred dash (LANDING-SPEC §2). Zero margin: the section owns the rhythm.
 */

.wp-block-separator.perceva-rule {
	width: var(--wp--custom--rule--width);
	max-width: var(--wp--custom--rule--width);
	height: var(--wp--custom--rule--thickness);
	border: 0;
	background-color: var(--wp--preset--color--verdict-green);
	opacity: 1;
	margin-block: 0;
	margin-inline: 0 !important;
}

/* --- Bands --------------------------------------------------------------- */

/* Columns stretch so bordered surfaces in a row end level. */
.perceva-band {
	align-items: stretch;
}

/* Heading-left, content-right split. The heading column is a label for the
   band, so it sits at the top rather than floating in the middle. */
.perceva-band--split .wp-block-column {
	align-self: flex-start;
}

/*
 * The exception, and it is about proportion rather than taste.
 *
 * flex-start is right when the right column is prose that begins at the top —
 * /faq and the honesty band both read as a label beside its first line. The
 * plugin band is the one place the right column is a single 800x1560 portrait
 * screenshot, so a two-word heading pinned to the top left a column of empty
 * paper roughly 700px tall beside it, which read as an unfinished section
 * rather than as restraint.
 *
 * Centring the label against the artifact removes the void by construction and
 * costs nothing: below 781px the columns stack and this stops applying.
 */
.perceva-band--middle .wp-block-column {
	align-self: center;
}

/* --- Hero ----------------------------------------------------------------
 * The hero leads with the artifact. The right column carries two surfaces, and
 * the pairing IS the argument:
 *
 *   first   perceva/example-report  — what a Perceva report looks like
 *   second  perceva/self-audit-tag  — what OURS says, which is 0
 *
 * The example card is tilted a degree off-axis and cropped by the section so it
 * reads as a product surface rather than a screenshot pasted onto a page. The
 * tag below it is counter-tilted by half as much, so the two read as two
 * separate sheets rather than one skewed graphic.
 *
 * The self-audit tag is NOT replaced by the card. It is LANDING-SPEC §1
 * signature element A and §6 requires it; it keeps its real count, its real
 * <time> and its real link, and only gives up the job of carrying the whole
 * column on its own.
 *
 * ---------------------------------------------------------------------------
 * WHY THIS IS A GRID AND NOT A SHINGLED STACK — READ BEFORE CHANGING IT.
 *
 * The two surfaces used to overlap on purpose: the tag was pulled up under the
 * card with `margin-block-start: calc(-1 * spacing-50)` and given an equal
 * `padding-block-start` to push its own text back out from under it. Those two
 * values cancel EXACTLY, so the design had precisely zero clearance by
 * construction — every remaining pixel of the card had to land on the tag's
 * first line. The card's `rotate` then supplied those pixels: rotation is a
 * paint-time transform, so the layout box says the two clear each other while
 * the painted corner drops (width / 2) * sin(1.2deg) below it.
 *
 * Measured on the running page before the fix, sweeping EVERY integer width
 * from 320 to 1920: the card's painted bottom edge sat below the top of the
 * tag's first text line at 1139 of 1601 widths — i.e. at every width from 782
 * (the columns breakpoint) to 1920 — by up to 5.03px. The card is opaque and
 * carried z-index 2, so it painted over that text everywhere.
 *
 * This is the THIRD time the tag has collided with something. It was fixed
 * once by abandoning fixed positioning, once by re-tuning offsets, and it came
 * back. So the approach changed rather than the numbers: the two surfaces are
 * now consecutive rows of a grid. A grid row cannot intrude into the next
 * row's track no matter what the content height or the viewport width is, so
 * the overlap is not tuned away, it is structurally unavailable. There is no
 * negative margin here and no z-index, because there is nothing to stack.
 *
 * The only thing that can still reach across the gap is the tilt, and it is
 * bounded rather than hoped about: the card's painted corners sit at most
 * (width / 2) * sin(1.2deg) = 0.0105 * width below its layout box, the card is
 * at most 42% of the 1120 grid (470px) wide, so the reach is at most ~5px into
 * a 40px gap — and below 782px the tilt is removed entirely. The card's
 * elevation offsets 12px into the same gap and stops well short too.
 *
 * The "two sheets on a desk" reading is kept by offsetting them HORIZONTALLY
 * instead: the card indents from the left and crops past the right grid edge,
 * the tag pulls back from the right. Horizontal offset costs nothing, because
 * neither surface is in the other's flow.
 */

/* Stretch, so the proof column can match the height of the claim beside it. */
.perceva-hero__band {
	align-items: stretch;
}

/*
 * The crop. Clipping at the section box means the card is cut at the 1120 grid
 * edge at EVERY viewport wide enough to show it, rather than floating in empty
 * space at 1600px and forcing a horizontal scrollbar at 1200px.
 *
 * overflow-x: clip, not hidden: clip creates no scroll container and does not
 * force the other axis to auto, so vertical focus rings inside the hero are
 * untouched.
 */
.perceva-section--first {
	overflow-x: clip;
}

/*
 * Two rows, one gap. `align-content: center` centres the pair against the
 * claim column beside it, which is what the old flex `justify-content` did;
 * everything else about this container has changed on purpose.
 */
.perceva-hero__proof {
	display: grid;
	grid-auto-rows: max-content;
	align-content: center;
	gap: var(--wp--preset--spacing--50);
}

/*
 * One contributor to the gap, not two. WordPress's flow layout also puts a
 * blockGap margin on every child of a column, which stacked on top of the grid
 * gap and made the real separation 64px where the token says 40 — the same
 * class of drift the section rhythm was rewritten to remove. The grid gap is
 * the single value now; change it there and the composition follows.
 */
.perceva-hero__proof > * {
	margin-block: 0 !important;
}

.perceva-hero__proof .perceva-report {
	margin-inline-start: var(--wp--preset--spacing--50);
	/*
	 * Past the grid edge, into the clip — but only by one spacing step. At 2.5rem
	 * the crop ate the location counts on the right of each issue row ("9
	 * locatio…"), which is the opposite of the point: a crop should take slack,
	 * never content. 1rem sits inside the card's own 1.5rem padding, with the
	 * ~8px the rotation adds at the corners still to spare.
	 */
	margin-inline-end: calc(-1 * var(--wp--preset--spacing--30));
	/*
	 * Individual transform property, not transform: rotate(). The load
	 * animation animates `translate`, and the two compose independently — a
	 * transform: translateY() keyframe would silently drop the rotation on its
	 * final frame and leave the card square.
	 */
	rotate: var(--wp--custom--tilt);
}

/*
 * Offset the other way and counter-tilted, so the two surfaces read as two
 * sheets rather than one skewed graphic. The offset is horizontal only — see
 * the block comment above for why there is no vertical overlap here any more.
 */
.perceva-hero__proof .perceva-tag {
	margin-inline-end: var(--wp--preset--spacing--60);
	rotate: calc(var(--wp--custom--tilt) / -2);
	box-shadow: var(--wp--preset--shadow--card);
}

/* --- The one moment ------------------------------------------------------
 * LANDING-SPEC §2 says "Nothing else moves. No scroll-triggered entrances" and
 * §6 says only the two signature elements move. This is a deliberate,
 * Jagdish-authorised exception (1 September 2026), and it is exactly one
 * orchestrated load moment: the hero settles once, and nothing on the page ever
 * moves again. No JavaScript, no IntersectionObserver, no scroll trigger.
 *
 * The ENTIRE block — the animation, the delays, and the hidden start state —
 * lives inside prefers-reduced-motion: no-preference. For anyone who asked for
 * less motion the animation is ABSENT rather than shortened: no animation
 * property is ever declared and there is no opacity:0 start state left behind
 * to recover from. That is stronger than the global 0.001ms override in
 * theme.json, which stays as a second layer but is not what this relies on.
 *
 * Only opacity and translate animate, so this contributes exactly zero to CLS.
 */

@media (prefers-reduced-motion: no-preference) {
	@keyframes perceva-settle {
		from {
			opacity: 0;
			translate: 0 var(--wp--custom--motion--settle-distance);
		}

		to {
			opacity: 1;
			translate: none;
		}
	}

	.perceva-hero__proof .perceva-report,
	.perceva-hero__proof .perceva-chip,
	.perceva-hero__proof .perceva-tag {
		/* `both` so nothing flashes at full opacity before its delay elapses. */
		animation: perceva-settle var(--wp--custom--motion--settle) cubic-bezier(0.2, 0, 0, 1) both;
	}

	.perceva-hero__proof .perceva-report {
		animation-delay: 60ms;
	}

	.perceva-hero__proof .perceva-chip {
		animation-duration: 300ms;
	}

	.perceva-hero__proof .perceva-chip:nth-child(1) {
		animation-delay: 220ms;
	}

	.perceva-hero__proof .perceva-chip:nth-child(2) {
		animation-delay: 280ms;
	}

	.perceva-hero__proof .perceva-chip:nth-child(3) {
		animation-delay: 340ms;
	}

	.perceva-hero__proof .perceva-tag {
		animation-delay: 400ms;
		animation-duration: 380ms;
	}
}

/* Demoted supporting prose: present, readable, and clearly not competing with
   the subhead. The path is H1, subhead, form (HIERARCHY). */
.perceva-hero__support {
	font-size: var(--wp--preset--font-size--medium);
}

/* Lead-in sits tight to the form it introduces, so they read as one unit. */
.perceva-hero__lead {
	margin-block-end: var(--wp--preset--spacing--20);
	color: var(--wp--preset--color--graphite);
}

/* --- Lists: markers that mean something ---------------------------------
 * A default disc says "list". A green tick in the same language as the section
 * rules says "this is a finding that passes" — the page's own vocabulary.
 * Decorative and empty, so nothing is announced to a screen reader.
 */

.perceva-list {
	padding-inline-start: 0;
}

.perceva-list li {
	margin-block-end: var(--wp--preset--spacing--20);
}

/*
 * UNORDERED lists get the inspector's tick in place of a bullet. Scoped to
 * `ul` on purpose — see the ordered-list note below.
 */
ul.perceva-list {
	list-style: none;
}

ul.perceva-list li {
	position: relative;
	/* Clears the tick plus one step of the spacing scale, rather than a number
	   picked to look right. */
	padding-inline-start: calc(0.75rem + var(--wp--preset--spacing--30));
}

ul.perceva-list li::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	inset-block-start: 0.7em;
	width: 0.75rem;
	height: var(--wp--custom--rule--thickness);
	background-color: var(--wp--preset--color--verdict-green);
}

/*
 * ORDERED lists KEEP their numbers. This used to be one rule covering both,
 * which put a tick where a number belonged on the two ordered lists we have —
 * /methodology's "the mechanics, plainly" steps and /not-an-overlay's two
 * things an overlay attempts. Both are read sequentially by the prose that
 * follows them ("That last point…", "Neither part…"), so the numbering is
 * content, not decoration.
 *
 * It was also an accessibility defect on our own pages: `list-style: none` on
 * a list is enough for Safari/VoiceOver to stop announcing it as a list at
 * all, so the structure disappeared for exactly the users this site is about.
 *
 * The marker stays native and is styled through ::marker rather than being
 * rebuilt with a counter — a real <ol> marker cannot fall out of step with the
 * list, and nothing here is reimplementing semantics the browser already has.
 */
ol.perceva-list {
	padding-inline-start: calc(0.75rem + var(--wp--preset--spacing--30));
}

ol.perceva-list li::marker {
	color: var(--wp--preset--color--verdict-green);
	font-variant-numeric: tabular-nums;
	font-weight: 600;
}

/* --- Pricing plans: bordered findings, not cards ------------------------- */

.perceva-plan {
	height: 100%;
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--30);
}

/* Reserving the same number of lines for the terms in every card is what makes
   the three CTAs land on one line, without inventing copy for the plan that
   has none. Previously the buttons were pushed to the bottom with an auto
   margin, which aligned nothing and left a hole under each heading. */
.perceva-plan__terms {
	/* Three lines of whatever the body line-height actually is — `lh` reads it
	   from the element rather than restating theme.json's 1.6 here, where it
	   could drift. */
	min-height: 3lh;
}

/* The founding plan is the one decision we want people to make. */
.perceva-plan--founding {
	border-color: var(--wp--preset--color--verdict-green);
	border-width: 2px;
}

/* Terms are reference text, not a headline — at large size they wrapped to four
   ragged lines in a third-width column and fought the plan name for attention. */
.perceva-plan__terms {
	color: var(--wp--preset--color--graphite);
	text-wrap: pretty;
}

/* The trailing note, not the button, takes the slack — so the buttons stay put
   and only the note floats to the card's bottom edge. */
.perceva-plan > p:last-child {
	margin-block-start: auto !important;
}

/* --- Capture form -------------------------------------------------------- */

.perceva-capture {
	max-width: 34rem;
	margin-inline: 0 !important;
}

.perceva-capture .ff-el-input--label label {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	color: var(--wp--preset--color--ink);
}

.perceva-capture input[type="email"],
.perceva-capture input[type="text"] {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	color: var(--wp--preset--color--ink);
	background-color: var(--wp--preset--color--paper);
	border: 1px solid var(--wp--preset--color--graphite);
	border-radius: var(--wp--custom--radius--interactive);
	padding: 0.75rem 1rem;
	width: 100%;
	height: auto;
}

.perceva-capture .ff-btn-submit,
.perceva-capture button[type="submit"] {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 600;
	color: var(--wp--preset--color--paper);
	background-color: var(--wp--preset--color--verdict-green);
	border: 1px solid var(--wp--preset--color--verdict-green);
	border-radius: var(--wp--custom--radius--interactive);
	padding: 0.75rem 1.5rem;
	cursor: pointer;
	width: auto;
}

/* --- Hero scan form (Phase C) ------------------------------------------- */

.perceva-scan-form__label {
	display: block;
	font-weight: 600;
	margin-bottom: var(--wp--preset--spacing--20);
}

.perceva-scan-form__row {
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--20);
	max-width: 34rem;
	margin-inline: 0;
}

.perceva-scan-form__input {
	flex: 1 1 18rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	color: var(--wp--preset--color--ink);
	background: var(--wp--preset--color--paper);
	border: 1px solid var(--wp--preset--color--graphite);
	border-radius: var(--wp--custom--radius--interactive);
	padding: 0.75rem 1rem;
}

.perceva-scan-form__submit {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 600;
	color: var(--wp--preset--color--paper);
	background: var(--wp--preset--color--verdict-green);
	border: 1px solid var(--wp--preset--color--verdict-green);
	border-radius: var(--wp--custom--radius--interactive);
	padding: 0.75rem 1.25rem;
	cursor: pointer;
}

/*
 * Two defects in the form plugin's own CSS, fixed here rather than in the
 * plugin. Both were found by walking the page with a keyboard and reading the
 * computed styles, not by axe — axe has no rule for either.
 *
 * ONE — the focus ring was gone. Fluent Forms sets outline-style: none on its
 * input and its submit button. theme.json's global ring is written with
 * :where(), which is deliberately zero-specificity so page CSS can override it;
 * that is fine for a theme and fatal here. LANDING-SPEC §4 makes the ring a
 * design signature, not a browser default grudgingly kept, and §4 also requires
 * focus visible EVERYWHERE — which included the one control the whole
 * pre-launch page exists to get someone to use. Restated at real specificity,
 * with !important because the plugin's own rule carries weight we have to beat.
 */
.perceva-capture :is(input, button, select, textarea):focus-visible {
	outline: var(--wp--custom--focus--width) solid var(--wp--preset--color--verdict-green) !important;
	outline-offset: var(--wp--custom--focus--offset) !important;
}

/*
 * TWO — the required asterisk was inside the field's accessible name. Fluent
 * Forms appends it with a ::after on the label, and pseudo-element content is
 * part of the accessible name, so the input announced as "Email address *".
 * A pseudo-element cannot take aria-hidden, but CSS content has an alt-text
 * form: everything after the slash is what assistive technology receives. An
 * empty string there keeps the asterisk on screen for sighted visitors and
 * takes it out of the name, which is back to exactly "Email address".
 *
 * The field is already `required` plus `aria-required`, so nothing is lost:
 * the requirement is still announced, once, by the attribute that means it.
 *
 * If a browser does not support the alt-text syntax the whole declaration is
 * invalid and the plugin's own rule applies unchanged — the asterisk stays
 * visible either way.
 *
 * !important is needed, not preferred: the plugin's selector carries four
 * classes to this rule's two, so specificity alone loses.
 *
 * The colour is the second half of this. Fluent Forms paints the asterisk in
 * its own danger red. LANDING-SPEC §2 is absolute that red means critical
 * severity and nothing else — "if red or amber shows up as decoration
 * anywhere, it's a bug" — and a required field is not a critical finding. The
 * requirement is already carried by `required` and `aria-required`, which is
 * where it belongs. To put the red back, delete the colour line.
 */
.perceva-capture .ff-el-is-required label::after {
	content: " *" / "" !important;
	color: var(--wp--preset--color--graphite) !important;
}

/* Administrator-only configuration notice. */
.perceva-capture--unconfigured {
	border: 1px solid var(--wp--custom--severity--serious);
	color: var(--wp--custom--severity--serious);
	padding: var(--wp--preset--spacing--30);
}

/* --- Left-aligned throughout --------------------------------------------
 * LANDING-SPEC §2 opens with "left-aligned throughout". WordPress's
 * constrained layout centres every block narrower than its container using
 * margin:auto !important, which quietly re-centres content inside cards,
 * columns and the footer — the plan cards were centring their headings and
 * terms by 15-50px each.
 *
 * One rule, scoped to our own containers, rather than chasing each element.
 * The sections themselves are unaffected: they are not inside .perceva-section.
 */

.perceva-section .is-layout-constrained > *,
.perceva-footer > * {
	margin-inline: 0 !important;
}

/* --- Header -------------------------------------------------------------- */

.perceva-bar__end {
	gap: var(--wp--preset--spacing--40);
}

/* --- Narrow viewports ---------------------------------------------------- */

/* --- Trust pages: /methodology, /not-an-overlay, /pricing, /privacy --------
 * Long-form prose in an ordinary page, styled with the same vocabulary as the
 * landing page rather than a second look. Only what the landing sections did
 * not already need lives here.
 */

/* --- The page hero -------------------------------------------------------
 * Every page and the 404 open the same way the landing page does: on the 1120
 * grid, left-aligned against the same line the wordmark and the footer sit on,
 * with the title in the display scale and an inspector's tick closing it.
 *
 * Before this, templates/page.html put the title and the content in a centred
 * 44rem column — measured at x=280 in a 1265 viewport while the header wordmark
 * sat at x=72. Long pages hid it, because a full column of text has a strong
 * left edge whatever the gutters do. The 404, with a heading and one sentence in
 * it, showed it plainly: a two-line page floating in the middle of the screen
 * while every other edge on the site was left-aligned. Same defect, one page
 * just happened to make it visible.
 *
 * The prose measure is unchanged — body copy still stops at --measure. What
 * moved is where that column starts.
 */

/*
 * The hero's own children sit on the grid line, not centred in it. The
 * left-alignment rule further down only reaches blocks inside a nested
 * constrained container; the hero's children are direct children of the
 * section, which that selector does not match. Without this the title centres
 * itself in the 1120 box the moment anything narrows it.
 *
 * The title takes the full grid width, exactly as the landing page's H1 does —
 * at the display scale the wrap is a function of the box, and 1120 is the box
 * the whole site is built on (Phase A measured that: 2 lines at every type size
 * from 72px to 84px above 1200px wide).
 */
.perceva-page-hero > * {
	margin-inline: 0 !important;
}

/* A long title in the display face is a real overflow risk on a phone — Phase C
   shipped exactly that bug on /scan. Break the word rather than the viewport. */
.wp-block-post-title,
.entry-content :is(h2, h3) {
	overflow-wrap: break-word;
}

/* Section rules belong to the heading they introduce, the same relationship the
   landing page's .perceva-section sets up. */
.entry-content > .perceva-rule + * {
	margin-block-start: var(--wp--custom--rhythm--tick) !important;
}

/*
 * ...and the rule itself gets the full section gap above it, so a 3,000-word
 * page breaks into readable sections instead of running as one column of
 * evenly-spaced paragraphs. This is the same --rhythm--section token the
 * landing page uses between its sections, so the two surfaces breathe alike.
 * Paragraph-to-paragraph spacing is deliberately NOT increased: widening that
 * makes a long page longer without making it easier to scan.
 */
.entry-content > .perceva-rule {
	margin-block-start: var(--wp--custom--rhythm--section) !important;
}

/* A heading that is not introduced by a rule still needs to read as a break. */
.entry-content > :is(h2, h3) {
	margin-block-start: var(--wp--custom--rhythm--stack) !important;
}

.entry-content > .perceva-rule + :is(h2, h3) {
	margin-block-start: var(--wp--custom--rhythm--tick) !important;
}

/* Tables read as printed findings: hairline rows, no zebra striping, aligned to
   the top because the cells are sentences of different lengths. A table wider
   than the column scrolls inside its own figure rather than moving the page. */
.perceva-table {
	overflow-x: auto;
}

.perceva-table table {
	border-collapse: collapse;
	width: 100%;
}

.perceva-table :is(th, td) {
	padding: var(--wp--preset--spacing--30);
	/* Core's table block draws a full box on every cell. A grid of boxes inside
	   the surface's own border is two borders saying the same thing; rows read
	   as a printed finding, columns do not need ruling. */
	border: 0;
	border-bottom: 1px solid var(--wp--preset--color--graphite);
	text-align: start;
	vertical-align: top;
	font-size: var(--wp--preset--font-size--small);
}

.perceva-table th {
	font-weight: 600;
}

.perceva-table tbody tr:last-child :is(th, td) {
	border-bottom: 0;
}

/* Quoting our own report line. The tick's colour and thickness, turned on its
   side — the page's existing vocabulary rather than a new decoration. */
.entry-content .wp-block-quote {
	border-inline-start: var(--wp--custom--rule--thickness) solid var(--wp--preset--color--verdict-green);
	padding-inline-start: var(--wp--preset--spacing--40);
	margin-inline: 0;
	font-size: var(--wp--preset--font-size--large);
}

/* Anything narrower than the content column has to be told to stay left.
   WordPress's constrained layout sets margin-inline:auto !important on every
   child, which centres it, and LANDING-SPEC §2 is left-aligned throughout —
   the same fight the landing sections already had to win. */
.entry-content > .wp-block-code,
.entry-content .wp-block-quote,
.entry-content > .perceva-stat,
.entry-content > .perceva-sources,
.perceva-plan > * {
	margin-inline: 0 !important;
}

/* --- The score formula ---------------------------------------------------
 * A finding surface, not a grey developer box: paper ground, hairline border,
 * and the tag's 3px Verdict Green edge so it reads as the same family of
 * stamped object as the self-audit tag and the stat callouts. Tabular numerals
 * because it is arithmetic, and the impact weights should line up.
 */
.entry-content .wp-block-code {
	border: 1px solid var(--wp--preset--color--graphite);
	border-left: 3px solid var(--wp--preset--color--verdict-green);
	border-radius: 0;
	background-color: var(--wp--preset--color--paper);
	padding: var(--wp--preset--spacing--40);
	overflow-x: auto;
	max-width: var(--wp--custom--measure);
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	font-variant-numeric: tabular-nums;
	color: var(--wp--preset--color--ink);
}

/* --- Pulled-out statistics ----------------------------------------------
 * A sourced figure, set apart from the column so a reader skimming a
 * 3,000-word page still meets the evidence. Same vocabulary as everything else
 * that stamps a finding: paper ground, hairline border, zero radius, a Verdict
 * Green edge.
 *
 * The figure is carried by SIZE, not by colour and not by position — the
 * sentence reads identically aloud, in the same order, with the same meaning,
 * and nothing about it depends on seeing the box. Severity colours are not
 * used here: a statistic is not a severity (LANDING-SPEC §2).
 *
 * The sentence stays whole. Setting the bare number large and demoting the
 * attribution to fine print is the thing this page exists to argue against;
 * see the note in inc/markup/page-methodology.php about the 57% figure.
 */
.perceva-stat {
	border: 1px solid var(--wp--preset--color--graphite);
	border-left: 3px solid var(--wp--preset--color--verdict-green);
	border-radius: 0;
	background-color: var(--wp--preset--color--paper);
	padding: var(--wp--preset--spacing--40);
	max-width: var(--wp--custom--measure);
}

.entry-content .perceva-stat > * {
	margin-inline: 0 !important;
}

.perceva-stat__line {
	font-size: var(--wp--preset--font-size--large);
	line-height: 1.35;
	font-variant-numeric: tabular-nums;
	text-wrap: pretty;
	margin-block: 0;
}

/* Attribution, inside the box with the figure it qualifies — never under it in
   a smaller colour as an afterthought. */
.perceva-stat__note {
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--graphite);
	margin-block: var(--wp--preset--spacing--30) 0 !important;
}

/* --- The long-form document layout ---------------------------------------
 * /methodology is ~3,000 words. Contents on the left, prose on the right, both
 * as real columns of a grid — which is what lets the contents panel be sticky
 * safely: its containing block is its own column, so it has nothing to cover.
 * A sticky element in the prose flow would sit on top of the text it is meant
 * to help you read.
 *
 * Below WordPress's columns breakpoint the two stack, the panel stops being
 * sticky (see blocks/page-contents/style.css), and the page is an ordinary
 * single column with an index at the top of it.
 */

.perceva-doc {
	align-items: stretch;
}

/* The prose keeps its measure inside the wider column; the column just decides
   where the measure starts. */
.perceva-doc__body .entry-content {
	max-width: var(--wp--custom--measure);
}

/* --- Sources -------------------------------------------------------------
 * The reference apparatus, set as one: a bordered list of hairline rows, the
 * same treatment the tables get, so it reads as the back matter of a report
 * rather than as more body copy. No green tick markers — a tick means "this
 * passes" in this theme's vocabulary, and a citation is not a finding.
 *
 * URLs are printed in full on purpose (they are the evidence), so they get
 * anywhere-breaking rather than being allowed to push the page sideways.
 */
.perceva-sources {
	list-style: none;
	padding: 0;
	margin-block: 0;
	max-width: var(--wp--custom--measure);
	border: 1px solid var(--wp--preset--color--graphite);
	border-radius: 0;
	background-color: var(--wp--preset--color--paper);
	font-size: var(--wp--preset--font-size--small);
	font-family: var(--wp--preset--font-family--body);
}

.perceva-sources li {
	padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--40);
	border-top: 1px solid var(--wp--preset--color--graphite);
	overflow-wrap: anywhere;
}

.perceva-sources li:first-child {
	border-top: 0;
}

/* --- The dark ground -----------------------------------------------------
 * One section on near-black, to break the cream monotony and to give the
 * honesty section the serious tone it is asking for. It is the honesty section
 * on purpose: it is the one place on the page where we say what the product
 * cannot do.
 *
 * READ THIS BEFORE PUTTING ANY OTHER COLOUR IN HERE. Measured against Ink:
 *
 *   Paper        #FAFAF7   16.05:1   AAA — this is the text
 *   Verdict green light    8.58:1    AAA — the tick, and any accent
 *   Caution amber #8A6100   3.03:1   graphics only, fails as text
 *   Verdict green #1E6B4F   2.61:1   fails text AND non-text
 *   Fail red     #B3261E    2.57:1   fails
 *   Graphite     #4A5147    2.04:1   fails everything
 *
 * So: no severity colour appears on the dark ground at all — none of the three
 * clears 4.5:1 — and the green here is the light token, never Verdict Green.
 * The chips live in the hero, on Paper, where their colours pass.
 *
 * The band carries no focusable element today (a heading and one paragraph),
 * but the focus ring is overridden anyway: the global ring is Verdict Green,
 * which would land at 2.61:1 here the moment anyone adds a link.
 *
 * Rhythm is untouched — this section pads by --rhythm--section like every
 * other one. A second spacing contributor here is exactly the drift the
 * round-two revision removed.
 */

.perceva-section--dark {
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--paper);
}

/*
 * Why this wrapper exists, and why removing it breaks the grid.
 *
 * The band is `alignfull`, so the section box is the whole viewport. Its own
 * children then get WordPress's constrained max-width and auto margins and land
 * on the 1120 line — but the rule fragment carries `margin-inline: 0` (it is
 * left-aligned by design, LANDING-SPEC §2), and against a viewport-wide box
 * that put it at x=24 while the heading beside it sat at x=80. Round one of the
 * Phase A design revision fixed exactly this bug ("section rules sat outside
 * the grid at left:24") and it came straight back the moment a section went
 * full-bleed.
 *
 * The wrapper restores a 1120 box for the content to be left-aligned against,
 * so the colour changes and the grid does not. Measured: the band's h2 and the
 * audiences h2 both start at x=80, and the rule starts at x=80 with them.
 */
.perceva-band-inner {
	max-width: 1120px;
}

.perceva-section--dark :is(h1, h2, h3, h4, p, li) {
	color: var(--wp--preset--color--paper);
}

.perceva-section--dark .wp-block-separator.perceva-rule {
	background-color: var(--wp--preset--color--verdict-green-light);
}

.perceva-section--dark :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline-color: var(--wp--preset--color--paper);
}

/* --- Product imagery -----------------------------------------------------
 * A real screenshot of the plugin panel, captured from a real WordPress block
 * editor with a real scan in it (see README, "Product imagery"). Not a mockup —
 * a mockup here would be the same species of dishonesty the example report card
 * goes to some length to avoid.
 *
 * It has its own section now (inc/markup/plugin-panel.php) rather than hanging
 * off the bottom of the audiences columns with no heading and no caption. That
 * also retires the 22% left indent it used to carry: the indent existed to
 * place it inside somebody else's composition, and in its own band it simply
 * fills its column.
 *
 * Depth comes from elevation and a counter tilt to the hero card. It never
 * overlaps the block above it, which cannot be made collision-proof against
 * text of unknown length — the same reasoning that took the shingle out of the
 * hero.
 *
 * width/height are on the <img> so the box is reserved before the bytes
 * arrive: this contributes zero CLS. It is far below the fold and lazy, so it
 * never touches LCP.
 */

.perceva-shot {
	/*
	 * 26rem, because the panel is 280 CSS px of real editor chrome captured at
	 * 2x: any wider and it is being upscaled, any narrower and the 13px panel
	 * type stops being readable, which would defeat showing a real report.
	 */
	max-width: 26rem;
	margin-block: 0;
	margin-inline: 0 !important;
	rotate: calc(var(--wp--custom--tilt) / -2);
}

.perceva-shot img {
	display: block;
	width: 100%;
	height: auto;
	border: 1px solid var(--wp--preset--color--graphite);
	border-radius: 0;
	box-shadow: var(--wp--preset--shadow--card);
}

/* The caption names the picture; the alt text describes what is in it. Findings
   type and Graphite, the same treatment the example card's caption gets, so the
   two read as the same species of label. */
.perceva-shot figcaption {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--graphite);
	margin-block-start: var(--wp--preset--spacing--30);
	text-align: start;
}

/* --- The blog: /blog and the article pages -------------------------------
 * Two articles is not a blog roll, and rendering two thin rows with an excerpt
 * and a "read more" under each would look like a section somebody forgot to
 * finish. So each entry is a finding surface — the same bordered, square,
 * paper-ground card the report surfaces and the plan cards use — stacked one
 * per row at full content width. Two of those read as a deliberately short
 * index; twenty of them read as an index that grew, with no change to any of
 * this. That is the reason the index is a real posts query rather than a
 * hand-maintained list of two links.
 *
 * There is no new copy anywhere in here. The standfirst under each title is
 * the article's own approved meta description, stored as the post excerpt, so
 * the index says nothing about an article that the article's own front matter
 * did not already say.
 */

.perceva-index__list {
	/* core/post-template is a <ul>. It is a list of articles, which is what a
	   list is for; it just must not look like body copy with bullets. */
	list-style: none;
	padding: 0;
	margin: 0;
	display: grid;
	gap: var(--wp--custom--rhythm--stack);
}

.perceva-entry {
	padding: var(--wp--preset--spacing--50);
	box-shadow: var(--wp--preset--shadow--card);
}

/* Date first, small and quiet, the way a filed report is dated above its
   title. It is a real <time> from the post, never a freshness signal we
   cannot back (CLAUDE.md rule 10). */
.perceva-entry__date {
	margin-block: 0 var(--wp--preset--spacing--30) !important;
	font-family: var(--wp--preset--font-family--body);
	font-variant-numeric: tabular-nums;
}

.perceva-entry__title {
	margin-block: 0 !important;
	line-height: 1.2;
	text-wrap: pretty;
	max-width: var(--wp--custom--measure);
	overflow-wrap: break-word;
}

.perceva-entry__standfirst {
	margin-block-start: var(--wp--preset--spacing--30) !important;
	max-width: var(--wp--custom--measure);
}

.perceva-entry__standfirst p {
	margin-block: 0;
}

/* Renders as nothing at all while there is one page of posts — core returns an
   empty string rather than an empty <nav>, so there is no bare landmark on the
   page today. It is here so the twentieth article does not need a template
   edit. */
.perceva-index__pagination {
	margin-block-start: var(--wp--custom--rhythm--stack);
	font-size: var(--wp--preset--font-size--small);
}

/* The article's own date sits under its H1, inside the page hero, on the same
   grid line as the title and the rule. */
.perceva-article__date {
	font-family: var(--wp--preset--font-family--body);
	font-variant-numeric: tabular-nums;
}

@media (max-width: 781px) {
	/*
	 * No section-padding override here on purpose. --wp--custom--rhythm--section
	 * governs every viewport, so there is exactly one number to change and no
	 * second value that can drift away from it.
	 */

	/* Stacked, everything flush left. Right-aligning the nav and CTA while the
	   wordmark sits left reads as two competing edges on a narrow screen. */
	.perceva-bar,
	.perceva-bar__end {
		justify-content: flex-start;
	}

	.perceva-bar__end {
		width: 100%;
		gap: var(--wp--preset--spacing--30);
	}

	.perceva-bar__end .wp-block-navigation {
		justify-content: flex-start;
	}

	/* The hero's proof element leads on mobile only after the action it
	   supports — the form should not be pushed below the fold by it. */
	.perceva-hero__proof {
		order: 2;
	}

	/*
	 * Off-axis surfaces are a wide-screen device. On a phone the card is nearly
	 * as wide as the viewport, so a tilt costs readable width at both corners
	 * and a crop cuts into content rather than into slack. Everything squares
	 * up and stacks; the elevation stays, because that still reads.
	 */
	.perceva-hero__proof .perceva-report,
	.perceva-hero__proof .perceva-tag,
	.perceva-shot {
		rotate: none;
		margin-inline: 0 !important;
	}

	.perceva-hero__proof .perceva-report {
		margin-inline-end: 0;
	}

	/*
	 * The phone had its own, smaller version of the same overlap — a -1.5rem
	 * pull under a 2.5rem top padding, which cleared the text by 16px and was
	 * therefore only ever one line-wrap away from the desktop bug. It is gone
	 * for the same reason: the grid row does the separating now, and one gap
	 * governs every viewport rather than two values that can drift apart.
	 */
	.perceva-hero__proof {
		gap: var(--wp--preset--spacing--40);
	}
}

/* --- The header on a phone ------------------------------------------------
 *
 * The header is one flex row that wraps. On a desktop that is exactly right.
 * On a 390px phone it wrapped into three stacked rows — wordmark, then nav,
 * then the button — each left-aligned, which read as a broken layout rather
 * than a designed one and pushed the hero another ~90px down the page.
 *
 * Below 600px the nav and the button share one row instead, which is the shape
 * a person expects and which costs the hero nothing.
 */
@media (max-width: 600px) {
	.perceva-bar__end {
		width: 100%;
		justify-content: flex-start !important;
		gap: var(--wp--preset--spacing--30) !important;
		align-items: center;
	}

	/* Tighter than the desktop bar, which wraps to three rows at this width. */
	.perceva-bar {
		row-gap: var(--wp--preset--spacing--30) !important;
	}
}

/* --- The hero on a phone --------------------------------------------------
 *
 * `perceva-section--first` carries a full section's padding above the H1,
 * which on a phone put ~150px of nothing between the header and the headline
 * and pushed the scan form — the reason the page exists — entirely below the
 * fold. The gap is a desktop proportion; on a small screen it is dead space.
 */
@media (max-width: 600px) {
	.perceva-section--first {
		padding-block-start: var(--wp--preset--spacing--40);
	}
}

/* --- The header lockup ----------------------------------------------------
 *
 * flex-wrap: nowrap so the mark and the wordmark stay together. Without it the
 * pair is two flex children of a wrapping bar, and on a narrow screen the mark
 * ends up on its own line above the name, which reads as a broken image rather
 * than a logo.
 */
.perceva-lockup {
	flex-wrap: nowrap !important;
	align-items: center;
}

.perceva-lockup .wp-block-site-title {
	margin: 0;
}
