/*
 * Page composition for the Signy AI theme.
 *
 * SCOPE: this file exists only for things the design system cannot know about
 * — how its components sit inside a WordPress page, and the few places where
 * WordPress's own global styles fight them. It is NOT a place to restyle
 * components. bundle.css and tokens.css are downloaded artefacts, never
 * edited, because tools/sync-design-system.sh overwrites them.
 *
 * THE RULE FOR THIS FILE: every rule must be load-bearing.
 *   - Before adding one, grep bundle.css to check it does not already do it.
 *     Five rules were removed from this file for duplicating bundle.css or
 *     targeting a property nothing changes.
 *   - After adding one, confirm in devtools that it is not struck through.
 *
 * KNOWN CONSTRAINT: bundle.css has a prefers-reduced-motion block that uses
 * !important and limits transition-property to colour, background-color,
 * border-color, box-shadow, opacity and outline-color. Under reduced motion
 * nothing here can animate height, padding or transform — do not try, it will
 * be struck through. That is the design system working as specified.
 */

/* ------------------------------------------------------------------ 1 ----
 * The header floats over the page; the hero starts at the very top.
 *
 * bundle.css makes .sg-header position:sticky, which still occupies flow
 * space, so the hero began below it. Taking the wrapper out of flow removes
 * the problem rather than compensating for it with arithmetic, which is what
 * two earlier attempts did and got wrong.
 *
 * .sg-header drops to static: sticky is redundant inside a fixed parent and
 * could not travel anyway. The condense-on-scroll is unaffected — enhance()
 * toggles .is-stuck from a scroll listener, not from sticky.
 */
:root {
	--signyai-header-block: calc( var( --layout-header-h, 76px ) + var( --space-4 ) * 2 );
}

.sg-page .wp-site-blocks > .wp-block-template-part:first-child {
	position: fixed;
	inset: 0 0 auto 0;
	z-index: 40;
}

.sg-page .sg-header {
	position: static;
}

/*
 * WordPress puts margin-block-start: 24px on every direct child of
 * .wp-site-blocks via :where(), main included. With the header out of flow
 * that margin is a white strip above the hero. :where() has zero specificity,
 * so one class beats it.
 *
 * If the hero stops sitting flush to the top, check this rule first.
 */
.sg-page .wp-site-blocks > main.wp-block-group {
	margin-block-start: 0;
}

/*
 * A hero is meant to run under the floating bar. A page that opens with a
 * heading is not, or its h1 hides behind it.
 */
.signyai-sections:not( .signyai-sections--hero-first ) {
	padding-top: var( --signyai-header-block );
}

/* ------------------------------------------------------------------ 2 ----
 * Let the ambient bloom bleed between sections.
 *
 * .sg-amb and .sg-hero__body both carry overflow: clip, which cut every glow
 * off straight at the section edge — the hard-edged colour bands. Releasing
 * it vertically lets the glow read as one continuous field down the page.
 *
 * The horizontal clip then has to go somewhere: the orbs are sized in vw and
 * sit deliberately outside their box (right: -10%, 46vw across), so without
 * it they widen the document and the page scrolls sideways. It goes on the
 * wrapper, not on body — overflow-x: clip pairs legally with overflow-y:
 * visible and creates no scroll container, so the vertical bleed survives.
 *
 * Shells keep their own clip: a dark slab needs its rounded edge crisp.
 */
.signyai-sections {
	overflow-x: clip;
}

.signyai-sections > .sg-section.sg-amb,
.signyai-sections > .sg-hero,
.signyai-sections > .sg-hero .sg-hero__body {
	overflow: visible;
}

/* ------------------------------------------------------------------ 3 ----
 * Line the header's LOGO up with the content beneath it.
 *
 * bundle.css puts the header bar in sg-container--wide (1440px) while every
 * section and the footer use sg-container (1200px), so the bar overhung the
 * page on both sides. The content width wins: it is the edge the eye tracks
 * down the page.
 *
 * But matching the CONTAINER is not the same as matching what is in it. The
 * header's contents sit inside the glass pill, which pads itself by --space-5,
 * so at 1200px the logo landed 24px right of the footer's logo — the one
 * misalignment on the page you cannot unsee once you have seen it.
 *
 * The pill's padding is therefore added back to the container, both sides, so
 * the pill's own content edge lands where the page's content edge is. Derived
 * from the two tokens rather than written as 1248px: either one can change and
 * this stays correct.
 *
 * Narrower than that, the max-width stops applying and the pill sits in the
 * gutter like any other card, with its logo inset by its own padding. That is
 * the design — a floating pill flush to the screen edge is not — so the two
 * logos part company below ~1248px, on purpose.
 */
.sg-header .sg-container--wide {
	max-width: calc( var( --layout-container ) + var( --space-5 ) * 2 );
}

/* ------------------------------------------------------------------ 4 ----
 * Soften the header's condense-on-scroll.
 *
 * bundle.css transitions the bar's height, the header's padding and the
 * shadow on --ease-standard, which front-loads its movement and reads as a
 * snap across 12px of travel. The brand book assigns --ease-in-out-soft to
 * "anything reversing or looping", and this reverses — it condenses scrolling
 * down and expands scrolling back up.
 *
 * Only the curve and duration change; the properties bundle.css transitions
 * stay as they are, so nothing moves that did not move before.
 */
.sg-page .sg-header,
.sg-page .sg-header .sg-header__bar {
	transition-duration: var( --duration-expand );
	transition-timing-function: var( --ease-in-out-soft );
}

/* ------------------------------------------------------------------ 8 ----
 * Match the figure's height to the column beside it.
 *
 * .sg-media--4x5 fixes an aspect ratio, so the figure ran past the bottom of
 * the answer cards it sits next to. Releasing the ratio and filling the grid
 * track makes the two columns end level; the image already has
 * object-fit: cover, so it crops rather than distorts.
 *
 * Scoped to the marked column, so every other 4x5 figure keeps its ratio.
 */
.signyai-media-match,
.signyai-media-match .sg-media,
.signyai-media-match .sg-media__frame {
	height: 100%;
}

.signyai-media-match .sg-media__frame {
	aspect-ratio: auto;
}

/* ------------------------------------------------------------------ 9 ----
 * The spam honeypot.
 *
 * Moved off screen rather than display:none or hidden. A bot that fills in
 * every field it can see in the markup is the thing being caught, and the
 * cheapest bots skip anything obviously hidden. tabindex="-1" and
 * aria-hidden on the field itself keep it away from anyone real; this only
 * has to keep it out of the layout.
 */
.signyai-honeypot {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* ------------------------------------------------------------------ 10 ----
 * The booking calendar.
 *
 * An iframe has no intrinsic height, so without one it collapses to 150px
 * and the calendar is unreachable. The value is the height Calendly and
 * Cal.com both render their month view at without an inner scrollbar.
 */
.signyai-booking iframe {
	display: block;
	width: 100%;
	height: 680px;
	border: 0;
	border-radius: var( --radius-lg );
}

@media (max-width: 640px) {
	.signyai-booking iframe {
		height: 560px;
	}
}
/* The signal-card blocks sit closer to their own edges than the other dark
 * slabs do. .sg-shell__inner gives every inverse shell --space-11 top and
 * bottom, which is right for the operating-model diagram and too much around
 * five short cards — the brief calls it out on the homepage diagnostic. */
.signyai-signals .sg-shell__inner {
	padding-block: var( --space-9 );
}

/* ---- the contact page ----
 *
 * A narrow column of copy against a wide one holding the calendar, and the
 * form full width below both. The calendar used to sit in the narrow column
 * beside the form, which gave a booking widget half the page and a fixed
 * height, so it arrived cropped.
 *
 * 1fr to 3fr is the quarter Tudor asked for. align-items: start, because the
 * two will never be the same height and stretching the copy column to match a
 * 680px calendar would leave a column of air under four lines of text. */
.signyai-contactpage {
	display: grid;
	grid-template-columns: minmax( 0, 1fr );
	gap: var( --space-8 );
	align-items: start;
}

@media ( min-width: 900px ) {
	.signyai-contactpage {
		grid-template-columns: minmax( 0, 1fr ) minmax( 0, 3fr );
		gap: var( --space-9 );
	}
}

/* The line under the invitation, and the one under that. Both are muted:
 * the heading is the thing being read, these qualify it. */
.signyai-contactpage__booking .signyai-contactpage__lede {
	margin: var( --space-4 ) 0 0;
	color: var( --ink-muted );
	max-width: 56ch;
}

.signyai-contactpage__booking .signyai-prose {
	margin-top: var( --space-3 );
}

.signyai-contactpage__booking .signyai-prose p { color: var( --ink-muted ); }

/* The calendar follows its invitation directly. The gap between them was
 * reading as a break between two unrelated blocks. */
.signyai-contactpage__booking .signyai-booking { margin-top: var( --space-6 ); }

/* ------------------------------------------------------------------ 11 ----
 * The prose voice.
 *
 * The design system's page previews all write a run of paragraphs the same
 * way: the opening one carries full ink at body-lg, and the ones after it
 * step down to --ink-muted. They do it with an inline style per paragraph,
 * which is not something a client writing in a visual editor will ever type,
 * and which wp_kses would filter anyway.
 *
 * THE SIZE IS NOT SET HERE. signyai_prose_classes() puts the design system's
 * own `body-lg` and `body` classes on the paragraphs, which is where every
 * other component in this theme gets its type from. An earlier version of
 * this block set font-size against `--type-body-lg-size`, a custom property
 * that does not exist in tokens.css — so it silently used the hardcoded
 * fallback beside it, 19px, where the brand's body-lg is 20px/32px. Inventing
 * a token name is how a stylesheet ends up a size off and nobody notices.
 *
 * Only the colour is left, because there is no muted-text class to apply:
 * grep `ink-muted` in bundle.css and every hit is a component, not a utility.
 *
 * SCOPED TO .signyai-prose, NOT .sg-prose, and that is the whole lesson of
 * the removed section 15 below: an accordion answer is also .sg-prose, and a
 * rule about "the first paragraph" turned a one-paragraph answer into a
 * heading. These rules describe a run of connective prose in a section, and
 * only signyai_prose() emits one.
 *
 * Inside a dark shell the muted token changes, so the rule is restated rather
 * than inherited: --ink-muted is a grey chosen for white grounds and is close
 * to unreadable on charcoal.
 */
.signyai-prose > p ~ p {
	color: var( --ink-muted );
}

.sg-shell--inverse .signyai-prose > p ~ p,
.sg-section--inverse .signyai-prose > p ~ p {
	color: var( --ink-inverse-muted );
}

/* ------------------------------------------------------------------ 12 ----
 * The pull quote.
 *
 * `.quote` is a real type class in tokens.css. The panel around it is not:
 * the design system draws it with a preview-local rule that never reached
 * bundle.css, so it is reproduced here at the same values.
 */
.signyai-quote {
	margin: 0;
	padding: var( --space-8 );
	border-radius: var( --radius-xl );
	background: rgba( 241, 240, 236, 0.06 );
	border: var( --stroke-hairline ) solid rgba( 241, 240, 236, 0.14 );
}

.signyai-quote p {
	margin: 0;
	color: var( --ink-inverse );
}

/* ------------------------------------------------------------------ 13 ----
 * Direct contact.
 *
 * A heading, then one line per destination, each under the last. The spacing
 * used to be margins on bare classes — .signyai-contact__next at (0,1,0)
 * against .sg-page p at (0,1,1) — so every one of them lost and the lines
 * ran together. A flex column needs no margins at all and cannot be
 * out-specified by a reset.
 */
.signyai-contactlinks {
	display: flex;
	flex-direction: column;
	gap: var( --space-2 );
}

.signyai-contactlinks > p { margin: 0; }

.signyai-contactlinks > .h5 {
	margin: 0 0 var( --space-2 );
}

.signyai-contact__link {
	color: var( --highlight );
}

.signyai-contactlinks .signyai-contact__note {
	margin-top: var( --space-4 );
	color: var( --ink-muted );
}

/* ------------------------------------------------------------------ 14 ----
 * The partner marquee.
 *
 * Two identical runs inside one track, translated by exactly half its width,
 * which puts the second run where the first started and makes the loop
 * seamless. The trailing gap lives on each run as padding rather than on the
 * track as a gap: a flex gap sits BETWEEN the runs and not after the second,
 * so the track would not be exactly twice one run and the loop would jump.
 *
 * overflow-x: clip rather than hidden, for the same reason as section 2 —
 * hidden would create a scroll container and cut the ambient bloom off.
 */
.signyai-marquee {
	overflow-x: clip;

	/*
	 * EDGE TO EDGE, out of the container it is rendered in. The strip is a
	 * band of movement rather than a column of content: stopped at the
	 * container's edge it reads as a widget sitting in the page, and the loop
	 * becomes obvious because you can see both ends of it.
	 *
	 * Safe because `logos` is the one shape with a FIXED ground - always
	 * `surface`, never inside a .sg-shell--inverse slab (signyai_section_ground()
	 * says so, with the reasoning). Inside a rounded slab this would break out
	 * of it and then be clipped by it.
	 *
	 * 100vw includes the scrollbar, so this overhangs the document by its
	 * width; .signyai-sections already carries overflow-x: clip for the
	 * ambient orbs, which have the same habit, and it absorbs this too.
	 */
	width: 100vw;
	margin-inline: calc( 50% - 50vw );

	/*
	 * And the ends dissolve rather than being cut, the same way the hero's
	 * photograph dissolves into its ground - same eleven stops, same reason.
	 * Alpha composites linearly, so an even ramp sits at half opacity across
	 * the middle of the fade and the eye reads that flat band as an edge,
	 * which is the thing the fade was there to remove. The stops below are
	 * that curve expressed as fractions of the fade width, mirrored.
	 *
	 * Unsupported, the strip is opaque to both edges - a hard cut, which is
	 * where this started.
	 */
	--signyai-marquee-fade: clamp( 64px, 10vw, 200px );
	--signyai-marquee-mask: linear-gradient( 90deg,
		rgba( 0, 0, 0, 0 ) 0,
		rgba( 0, 0, 0, 0.015 ) calc( var( --signyai-marquee-fade ) * 0.138 ),
		rgba( 0, 0, 0, 0.06 ) calc( var( --signyai-marquee-fade ) * 0.259 ),
		rgba( 0, 0, 0, 0.14 ) calc( var( --signyai-marquee-fade ) * 0.362 ),
		rgba( 0, 0, 0, 0.27 ) calc( var( --signyai-marquee-fade ) * 0.466 ),
		rgba( 0, 0, 0, 0.45 ) calc( var( --signyai-marquee-fade ) * 0.552 ),
		rgba( 0, 0, 0, 0.65 ) calc( var( --signyai-marquee-fade ) * 0.638 ),
		rgba( 0, 0, 0, 0.82 ) calc( var( --signyai-marquee-fade ) * 0.724 ),
		rgba( 0, 0, 0, 0.93 ) calc( var( --signyai-marquee-fade ) * 0.810 ),
		rgba( 0, 0, 0, 0.98 ) calc( var( --signyai-marquee-fade ) * 0.897 ),
		rgba( 0, 0, 0, 1 ) var( --signyai-marquee-fade ),
		rgba( 0, 0, 0, 1 ) calc( 100% - var( --signyai-marquee-fade ) ),
		rgba( 0, 0, 0, 0.98 ) calc( 100% - var( --signyai-marquee-fade ) * 0.897 ),
		rgba( 0, 0, 0, 0.93 ) calc( 100% - var( --signyai-marquee-fade ) * 0.810 ),
		rgba( 0, 0, 0, 0.82 ) calc( 100% - var( --signyai-marquee-fade ) * 0.724 ),
		rgba( 0, 0, 0, 0.65 ) calc( 100% - var( --signyai-marquee-fade ) * 0.638 ),
		rgba( 0, 0, 0, 0.45 ) calc( 100% - var( --signyai-marquee-fade ) * 0.552 ),
		rgba( 0, 0, 0, 0.27 ) calc( 100% - var( --signyai-marquee-fade ) * 0.466 ),
		rgba( 0, 0, 0, 0.14 ) calc( 100% - var( --signyai-marquee-fade ) * 0.362 ),
		rgba( 0, 0, 0, 0.06 ) calc( 100% - var( --signyai-marquee-fade ) * 0.259 ),
		rgba( 0, 0, 0, 0.015 ) calc( 100% - var( --signyai-marquee-fade ) * 0.138 ),
		rgba( 0, 0, 0, 0 ) 100% );

	-webkit-mask-image: var( --signyai-marquee-mask );
	mask-image: var( --signyai-marquee-mask );

	/* Stated, not left to the initial values: a mask with no size and no
	 * repeat is tiled by some engines, which puts a fade at every repetition. */
	-webkit-mask-size: 100% 100%;
	mask-size: 100% 100%;
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
}

.signyai-marquee__track {
	display: flex;
	width: max-content;
	animation: signyai-marquee var( --signyai-marquee-duration, 42s ) linear infinite;
}

/* Wider than the wrapping strip's gap. Without the pill there is no border to
 * say where one partner ends and the next begins, so the air has to. */
.signyai-marquee__run {
	flex-wrap: nowrap;
	gap: var( --space-8 );
	padding-right: var( --space-8 );
}

/* Stops for a pointer AND for a keyboard. focus-within is the half people
 * forget, and it is the half that decides whether the row is usable by
 * someone tabbing through the page. */
.signyai-marquee:hover .signyai-marquee__track,
.signyai-marquee:focus-within .signyai-marquee__track {
	animation-play-state: paused;
}

@keyframes signyai-marquee {
	to {
		transform: translateX( -50% );
	}
}

/*
 * The static fallback the LogoStrip component's own note asks for. This is
 * the theme's own animation, not one of bundle.css's, so nothing overrides
 * it with !important and the rule actually applies — see the guard in
 * StylesheetIntegrityTest, which allows a reduced-motion block only for
 * theme-owned selectors.
 */
/*
 * COMMENTED OUT ON REQUEST, like the one in bundle.css. Kept in place rather
 * than deleted so it can be switched back on by removing two characters.
 *
 * While it is off the marquee animates regardless of the visitor's operating
 * system setting. People turn that setting on because motion causes migraines
 * and vestibular symptoms; this disables that accommodation.
 *
 * TO REVERT: uncomment, and update StylesheetIntegrityTest.
 *
@media ( prefers-reduced-motion: reduce ) {
	.signyai-marquee__track {
		animation: none;
	}

	.signyai-marquee__run[aria-hidden="true"] {
		display: none;
	}
}
*/

/* ------------------------------------------------------------------ 15 ----
 * (was: a size bump on an accordion answer's first paragraph)
 *
 * REMOVED, and worth saying why so nobody adds it back. It set the first
 * paragraph of every accordion panel at h4 and semibold, to reproduce the
 * lead line the design system gives each service pillar.
 *
 * But most accordions have no lead line — the homepage objections are a
 * single paragraph of ordinary prose — and `p:first-child` matched that one
 * paragraph too, so the whole answer came out big and bold.
 *
 * The answer is now a visual editor, so which line is bold is the client's
 * choice, made per paragraph, and the brand colour comes from [[brackets]].
 * That is a decision the CMS can express; a blanket rule about first
 * paragraphs is not.
 */

/* ------------------------------------------------------------------ 16 ----
 * Numbered lists inside edited prose.
 *
 * bundle.css sets `list-style: none` on every list, because its own bullet
 * component draws a dot instead. signyai_prose_classes() converts a bulleted
 * list into that component, but a numbered list has no counterpart in the
 * design system and losing the numbers would make it unreadable.
 *
 * So the minimum is restored here and nothing more: this is not a designed
 * style, it is a list that still reads as one.
 */
.signyai-prose ol,
.sg-accordion__panel ol {
	list-style: decimal;
	padding-left: var( --space-6 );
}

.signyai-prose ol li,
.sg-accordion__panel ol li {
	margin-left: var( --space-2 );
}

/* ------------------------------------------------------------------ 17 ----
 * A footer line that is a statement rather than a destination.
 *
 * The third footer column ends on "Based in Europe. Working globally." — a
 * sentence in a list of links. It cannot carry .sg-footer__link: that class
 * changes colour and slides sideways on hover, and something that reacts to
 * the pointer but does nothing when clicked is a broken link, not a caption.
 *
 * A theme-owned class rather than a rule on the design system's, because that
 * is the standing agreement here: theme.css may place a component, never
 * restyle one. The colour is the same muted inverse ink .sg-footer__link
 * starts from, so the column reads as one block of type.
 */
.signyai-footer__note {
	display: block;
	color: var( --ink-inverse-muted );
}

/* ------------------------------------------------------------------ 18 ----
 * Room above an accordion row that was linked to directly.
 *
 * The header is sticky and 76px tall (64px once the page has scrolled), so a
 * browser jumping to a fragment parks the row's heading underneath it. This
 * is placement, not style: it moves where a component lands and changes
 * nothing about how it looks.
 *
 * Applied to the item rather than to :target, because bundle.js re-aims at the
 * trigger after opening the row and both scrolls need the same offset.
 */
.sg-accordion__item,
.sg-accordion__trigger {
	/* CHANGED HERE: was --space-9 + a hard-coded 76px, which left 64px of air
	   under the bar — far too much on a phone, where the row landed well below
	   the fold of the thing you clicked. --space-5 puts it ~24px under the bar
	   at every width, and the bar's height now comes from its own token rather
	   than a number repeated here. */
	scroll-margin-top: calc( var( --layout-sticky-top, 76px ) + var( --space-5 ) );
}

/* ------------------------------------------------------------------ 19 ----
 * The booking anchor clears the sticky header.
 *
 * Every "Book a Discovery Call" button points at /contact/#booking. The
 * header is position:sticky, so the browser scrolls the card's top edge to
 * y=0 and the header then covers it. Section 18 does the same for accordion
 * rows arrived at by fragment; this is the same fault in a different place.
 */
.signyai-booking {
	scroll-margin-top: calc( var( --layout-sticky-top, 96px ) + var( --space-4 ) );
}

/* ------------------------------------------------------------------ 20 ----
 * The sticky heading lights word by word as the section scrolls.
 *
 * These are theme-owned classes, written by bundle.js's ScrollWords at
 * runtime — theme.css is not restyling a design-system component here, it is
 * styling markup the theme itself adds.
 *
 * Opacity rather than a colour swap, deliberately: these headings sit on
 * Off-White, on white and on charcoal, and one dimmed colour cannot be right
 * on all three. Opacity is.
 *
 * Without JavaScript no .signyai-word is ever created, so the heading renders
 * at full strength and nothing below applies. That is the intended fallback,
 * not an accident.
 */
.signyai-word {
	transition: opacity var( --duration-base ) var( --ease-standard );
}

.signyai-word[data-lit="false"] {
	opacity: 0.26;
}

.signyai-word[data-lit="true"] {
	opacity: 1;
}

/*
 * COMMENTED OUT ON REQUEST, with the other reduced-motion rules. Uncomment to
 * show the heading whole and still, with no scroll-driven fade.
 *
@media ( prefers-reduced-motion: reduce ) {
	.signyai-word,
	.signyai-word[data-lit="false"] {
		opacity: 1;
		transition: none;
	}
}
*/

/* ------------------------------------------------------------------ 20b ---
 * A SPECIFICITY TRAP, read this before writing a rule below.
 *
 * bundle.css line 35 resets margins on every p, h1-h5, ul, ol, figure and
 * blockquote inside .sg-page. That selector is (0,1,1). A rule written as a
 * single class on one of those elements is (0,1,0) and LOSES, whatever the
 * load order — theme.css coming last does not help, because specificity is
 * decided before order.
 *
 * It is silent: the declaration is valid, DevTools shows it, and it does
 * nothing. Three separate spacing fixes below were dead for this reason.
 *
 * So anywhere a margin is set on one of those elements, the selector names a
 * parent as well — .signyai-article .signyai-article__media rather than
 * .signyai-article__media. Two classes beats one class and one element.
 */

/* ------------------------------------------------------------------ 21 ----
 * The Revenue Signal Diagnostic.
 *
 * Theme-owned markup, so theme-owned classes, reusing the system's tokens
 * throughout — same radii, spacing, borders and focus ring as the rest of the
 * site, so the tool reads as part of the page rather than embedded in it.
 *
 * REBUILT AS A LIST, NOT A STEPPER. The five signal areas are the accordion
 * from bundle.css — one component, one set of keyboard behaviour, one easing
 * — and what is here is the panel contents, the progress bar that carries the
 * submit, and the results. There is no Back, no Next and no email step.
 *
 * READ THIS BEFORE ADDING A `display` ANYWHERE BELOW. The dialog and the
 * results both use the `hidden` attribute, which is only a UA rule
 * (`display: none`), so ANY author `display` on the same element beats it and
 * the element stays visible. That is how the page once ended up showing two
 * "See my results" buttons at once. The guard below makes `hidden` win; keep
 * it, and keep it first.
 */
.signyai-diag [hidden],
.signyai-diag__report [hidden] { display: none !important; }

.signyai-diag { margin-top: var( --space-8 ); }

/* ---- the progress bar, which carries the submit ----
 *
 * The count, the fill and the button all answer "how much is left", so they
 * sit in one row rather than in three places. It is a pill rather than a bare
 * rule because it holds a button, and a button needs a ground to sit on.
 *
 * Sticky, so it stays in reach while you work down five open areas — but only
 * where there is room for it: at phone height a bar pinned to the top takes a
 * question's worth of screen. */
.signyai-diag__bar {
	/*
	 * THE SAME WIDTH AS THE CONTENT IT BELONGS TO. It was stepped out to the
	 * header pill's width, which read as a third width on a page that already
	 * has two — the bar was visibly wider than the accordion under it, which
	 * is the thing it is counting.
	 */
	display: flex;
	align-items: center;
	gap: var( --space-5 );
	padding: var( --space-3 ) var( --space-3 ) var( --space-3 ) var( --space-6 );
	border: var( --stroke-hairline ) solid var( --border-subtle );
	border-radius: var( --radius-full );
	background: var( --surface );
	box-shadow: var( --shadow-sm );
	margin-bottom: var( --space-7 );
}

@media ( min-height: 700px ) {
	.signyai-diag__bar {
		position: sticky;
		top: calc( var( --layout-sticky-top, 76px ) + var( --space-4 ) );
		z-index: 3;
	}
}

.signyai-diag__bar .signyai-diag__count {
	flex: 0 0 auto;
	margin: 0;
	color: var( --ink-muted );
	white-space: nowrap;
}

.signyai-diag__count strong {
	color: var( --ink );
	font-size: 17px;
}

.signyai-diag__track {
	flex: 1 1 auto;
	height: 4px;
	border-radius: var( --radius-full );
	background: var( --border-subtle );
	overflow: hidden;
}

/*
 * BY CLASS, NEVER `> span`. The benchmark mark is a span inside this same
 * track, so `.signyai-diag__track > span` matched it too — and at (0,1,1),
 * one class plus one element, it OUT-SPECIFIED `.signyai-diag__benchmark` at
 * (0,1,0). The mark therefore took the fill's cobalt background, its width
 * and its height, and every declaration meant for it lost silently: valid
 * CSS, listed in DevTools, struck through. Same shape of fault as the
 * `.sg-page p` margin reset, and the second time it has bitten.
 */
.signyai-diag__meter-fill {
	display: block;
	width: var( --sg-fill, 0% );
	height: 100%;
	border-radius: var( --radius-full );
	background: var( --surface-brand );
	transition: width var( --duration-expand ) var( --ease-standard );
}

/*
 * A RESULT bar starts at nothing and grows when it is scrolled to; the
 * progress bar above the questions does not, because it answers to what you
 * have just ticked and a bar waiting to be scrolled past would be wrong every
 * time. Both wear the same class, so this is where the two part.
 */
.signyai-diag__track--result .signyai-diag__meter-fill { width: 0; }

.signyai-diag__track--result .signyai-diag__meter-fill[data-filled="true"] {
	width: var( --sg-fill, 0% );
	transition: width var( --duration-slow, 720ms ) var( --ease-out-soft );
}

.signyai-diag__bar .sg-btn { flex: 0 0 auto; }

/* Not `disabled`: a disabled button fires no click, and the click is what
 * opens the dialog that explains what is missing. */
.signyai-diag__go[aria-disabled="true"] { opacity: 0.55; }

@media ( max-width: 768px ) {
	.signyai-diag__bar {
		flex-wrap: wrap;
		border-radius: var( --radius-lg );
		padding: var( --space-4 );
	}

	.signyai-diag__track { order: 3; flex-basis: 100%; }
	.signyai-diag__bar .sg-btn { margin-left: auto; }

	/* The accordion row no longer has to clear a progress bar pinned under
	 * the header — it is in the flow at this width. */
	.signyai-diag__areas .sg-accordion__item {
		scroll-margin-top: calc( var( --layout-sticky-top, 76px ) + var( --space-5 ) );
	}

	.signyai-diag__bar-row { padding: 0; }

	/* The tile's own padding is most of a phone's width at --space-7. */
	.signyai-diag__tile { padding: var( --space-6 ); }
}

/* The filter wants the whole row before the chips do. At 260px of minimum it
 * was sharing a line with them on a small phone and both were cramped. */
@media ( max-width: 640px ) {
	.signyai-glossary__controls { gap: var( --space-4 ); }
	.signyai-glossary__search { flex: 1 1 100%; max-width: none; }
}

/* ---- the five areas ----
 *
 * The row trim is the accordion's. The only thing added is the state: a
 * finished area turns its number chip Mint, so the list reads as a record of
 * where you have been without opening anything.
 *
 * Mint as a GROUND with `on-accent` ink on it, never as a text colour — mint
 * on white is 1.6:1 and the brand book makes that a rule. */
.signyai-diag__areas { margin-bottom: var( --space-8 ); }

/* Finishing an area scrolls to the next one, and two sticky things are in the
 * way: the site header, and the progress bar pinned under it. The sum clears
 * both, so the row lands with its first question in view rather than behind
 * them. */
.signyai-diag__areas .sg-accordion__item {
	scroll-margin-top: calc( var( --layout-sticky-top, 76px ) + var( --space-10 ) );
}

.signyai-diag__area.is-complete .sg-accordion__num {
	background: var( --surface-accent );
	color: var( --on-accent );
	border-color: transparent;
}

/* ---- questions ---- */
.signyai-diag__q {
	border: 0;
	padding: 0;
	margin: 0 0 var( --space-8 );
}

.signyai-diag__q-text { padding: 0; margin-bottom: var( --space-4 ); }

/* The number is in the legend so a screen reader announces it with the
 * question, and muted so the question itself is what you read. */
.signyai-diag__q-num { color: var( --ink-muted ); }

.signyai-diag__opts {
	display: grid;
	gap: var( --space-3 );
}

/* The whole row is the target, not the 4px dot inside it. */
.signyai-diag__opt {
	display: flex;
	align-items: flex-start;
	gap: var( --space-3 );
	padding: var( --space-4 ) var( --space-5 );
	border: var( --stroke-hairline ) solid var( --border-subtle );
	border-radius: var( --radius-md );
	background: var( --surface );
	cursor: pointer;
	transition:
		border-color var( --duration-base ) var( --ease-standard ),
		background-color var( --duration-base ) var( --ease-standard );
}

.signyai-diag__opt:hover { border-color: rgba( 38, 26, 192, 0.45 ); }

.signyai-diag__opt input {
	flex: 0 0 auto;
	margin-top: 0.2em;
	accent-color: var( --highlight );
}

.signyai-diag__opt:has( input:checked ) {
	border-color: var( --highlight );
	background: rgba( 38, 26, 192, 0.05 );
}


.signyai-diag__error {
	color: var( --ink );
	background: rgba( 38, 26, 192, 0.07 );
	border-left: 3px solid var( --highlight );
	padding: var( --space-4 ) var( --space-5 );
	border-radius: var( --radius-md );
	margin-bottom: var( --space-6 );
}

/* ---- the dialog ----
   A <dialog> arrives with a 2px black border and square corners from the UA
   stylesheet. Every one of these declarations is undoing that. */
.signyai-diag__dialog {
	border: var( --stroke-hairline ) solid var( --border-subtle );
	border-radius: var( --radius-lg );
	padding: var( --space-8 );
	max-width: 30rem;
	width: calc( 100% - var( --space-8 ) );
	color: var( --ink );
	background: var( --surface );
	box-shadow: var( --shadow-lg, var( --shadow-md ) );
}

.signyai-diag__dialog::backdrop {
	background: rgba( 17, 21, 22, 0.5 );
}

.signyai-diag__dialog p {
	color: var( --ink-muted );
	margin: var( --space-4 ) 0 var( --space-4 );
}

.signyai-diag__dialog-actions {
	display: flex;
	gap: var( --space-6 );
	flex-wrap: wrap;
}

/* ---- the result ----
 *
 * The tile and the bars, side by side above 900px. The tile is the system's
 * own inverse shell, so the rounded slab, the grain and the ink are the ones
 * every other dark block on the site uses. */
.signyai-diag__result {
	display: grid;
	grid-template-columns: minmax( 0, 1fr );
	gap: var( --space-7 );
	align-items: start;
}

@media ( min-width: 900px ) {
	.signyai-diag__result {
		grid-template-columns: minmax( 0, 320px ) minmax( 0, 1fr );
		gap: var( --space-8 );
	}
}

/*
 * NO `> *` POSITIONING RULE ON THE TILE, whatever it looks like it needs.
 * .sg-amb already lifts real children above the bloom, and a second copy of
 * that rule out-orders .sg-amb__orb and drops a 600px orb into the flow —
 * which is what once made this tile render 1005px tall against 558px of bars.
 */
.signyai-diag__tile {
	padding: var( --space-7 );
	/*
	 * A GRADIENT, which the brand book reserves for the campaign landing
	 * template — "gradients are backgrounds only, and only the campaign
	 * landing template uses one". Tudor's call, and the design system's own
	 * DiagnosticResult does it: the tile is the one thing on the page that is
	 * a verdict rather than a measurement, and the lift separates it from the
	 * five flat bars beside it. Flagged rather than quietly done.
	 *
	 * Built from the two tokens it blends — charcoal into cobalt — rather
	 * than from --gradient-dark-1, which opens on mint and would put a green
	 * corner on a 320px tile. The last stop sits past the edge so the cobalt
	 * never fully arrives and the ink stays at contrast across the whole
	 * surface.
	 */
	background-image: linear-gradient(
		152deg,
		var( --surface-inverse ) 0%,
		var( --surface-inverse ) 40%,
		var( --surface-brand ) 165%
	);
	/* The bars are a long column and the tile is short. Sticking it keeps the
	 * headline beside whichever bar you are reading instead of scrolling away
	 * at the first one. */
	position: sticky;
	top: calc( var( --layout-sticky-top, 76px ) + var( --space-6 ) );
}

@media ( max-width: 899px ) {
	.signyai-diag__tile { position: static; }
}

.signyai-diag__tile .signyai-diag__headline-score {
	margin: 0;
	display: flex;
	align-items: baseline;
	gap: var( --space-1 );
	color: var( --surface-accent );
}

.signyai-diag__headline-max { color: var( --ink-inverse-muted ); }

.signyai-diag__tile .signyai-diag__headline-band {
	margin: var( --space-3 ) 0 var( --space-3 );
	color: var( --ink-inverse );
}

/* The sentence is what makes the band a finding rather than a word. Muted,
 * because the number and the band above it are the headline and this explains
 * them — three things at one weight is three headlines. */
.signyai-diag__tile .signyai-diag__headline-note {
	margin: 0 0 var( --space-7 );
	color: var( --ink-inverse-muted );
	max-width: 34ch;
}

/* The ladder is a distance, not a value: four rungs, every one up to where
 * you are lit. It repeats what the band already says in words, which is the
 * point — the word names it, the rungs place it. */
.signyai-diag__ladder {
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: 1fr;
	gap: var( --space-2 );
}

.signyai-diag__rung {
	height: 4px;
	border-radius: var( --radius-full );
	background: rgba( 241, 240, 236, 0.18 );
}

.signyai-diag__rung.is-at { background: var( --surface-accent ); }

.signyai-diag__tile .signyai-diag__ladder-ends {
	display: flex;
	justify-content: space-between;
	gap: var( --space-4 );
	margin: var( --space-2 ) 0 0;
	color: var( --ink-inverse-muted );
}

/* ---- one measured bar per signal area ---- */
.signyai-diag__bar-row + .signyai-diag__bar-row {
	margin-top: var( --space-7 );
	padding-top: var( --space-7 );
	border-top: var( --stroke-hairline ) solid var( --border-subtle );
}

.signyai-diag__bar-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var( --space-4 );
	margin-bottom: var( --space-3 );
}

.signyai-diag__bar-row .signyai-diag__bar-name { margin: 0; }

.signyai-diag__bar-row .signyai-diag__bar-value {
	margin: 0;
	flex: 0 0 auto;
	color: var( --ink-muted );
	white-space: nowrap;
}

/* The track needs a height of its own here and a position, because the
 * benchmark stands inside it. */
.signyai-diag__track--result {
	position: relative;
	height: 10px;
	overflow: visible;
}

.signyai-diag__track--result > .signyai-diag__meter-fill {
	max-width: 100%;
}

/*
 * THE BENCHMARK IS A DIFFERENT KIND OF MARK, NOT A DIFFERENT COLOUR. A rule
 * standing across the track with a surface-coloured ring either side, so it
 * stays visible wherever it lands — including inside the fill, which is where
 * it lands whenever somebody is doing well. Somebody who cannot separate two
 * hues at all still reads the comparison, which is not true of a second
 * coloured bar.
 */
.signyai-diag__benchmark {
	position: absolute;
	top: -7px;
	bottom: -7px;
	left: var( --sg-at, 50% );
	width: 3px;
	margin-left: -1.5px;
	border-radius: var( --radius-full );
	background: var( --ink );
	/* The ring is what makes it SPLIT the bar rather than sit on it. Three
	 * pixels of the page's own ground either side, so the fill reads as cut
	 * at the benchmark wherever the mark lands — including inside the fill,
	 * which is where it lands whenever somebody is doing well. */
	box-shadow: 0 0 0 3px var( --surface );
}

/* Scoped, every one of them. bundle.css zeroes the margin on every p, h1-h5,
 * ul, ol, figure and blockquote inside .sg-page, and that selector is (0,1,1).
 * A theme rule written as one class on one of those elements is (0,1,0) and
 * loses — the declaration is valid, DevTools lists it, and it does nothing.
 * StylesheetIntegrityTest::testSpacingOnResetElementsOutSpecifiesTheReset
 * pins the ones that matter. */
.signyai-diag__bar-row .signyai-diag__gap,
.signyai-diag__bar-row .signyai-diag__bar-rec {
	margin: var( --space-3 ) 0 0;
	color: var( --ink-muted );
}

/* The figure is the one thing in the line somebody scans for. */
.signyai-diag__bar-row .signyai-diag__gap strong {
	color: var( --ink );
	font-weight: 600;
}

.signyai-diag__bar-row .signyai-diag__bar-rec { color: var( --ink ); }

.signyai-diag__row-foot { margin-top: var( --space-4 ); }

/* One series, so there is no legend for identity. This names what the two
 * marks ARE, which is a different job. */
.signyai-diag__bars .signyai-diag__key {
	display: flex;
	align-items: center;
	gap: var( --space-2 );
	margin: var( --space-7 ) 0 0;
	color: var( --ink-muted );
}

.signyai-diag__key-score,
.signyai-diag__key-mark {
	display: inline-block;
	flex: 0 0 auto;
}

.signyai-diag__key-score {
	width: 10px;
	height: 10px;
	border-radius: var( --radius-full );
	background: var( --surface-brand );
}

.signyai-diag__key-mark {
	width: 2px;
	height: 12px;
	margin-left: var( --space-3 );
	background: var( --ink );
}

/* ---- the ask underneath ---- */
.signyai-diag__close {
	margin-top: var( --space-9 );
	padding-top: var( --space-8 );
	border-top: var( --stroke-hairline ) solid var( --border-subtle );
}

.signyai-diag__close .sg-sectionhead__sub { color: var( --ink-muted ); max-width: 62ch; }



/*
 * COMMENTED OUT ON REQUEST, with the other reduced-motion rules.
 *
@media ( prefers-reduced-motion: reduce ) {
	.signyai-diag__meter-fill { transition: none; }
}
*/

/* ------------------------------------------------------------------ 22 ----
 * Insights: the index, the cards and the article.
 *
 * Theme-owned classes. The cards deliberately do not reuse .sg-card: an
 * article card is a full-bleed image above a fixed run of meta, and .sg-card
 * is a padded container that would have to be unpicked to allow it.
 */

/* ---- chips: categories and the filter row ---- */
.signyai-chips {
	display: flex;
	flex-wrap: wrap;
	gap: var( --space-2 );
	margin-bottom: var( --space-3 );
	position: relative;
	z-index: 1; /* above a card's stretched link, so a chip stays clickable */
}

.signyai-chip {
	display: inline-flex;
	align-items: center;
	padding: var( --space-1 ) var( --space-3 );
	border-radius: var( --radius-md );
	border: var( --stroke-hairline ) solid var( --border-subtle );
	background: var( --surface );
	color: var( --ink-muted );
	text-decoration: none;
	transition:
		color var( --duration-base ) var( --ease-standard ),
		border-color var( --duration-base ) var( --ease-standard );
}

.signyai-chip:hover { color: var( --highlight ); border-color: var( --highlight ); }

.signyai-chip.is-current {
	background: var( --highlight );
	border-color: var( --highlight );
	color: var( --ink-inverse );
}

.signyai-insights__filters { margin-bottom: var( --space-8 ); }

/* ---- cards ---- */
.signyai-insights {
	display: grid;
	gap: var( --space-6 );
	grid-template-columns: repeat( auto-fill, minmax( 300px, 1fr ) );
	margin-top: var( --space-7 );
}

.signyai-insight {
	position: relative; /* the containing block for the stretched link */
	display: flex;
	flex-direction: column;
	border: var( --stroke-hairline ) solid var( --border-subtle );
	border-radius: var( --radius-lg );
	background: var( --surface );
	overflow: hidden;
	transition:
		border-color var( --duration-base ) var( --ease-standard ),
		transform var( --duration-base ) var( --ease-standard );
}

/* A dimmed cobalt, not the full highlight: at full strength the border read
   as a selected state rather than a hover. */
.signyai-insight:hover {
	border-color: rgba( 38, 26, 192, 0.3 );
	transform: translateY( -3px );
}

/* No outline on the card for :focus-within — the hover treatment already
   marks it, and the real link inside keeps its own :focus-visible ring, which
   is the one that matters for keyboard users. */
.signyai-insight:focus-within { border-color: rgba( 38, 26, 192, 0.3 ); }

.signyai-insight__media {
	aspect-ratio: 16 / 10;
	background: var( --surface-alt );
	overflow: hidden;
}

.signyai-insight__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.signyai-insight__body {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	padding: var( --space-7 ) var( --space-6 ) var( --space-6 );
}

.signyai-insight .signyai-insight__title { margin-bottom: var( --space-3 ); }

.signyai-insight__title a {
	color: inherit;
	text-decoration: none;
}

/* This is what makes the whole card clickable while keeping one real link. */
.signyai-insight__title a::after {
	content: "";
	position: absolute;
	inset: 0;
}

.signyai-insight__title a:focus-visible {
	outline: var( --stroke-focus ) solid var( --focus );
	outline-offset: var( --sg-focus-offset, 2px );
	border-radius: var( --radius-xs );
}
.signyai-insight:hover .signyai-insight__title a { color: var( --highlight ); }
.signyai-insight__excerpt { color: var( --ink-muted ); }

.signyai-insight .signyai-insight__meta {
	margin-top: auto;
	padding-top: var( --space-4 );
	color: var( --ink-muted );
}

/* ---- the lead article ---- */
.signyai-insight--feature {
	display: grid;
	grid-template-columns: minmax( 0, 1.1fr ) minmax( 0, 1fr );
	align-items: stretch;
}

/* 16/12, not auto. With `auto` the media had no intrinsic height of its own,
   so the row took it from the image's natural proportions — a portrait photo
   made the whole lead card as tall as the viewport. */
.signyai-insight--feature .signyai-insight__media {
	aspect-ratio: 16 / 12;
	height: 100%;
}

.signyai-insight--feature .signyai-insight__body {
	justify-content: center;
	padding: var( --space-8 ) var( --space-8 ) var( --space-8 ) var( --space-9 );
}

@media ( max-width: 860px ) {
	.signyai-insight--feature { grid-template-columns: minmax( 0, 1fr ); }
	.signyai-insight--feature .signyai-insight__media { aspect-ratio: 16 / 10; }
	.signyai-insight--feature .signyai-insight__body { padding: var( --space-6 ); }
}

.signyai-insights__paging { margin-top: var( --space-9 ); }

.signyai-insights__paging .nav-links {
	display: flex;
	gap: var( --space-4 );
	flex-wrap: wrap;
	align-items: center;
}

.signyai-insights__paging .page-numbers {
	display: inline-flex;
	min-width: 38px;
	height: 38px;
	align-items: center;
	justify-content: center;
	padding-inline: var( --space-2 );
	border-radius: var( --radius-md );
	border: var( --stroke-hairline ) solid var( --border-subtle );
	text-decoration: none;
	color: var( --ink );
}

.signyai-insights__paging .page-numbers.current {
	background: var( --highlight );
	border-color: var( --highlight );
	color: var( --ink-inverse );
}

/* ---- breadcrumbs ---- */
.signyai-crumbs { margin-bottom: var( --space-6 ); }

.signyai-crumbs ol {
	display: flex;
	flex-wrap: wrap;
	gap: var( --space-2 );
	list-style: none;
	margin: 0;
	padding: 0;
	color: var( --ink-muted );
}

.signyai-crumbs li + li::before {
	content: "/";
	margin-right: var( --space-2 );
	color: var( --border-strong, var( --ink-muted ) );
}

.signyai-crumbs a { color: inherit; }
.signyai-crumbs a:hover { color: var( --highlight ); }

/* ---- the article ---- */
.signyai-article__head { margin-bottom: var( --space-7 ); }
.signyai-article .signyai-article__meta { color: var( --ink-muted ); margin-top: var( --space-3 ); }

.signyai-article .signyai-article__media {
	margin: 0 0 var( --space-8 );
	border-radius: var( --radius-lg );
	overflow: hidden;
}

.signyai-article__media img { width: 100%; height: auto; display: block; }

.signyai-siblings {
	display: grid;
	gap: var( --space-4 );
	grid-template-columns: repeat( auto-fit, minmax( 240px, 1fr ) );
	margin-top: var( --space-10 );
	padding-top: var( --space-7 );
	border-top: var( --stroke-hairline ) solid var( --border-subtle );
}

.signyai-sibling {
	display: flex;
	flex-direction: column;
	gap: var( --space-2 );
	padding: var( --space-5 );
	border: var( --stroke-hairline ) solid var( --border-subtle );
	border-radius: var( --radius-lg );
	text-decoration: none;
	color: inherit;
	transition: border-color var( --duration-base ) var( --ease-standard );
}

.signyai-sibling:hover { border-color: var( --highlight ); color: var( --highlight ); }
.signyai-sibling .eyebrow { color: var( --ink-muted ); }

/* ------------------------------------------------------------------ 23 ----
 * The services accordion fills its card.
 *
 * bundle.css caps a panel's prose at 78ch and every paragraph at
 * --layout-measure. That is the right reading width for a question-and-answer
 * accordion in a narrow column; it is the wrong one for the service pillars,
 * which hold the full container and left a third of each card empty.
 *
 * Width only — no type, no colour. See the rule at the top of this file.
 */
.sg-accordion__panel .sg-prose,
.sg-accordion__panel .sg-prose p { max-width: none; }


/* ------------------------------------------------------------------ 24 ----
 * The glossary.
 *
 * A THEME COMPONENT. There is no Glossary in the design system, so none of
 * this touches an `sg-` class — see the rule at the top of this file, and the
 * note in inc/components/glossary.php. It borrows the system's TYPE classes
 * (`h3`, `h4`, `body`, `body-sm`, `label`) and the form's `sg-field`/`sg-input`
 * because bundle.css styles almost nothing by element.
 *
 * READ THIS BEFORE ADDING A `display` BELOW. The filter shows and hides terms,
 * groups and the empty line with the `hidden` attribute, and `hidden` is only
 * a UA rule — any author `display` on the same element beats it and the thing
 * stays on screen. That is how the diagnostic ended up showing two "See my
 * results" buttons at once. The guard is first and stays first.
 */
.signyai-glossary [hidden] { display: none !important; }

.signyai-glossary__controls {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: var( --space-5 );
	margin-bottom: var( --space-7 );
}

.signyai-glossary__search {
	flex: 1 1 260px;
	max-width: 420px;
}

.signyai-glossary__jump {
	display: flex;
	flex-wrap: wrap;
	gap: var( --space-2 );
}

/* The chips are the glossary's own, not .sg-logostrip__item: that class is a
 * partner's name and is shared by things that look nothing alike. */
.signyai-glossary__chip {
	display: inline-block;
	padding: var( --space-2 ) var( --space-4 );
	border: var( --stroke-hairline ) solid var( --border-subtle );
	border-radius: var( --radius-full );
	background: var( --surface );
	color: var( --ink-muted );
	font-size: 14px;
	font-weight: 600;
	line-height: 1.4;
	text-decoration: none;
	transition:
		color var( --duration-fast ) var( --ease-standard ),
		border-color var( --duration-fast ) var( --ease-standard );
}

.signyai-glossary__chip:hover {
	color: var( --ink );
	border-color: var( --surface-brand );
}

.signyai-glossary__status {
	min-height: 1.5em;
	margin: 0 0 var( --space-5 );
	color: var( --ink-muted );
}

.signyai-glossary__group + .signyai-glossary__group {
	margin-top: var( --space-9 );
}

.signyai-glossary__grouphead {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: var( --space-3 );
	padding-bottom: var( --space-4 );
	border-bottom: var( --stroke-hairline ) solid var( --border-subtle );
	margin-bottom: var( --space-6 );
}

.signyai-glossary__title {
	margin: 0;
}

.signyai-glossary__count {
	margin: 0;
	color: var( --ink-muted );
	white-space: nowrap;
}

/*
 * Two-up above 900px, which is the brand book's rule for a list longer than
 * five items. One column below it: a definition is a sentence, and a sentence
 * in a 160px column is a column of single words.
 */
.signyai-glossary__list {
	display: grid;
	grid-template-columns: minmax( 0, 1fr );
	gap: var( --space-6 ) var( --space-8 );
	margin: 0;
}

@media ( min-width: 900px ) {
	.signyai-glossary__list {
		grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
	}
}

/*
 * scroll-margin-top clears the floating header, so a deep link lands with the
 * term under the bar rather than behind it. Same sum as the services
 * accordion's.
 *
 * The :target ground below is painted by a pseudo-element rather than by this
 * element's own background, and that is a layout decision, not a flourish.
 * The highlight has to be bigger than the text to read as a highlight, and
 * the two obvious ways of getting that both cost something: padding on the
 * term pushes every term after it down AFTER the browser has already scrolled
 * to the one you asked for, and cancelling the padding with a negative margin
 * pulls the outer columns past the container and into the page's overflow
 * clip. An absolutely positioned inset has no layout at all.
 *
 * isolation is what keeps `z-index: -1` inside: without it the pseudo-element
 * paints behind the SECTION's background, not behind the term's own text, and
 * the highlight is invisible.
 */
.signyai-glossary__term {
	position: relative;
	isolation: isolate;
	scroll-margin-top: calc( var( --layout-sticky-top, 96px ) + var( --space-6 ) );
}

.signyai-glossary__term::before {
	content: "";
	position: absolute;
	z-index: -1;
	inset: calc( var( --space-3 ) * -1 ) calc( var( --space-4 ) * -1 );
	border-radius: var( --radius-md );
	background: transparent;
	pointer-events: none;
}

.signyai-glossary__name {
	margin: 0 0 var( --space-2 );
	color: var( --ink );
}

.signyai-glossary__def {
	margin: 0;
	color: var( --ink-muted );
	max-width: 68ch;
}

/*
 * The term you followed a link to lights up and then settles, so you can see
 * which of fifty you landed on. Mint as a GROUND, never as type — it is 1.6:1
 * against white, and the brand book makes that a rule rather than a taste.
 */
.signyai-glossary__term:target::before {
	animation: signyai-glossary-land 2.8s var( --ease-out-soft ) both;
}

@keyframes signyai-glossary-land {
	0%, 45% { background: var( --surface-accent ); }
	100% { background: transparent; }
}

.signyai-glossary__empty {
	margin: var( --space-8 ) 0 0;
	color: var( --ink-muted );
}

/*
 * Collapsed, inside /services/. The accordion brings its own card, hairline
 * and padding from bundle.css, so all this has to do is stop the list from
 * running the full width of an open panel and take the group gap back out —
 * the rows space themselves.
 */
.signyai-glossary--collapsed .signyai-glossary__list {
	gap: var( --space-5 ) var( --space-8 );
}

.signyai-glossary--collapsed .signyai-glossary__name {
	margin-bottom: var( --space-1 );
}

/* ---------------------------------------------------------------------------
   The diagnostic sits closer to the page header than two sections normally do.

   .sg-section is padding-block: var(--space-11) top and bottom, so a header
   above the instrument put 96px + 96px between the standfirst and the "Start
   here" pill — space-22 of empty page before the thing the page is for. Both
   halves come down to space-5, which is space-10 together.

   POSITION ONLY. The section keeps bundle.css's padding everywhere else; this
   is where two particular sections sit relative to each other, which is what
   theme.css is for.

   The previous-sibling half needs :has(). Browsers without it fall back to the
   standard spacing — the page is merely roomier, never broken.
   --------------------------------------------------------------------------- */
.signyai-diagsection { padding-block-start: var( --space-5 ); }

.sg-section:has( + .signyai-diagsection ) { padding-block-end: var( --space-5 ); }

/* The same close-up on Client Results, where the cards follow the page header
   directly and had the full space-22 between them.

   SCOPED TO "DIRECTLY AFTER A PAGE HEADER", not to the cards themselves: the
   results block is also the homepage's client section, where it follows the
   objection accordion and wants the standard rhythm. An adjacent-sibling
   selector says which case this is; a class on the cards alone could not. */
.signyai-pagehead:has( + .signyai-resultsection ) { padding-block-end: var( --space-5 ); }

.signyai-pagehead + .signyai-resultsection { padding-block-start: var( --space-5 ); }

/* ---------------------------------------------------------------------------
   Contact Form 7, wearing the design system's clothes.

   The form template in inc/contact-form.php writes .sg-field, .sg-input,
   .sg-select, .sg-selectwrap and .sg-textarea itself, so the controls need no
   restyling. What is left is CF7's own wrappers and messages, which are its
   classes and not the design system's — styling .wpcf7-* here is exactly what
   theme.css is for, and none of it reaches an sg-* component.

   EVERY RULE BELOW IS DELIBERATELY OVER-SPECIFIED, and that is not a style
   choice. CF7 ships its own stylesheet and writes its response box as
   `.wpcf7 form .wpcf7-response-output` — (0,2,1). The first version of this
   file matched on `.wpcf7-response-output` alone, (0,1,0), and lost every
   declaration to it silently: the box kept CF7's 2px blue border and its
   `2em 0.5em 1em` margin on a page that uses neither. `.sg-page` is on <body>
   and buys a class each time.
   --------------------------------------------------------------------------- */

/* CF7 wraps every control in an inline span. Left inline, a 100%-wide input
   sits in a shrink-to-fit box and the field collapses to the placeholder. */
.sg-page .wpcf7-form-control-wrap { display: block; }

/* Its per-field validation line, where .sg-field__error puts it. */
.sg-page .wpcf7-not-valid-tip {
	display: block;
	margin-top: var( --space-2 );
	font: var( --text-body-sm );
	color: #b3261e;
}

/* The same red hairline bundle.css gives an invalid field, driven by CF7's
   own class rather than by data-invalid. */
.sg-page .wpcf7-form-control.wpcf7-not-valid { border-color: #b3261e; }

/* THE RESPONSE BOX — success, failure and the validation summary all land
   here, at the very end of the form, under the note. Full width so it lines
   up with the fields above it rather than floating in CF7's own half-em
   indent. Body ink on a tinted ground: the border alone is CF7's convention
   and reads as a browser artefact next to this form's own error styling. */
.sg-page .wpcf7 form .wpcf7-response-output {
	margin: var( --space-6 ) 0 0;
	padding: var( --space-4 ) var( --space-5 );
	border: 1px solid var( --border-subtle );
	border-radius: var( --radius-sm );
	font: var( --text-body-sm );
	color: var( --ink );
}

/* Sent: mint, which is a BACKGROUND colour on a light ground and never a text
   colour — mint on white is 1.6:1. The words stay in body ink. */
.sg-page .wpcf7 form.sent .wpcf7-response-output {
	border-color: var( --surface-accent );
	background: rgba( 60, 221, 144, 0.12 ); /* Mint #3CDD90 */
}

/* Everything that went wrong reads the same, because to the person filling in
   the form it is the same: it did not send. */
.sg-page .wpcf7 form.invalid .wpcf7-response-output,
.sg-page .wpcf7 form.unaccepted .wpcf7-response-output,
.sg-page .wpcf7 form.spam .wpcf7-response-output,
.sg-page .wpcf7 form.aborted .wpcf7-response-output,
.sg-page .wpcf7 form.failed .wpcf7-response-output {
	border-color: #b3261e;
	background: rgba( 179, 38, 30, 0.07 );
}

/* The submit row, and the reassurance line under it. The note is a <p>, and
   bundle.css zeroes every <p> margin inside .sg-page at (0,1,1) — so a bare
   class would lose and the sentence would sit flat against the button, which
   is exactly what it did. Two classes beats a class and an element. */
.sg-page .signyai-cf7__actions {
	display: flex;
	align-items: center;
	gap: var( --space-4 );
	margin-top: var( --space-7 );
}

.sg-page .signyai-cf7__note {
	margin-top: var( --space-4 );
	color: var( --ink-muted );
}

/* THE CHALLENGE WAITS FOR CONSENT.

   Cloudflare's widget is a third party, and asking somebody to pass it before
   they have agreed to anything puts the third party first. It is hidden until
   the consent box is ticked, and shown in place when it is.

   Hidden, NOT un-rendered: the widget still loads with the page and has its
   token ready by the time the box is ticked, so revealing it costs no wait.
   Removing it from the DOM instead would mean re-rendering and a pause at
   exactly the moment somebody is trying to send.

   :has() does the whole thing with no script. A browser without it shows the
   challenge from the start — which is where it was before, so the fallback is
   the old behaviour rather than a broken form. */
.sg-page .signyai-cf7__shield { display: none; }

.sg-page .signyai-cf7__consent:has( input[type="checkbox"]:checked ) + .signyai-cf7__shield {
	display: block;
}

/* Turnstile renders nothing at all until Cloudflare keys are entered, so this
   only takes space once it is real. */
.sg-page .wpcf7-turnstile:not(:empty) { margin-top: var( --space-6 ); }

/* The GDPR consent row. CF7's acceptance renders as nested spans — a wrap, a
   control, a list item, a label — none of which the design system has an
   opinion about, so the row is laid out here.

   align-items: flex-start keeps the box on the first line when the sentence
   wraps, which it does on a phone. */
.sg-page .signyai-cf7__consent { margin-top: var( --space-6 ); }

.sg-page .signyai-cf7__consent .wpcf7-list-item { margin: 0; }

.sg-page .signyai-cf7__consent label {
	display: flex;
	align-items: flex-start;
	gap: var( --space-3 );
	font: var( --text-body-sm );
	color: var( --ink );
	cursor: pointer;
}

.sg-page .signyai-cf7__consent input[type="checkbox"] {
	flex: 0 0 auto;
	width: 18px;
	height: 18px;
	margin-top: 3px; /* optical, to sit on the first line's cap height */
	accent-color: var( --highlight );
	cursor: pointer;
}

/* The link inside the consent sentence is the one place a visitor is asked to
   read something before agreeing, so it is underlined rather than relying on
   colour alone. */
.sg-page .signyai-cf7__consent a {
	color: var( --highlight );
	text-decoration: underline;
}

/* ---------------------------------------------------------------------------
   Tables in an article.

   bundle.css styles almost nothing by element and the design system has no
   table component, so a table in a post — imported from a document or
   inserted as a block — had no borders, no padding and no width at all. This
   is the theme's own class, added by signyai_article_classes(), and nothing
   here reaches an sg-* component.

   Hairlines rather than fills: the brand book's depth order is hairline, then
   soft shadow, then a change of surface, and a grid of tinted cells is three
   steps past what a table needs.
   --------------------------------------------------------------------------- */
.signyai-article__table {
	width: 100%;
	border-collapse: collapse;
	margin-block: var( --space-6 );
	font: var( --text-body-sm );
}

.signyai-article__table th,
.signyai-article__table td {
	padding: var( --space-3 ) var( --space-4 );
	border: 1px solid var( --border-subtle );
	text-align: left;
	vertical-align: top;
}

/* The header row carries its weight with type, not with a fill. */
.signyai-article__table th {
	font-weight: 600;
	color: var( --ink );
	border-bottom-width: 2px;
}

.signyai-article__table td { color: var( --ink-muted ); }

/* The first cell of a row is usually the thing being measured, so it reads as
   the label rather than as another value. */
.signyai-article__table tbody td:first-child { color: var( --ink ); }

/* RESPONSIVE BY SCROLLING, NOT BY SQUASHING.

   A five-column table of headline numbers does not fit a phone at any font
   size. Shrinking the columns to make it fit turned "More pipeline vs. prior
   campaigns" into six words on six lines — smaller, and unreadable.

   So the columns keep a width worth reading and the box around them scrolls.
   The minimum is per CELL rather than on the table, which is what makes this
   a general rule: a two-column table still fits a phone and never scrolls,
   while an eight-column one scrolls further. Nothing has to be configured
   per table.

   A client who needs something else — stacked rows, a hidden column — can
   say so in the post's HTML, and these rules are weak enough to be overridden
   by anything they write there. */
.signyai-article__tablewrap {
	overflow-x: auto;
	margin-block: var( --space-6 );
	/* Momentum scrolling on iOS, where a short swipe otherwise stops dead. */
	-webkit-overflow-scrolling: touch;
}

.signyai-article__tablewrap > .signyai-article__table { margin-block: 0; }

/* The figure the block editor wraps a table in has its own default margins
   and would indent the scroll box from the text around it. */
figure.wp-block-table { margin-inline: 0; }

@media (max-width: 900px) {
	.signyai-article__table th,
	.signyai-article__table td { min-width: 11rem; }
}

/* ---------------------------------------------------------------------------
   An article's reading measure.

   The container is the standard one now, so the article aligns with every
   other page — but 1200px of body copy is a line nobody tracks back from.
   The brand book caps body at 68ch, so the measure sits on the TEXT rather
   than on the column, which leaves tables, images and anything else wide to
   use the full width.
   --------------------------------------------------------------------------- */
.signyai-article__body > p,
.signyai-article__body > h2,
.signyai-article__body > h3,
.signyai-article__body > h4,
.signyai-article__body > ul,
.signyai-article__body > ol,
.signyai-article__body > blockquote,
.signyai-article__body > .signyai-imported > p,
.signyai-article__body > .signyai-imported > h2,
.signyai-article__body > .signyai-imported > h3,
.signyai-article__body > .signyai-imported > h4,
.signyai-article__body > .signyai-imported > ul,
.signyai-article__body > .signyai-imported > ol,
.signyai-article__body > .signyai-imported > blockquote {
	max-width: 68ch;
}


/* The diagnostic section is scrolled to when the questionnaire finishes, so
   it needs the same clearance the accordion rows have — otherwise the sticky
   header covers the eyebrow it is bringing back into view. */
.signyai-diagsection {
	scroll-margin-top: calc( var( --layout-sticky-top, 76px ) + var( --space-5 ) );
}


/* The byline. On a card it sits on its own line under the date; on the
   article it shares the date's row, separated by a dot that only exists
   while there is an author to separate — the `+` sees to that, so a post
   whose author has no display name is one date and nothing else. */
.signyai-insight__author { display: block; }

.signyai-article__author + time::before {
	content: "·";
	margin-inline: var( --space-2 );
}


/* Bulleted lists in an article.

   bundle.css sets `list-style: none` on every list, because the design
   system draws its own dot instead. An article cannot use that component —
   see the note in signyai_article_classes() for why the attempt rendered
   nothing at all — so the browser's marker is restored here, exactly as it
   is for the numbered lists in edited prose above.

   Scoped to the article body on purpose. `.signyai-prose ul` would reach
   section prose, where signyai_prose_classes() DOES build the component,
   and every one of those bullets would get a second marker beside its dot. */
.signyai-article__body ul,
.signyai-article__body .signyai-imported ul {
	list-style: disc;
	padding-left: var( --space-6 );
}

.signyai-article__body ul ul {
	list-style: circle;
	margin-block: var( --space-3 );
}

.signyai-article__body li { margin-left: var( --space-2 ); }


/* The offer to email a diagnostic report.

   It borrows .signyai-diag__dialog for the shell, so only what differs is
   here: the fields between the question and the buttons, and a skip that
   reads as a way out rather than as a second button competing with the one
   beside it. */
.signyai-diag__capture { max-width: 34rem; }

/* gap: 0, written as 0. There is no --space-0 in tokens.css — the scale
   starts at --space-1 — and `var( --space-0 )` would be invalid at computed
   value time, which silently resets the property to its initial value. It
   would have looked right for the wrong reason, which is the fault
   theme.css already carries a note about further up. */
.signyai-diag__capture-fields {
	display: grid;
	gap: 0;
	margin-bottom: var( --space-4 );
}

/* The dialog mutes and spaces every <p> inside it, which is right for the
   one sentence under the heading and wrong for the notice under the form. */
.signyai-diag__capture .signyai-diag__capture-note {
	margin: 0 0 var( --space-4 );
}

/* Stacked, and scoped to this dialog — the "Almost there" one has two
   buttons that belong side by side. align-self centres on the cross axis,
   which in a column is the horizontal one, so the submit stays left and only
   the way out sits in the middle. */
.signyai-diag__capture .signyai-diag__dialog-actions {
	flex-direction: column;
	align-items: flex-start;
}

.signyai-diag__capture .signyai-diag__skip { align-self: center; }

/* A link, not a button. Declining is the quiet choice and should look like
   one — but it has to BE a submit, so the button chrome comes off here. */
.signyai-diag__skip {
	appearance: none;
	border: 0;
	padding: 0;
	background: none;
	color: var( --ink-muted );
	text-decoration: underline;
	cursor: pointer;
	font: inherit;
}

.signyai-diag__skip:hover { color: var( --highlight ); }

.signyai-diag__mailed { color: var( --ink-muted ); }
