/* ==========================================================================
   Moonfoos — front end and editor
   ==========================================================================

   WHY THERE IS NO @layer HERE
   Cascade layers would be the tidier way to organise this, but everything
   WordPress itself prints — core block styles, and the generated global styles
   from theme.json — is *unlayered*, and unlayered rules beat layered ones
   regardless of specificity. Putting this file in a layer would therefore hand
   every conflict to core. So: plain cascade, ordered from tokens outward, with
   single-class selectors and no !important outside the two places noted.

   WHAT IS MODERN HERE, AND WHAT IS DELIBERATELY NOT
   In:  container queries for the card grids, color-mix() for interaction
        states, :has() for parent-aware styling, scroll-driven reveal
        animations, and fluid type via theme.json (min/max pairs, no media
        queries for headings).
   Out: @layer (above), and view transitions — the sticky header makes a
        whole-document cross-fade look broken, and naming every element to
        avoid that costs more than the effect is worth.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
	--mf-ink: var(--wp--preset--color--ink);
	--mf-body: var(--wp--preset--color--body);
	--mf-muted: var(--wp--preset--color--muted);
	--mf-line: var(--wp--preset--color--line);
	--mf-base: var(--wp--preset--color--base);
	--mf-soft: var(--wp--preset--color--soft);
	--mf-accent: var(--wp--preset--color--accent);
	--mf-accent-dk: var(--wp--preset--color--accent-dark);
	--mf-accent-soft: var(--wp--preset--color--accent-soft);
	--mf-frame: var(--wp--preset--color--frame);
	--mf-frame-line: var(--wp--preset--color--frame-line);
	--mf-chrome: var(--wp--preset--color--chrome);
	--mf-tile: var(--wp--preset--color--tile);
	--mf-avatar: var(--wp--preset--color--avatar);
	--mf-placeholder: var(--wp--preset--color--placeholder);
	--mf-placeholder-2: var(--wp--preset--color--placeholder-2);
	--mf-on-dark: var(--wp--preset--color--on-dark);

	--mf-r-sm: var(--wp--custom--radius--sm);
	--mf-r: var(--wp--custom--radius--lg);
	--mf-r-xl: var(--wp--custom--radius--xl);

	--mf-header-h: var(--wp--custom--header--height);
	--mf-gutter: 28px;
}

/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */

html {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}
}

/* Border-box everywhere. Core only sets this on `.wp-block-group`, so block
   elements were border-box while plain form controls were not — and
   `.mf-field input { width: 100%; padding: 12px 14px }` therefore rendered
   30px wider than its grid column. Two per row, each spilling 8px past its
   neighbour, which is the "fields are overlapping" the client reported.
   Inheriting from `html` rather than using a bare `*` keeps it overridable
   and matches what core already does to the blocks around it. */
html {
	box-sizing: border-box;
}

*,
*::before,
*::after {
	box-sizing: inherit;
}

body {
	margin: 0;
	background: var(--mf-base);
	color: var(--mf-body);
}

/* The sticky header would otherwise land on top of an anchor target. */
:target {
	scroll-margin-top: calc(var(--mf-header-h) + 24px);
}

p {
	margin: 0;
}

a {
	text-decoration: none;
}

img {
	max-width: 100%;
	height: auto;
}

/* --------------------------------------------------------------------------
   3. Section rhythm
   -------------------------------------------------------------------------- */

/* The horizontal gutter lives here rather than in theme.json's root padding.
   Root-padding-aware alignment only escapes .alignfull for *direct* children of
   the padded container, and these sections sit inside <main>, so the full-bleed
   bands would have been inset by the gutter and shown white side bars. Owning
   the gutter explicitly keeps the bleed predictable. The constrained layout
   still centres each section's children to contentSize (1180px). */
.mf-sec,
.mf-strip,
.mf-band,
.mf-hero {
	padding-inline: var(--mf-gutter);
}

.mf-sec {
	padding-block: 104px;
}

.mf-sec--soft {
	background: var(--mf-soft);
	border-top: 1px solid var(--mf-line);
	border-bottom: 1px solid var(--mf-line);
}

.mf-sec--tight {
	padding-block: 88px 0;
}

.mf-strip {
	padding-block: 44px 52px;
	border-bottom: 1px solid var(--mf-line);
}

.mf-band {
	background: var(--mf-soft);
	border-top: 1px solid var(--mf-line);
	padding-block: 96px;
	text-align: center;
}

.mf-band>* {
	text-align: center;
}

/* Container context for the grids. Must sit *inside* the section, because a
   container query cannot style the container itself — and must not be the
   full-bleed section, or every query would just measure the viewport. */
.mf-cq {
	container-type: inline-size;
}

/* --------------------------------------------------------------------------
   4. Shared type
   -------------------------------------------------------------------------- */

.mf-eyebrow {
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	letter-spacing: .09em;
	text-transform: uppercase;
	color: var(--mf-accent-dk);
	margin-bottom: 16px;
}

.mf-lede {
	font-size: var(--wp--preset--font-size--lede);
	color: var(--mf-body);
	max-width: 660px;
	margin-top: 20px;
}

.mf-center {
	text-align: center;
}

.mf-center .mf-lede {
	margin-inline: auto;
}

.mf-narrow {
	max-width: 820px;
}

/* --------------------------------------------------------------------------
   5. Buttons
   --------------------------------------------------------------------------
   Both buttons in a CTA row are clones of the reference site's button family —
   their .u-btn--1 and .u-btn--4 — and carry the same hover gesture: the blob
   shrinks to .875 while its arrows change places, one leaving to the right as
   an identical one arrives from the left, and the blob's colour swaps with the
   label's. .6s cubic-bezier(.25, 1, .5, 1) throughout, which is theirs; the
   shapes, the timings and the distance travelled are all theirs too.

   All of it is CSS over core's own button markup: the <a> is the label slab,
   its ::before is the shaped right end (the reference's 18x48 label_corner) and
   its ::after is the blob. Nothing in a page is rewritten, so the buttons stay
   editable in the block editor and no live content has to be touched.

   Two of the three shapes are curves, and a box border cannot follow a curve,
   so each is an opaque fill clipped by its own path — mask-image, the same
   technique the tile corner cut uses. The blob's mask does double duty: the two
   arrows are the element's own background layers, so the mask that gives the
   blob its outline also keeps the incoming arrow, waiting clear of the blob on
   the left, from ever being seen.

   The blob is the same 51x48 curve the industry tiles carry in their corner, at
   the same size, so a button and a card read as one component.

   The label is set in the reference's voice: mono, uppercase. Roboto Mono is
   named first but is not self-hosted — the site self-hosts Inter precisely so
   no visitor's address reaches a font CDN, and adding a second webfont for one
   label is not worth breaking that for. Machines without it fall back through
   the system mono stack, which is what the reference falls back to as well, so
   the two are identical on any machine that has neither.
   -------------------------------------------------------------------------- */

.mf-cta-row {
	display: flex;
	gap: 14px;
	align-items: center;
	margin-top: 36px;
	flex-wrap: wrap;

	/* The button family's own tokens. */
	--mf-btn-mono: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
	--mf-btn-dur: .6s;
	--mf-btn-ease: cubic-bezier(.25, 1, .5, 1);

	/* The label slab and the blob, and what each becomes on hover: they trade
	   colours. The label is the brand ink at rest and the blob the accent, which
	   is the same accent the tiles' buttons carry at rest. */
	--mf-btn-bg: var(--mf-ink);
	--mf-btn-bg-hover: var(--mf-accent);
	--mf-btn-blob: var(--mf-accent);
	--mf-btn-blob-hover: var(--mf-ink);
	--mf-btn-blob-scale: .875;

	/* How far the blob sits past the label: the gap, then the blob's own 51px.
	   The gap narrows by 4 on hover as the label's padding-right grows by 6,
	   which is the reference's own pair of hover rules.

	   It is the row's flex item that carries it, not the button: the blob hangs
	   outside the label's box, and a margin on the label is not part of the box
	   the row lays out, so the next button would be placed on top of it. */
	--mf-btn-tail: 67px;
	--mf-btn-tail-hover: calc(var(--mf-btn-tail) - 4px);

	/* The three shapes, drawn once. Each copy of a curve is the client's own
	   path, so the button is their geometry rather than a redrawing of it. The
	   arrow is theirs too, at the size they set it. */
	--mf-btn-corner-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 48' preserveAspectRatio='none'%3E%3Cpath fill='%23000' d='M0 0h5.63c7.808 0 13.536 7.337 11.642 14.91l-6.09 24.359A11.527 11.527 0 0 1 0 48V0Z'/%3E%3C/svg%3E");
	--mf-btn-blob-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 51 48' preserveAspectRatio='none'%3E%3Cpath fill='%23000' d='M6.728 9.09A12 12 0 0 1 18.369 0H39c6.627 0 12 5.373 12 12v24c0 6.627-5.373 12-12 12H12.37C4.561 48-1.167 40.663.727 33.09l6-24Z'/%3E%3C/svg%3E");
	--mf-btn-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' fill='none' viewBox='0 0 10 10'%3E%3Cpath fill='%23fff' d='M7.703 5.8H.398V4.6h7.305l-3.36-3.36.855-.84 4.8 4.8-4.8 4.8-.855-.84 3.36-3.36Z'/%3E%3C/svg%3E");
}

/* The blob hangs 67px past the label, so the row has to be told about it or the
   next button is laid out underneath. */
.mf-cta-row>.wp-block-button {
	margin-right: var(--mf-btn-tail);
}

.mf-band .mf-cta-row {
	justify-content: center;
}

/* The label slab. 12px on the left because that is the radius of the blob's own
   rounded corners, so the two ends of the button are drawn to the same curve.
   It is square on the right: the shaped end continues from there. */
.mf-cta-row .wp-block-button__link {
	position: relative;
	display: inline-flex;
	align-items: center;
	height: 48px;
	padding: 8px 8px 8px 20px;
	border: 0;
	border-radius: 12px 0 0 12px;
	background: var(--mf-btn-bg);
	color: var(--mf-base);
	font-family: var(--mf-btn-mono);
	font-size: 13px;
	font-weight: 500;
	line-height: 1;
	letter-spacing: .02em;
	text-transform: uppercase;
}

/* The shaped right end. It spans the gap the label's margin leaves and laps 2px
   over the label itself, as the reference's does, so no seam can open between
   them at any width. */
.mf-cta-row .wp-block-button__link::before {
	content: "";
	position: absolute;
	top: 0;
	right: -16px;
	width: 18px;
	height: 100%;
	background: var(--mf-btn-bg);
	-webkit-mask-image: var(--mf-btn-corner-glyph);
	-webkit-mask-repeat: no-repeat;
	-webkit-mask-position: 0 0;
	-webkit-mask-size: 100% 100%;
	mask-image: var(--mf-btn-corner-glyph);
	mask-repeat: no-repeat;
	mask-position: 0 0;
	mask-size: 100% 100%;
}

/* The blob, with both arrows in it. They are the same background layer twice:
   one at its place, one a layer-width and 22px further left — outside the mask,
   and so invisible. On hover the two exchange positions, which is the
   cross-slide, with no third element to keep in step. */
.mf-cta-row .wp-block-button__link::after {
	content: "";
	position: absolute;
	top: 0;
	right: calc(-1 * var(--mf-btn-tail));
	width: 51px;
	height: 48px;
	background-color: var(--mf-btn-blob);
	background-image: var(--mf-btn-arrow), var(--mf-btn-arrow);
	background-repeat: no-repeat, no-repeat;
	/* The reference's own pair of positions. The glyph sits 22px in rather than
	   dead centre, because the blob's left edge is sheared; the second copy
	   waits clear of the mask to the left. */
	background-position: calc(-100% - 22px) 50%, 22px 50%;
	background-size: 10px 10px, 10px 10px;
	-webkit-mask-image: var(--mf-btn-blob-glyph);
	-webkit-mask-repeat: no-repeat;
	-webkit-mask-position: 0 0;
	-webkit-mask-size: 100% 100%;
	mask-image: var(--mf-btn-blob-glyph);
	mask-repeat: no-repeat;
	mask-position: 0 0;
	mask-size: 100% 100%;
	/* The blob shrinks towards the outside edge, so the corner it sits in stays
	   put and the air opens up between them. */
	transform-origin: 100% 50%;
}

.mf-cta-row .wp-block-button__link:is(:hover, :focus-visible) {
	background: var(--mf-btn-bg-hover);
	padding-right: 14px;
}

.mf-cta-row .wp-block-button__link:is(:hover, :focus-visible)::before {
	background: var(--mf-btn-bg-hover);
}

.mf-cta-row .wp-block-button__link:is(:hover, :focus-visible)::after {
	right: calc(-1 * var(--mf-btn-tail-hover));
	background-color: var(--mf-btn-blob-hover);
	background-position: 22px 50%, calc(100% + 22px) 50%;
	transform: scale(var(--mf-btn-blob-scale));
}

/* The secondary is the reference's .u-btn--4: no slab and no shaped end, just
   the label in the same voice with the same blob beside it, which is how their
   own secondary CTA is built. The blob does all the work in both cases.

   Its gap is the reference's 7px column-gap rather than the slab's 16, and it
   does not close on hover — their secondary has nothing to close it against. */
.mf-cta-row>.wp-block-button.is-style-outline {
	--mf-btn-tail: 58px;
	--mf-btn-tail-hover: 58px;
}

.mf-cta-row .is-style-outline>.wp-block-button__link {
	border: 0;
	background: none;
	padding: 0 0 0 8px;
	color: var(--mf-ink);
}

.mf-cta-row .is-style-outline>.wp-block-button__link::before {
	content: none;
}

.mf-cta-row .is-style-outline>.wp-block-button__link:is(:hover, :focus-visible) {
	background: none;
	padding-right: 0;
	color: var(--mf-ink);
}

@media (prefers-reduced-motion: no-preference) {

	.mf-cta-row .wp-block-button__link,
	.mf-cta-row .wp-block-button__link::before {
		transition:
			background-color var(--mf-btn-dur) var(--mf-btn-ease),
			padding-right var(--mf-btn-dur) var(--mf-btn-ease);
	}

	.mf-cta-row .wp-block-button__link::after {
		transition:
			transform var(--mf-btn-dur) var(--mf-btn-ease),
			background-color var(--mf-btn-dur) var(--mf-btn-ease),
			background-position var(--mf-btn-dur) var(--mf-btn-ease),
			right var(--mf-btn-dur) var(--mf-btn-ease);
	}
}

/* --------------------------------------------------------------------------
   6. Header
   -------------------------------------------------------------------------- */

.mf-header {
	position: sticky;
	top: 0;
	z-index: 50;
	border-bottom: 1px solid var(--mf-line);
}

/* The frosted bar is painted by a pseudo-element rather than by `.mf-header`
   itself, and that is load-bearing rather than stylistic.

   `backdrop-filter` makes an element a containing block for its `position:
   fixed` descendants. The navigation block's mobile menu is one of those: core
   positions it `fixed` with `inset: 0` and expects the viewport. With the blur
   on `.mf-header`, that `inset: 0` resolved against the 74px header box instead,
   so opening the menu produced a 74px letterbox with `overflow: auto` — the
   links started at y=56 and were clipped away, and the hero showed through
   underneath. That is the "menu when clicked is not well displayed on mobile"
   report, reproduced and measured on a 390x844 viewport.

   A pseudo-element is a child of the header, never an ancestor of the overlay,
   so it blurs the page behind the bar without capturing the menu. Setting
   `backdrop-filter: none` on the header alone was verified to restore the
   overlay to the full 844px, which is what pinned the cause. */
.mf-header::before {
	content: "";
	position: absolute;
	inset: 0;
	/* Negative z-index paints this under the header's own content. The header
	   carries z-index: 50, so it still creates the stacking context that keeps
	   the whole bar above the page. */
	z-index: -1;
	background: color-mix(in srgb, var(--mf-base) 92%, transparent);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
}

/* Core prints its own padding on the constrained header group; the design wants
   the bar flush to the edges, with the row centred in it.

   The centring is not cosmetic. A constrained group is a block container, so
   the row sat at the very top of the bar: measured at 1440x900, a 40px row in a
   74px bar left the lockup 0px from the top of the viewport and 34px of dead
   space under it. That is the client's "the menu has no enough margin or padding
   to the top". `justify-content` centres it at any bar height and at any width
   where the nav wraps to a second line, which a hand-picked padding would not. */
.mf-header>.wp-block-group {
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-height: var(--mf-header-h);
	padding-inline: var(--mf-gutter);
}

/* `width: 100%` restores the horizontal geometry the block layout gave for
   free. Core centres the row with `margin-inline: auto`, and in a flex column
   auto cross-axis margins suppress `align-self: stretch` — so without a definite
   width the row would shrink-wrap its three children instead of filling the
   1180px column, and the lockup would slide inwards off the gutter. */
.mf-header .mf-header__row {
	width: 100%;
}

.mf-logo {
	display: flex;
	align-items: center;
	min-width: 0;
}

.mf-logo__link {
	display: block;
	flex: none;
	/* An inline image sits on the text baseline and leaves a descender gap
	   below it; zeroing the line box keeps the lockup optically centred. */
	line-height: 0;
}

/* The real lockup — mark and wordmark in one image, 1990x277 (7.18:1). Sized by
   height so it stays centred in the bar at every width; width follows from the
   intrinsic ratio. This replaced a hand-made 22x22 gradient square standing in
   for the mark beside the site-title text. */
.mf-logo__img {
	display: block;
	height: 30px;
	width: auto;
}

.mf-header__actions {
	display: flex;
	align-items: center;
	gap: 22px;
	flex: none;
}

/* The header CTA must never reflow or shrink. Without this it is the only
   shrinkable item in the bar: it collapses to ~72px (44px of which is padding),
   the label wraps onto five lines, and that is what set the header to 120px at
   390px instead of 74px. */
.mf-header__actions .wp-element-button {
	white-space: nowrap;
	flex: none;
	/* Deliberately smaller than the hero and band CTA, which keeps the
	   .mf-cta-row sizing further down. At core's default this button measured
	   140x51 inside a 74px bar — it read as the biggest thing on the page. */
	padding: 10px 18px;
	font-size: 15px;
	line-height: 1.35;
}

/* Core's navigation block ships a 600px overlay breakpoint, which is far too
   narrow for this bar — four items plus two actions overflow well before that.
   These overrides move it to 1000px. Scoped to .mf-header so they cannot leak
   into a navigation block placed in page content. */
.mf-header .wp-block-navigation__container {
	gap: 30px;
	font-size: var(--wp--preset--font-size--text-sm);
	font-weight: 500;
}

.mf-header .wp-block-navigation-item__content {
	color: var(--mf-ink);
}

/* The chevron. This rule used to draw a chevron *on the button* with a rotated
   border — but core's button already contains core's own 12x12 chevron SVG, so
   the item rendered two glyphs on top of each other, one of them an 8x8 box spun
   45deg, and core's SVG was dragged round with it (measured: the button carried
   `transform: rotate(45deg)` and the SVG inside it `matrix(-1,0,0,-1)`, i.e.
   both rotated and flipped). That is the client's "the dropdown icon on solution
   is buggy". The button is stripped back to a bare hit area and core's SVG is
   now the only mark, sized here and rotated on core's own `aria-expanded`. */
.mf-header .wp-block-navigation-submenu__toggle {
	border: 0;
	background: none;
	width: auto;
	height: auto;
	padding: 0;
	margin-left: 5px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	color: inherit;
	transform: none;
}

.mf-header .wp-block-navigation-submenu__toggle svg {
	display: block;
	width: 12px;
	height: 12px;
	stroke: currentColor;
	transition: transform .18s ease;
}

/* Two classes and an attribute, so it outranks whatever core uses to flip the
   icon in the open state — that rule is what left the SVG at `scale(-1,-1)`. */
.mf-header .wp-block-navigation-submenu__toggle[aria-expanded="true"] svg {
	transform: rotate(180deg);
}

/* The dropdown panel. Core positions the submenu absolutely and owns the
   open/close state, but it supplies no surface — without this it renders as
   bare text floating over the page.
   No min-width here on purpose. Core's own `[aria-expanded="true"] ~ …` rule
   sets `min-width: 200px` at (0,5,0); any floor we pick is either defeated or
   too narrow for the longest label ("General Operations Management"), which
   then wraps onto two lines and makes that one row twice the height of the
   others. The `white-space: nowrap` below sizes the panel to its widest label
   instead, so every row is one line whatever the client renames them to. */
.mf-header .wp-block-navigation__submenu-container {
	display: flex;
	flex-direction: column;
	gap: 2px;
	padding: 10px;
	background: var(--mf-base);
	border: 1px solid var(--mf-line);
	border-radius: var(--mf-r);
	box-shadow: 0 14px 34px rgb(42 73 121 / .13);
}

/* (0,6,0) deliberately. Core styles these anchors with
   `.wp-block-navigation .has-child .wp-block-navigation__submenu-container >
   .wp-block-navigation-item > .wp-block-navigation-item__content` at (0,5,0),
   which sets `padding: 0.5em 1em` and `display: flex` — so anything less loses
   and the padding silently reverts to core's em-based box. The mobile branch
   below already had to reach (0,6,0) for the same reason. Selector shape
   mirrors core's own so it stays readable. */
.mf-header .wp-block-navigation .has-child .wp-block-navigation__submenu-container .wp-block-navigation-item .wp-block-navigation-item__content {
	display: block;
	padding: 10px 14px;
	border-radius: var(--mf-r-sm);
	font-size: var(--wp--preset--font-size--text-sm);
	white-space: nowrap;
	color: var(--mf-body);
}

.mf-header .wp-block-navigation .has-child .wp-block-navigation__submenu-container .wp-block-navigation-item .wp-block-navigation-item__content:hover {
	background: var(--mf-soft);
	color: var(--mf-ink);
}

/* --------------------------------------------------------------------------
   The Solutions mega panel — item 3 of the client's comment.

   Core's navigation block can express a submenu only as a flat list of links: no
   headings, no column groups, no promo cell. The panel is therefore rendered in
   PHP by the `moonfoos/mega-panel` block and sits in parts/header.html *after*
   the navigation block, so core renders it as a *sibling of the navigation list*
   rather than as a child of the "Solutions" item. Two consequences fall out of
   that, and both are load-bearing:

   1. The open state cannot be `li:hover`. It is a `:has()` on the header instead
      — see the desktop block at the end of this section — testing core's own
      `aria-expanded` on the item's chevron button, which core sets for hover
      *and* for click, so one selector serves both gestures.
   2. The pointer has to reach the panel from the item without crossing a gap, or
      core's `pointerleave` closes the menu mid-journey. The item's own `::after`
      is that bridge.

   It was a `wp:html` child of the navigation block until 2026-09-16, and that is
   the arrangement that broke: `core/html` is not in `core/navigation`'s
   `allowedBlocks`, so the editor's next save stripped the delimiters and left the
   markup as freeform content, which core then drops on render. Rendering it in PHP
   puts it beyond reach of the editor, which is why it is a block in the theme and
   not markup in a post.

   Hidden below 1000px. The mobile overlay keeps core's own submenu, which is
   already an indented list there and reads better than three columns at 390px. */
.mf-nav__mega {
	display: none;
	position: absolute;
	/* Resolves against .mf-header, the only positioned ancestor — the navigation
	   block's own wrappers and the bar are made static on desktop. 100% of a
	   containing block is its padding box, so this is the header's bottom edge
	   less its 1px border; the border is added back so the hairline stays
	   visible above the panel instead of being painted over. */
	top: calc(100% + 1px);
	left: 0;
	right: 0;
	padding-inline: var(--mf-gutter);
	background: var(--mf-base);
	border-bottom: 1px solid var(--mf-line);
	box-shadow: 0 18px 34px rgb(42 73 121 / .10);
	visibility: hidden;
	opacity: 0;
	transform: translateY(-6px);
	transition: opacity .16s ease, transform .16s ease, visibility .16s;
}

.mf-nav__mega-inner {
	display: grid;
	grid-template-columns: 300px minmax(0, 1fr) minmax(0, 1fr);
	gap: 56px;
	max-width: var(--wp--style--global--content-size, 1180px);
	margin-inline: auto;
	padding-block: 30px 34px;
}

.mf-nav__mega-kicker {
	margin: 0 0 10px;
	font-size: var(--wp--preset--font-size--text-sm);
	font-weight: 600;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--mf-accent);
}

.mf-nav__mega-copy {
	margin: 0 0 22px;
	font-size: 15px;
	line-height: 1.5;
	color: var(--mf-body);
}

/* The play-in-a-pill from the client's reference: a soft capsule with the mark
   in the accent, which is the one saturated element in the panel. */
.mf-nav__mega-video {
	display: inline-flex;
	align-items: center;
	gap: 12px;
	padding: 8px 18px 8px 9px;
	border-radius: 999px;
	background: var(--mf-soft);
	color: var(--mf-ink);
	font-size: 15px;
	font-weight: 600;
	text-decoration: none;
	transition: background .16s ease;
}

.mf-nav__mega-video:hover {
	background: color-mix(in srgb, var(--mf-ink) 11%, transparent);
}

.mf-nav__mega-play {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: none;
	width: 26px;
	height: 26px;
	border-radius: 50%;
	background: var(--mf-accent);
	color: var(--mf-base);
}

.mf-nav__mega-play svg {
	display: block;
	width: 13px;
	height: 13px;
}

.mf-nav__mega-title {
	margin: 0 0 12px;
	font-size: var(--wp--preset--font-size--text-sm);
	font-weight: 600;
	color: var(--mf-ink);
}

.mf-nav__mega-list {
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin: 0;
	padding: 0;
	list-style: none;
}

/* The negative inline margin cancels the padding, so the hover surface bleeds
   into the column gutter on both sides while the labels stay on the column's
   own left edge — the same trick the header's own items use. */
.mf-nav__mega-link {
	display: block;
	padding: 8px 12px;
	margin-inline: -12px;
	border-radius: var(--mf-r-sm);
	text-decoration: none;
}

.mf-nav__mega-link:hover {
	background: var(--mf-soft);
}

.mf-nav__mega-name {
	display: block;
	font-size: 15px;
	font-weight: 500;
	color: var(--mf-ink);
}

.mf-nav__mega-link:hover .mf-nav__mega-name {
	color: var(--mf-accent-dk);
}

.mf-nav__mega-desc {
	display: block;
	margin-top: 2px;
	font-size: 13.5px;
	line-height: 1.45;
	color: var(--mf-muted);
}

/* The overlay is opaque and covers the whole viewport, so while it is open the
   bar's own "Book a demo" button cannot be reached. This is that same action
   again, inside the overlay — the bar deliberately never drops the CTA, and
   covering it would undo that.

   It is a `core/buttons` block and the last item of the header menu, which is
   the only way to keep it both inside the overlay and editable: `core/html` is
   not in the navigation block's `allowedBlocks` and was stripped the first time
   the menu was saved from the editor, which is what lost this CTA. `core/buttons`
   is allowed, so it survives, and the client can relabel, move or delete it like
   any other menu item. Core renders a non-<li> child after the `</ul>` rather
   than inside it, and in the overlay the responsive container is a flex column,
   so it lands as the last row under the links — the same place the old
   hand-written div sat.

   `display: none` has to be declared at (0,2,0), not (0,1,0): `core/buttons` is
   a layout group, so core's `body .is-layout-flex { display: flex }` at (0,1,1)
   outranks a bare class. The old markup was a plain <div>, which is the only
   reason the old rule was ever heard. See CLAUDE.md. */
.mf-header .mf-nav__cta {
	display: none;
}

@media (max-width: 999px) {
	.mf-header .wp-block-navigation__responsive-container:not(.is-menu-open) {
		display: none;
	}

	.mf-header .wp-block-navigation__responsive-container-open {
		display: flex;
	}

	/* Core styles the open overlay with
	   `padding: clamp(1rem, var(--wp--style--root--padding-*), 20rem)`. This theme
	   sets `useRootPaddingAwareAlignments: false`, so those custom properties are
	   never emitted — the clamp is invalid at computed-value time and the whole
	   declaration falls back to its initial value, i.e. zero. The menu therefore
	   rendered flush against the left edge of the screen. These selectors carry
	   `:not(.disable-default-overlay)` to match core's own specificity, plus one
	   class for `.mf-header`, so they win without an `!important`. */
	.mf-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) {
		padding: 0 var(--mf-gutter) var(--mf-gutter);
		background-color: var(--mf-base);
		color: var(--mf-ink);
	}

	/* Core reserves 2rem + 24px above the first link, which is shorter than the
	   bar: the links would still start underneath the close button. Step down
	   past the header instead, so the two gaps are the same number. */
	.mf-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__responsive-container-content {
		padding-top: var(--mf-header-h);
	}

	/* The close button is absolutely positioned against the overlay's padding
	   box, so it is not moved by the padding above. Core's own offsets are two
	   more of the invalid root-padding clamps and resolve to 0, which pinned it
	   into the very corner; put it on the gutter and centre it on the bar, where
	   the burger it replaces sits. */
	.mf-header .wp-block-navigation__responsive-container-close {
		top: calc((var(--mf-header-h) - 24px) / 2);
		right: var(--mf-gutter);
		color: var(--mf-ink);
	}

	/* A full-screen surface wants menu type, not the 14.5px the bar uses, and
	   rows rather than a wrapped run of links.

	   `align-self` is what makes the rows full width: core sets
	   `align-items: var(--navigation-layout-justification-setting)` on the
	   content, and this navigation declares `justifyContent: left`, so the
	   container is flex-start and shrink-wraps its longest label. Without the
	   stretch each hairline was only as wide as the word above it. */
	.mf-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__container {
		align-self: stretch;
		gap: 0;
		font-size: 24px;
		font-weight: 600;
		letter-spacing: -.01em;
	}

	.mf-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation-item {
		width: 100%;
	}

	/* core sets `align-items: flex-start` on the item, so the anchor would
	   otherwise shrink-wrap its label and the hairline under it would stop
	   mid-row. */
	.mf-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation-item__content {
		align-self: stretch;
		display: block;
		padding-block: 18px;
		border-bottom: 1px solid var(--mf-line);
		color: var(--mf-ink);
	}

	/* `align-items` on the overlay's content column is
	   `--navigation-layout-justification-setting`, which this navigation sets to
	   flex-start, so the bar would shrink-wrap its button and sit left. That is
	   what is wanted here: a pill on the gutter, not a full-width bar. */
	.mf-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .mf-nav__cta {
		display: flex;
		margin-top: 30px;
	}

	/* The surface is declared on core's own button classes — `mf-nav__cta-link`
	   is gone with the hand-written markup. This is deliberately not `.mf-cta-row`
	   sizing: a row of buttons inside a menu is the wrong shape, so the numbers
	   are the overlay's own, unchanged from what the div carried. */
	.mf-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .mf-nav__cta .wp-block-button__link {
		display: inline-block;
		padding: 14px 26px;
		border-radius: var(--mf-r);
		background: var(--mf-accent);
		color: var(--mf-base);
		font-size: 16px;
		font-weight: 600;
	}

	/* Inside the overlay the submenu is part of one tall list, not a floating
	   panel, so the dropdown surface is unwound and the children are indented
	   under their parent instead. The item and anchor rules above already give
	   them full-width rows and their own hairline.

	   `width: 100%` is what makes that last part true. The parent `li` is a
	   wrapping row flex container — anchor, chevron button, then the submenu —
	   so the wrapped `ul` lands on its own flex line and shrink-wraps to its
	   widest label. Unset, the children's hairlines stopped two-thirds of the
	   way across while every sibling row ran the full gutter width. With
	   `border-box` (the reset in §2) the 100% includes the 20px indent, so the
	   child rows line up with their parent's right edge. */
	.mf-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__submenu-container {
		position: static;
		width: 100%;
		min-width: 0;
		padding: 0 0 0 20px;
		background: transparent;
		border: 0;
		box-shadow: none;
	}

	.mf-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
		padding: 15px 0;
		font-size: 19px;
		font-weight: 500;
		/* The desktop rule above is nowrap so the floating panel sizes to its
		   widest label. Here the overlay is a fixed 390px with the list already
		   indented 20px, so a long label has to be allowed to wrap — an
		   overflowing row would give the whole overlay a horizontal scroll. */
		white-space: normal;
		color: var(--mf-body);
	}
}

/* Below this the bar tightens rather than dropping the CTA: "Book a demo" is the
   primary action and would otherwise be unreachable from the header on a phone.
   Written at (0,2,0) because core emits `.is-layout-flex { gap: var(--wp--style
   --block-gap) }` and `body .is-layout-flex`, at (0,1,0) and (0,1,1). */
@media (max-width: 600px) {
	.mf-header .mf-header__row {
		gap: 16px;
	}

	.mf-header__actions .wp-element-button {
		padding-inline: 15px;
		font-size: 14.5px;
	}

	/* At full size the lockup alone is 215px wide, which with the burger and the
	   CTA does not fit a phone. Shrink the logo rather than the CTA — the CTA is
	   the primary action and has a legibility floor the logo does not. */
	.mf-logo__img {
		height: 25px;
	}
}

@media (max-width: 420px) {
	.mf-logo__img {
		height: 22px;
	}

	.mf-header__actions .wp-element-button {
		padding-inline: 12px;
		font-size: 14px;
	}
}

@media (min-width: 1000px) {
	.mf-header .wp-block-navigation__responsive-container-open {
		display: none;
	}

	/* Core's `:not(.hidden-by-default):not(.is-menu-open)` variant of this
	   selector is (0,3,0), so this rule has to mirror that shape to be heard at
	   all — a bare `.mf-header .wp-block-navigation__responsive-container` is
	   (0,2,0), and the `position: static` that used to sit here was silently
	   losing to core's `position: relative`. */
	.mf-header .wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open) {
		display: block;
		position: static;
		background: none;
	}

	/* The mega panel is absolutely positioned and measures its containing block
	   with `top: 100%`, which has to be the header's own bottom edge. Four
	   elements between it and the header are positioned and would otherwise take
	   that job, each giving a different wrong answer — measured with the nav as
	   the containing block the panel came out 497px wide at the nav's own x, a
	   dropdown the size of the menu:

	   - the navigation block itself: core's `.wp-block-navigation{position:
	     relative}`;
	   - the responsive dialog (core: `position: relative`);
	   - the responsive container, above;
	   - and any constrained group, because core styles
	     `:where(.wp-block-group.wp-block-group-is-layout-constrained)
	     { position: relative }` — that is the header's bar, and `:where()`
	     carries no specificity, so claiming it costs nothing.

	   Nothing on desktop needs any of them positioned: the only absolutely
	   positioned things left inside are the hidden submenu (contained by its own
	   item) and the mobile overlay, which is `fixed`, not absolute. */
	.mf-header .mf-nav,
	.mf-header .wp-block-navigation__responsive-dialog,
	.mf-header .mf-header__bar {
		position: static;
	}

	/* The mega panel replaces core's flat dropdown — but only where `:has()` can
	   drive it, so every rule that takes the dropdown away is inside the same
	   `@supports`. In a browser without `:has()` core's own submenu stays and the
	   item still opens something, which is the graceful way round.

	   Both the trigger and the swap are scoped to one menu item, marked with
	   `rel="mf-mega"` on its anchor. That marker is the whole reason this is a
	   `[rel~=]` selector and not a class: `core/navigation-submenu` supports
	   neither `className` nor a stable `id` — its `id` attribute is the linked
	   post's ID, and core reads it for the current-item state — so `rel` is the
	   only schema-valid attribute core renders onto the item that a selector can
	   reach. It is invisible, and the editor preserves it.

	   The scoping is what keeps the menu the client's: without it any submenu
	   added later would have its own dropdown hidden and would open this panel
	   instead. Now an unmarked submenu keeps core's dropdown, and if the marker
	   is ever lost the pair degrades together — no swap, no panel, and the item
	   still opens core's flat list. */
	@supports selector(:has(*)) {

		/* (0,5,0) unmarked: a deliberate tie with core's own open-state rule,
		   `.wp-block-navigation .has-child .wp-block-navigation-submenu__toggle
		   [aria-expanded=true] ~ .wp-block-navigation__submenu-container`, which
		   is what makes the panel visible. This file loads after core's, so the
		   tie is enough. `display` is safe to claim outright because core hides
		   the submenu with height/width/visibility instead, leaving `display` at
		   its base value. */
		.mf-header .wp-block-navigation .has-child:has(> .wp-block-navigation-item__content[rel~="mf-mega"]) .wp-block-navigation__submenu-container.wp-block-navigation-submenu {
			display: none;
		}

		.mf-header .mf-nav__mega {
			display: block;
		}

		/* One open state, two triggers: core's own `aria-expanded` (set by its
		   hover *and* click handlers, so both gestures open the panel through the
		   button's real state) and a hover on the panel itself, which is what
		   keeps it open once the pointer is on it.
		   The marked item is named by its anchor→toggle *sibling pair*, not by
		   nesting one `:has()` inside another. `:has()` may not contain `:has()`
		   — the nested form is invalid, and an invalid selector takes its whole
		   rule down with it, silently. That is not hypothetical: the first version
		   of this rule was written nested, the parser dropped it, and the panel
		   never opened at all. Core renders the item's content anchor
		   immediately before its toggle button, so `[rel~="mf-mega"] ~ toggle`
		   reaches the marked item's own button and no other. */
		/* `visibility` alone does the hit-testing, and it must: `pointer-events`
		   is not a transitionable property, so gating it on the open state made
		   it flip to `none` on the same frame core's `pointerleave` cleared
		   `aria-expanded` — before the pointer had crossed the bridge — and the
		   panel could never be entered. Measured with the pointer walking down
		   from the item: it closed at 12px below the item's bottom edge. */
		.mf-header:has(.mf-nav .has-child > .wp-block-navigation-item__content[rel~="mf-mega"] ~ .wp-block-navigation-submenu__toggle[aria-expanded="true"]) .mf-nav__mega,
		.mf-header:has(.mf-nav__mega:hover) .mf-nav__mega {
			visibility: visible;
			opacity: 1;
			transform: none;
		}

		/* The open item takes a pill, as the client's reference does, without
		   moving a thing: the padding is cancelled by an equal negative margin,
		   so the item's outer box — and with it the row's whole rhythm — is
		   exactly what it was. */
		.mf-header .mf-nav li.has-child {
			--mf-nav-bridge: 26px;
			padding: 5px 12px;
			margin: -5px -12px;
			border-radius: 999px;
			transition: background .16s ease;
		}

		/* The bridge from the item down to the panel: an invisible 26px strip
		   hanging under the item, which is exactly as wide as the item and so
		   overlaps nothing else in the bar. It carries no z-index on purpose.
		   As a `z-index: -1` pseudo-element on the *panel* it never worked —
		   hit-testing follows paint order, and the header's bar is an ordinary
		   in-flow box with a transparent background, which is still a hit target,
		   so the bar won every time. Measured there, `elementFromPoint` returned
		   `.mf-header__bar` and the panel closed under a slow pointer. Being part
		   of the item has a second benefit: the pointer is still "inside" the
		   item while it crosses, so core's `pointerleave` — and with it
		   `aria-expanded` — is never cleared mid-journey. */
		.mf-header .mf-nav li.has-child::after {
			content: "";
			position: absolute;
			left: 0;
			right: 0;
			top: 100%;
			height: var(--mf-nav-bridge);
		}

		.mf-header .mf-nav li.has-child:has(.wp-block-navigation-submenu__toggle[aria-expanded="true"]) {
			background: var(--mf-soft);
		}
	}
}

/* --------------------------------------------------------------------------
   7. Hero
   -------------------------------------------------------------------------- */

.mf-hero {
	padding-block: 88px 0;
}

.mf-hero h1 {
	max-width: 850px;
}

.mf-hero .mf-lede {
	max-width: 640px;
}

.mf-trust {
	display: flex;
	align-items: center;
	gap: 14px;
	margin-top: 30px;
	font-size: 14.5px;
	color: var(--mf-muted);
}

.mf-avatars {
	display: flex;
}

.mf-avatars span {
	width: 30px;
	height: 30px;
	border-radius: 50%;
	background: var(--mf-avatar);
	border: 2px solid var(--mf-base);
	margin-left: -9px;
}

.mf-avatars span:first-child {
	margin-left: 0;
}

.mf-hero__visual {
	margin-top: 64px;
}

/* --------------------------------------------------------------------------
   8. Placeholder frames
   --------------------------------------------------------------------------

   Every grey frame in the design is a stand-in for an asset that does not exist
   yet. They are built as groups containing a label, not as empty image blocks:
   an <img> with no src is invalid block markup and the editor flags it, whereas
   a labelled group is valid, editable and translatable. To place the real
   asset, drop an image block in and delete the label. */

.mf-frame {
	position: relative;
	overflow: hidden;
	display: grid;
	place-items: center;
	background: var(--mf-frame);
	border: 1px solid var(--mf-frame-line);
	border-radius: var(--mf-r);
}

.mf-frame p,
.mf-ph {
	font-size: 12.5px;
	font-weight: 500;
	letter-spacing: .04em;
	color: var(--mf-placeholder);
	text-align: center;
	padding: 16px;
	margin: 0;
}

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

/* The editor wraps every image in a <figure>. Left alone, the frame's auto
   grid row takes the figure's intrinsic height, so the image is letterboxed
   in the middle of the panel (or overflows it vertically). A 1fr row bounds
   the row to the frame, and the stretched figure lets object-fit crop. */
.mf-frame:has(> .wp-block-image) {
	grid-template-rows: minmax(0, 1fr);
}

.mf-frame > .wp-block-image {
	width: 100%;
	height: 100%;
	margin: 0;
}

/* Once a real image lands, the label is redundant even if it was left behind. */
.mf-frame:has(img) .mf-ph {
	display: none;
}

.mf-frame--browser {
	padding-top: 34px;
}

.mf-frame--browser::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 34px;
	background-color: var(--mf-base);
	border-bottom: 1px solid var(--mf-frame-line);
	background-image:
		radial-gradient(circle at 20px 17px, var(--mf-chrome) 4px, transparent 4px),
		radial-gradient(circle at 36px 17px, var(--mf-chrome) 4px, transparent 4px),
		radial-gradient(circle at 52px 17px, var(--mf-chrome) 4px, transparent 4px);
}

.mf-frame--hero {
	aspect-ratio: 16 / 9.4;
}

.mf-frame--wide {
	aspect-ratio: 16 / 7;
}

.mf-frame--tall {
	aspect-ratio: 4 / 3;
}

/* A 16:9.4 frame at 390px is 220px tall — too letterboxed to read as a product
   shot, so the hero frame gets squarer as it narrows. */
@container (max-width: 640px) {
	.mf-frame--hero {
		aspect-ratio: 4 / 3;
	}

	.mf-frame--wide {
		aspect-ratio: 3 / 2;
	}
}

/* --------------------------------------------------------------------------
   9. Logo strip
   -------------------------------------------------------------------------- */

.mf-strip__title {
	font-size: var(--wp--preset--font-size--eyebrow);
	text-transform: uppercase;
	letter-spacing: .09em;
	color: var(--mf-muted);
	text-align: center;
	margin-bottom: 26px;
	font-weight: 600;
}

.mf-logos {
	display: grid;
	grid-template-columns: repeat(6, 1fr);
	gap: 24px;
	align-items: center;
}

.mf-logos>* {
	height: 26px;
	background: var(--mf-tile);
	border-radius: 6px;
}

@container (max-width: 860px) {
	.mf-logos {
		grid-template-columns: repeat(3, 1fr);
	}
}

@container (max-width: 460px) {
	.mf-logos {
		grid-template-columns: repeat(2, 1fr);
	}
}

/* --------------------------------------------------------------------------
   10. Card grids
   -------------------------------------------------------------------------- */

.mf-grid {
	display: grid;
	gap: 26px;
	margin-top: 56px;
	grid-template-columns: 1fr;
}

.mf-g3 {
	grid-template-columns: repeat(3, 1fr);
}

.mf-g4 {
	grid-template-columns: repeat(4, 1fr);
}

@container (max-width: 940px) {
	.mf-g3 {
		grid-template-columns: repeat(2, 1fr);
	}

	.mf-g4 {
		grid-template-columns: repeat(2, 1fr);
	}
}

@container (max-width: 600px) {

	.mf-g3,
	.mf-g4 {
		grid-template-columns: 1fr;
	}
}

.mf-card {
	background: var(--mf-base);
	border: 1px solid var(--mf-line);
	border-radius: var(--mf-r);
	padding: 30px;
}

.mf-card p {
	font-size: var(--wp--preset--font-size--text-sm);
	margin-top: 9px;
	color: var(--mf-body);
}

.mf-card h3 {
	margin: 0;
}

/* The one highlighted module in the capabilities grid. */
.mf-card--feature {
	border-color: var(--mf-accent);
	box-shadow: 0 0 0 3px var(--mf-accent-soft);
}

.mf-card__icon {
	width: 42px;
	height: 42px;
	border-radius: var(--mf-r-sm);
	background: var(--mf-accent-soft);
	display: flex;
	align-items: center;
	justify-content: center;
	margin-bottom: 18px;
	flex: none;
}

.mf-card__icon svg {
	width: 21px;
	height: 21px;
	stroke: var(--mf-accent-dk);
	fill: none;
	stroke-width: 1.9;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* --------------------------------------------------------------------------
   11. Bento (the two portals)
   -------------------------------------------------------------------------- */

.mf-bento {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 26px;
	margin-top: 56px;
}

.mf-bento .mf-card {
	padding: 40px;
}

.mf-bento .mf-card h3 {
	font-size: 24px;
}

.mf-bento .mf-card>p {
	font-size: 16.5px;
	margin-top: 12px;
}

.mf-bento .mf-frame {
	margin-top: 28px;
}

@container (max-width: 820px) {
	.mf-bento {
		grid-template-columns: 1fr;
	}
}

/* --------------------------------------------------------------------------
   12. Industries
   -------------------------------------------------------------------------- */

.mf-metier {
	position: relative;
	border-radius: var(--mf-r);
	overflow: hidden;
	border: 1px solid var(--mf-frame-line);
	aspect-ratio: 3 / 3.4;
	display: flex;
	align-items: flex-end;
}

.mf-metier .mf-frame {
	position: absolute;
	inset: 0;
	border: 0;
	border-radius: 0;
}

.mf-metier__label {
	position: relative;
	width: 100%;
	padding: 22px;
	background: linear-gradient(transparent, rgb(42 73 121 / .92));
}

.mf-metier__label span,
.mf-metier__label p {
	color: var(--mf-base);
	font-weight: 600;
	font-size: 17px;
	letter-spacing: normal;
	text-align: left;
	padding: 0;
	margin: 0;
}

@container (max-width: 600px) {

	/* Full-bleed portrait crops stack into a very tall column on a phone. */
	.mf-metier {
		aspect-ratio: 16 / 11;
	}
}

/* --------------------------------------------------------------------------
   13. Proof
   -------------------------------------------------------------------------- */

/* The impact band: four bottom-aligned columns on a shared 480px scale, where
   one column at a time opens into a card. Modelled on the Mews "Powering
   hospitality impact" band, which cycles the same way.

   Bar height is a deliberate rhythm, NOT a plot. The figures are in different
   units (cleaners, sites, a percentage), so no shared scale exists that could
   encode them; the collapsed heights are hand-set and the open panel always
   takes the full 480px. If these are ever replaced with comparable
   percentages, move to height: calc(var(--mf-value) * 1%) and the band becomes
   a real chart.

   Opening is driven by `.is-active`, toggled in assets/js/stats.js. The band is
   fully readable with JavaScript disabled: every figure and label is in the
   markup and always visible, and only the headline, the link and the media —
   the three things that need the extra width — are hidden until a panel opens.

   The width transition rides on `flex-grow` rather than `width` so the columns
   keep dividing whatever space is left after the gaps. */
.mf-bars {
	display: flex;
	align-items: flex-end;
	gap: 18px;
	height: 480px;
	margin-top: 56px;
}

.mf-bar {
	--mf-pad-y: 30px;
	--mf-pad-x: 26px;

	/* The media column is 38% of the *content* box, but the two things that
	   have to agree on where it starts measure different boxes: `width` on the
	   absolutely positioned media resolves against the panel's padding box,
	   while `margin-right` on the body resolves against the content box, which
	   is 2 × --mf-pad-x narrower. Subtracting that back off puts the media on
	   the content box, so the headline and the figure can clear it by the same
	   gutter. `.76` is 2 × .38 — change one and it has to change too. */
	--mf-media-w: 38%;
	--mf-media-box: calc(var(--mf-media-w) - var(--mf-pad-x) * .76);
	--mf-media-clear: 26px;

	flex: 1 1 0;
	height: var(--mf-h, 60%);
	min-width: 0;
	position: relative;
	overflow: hidden;
	display: flex;
	flex-direction: column;
	/* Figure and label sit on the floor of the panel in both states, which is
	   what gives the band its bottom-aligned silhouette. */
	justify-content: flex-end;
	gap: 24px;
	padding: var(--mf-pad-y) var(--mf-pad-x);
	border-radius: var(--mf-r) var(--mf-r) 0 0;
	background: var(--mf-tile);
	transition: flex-grow .7s cubic-bezier(.4, 0, .2, 1),
		height .7s cubic-bezier(.4, 0, .2, 1),
		background-color .4s ease;
}

.mf-bar.is-active {
	flex-grow: 4.6;
	height: 100%;
}

.mf-bar__body {
	display: flex;
	flex-direction: column;
	gap: 10px;
	min-width: 0;
}

/* Collapsed panels are the control that opens them, so they are focusable and
   announced as buttons. Once open the panel holds a real link, so the button
   role is dropped rather than nesting interactive content inside it. */
.mf-bar:not(.is-active) {
	cursor: pointer;
}

.mf-bar:focus-visible {
	outline: 2px solid var(--mf-accent);
	outline-offset: -4px;
}

.mf-bar--1 {
	--mf-h: 52%;
}

.mf-bar--2 {
	--mf-h: 38%;
	background: var(--mf-chrome);
}

.mf-bar--3 {
	--mf-h: 78%;
	background: var(--mf-ink);
}

.mf-bar--4 {
	--mf-h: 70%;
	background: var(--mf-accent);
}

/* Collapsed, the three narrow columns share about a seventh of the band, so a
   fixed figure would be clipped by `overflow: hidden`. Sizing it off the band
   width and letting the open state grow it keeps the number whole in both
   states and makes the change of size part of the opening motion. */
.mf-bar__n {
	font-size: clamp(24px, 2.6cqw, 40px);
	font-weight: 700;
	letter-spacing: -.035em;
	line-height: 1;
	color: var(--mf-ink);
	/* Tabular figures stop the number shifting sideways as it counts up. */
	font-variant-numeric: tabular-nums;
	transition: font-size .7s cubic-bezier(.4, 0, .2, 1);
}

.mf-bar.is-active .mf-bar__n {
	font-size: clamp(48px, 5.6cqw, 92px);
}

.mf-bar__l {
	font-size: 11px;
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
	line-height: 1.45;
	color: var(--mf-body);
	max-width: 18ch;
	transition: font-size .7s cubic-bezier(.4, 0, .2, 1);
}

.mf-bar.is-active .mf-bar__l {
	font-size: var(--wp--preset--font-size--eyebrow);
}

.mf-bar--4 .mf-bar__n,
.mf-bar--4 .mf-bar__l {
	color: var(--mf-base);
}

/* The testimonial panel carries the quote in the slot where the other three
   carry a figure. Collapsed it is set small enough to read as texture inside a
   narrow column; open it steps up to a size worth actually reading. The whole
   sentence is in the markup in both states — nothing is truncated. */
.mf-bar__quote {
	font-size: 12.5px;
	line-height: 1.45;
	font-weight: 500;
	letter-spacing: -.005em;
	color: var(--mf-base);
	transition: font-size .7s cubic-bezier(.4, 0, .2, 1);
}

.mf-bar.is-active .mf-bar__quote {
	font-size: 19px;
}

/* The attribution is the one thing in the band that is withheld while a panel
   is shut. Collapsed the quote is texture — far too small to read — so a name
   under it is noise, and it is at its most useful in the moment the panel
   opens. Nothing else is treated this way: the quote itself, every figure and
   every label is present in both states. */
.mf-bar__by {
	display: none;
	margin-top: 12px;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	letter-spacing: .07em;
	text-transform: uppercase;
	color: var(--mf-on-dark);
}

.mf-bar.is-active .mf-bar__by {
	display: block;
}

/* ---- the parts that only exist while a panel is open ---- */

/* Absolutely positioned so they contribute nothing to the collapsed layout;
   the opening animation is a crossfade rather than a reflow. The delays are
   asymmetric on purpose: nothing appears until the panel has begun to widen,
   and everything is gone before it finishes closing. */
.mf-bar__head,
.mf-bar__media {
	position: absolute;
	opacity: 0;
	visibility: hidden;
	transition: opacity .25s ease, visibility 0s linear .25s;
}

.mf-bar__head {
	top: var(--mf-pad-y);
	left: var(--mf-pad-x);
	right: var(--mf-pad-x);
}

.mf-bar__media {
	top: var(--mf-pad-y);
	right: var(--mf-pad-x);
	bottom: var(--mf-pad-y);
	width: var(--mf-media-box);
}

.mf-bar.is-active .mf-bar__head,
.mf-bar.is-active .mf-bar__media {
	opacity: 1;
	visibility: visible;
	transition: opacity .35s ease .3s, visibility 0s linear .3s;
}

/* The media is taken out of flow, so it collides with anything left in it.
   The headline and the body each have to step around it by hand — and they
   both need this, not just the headline: the testimonial panel has no headline,
   and without the body's margin its quote runs underneath the image and is
   clipped. */
.mf-bar.is-active .mf-bar__head {
	right: calc(var(--mf-media-box) + var(--mf-pad-x) + var(--mf-media-clear));
}

.mf-bar.is-active .mf-bar__body {
	margin-right: calc(var(--mf-media-w) + var(--mf-media-clear));
}

.mf-bar__title {
	font-size: 21px;
	line-height: 1.3;
	font-weight: 600;
	letter-spacing: -.02em;
	color: var(--mf-ink);
	max-width: 22ch;
}

/* The arrow is part of the link text, not a `::after`, so the whole label is
   one click target and a translator sees the arrow as part of the string. */
.mf-bar__link {
	margin-top: 14px;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	letter-spacing: .07em;
	text-transform: uppercase;
}

.mf-bar__link a {
	color: var(--mf-accent);
	text-decoration: none;
	white-space: nowrap;
}

.mf-bar__link a:hover {
	text-decoration: underline;
}

.mf-bar__media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: var(--mf-r-sm);
}

/* The placeholder frame, until a real photograph lands here. */
.mf-bar__media .mf-frame {
	width: 100%;
	height: 100%;
	border-radius: var(--mf-r-sm);
}

.mf-bar--4 .mf-bar__title {
	color: var(--mf-base);
}

.mf-bar--4 .mf-bar__link a {
	color: var(--mf-base);
}

/* Below this the four columns are too narrow to open into a card, so the band
   becomes a stack and every panel shows its full contents at once. The JS
   reads this same breakpoint back out of the computed `display`, so the two
   cannot drift. */
@container (max-width: 720px) {
	.mf-bars {
		display: grid;
		grid-template-columns: 1fr;
		gap: 16px;
		height: auto;
	}

	.mf-bar,
	.mf-bar.is-active {
		height: auto;
		flex-grow: 0;
		border-radius: var(--mf-r);
		padding: 26px;
		cursor: auto;
		justify-content: flex-start;
	}

	.mf-bar__l {
		max-width: none;
	}

	/* Nothing is collapsed here, so every panel gets the open state's sizes
	   regardless of which one the script considers active. */
	.mf-bar__n,
	.mf-bar.is-active .mf-bar__n {
		font-size: clamp(40px, 7cqw, 60px);
	}

	.mf-bar__l,
	.mf-bar.is-active .mf-bar__l {
		font-size: var(--wp--preset--font-size--eyebrow);
	}

	.mf-bar__quote,
	.mf-bar.is-active .mf-bar__quote {
		font-size: 17px;
	}

	/* Stacked, nothing is collapsed, so the attribution the accordion withholds
	   is shown on every panel. */
	.mf-bar__by,
	.mf-bar.is-active .mf-bar__by {
		display: block;
		font-size: var(--wp--preset--font-size--eyebrow);
	}

	.mf-bar__head,
	.mf-bar__media,
	.mf-bar.is-active .mf-bar__head,
	.mf-bar.is-active .mf-bar__media {
		position: static;
		opacity: 1;
		visibility: visible;
		width: auto;
	}

	.mf-bar__media {
		height: 200px;
	}
}

/* --------------------------------------------------------------------------
   14. Moonschat
   --------------------------------------------------------------------------

   The only dark band on the page. Moonschat is the one capability that is not
   about doing the work but about asking questions of it, so it gets its own
   visual register instead of another soft-grey section. */
.mf-sec--ink {
	background: var(--mf-ink);
	border-top: 1px solid var(--mf-ink);
	border-bottom: 1px solid var(--mf-ink);
	color: var(--mf-base);
}

.mf-sec--ink h2 {
	color: var(--mf-base);
}

.mf-sec--ink .mf-lede {
	color: var(--mf-on-dark);
}

/* The accent is unreadable against this navy at 13px, so the eyebrow is the
   accent mixed most of the way to white. Same treatment for the icons. */
.mf-sec--ink .mf-eyebrow {
	color: color-mix(in srgb, var(--mf-accent) 45%, var(--mf-base));
}

/* The wordmark stands in for the eyebrow that opens every other section. It is
   the yellow lockup, which is only legible on this navy band — on white the
   #FFCC00 wordmark is about 1.4:1. Everywhere else the Moonschat name is set as
   text. Height, not width, is fixed so the mark scales with the lockup. */
.mf-ai__mark {
	display: block;
	height: 30px;
	width: auto;
	margin-bottom: 16px;
}

.mf-ai__mark_logo {
	display: block;
	height: 100px;
	width: auto;
	margin-bottom: 16px;
	margin-block-start: 0;
	align-items: left;

}

.mf-ai {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 64px;
	align-items: center;
	margin-top: 56px;
}

.mf-ai__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 26px;
}

.mf-ai__item {
	display: grid;
	grid-template-columns: 32px 1fr;
	gap: 16px;
	align-items: start;
}

.mf-ai__item svg {
	width: 21px;
	height: 21px;
	margin-top: 3px;
	stroke: color-mix(in srgb, var(--mf-accent) 45%, var(--mf-base));
	fill: none;
	stroke-width: 1.9;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.mf-ai__item h3 {
	margin: 0;
	font-size: var(--wp--preset--font-size--heading-3);
	color: var(--mf-base);
}

.mf-ai__item p {
	margin-top: 7px;
	font-size: var(--wp--preset--font-size--text-sm);
	color: var(--mf-on-dark);
}

/* An illustration of the product, not a screenshot of it — so it is built from
   markup rather than another grey placeholder frame.

   It holds three exchanges. Where JavaScript is available and no reduced-motion
   preference is set, `chat.js` adds `.is-carousel` and they become a carousel
   that advances every 3s, sliding left. Everywhere else `.mf-chat__track` stays
   a plain stack and all three exchanges are simply readable — the fallback is
   the full content, not a degraded version of it. That is why the carousel is
   opt-in: a track that began clipped would put exchanges 2 and 3 out of reach
   for anyone who never receives the script. */
/* The horizontal inset is carried by the children, not by the frame. `overflow:
   hidden` clips at the padding box, so 18px of horizontal padding on the frame
   left 18px of the next slide visible in that padding — a sliver of the
   following bubble riding along the right edge. With the frame's padding at
   zero the padding box *is* the content box, and the neighbour starts exactly
   where the clip ends. */
.mf-chat {
	padding-block: 18px;
	border-radius: var(--mf-r-xl);
	border: 1px solid color-mix(in srgb, var(--mf-base) 14%, transparent);
	background: color-mix(in srgb, var(--mf-base) 6%, transparent);
}

/* The clip lives on the frame rather than on the track. The track is the thing
   that moves, and `overflow: hidden` on a transformed element carries its own
   clip box along with it — nothing would ever appear to slide. */
.mf-chat.is-carousel {
	overflow: hidden;
}

.mf-chat__bar {
	display: flex;
	align-items: center;
	gap: 7px;
	padding: 4px 24px 16px;
	border-bottom: 1px solid color-mix(in srgb, var(--mf-base) 12%, transparent);
}

/* The three title-bar dots double as the slide indicator: the active one takes
   the accent. In the stacked fallback they stay uniformly grey, which is what
   they always were. */
.mf-chat__bar>span {
	flex: none;
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background: color-mix(in srgb, var(--mf-base) 22%, transparent);
	transition: background-color .35s ease;
}

.mf-chat__bar>span.is-active {
	background: var(--mf-accent);
}

/* The Moonschat app badge, sitting where a title bar's application icon goes.
   Its tile is #353C47 on a bar that is itself a light wash over dark navy, so
   without the hairline ring the square edge disappears. */
.mf-chat__mark {
	width: 18px;
	height: 18px;
	margin-left: 5px;
	border-radius: 5px;
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--mf-base) 16%, transparent);
}

.mf-chat__bar p {
	margin-left: 0;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	letter-spacing: .07em;
	text-transform: uppercase;
	color: var(--mf-on-dark);
}

.mf-chat__track {
	display: grid;
	gap: 14px;
	padding-top: 16px;
}

/* One slide per track width, and no gap between them, so a translate of exactly
   -100% per step lands flush. The seamless loop at the end of the track depends
   on that being exact — a gap would show the join. */
.mf-chat.is-carousel .mf-chat__track {
	display: flex;
	gap: 0;
	transition: transform .55s cubic-bezier(.4, 0, .2, 1);
}

/* Set for the single frame where the loop snaps from the cloned first slide
   back onto the real one. Without it that jump animates, and the carousel
   rewinds across the whole track instead of continuing leftwards. */
.mf-chat.is-carousel .mf-chat__track.is-snapping {
	transition: none;
}

.mf-chat__slide {
	display: flex;
	flex-direction: column;
	gap: 14px;
	padding-inline: 18px;
}

/* Every slide is as tall as the tallest, so the frame does not change height as
   the carousel advances. */
.mf-chat.is-carousel .mf-chat__slide {
	flex: 0 0 100%;
	min-width: 100%;
}

.mf-chat__msg {
	max-width: 92%;
	padding: 14px 16px;
	border-radius: var(--mf-r);
	font-size: var(--wp--preset--font-size--text-sm);
	line-height: 1.5;
}

.mf-chat__msg p {
	margin: 0;
}

/* The question is a core paragraph, so it is the bubble itself rather than a
   bubble containing one. */
.mf-chat__msg--user {
	align-self: flex-end;
	background: var(--mf-accent);
	color: var(--mf-base);
	border-bottom-right-radius: 5px;
}

.mf-chat__msg--bot {
	background: var(--mf-base);
	color: var(--mf-ink);
	border-bottom-left-radius: 5px;
}

/* A bot answer is several paragraphs now, so they need a rhythm between them;
   the single paragraph this used to be got it from its own line-height. */
.mf-chat__msg>p+p {
	margin-top: 10px;
}

/* A row is one paragraph with the value in <strong>; flex pushes the two ends
   apart. Kept as ordinary paragraphs so the whole exchange stays editable in
   the block editor and reachable by Polylang. */
.mf-chat__cards {
	display: grid;
	gap: 6px;
	margin-top: 12px;
	padding-top: 12px;
	border-top: 1px solid var(--mf-line);
}

.mf-chat__cards p {
	display: flex;
	justify-content: space-between;
	gap: 12px;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	color: var(--mf-body);
}

.mf-chat__cards strong {
	font-weight: 700;
	color: var(--mf-accent-dk);
}

/* The third exchange ends on the agent asking which option to take. Chips, not
   buttons: nothing in the illustration is clickable. */
.mf-chat__choices {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 12px;
	padding-top: 12px;
	border-top: 1px solid var(--mf-line);
}

.mf-chat__choices p {
	padding: 8px 11px;
	border: 1px solid var(--mf-line);
	border-radius: var(--mf-r-sm);
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	color: var(--mf-ink);
}

/* The status line is a paragraph rather than a div wrapping a dot span, so the
   pulsing dot is a pseudo-element and the whole line stays editable copy.

   `margin-top: auto` pins it to the foot of the slide. The carousel stretches
   every slide to the tallest, and the exchanges are not the same length; pinning
   the status makes the leftover space fall between the answer and the status,
   which is where a chat window keeps it, and lines the three of them up. */
.mf-chat__status {
	display: flex;
	align-items: center;
	gap: 9px;
	margin-top: auto;
	padding: 4px 24px;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	letter-spacing: .05em;
	text-transform: uppercase;
	color: var(--mf-on-dark);
}

.mf-chat__status::before {
	content: "";
	flex: none;
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--mf-accent);
	animation: mf-pulse 1.4s ease-in-out infinite;
}

@keyframes mf-pulse {

	0%,
	100% {
		opacity: 1;
	}

	50% {
		opacity: .25;
	}
}

@media (prefers-reduced-motion: reduce) {
	.mf-chat__status::before {
		animation: none;
	}

	.mf-chat.is-carousel .mf-chat__track {
		transition: none;
	}
}

.mf-ai__strip {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 32px;
	margin-top: 72px;
	padding-top: 40px;
	border-top: 1px solid color-mix(in srgb, var(--mf-base) 16%, transparent);
}

.mf-ai__strip h3 {
	margin: 0;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	letter-spacing: .07em;
	text-transform: uppercase;
	color: color-mix(in srgb, var(--mf-accent) 45%, var(--mf-base));
}

.mf-ai__strip p {
	margin-top: 9px;
	font-size: var(--wp--preset--font-size--text-sm);
	color: var(--mf-on-dark);
}

@container (max-width: 900px) {
	.mf-ai {
		grid-template-columns: 1fr;
		gap: 46px;
	}

	.mf-ai__strip {
		grid-template-columns: 1fr;
		gap: 24px;
	}
}

/* --------------------------------------------------------------------------
   15. Integrations
   -------------------------------------------------------------------------- */

.mf-ints {
	display: grid;
	grid-template-columns: repeat(6, 1fr);
	gap: 18px;
	margin-top: 48px;
}

.mf-int {
	aspect-ratio: 1.9 / 1;
	border: 1px solid var(--mf-line);
	border-radius: var(--mf-r-sm);
	display: grid;
	place-items: center;
	font-size: var(--wp--preset--font-size--eyebrow);
	color: var(--mf-placeholder-2);
	font-weight: 600;
	background: var(--mf-base);
	text-align: center;
	padding: 6px;
}

/* The brand marks are backgrounds, so the brand name stays in the markup for
   screen readers and translators; the text is only visually hidden. */
.mf-int--silae,
.mf-int--sage,
.mf-int--pennylane,
.mf-int--sap,
.mf-int--fortnox,
.mf-int--mews {
	background-repeat: no-repeat;
	background-position: center;
	color: transparent;
}

.mf-int--silae {
	background-image: url("../img/integrations/silae-black.svg");
	background-size: 58% auto;
}

.mf-int--sage {
	background-image: url("../img/integrations/sage-black.svg");
	background-size: 46% auto;
}

.mf-int--pennylane {
	background-image: url("../img/integrations/pennylane-black.svg");
	background-size: 68% auto;
}

.mf-int--sap {
	background-image: url("../img/integrations/sap-black.svg");
	background-size: 52% auto;
}

.mf-int--fortnox {
	background-image: url("../img/integrations/fortnox-black.svg");
	background-size: 62% auto;
}

.mf-int--mews {
	background-image: url("../img/integrations/mews-black.svg");
	background-size: 66% auto;
}

@container (max-width: 900px) {
	.mf-ints {
		grid-template-columns: repeat(3, 1fr);
	}
}

@container (max-width: 480px) {
	.mf-ints {
		grid-template-columns: repeat(2, 1fr);
	}
}

/* --------------------------------------------------------------------------
   16. Resources
   -------------------------------------------------------------------------- */

.mf-post .mf-frame {
	margin-bottom: 20px;
}

.mf-post__tag {
	font-size: 12.5px;
	font-weight: 600;
	color: var(--mf-accent-dk);
	text-transform: uppercase;
	letter-spacing: .07em;
}

.mf-post h3 {
	margin-top: 10px;
}

/* --------------------------------------------------------------------------
   17. Footer
   -------------------------------------------------------------------------- */

.mf-footer {
	padding-block: 72px 40px;
	padding-inline: var(--mf-gutter);
	border-top: 1px solid var(--mf-line);
}

.mf-fgrid {
	display: grid;
	grid-template-columns: 2fr 1.2fr 1fr 1fr 1fr;
	gap: 40px;
}

.mf-fgrid h4,
.mf-fcol__title {
	font-size: 13.5px;
	text-transform: uppercase;
	letter-spacing: .08em;
	color: var(--mf-muted);
	margin-bottom: 16px;
	font-weight: 600;
}

/* The Industries column carries a second group — the five role pages — under
   its own title. Without this the two lists read as one column of eight
   unrelated links: the heading that separates them sits directly under the
   last item of the one above. */
.mf-fgrid .mf-fcol__title:not(:first-child) {
	margin-top: 26px;
}

.mf-fgrid ul {
	list-style: none;
	margin: 0;
	padding: 0;
	font-size: var(--wp--preset--font-size--text-sm);
	line-height: 2.1;
}

.mf-fgrid a {
	color: var(--mf-body);
}

.mf-fgrid a:hover {
	color: var(--mf-accent-dk);
}

.mf-legal {
	margin-top: 56px;
	padding-top: 26px;
	border-top: 1px solid var(--mf-line);
	display: flex;
	justify-content: space-between;
	font-size: 14px;
	color: var(--mf-muted);
	flex-wrap: wrap;
	gap: 12px;
}

.mf-legal a {
	color: var(--mf-muted);
}

.mf-legal a:hover {
	color: var(--mf-accent-dk);
}

@container (max-width: 900px) {
	.mf-fgrid {
		grid-template-columns: 1fr 1fr;
	}
}

@container (max-width: 560px) {
	.mf-fgrid {
		grid-template-columns: 1fr;
		gap: 32px;
	}

	.mf-legal {
		flex-direction: column;
	}
}

/* --------------------------------------------------------------------------
   18. Video showcase
   -------------------------------------------------------------------------- */

.mf-showcase {
	position: relative;
	margin-top: 48px;
}

.mf-stage {
	position: relative;
	overflow: hidden;
	border-radius: var(--mf-r);
	background: var(--mf-frame);
	aspect-ratio: 4 / 3;
	min-height: 420px;
}

@media (min-width: 1024px) {
	.mf-stage {
		aspect-ratio: auto;
		min-height: 520px;
	}
}

.mf-panel {
	position: absolute;
	inset: 0;
	opacity: 0;
	pointer-events: none;
	transition: opacity .45s ease;
}

.mf-panel.is-active {
	opacity: 1;
	pointer-events: auto;
}

.mf-panel video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: left center;
	display: block;
}

.mf-toggle {
	position: absolute;
	top: 16px;
	right: 16px;
	z-index: 10;
	width: 30px;
	height: 30px;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
	border-radius: 50%;
	transition: opacity .2s;
}

@media (min-width: 640px) {
	.mf-toggle {
		top: 20px;
		right: 20px;
	}
}

.mf-toggle:hover {
	opacity: .8;
}

.mf-ico-play {
	display: none;
}

.mf-toggle[data-state="paused"] .mf-ico-pause {
	display: none;
}

.mf-toggle[data-state="paused"] .mf-ico-play {
	display: block;
}

.mf-tabs {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 10;
	display: flex;
	gap: 4px;
	padding: 0 24px 18px;
}

@media (min-width: 640px) {
	.mf-tabs {
		padding: 0 32px 32px;
	}
}

@media (max-width: 639px) {
	.mf-tabs {
		justify-content: center;
		flex-wrap: wrap;
	}
}

.mf-tab {
	display: inline-flex;
	align-items: center;
	min-height: 28px;
	padding: 0 12px;
	border: 0;
	border-radius: 999px;
	background: rgb(0 0 0 / .45);
	color: var(--mf-base);
	cursor: pointer;
	font-size: 11px;
	font-weight: 700;
	line-height: 1;
	letter-spacing: .02em;
	transition: background-color .2s;
	white-space: nowrap;
}

.mf-tab:hover {
	background: rgb(0 0 0 / .6);
}

.mf-tab.is-active {
	background: #000;
}

.mf-tab:focus-visible {
	outline: 2px solid var(--mf-base);
	outline-offset: 2px;
}

.mf-tab__short {
	display: inline;
}

.mf-tab__long {
	display: none;
}

@media (min-width: 1280px) {
	.mf-tab__long {
		display: inline;
	}

	.mf-tab__short {
		display: none;
	}
}

.mf-panel video:not([src]):not(:has(source)) {
	background: var(--mf-frame);
}

@media (prefers-reduced-motion: reduce) {
	.mf-panel {
		transition: none;
	}
}

/* --------------------------------------------------------------------------
   19. Forms
   -------------------------------------------------------------------------- */

.mf-form {
	margin-top: 32px;
	max-width: 640px;
}

.mf-form__row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 22px;
}

.mf-field {
	display: flex;
	flex-direction: column;
	gap: 7px;
	margin-bottom: 22px;
}

.mf-field label {
	font-size: var(--wp--preset--font-size--text-sm);
	font-weight: 600;
	color: var(--mf-ink);
}

.mf-field label span {
	color: var(--mf-accent);
}

.mf-field input,
.mf-field select,
.mf-field textarea {
	width: 100%;
	padding: 12px 14px;
	font: inherit;
	font-size: var(--wp--preset--font-size--body);
	color: var(--mf-ink);
	background: var(--mf-base);
	border: 1px solid var(--mf-frame-line);
	border-radius: var(--mf-r-sm);
}

.mf-field textarea {
	resize: vertical;
	min-height: 120px;
}

.mf-field input:focus-visible,
.mf-field select:focus-visible,
.mf-field textarea:focus-visible {
	border-color: var(--mf-accent);
	outline: 2px solid var(--mf-accent);
	outline-offset: 1px;
}

.mf-consent {
	margin-bottom: 22px;
	max-width: 640px;
}

/* The label is a flex row of exactly two children: the box and the sentence.
   That only holds because the sentence is wrapped in a <span>. Left as bare
   text it would be chopped into anonymous flex items at every element
   boundary, and the inline privacy-policy link would be pushed out into its
   own column with the trailing full stop orphaned after it — which is exactly
   what happened before the span was added. */
.mf-consent label {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	font-size: var(--wp--preset--font-size--text-sm);
	line-height: 1.5;
	color: var(--mf-body);
}

.mf-consent label>span {
	min-width: 0;
}

.mf-consent input {
	flex: none;
	width: 16px;
	height: 16px;
	margin-top: 3px;
	accent-color: var(--mf-accent);
}

/* Honeypot: pushed off-screen rather than display:none. Hidden inputs are a
   well-known pattern that bots skip; a field that merely sits out of view is
   not, and a human never reaches it because it is not in the tab order either. */
.mf-form__hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.mf-form__done {
	max-width: 640px;
	padding: 32px;
	background: var(--mf-soft);
	border: 1px solid var(--mf-frame-line);
	border-radius: var(--mf-r);
}

.mf-form__done h3 {
	margin-bottom: 8px;
}

.mf-form__error {
	max-width: 640px;
	margin-bottom: 24px;
	padding: 14px 18px;
	color: var(--mf-ink);
	background: var(--mf-accent-soft);
	border-left: 3px solid var(--mf-accent);
	border-radius: var(--mf-r-sm);
}

@media (max-width: 600px) {
	.mf-form__row {
		grid-template-columns: 1fr;
		gap: 0;
	}
}

/* --------------------------------------------------------------------------
   20. Scroll-driven reveal
   --------------------------------------------------------------------------

   Progressive enhancement only. Without support, or with reduced motion
   requested, the @supports/media guards fail and the content renders in its
   final state — never hidden. */
@keyframes mf-rise {
	from {
		opacity: 0;
		translate: 0 18px;
	}

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

/* Bars grow up from the baseline instead of rising. clip-path is used rather
   than scale so the contents are revealed rather than squashed — a scale on a
   flex item distorts its text through the whole transition. */
@keyframes mf-grow {
	from {
		clip-path: inset(100% 0 0 0);
	}

	to {
		clip-path: inset(0 0 0 0);
	}
}

@supports (animation-timeline: view()) {
	@media (prefers-reduced-motion: no-preference) {

		.mf-reveal,
		.mf-stagger>* {
			animation: mf-rise linear both;
			animation-timeline: view();
			animation-range: entry 0% entry 45%;
		}

		/* Staggering the range rather than the delay is what makes a grid
		   resolve as a wave left-to-right. With a scroll timeline a delay is
		   measured along the timeline, so it would shift the whole wave
		   instead of separating its members. Six steps covers the widest grid
		   on the page; extra children simply share the last one. */
		.mf-stagger>*:nth-child(2) {
			animation-range: entry 7% entry 52%;
		}

		.mf-stagger>*:nth-child(3) {
			animation-range: entry 14% entry 59%;
		}

		.mf-stagger>*:nth-child(4) {
			animation-range: entry 21% entry 66%;
		}

		.mf-stagger>*:nth-child(5) {
			animation-range: entry 28% entry 73%;
		}

		.mf-stagger>*:nth-child(6) {
			animation-range: entry 35% entry 80%;
		}

		.mf-bar {
			animation: mf-grow linear both;
			animation-timeline: view();
			animation-range: entry 10% entry 56%;
		}

		.mf-bar:nth-child(2) {
			animation-range: entry 16% entry 62%;
		}

		.mf-bar:nth-child(3) {
			animation-range: entry 22% entry 68%;
		}

		.mf-bar:nth-child(4) {
			animation-range: entry 28% entry 74%;
		}
	}
}

/* A lift on hover. Uses transform, not the translate property: mf-rise animates
   translate and, with fill-mode both, its final value keeps winning over any
   normal declaration — so a hover written with translate would never apply. */
@media (prefers-reduced-motion: no-preference) {
	.mf-card {
		transition: transform .2s ease, border-color .2s ease;
	}

	.mf-card:hover {
		transform: translateY(-3px);
		border-color: var(--mf-frame-line);
	}
}

/* --------------------------------------------------------------------------
   21. Narrow viewports
   -------------------------------------------------------------------------- */

@media (max-width: 780px) {
	.mf-sec {
		padding-block: 72px;
	}

	.mf-sec--soft {
		padding-block: 72px;
	}

	.mf-hero {
		padding-block: 56px 0;
	}

	.mf-band {
		padding-block: 64px;
	}

	.mf-footer {
		padding-block: 56px 32px;
	}

	.mf-hero__visual {
		margin-top: 40px;
	}

	.mf-grid,
	.mf-bento {
		margin-top: 40px;
	}

	.mf-legal {
		margin-top: 40px;
	}

	.mf-bars,
	.mf-ai {
		margin-top: 40px;
	}

	.mf-ai__strip {
		margin-top: 52px;
		padding-top: 32px;
	}
}

@media (max-width: 420px) {
	:root {
		--mf-gutter: 20px;
	}

	.mf-card {
		padding: 24px;
	}

	.mf-bento .mf-card {
		padding: 26px;
	}

	.mf-chat {
		padding-block: 12px;
	}

	.mf-chat__bar,
	.mf-chat__status {
		padding-inline: 18px;
	}

	.mf-chat__slide {
		padding-inline: 12px;
	}
}

/* --------------------------------------------------------------------------
   22. Tiles (the rich card)
   --------------------------------------------------------------------------

   A category pill and a meta line sharing the top row, the title under them, a
   picture filling the middle, and an arrow button seated in a shaped cut in the
   bottom-right corner.

   The cut is the client's own shape, not an invention of ours. They pasted the
   markup for it: an 87×90 `btn_bg` hook — a bar across the top, a leg down the
   left, joined by a 16px concave arc — and a 51×48 button sitting in the crook,
   flush to the card's corner. Their reference renders it as a filled patch, and
   the first two attempts here cut the corner with a polygon and then with a
   circle whose centre was the card's corner, on the argument that a circle is
   the only curve tangent to both edges it meets. The client rejected both: the
   card's edge near the button should follow the button, not sweep past it. So
   the hook is used as-is — one path is both the mask that removes the corner and
   the stroke that outlines it, and both are scaled from `--mf-tile-go` by the
   same two ratios the 51×48 button glyph is scaled by. That is what keeps the
   crook kissing the button exactly as it does in their reference at any size.

   Two consequences drive the structure:

   - `mask-image` rather than `clip-path`, so the geometry can also be applied
     to the border. A box border cannot follow a curve, so the outline of the
     cut is stroked separately by .mf-tile__arc, from the same path the mask
     uses — the two are the same curve by construction.

   - The mask clips an element's descendants as well as its own background, so
     the arrow button and the arc are siblings of the body, not children. An
     arrow inside the masked body would be cut away by the very corner it is
     meant to occupy.

   Colour is the customisation axis. Every colour the tile uses is read from a
   `--mf-tile-*` token, so a tone modifier only redeclares the token set and
   nothing below has to know which tone is in play. `mf-tile--base` is the
   default and is written without a modifier, so an unclassed tile is the light
   one.

   The whole card is a single link. The arrow and any link label are
   decoration — one destination, one anchor, rather than two pointing at the
   same place.
   -------------------------------------------------------------------------- */

.mf-tile {
	position: relative;

	/* A grid row stretches every card to the tallest, so the wrapper is very
	   often taller than its own body. The body has to be told to fill it: left
	   as a plain block it stops at its content, and a card that is not the
	   tallest in its row leaves its cut corner, its arc and its arrow button
	   stranded in empty space below the card — which is exactly what happened
	   the first time a real photograph was dropped into one of these.

	   Safe against core: these groups are `is-layout-flow`, and the layout
	   style engine only forces `display` on `is-layout-flex` and
	   `is-layout-constrained`. */
	display: flex;
	flex-direction: column;

	/* Geometry. `--mf-tile-go` is the arrow button and everything at the corner
	   is measured from it: the hook is the client's 87×90 and their button is
	   51×48, so the two ratios below are what reproduce that shape at whatever
	   size the button is set to — including the kiss between the button's
	   top-left corner and the concave arc, which fixed lengths could not hold
	   once the button was resized.

	   The button sits flush in the card's corner, as it does in the reference,
	   so there is no inset token. */
	--mf-tile-go: 46px;
	--mf-tile-hook-w: calc(var(--mf-tile-go) * 87 / 51);
	--mf-tile-hook-h: calc(var(--mf-tile-go) * 90 / 48);
	--mf-tile-pad: 26px;
	--mf-tile-gap: 18px;

	/* Tone. */
	--mf-tile-bg: var(--mf-base);
	--mf-tile-line: var(--mf-line);
	--mf-tile-title: var(--mf-ink);
	--mf-tile-meta: var(--mf-muted);
	--mf-tile-more: var(--mf-body);
	--mf-tile-pill-bg: color-mix(in srgb, var(--mf-ink) 7%, transparent);
	--mf-tile-pill-fg: var(--mf-ink);
	--mf-tile-swatch: var(--mf-accent);
	--mf-tile-media-bg: var(--mf-frame);
	--mf-tile-media-line: var(--mf-frame-line);
	--mf-tile-media-label: var(--mf-placeholder);
	--mf-tile-go-bg: var(--mf-accent);
	--mf-tile-go-fg: var(--mf-base);

	/* What the button becomes on hover, and the timing it gets there in — the
	   reference's own .6s and cubic-bezier(.25, 1, .5, 1). The pair trades
	   colours with the card: the button takes the brand ink while the ink and
	   deep cards, which are already made of it, gain a button that joins them
	   with only the pale cut between. The accent card overrides the pair below,
	   for the same reason its resting arrow is inverted. */
	--mf-tile-go-bg-hover: var(--mf-ink);
	--mf-tile-go-fg-hover: var(--mf-base);
	--mf-tile-go-scale: .875;
	--mf-tile-dur: .6s;
	--mf-tile-ease: cubic-bezier(.25, 1, .5, 1);

	/* The arrow the two sliding copies are masked from — the same glyph the
	   markup draws, so the copies land exactly on the arrow they replace. */
	--mf-tile-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 51 48' preserveAspectRatio='none'%3E%3Cpath d='M13 24h24M29 16l8 8-8 8' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

.mf-tile__body {
	display: flex;
	flex-direction: column;
	/* Fills the wrapper, whatever height the row settles at — see the note on
	   .mf-tile. The media panel below is the item that absorbs the slack. */
	flex: 1;
	gap: var(--mf-tile-gap);
	min-height: 396px;
	padding: var(--mf-tile-pad);
	background: var(--mf-tile-bg);
	border: 1px solid var(--mf-tile-line);
	border-radius: var(--mf-r-xl);
	/* The cut replaces the radius on that one corner, so it is squared off here
	   and the curve is supplied by the mask. */
	border-bottom-right-radius: 0;

	/* Two layers, XOR-composited: the hook cut, opaque where the corner is
	   removed, over an opaque sheet the size of the element. `exclude` keeps
	   only what does not overlap, so the result is the sheet minus the cut.

	   Both layers are pinned — the cut to the bottom-right at the hook's own
	   size, the sheet to the whole element. An engine that ignores
	   `mask-composite` composites the two with `add` instead, which leaves the
	   card whole rather than masking it down to the cut alone; the arc below
	   then reads as a hairline on an intact corner, which is the graceful
	   failure rather than the destructive one.

	   The SVG's own box is the client's 87×90, and the element stretches it
	   (`preserveAspectRatio="none"`) to the size the ratios above work out.

	   The cut layer is 1px wider and taller than the hook and overhangs the
	   card's right and bottom edges by that 1px, with the path extended to the
	   extra edge. Without the overhang the cut's boundary lands exactly on the
	   element's own edge: rasterisation leaves the outermost pixel column about
	   a quarter opaque, and the card's navy shows through as a hairline down
	   the right side of the arrow button and along its bottom — visible on the
	   dark tiles. The extra pixel moves that boundary outside the card.

	   The viewBox is scaled up by the same 1px so the curve itself lands where
	   it always did: 87 × (W + 1) / W and 90 × (H + 1) / H, which reduces to
	   87 + 51/46 and 90 + 48/46. Only the outer edges of the removed region
	   move.

	   Not `position: relative` — the title's stretched link below is sized
	   against the nearest positioned ancestor, and that has to stay the
	   wrapper so the anchor covers the whole card. */
	-webkit-mask-image:
		url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 88.1087 91.0435' preserveAspectRatio='none'%3E%3Cpath fill='%23000' d='M88.1087 18H87C87 26.837 79.837 34 71 34H50.663A16 16 0 0 0 35.431 45.104L23.71 81.57A12.146 12.146 0 0 1 12.145 90V91.0435H88.1087Z'/%3E%3C/svg%3E"),
		linear-gradient(#000, #000);
	-webkit-mask-repeat: no-repeat, no-repeat;
	-webkit-mask-position: calc(100% + 1px) calc(100% + 1px), 0 0;
	-webkit-mask-size: calc(var(--mf-tile-hook-w) + 1px) calc(var(--mf-tile-hook-h) + 1px), 100% 100%;
	-webkit-mask-composite: xor;

	mask-image:
		url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 88.1087 91.0435' preserveAspectRatio='none'%3E%3Cpath fill='%23000' d='M88.1087 18H87C87 26.837 79.837 34 71 34H50.663A16 16 0 0 0 35.431 45.104L23.71 81.57A12.146 12.146 0 0 1 12.145 90V91.0435H88.1087Z'/%3E%3C/svg%3E"),
		linear-gradient(#000, #000);
	mask-repeat: no-repeat, no-repeat;
	mask-position: calc(100% + 1px) calc(100% + 1px), 0 0;
	mask-size: calc(var(--mf-tile-hook-w) + 1px) calc(var(--mf-tile-hook-h) + 1px), 100% 100%;
	mask-composite: exclude;
}

/* The outline of the cut, which the box border cannot follow. The same path the
   mask uses, drawn open: the mask closes it along the card's right and bottom
   edges, where the border already runs, so only the curve needs stroking.
   `non-scaling-stroke` keeps it at 1px through the non-uniform stretch. */
.mf-tile__arc {
	position: absolute;
	right: 0;
	bottom: 0;
	width: var(--mf-tile-hook-w);
	height: var(--mf-tile-hook-h);
	overflow: visible;
	pointer-events: none;
}

.mf-tile__arc path {
	fill: none;
	stroke: var(--mf-tile-line);
	stroke-width: 1;
	vector-effect: non-scaling-stroke;
}

.mf-tile__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px 16px;
	flex-wrap: wrap;
}

/* The swatch is a repeat of the colour cue the pill already carries in its
   text, so it is drawn rather than marked up. */
.mf-tile__pill {
	display: inline-flex;
	align-items: center;
	gap: 9px;
	padding: 7px 13px;
	border-radius: 9px;
	background: var(--mf-tile-pill-bg);
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: .11em;
	text-transform: uppercase;
	color: var(--mf-tile-pill-fg);
}

.mf-tile__pill::before {
	content: "";
	flex: none;
	width: 11px;
	height: 11px;
	border-radius: 2px;
	background: var(--mf-tile-swatch);
}

.mf-tile__meta {
	font-size: 11.5px;
	font-weight: 500;
	letter-spacing: .11em;
	text-transform: uppercase;
	color: var(--mf-tile-meta);
}

.mf-tile__title {
	margin: 0;
	font-size: 21px;
	line-height: 1.36;
	letter-spacing: -.01em;
	color: var(--mf-tile-title);
}

/* Stretches the title's anchor over the whole wrapper, so the card is
   clickable everywhere outside the cut. */
.mf-tile__title a::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
}

/* The picture. Deliberately the same primitive as .mf-frame — a placeholder
   label centred in a tinted panel that a real image replaces outright — so a
   tile with no photograph yet looks the same as every other unshot slot on the
   site rather than like a broken card. Drop a wp:image block in here and it
   fills the panel cover-cropped with no further change.

   The panel owns its shape rather than taking it from the photograph. Left to
   size itself it takes the image's own height, so a portrait upload makes one
   card half again as tall as its neighbours and drags the whole row with it —
   a 320x480 photo produced a 670px row. With a fixed ratio the window is the
   same in every card and whatever is dropped in is cropped to it, which is
   what makes three independently-edited cards stay the same size.

   `flex: 1 1 auto`, not `flex: 1`. The shorthand's `1` resolves the basis to
   0, which would throw the ratio away and let the panel take the whole of the
   body's slack; `auto` keeps the ratio as the starting height and still lets
   the panel grow into any height a taller sibling forces on the row. */
.mf-tile__media {
	position: relative;
	overflow: hidden;
	display: grid;
	place-items: center;
	aspect-ratio: 4 / 3;
	flex: 1 1 auto;
	min-height: 132px;
	background: var(--mf-tile-media-bg);
	border: 1px solid var(--mf-tile-media-line);
	border-radius: var(--mf-r);
}

/* Absolutely positioned, not a grid item. As a centred grid item the track is
   sized by the image itself, so `height: 100%` resolves against an indefinite
   track, falls back to the intrinsic height, and `object-fit` never runs — the
   photo was simply overhanging the panel and being clipped by it. Which part
   you saw then depended on the alignment rather than on the crop. Pinning it to
   the panel's own box is what makes cover-crop actually happen. */
.mf-tile__media img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.mf-tile__media .mf-ph {
	margin: 0;
	padding: 14px;
	font-size: 12px;
	font-weight: 500;
	letter-spacing: .04em;
	text-align: center;
	color: var(--mf-tile-media-label);
}

/* Once a real image lands, the label is redundant even if it was left behind. */
.mf-tile__media:has(img) .mf-ph {
	display: none;
}

.mf-tile__more {
	margin-top: auto;
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: .11em;
	text-transform: uppercase;
	color: var(--mf-tile-more);
	/* The cut reaches the full width of the hook along the bottom edge, so the
	   label clears the whole of it rather than a fraction. */
	padding-right: var(--mf-tile-hook-w);
}

/* The arrow button, drawn from the reference markup: a rounded square whose
   lower-left corner is sheared off. Not mirrored — the shear is what runs
   parallel to the hook's slanted leg, and mirroring it would put the shear on
   the card's corner, which the reference rounds. */
.mf-tile__go {
	position: absolute;
	right: 0;
	bottom: 0;
	width: var(--mf-tile-go);
	height: var(--mf-tile-go);
	/* Clips the two arrows below. Each is the full size of the button so that the
	   glyph inside it lands where the markup's own arrow does, and the one
	   waiting off to the left would otherwise be painted over the card. */
	overflow: hidden;
	pointer-events: none;
}

.mf-tile__go svg {
	display: block;
	width: 100%;
	height: 100%;
}

.mf-tile__go-shape {
	fill: var(--mf-tile-go-bg);
}

.mf-tile__go-arrow {
	fill: none;
	stroke: var(--mf-tile-go-fg);
	stroke-width: 2.4;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* The hover carries two arrows, not one — the reference's own arrangement, and
   the reason it works: a transition interpolates a single value, so one arrow
   can only ever travel one way, and the effect is an arrow leaving to the right
   while an identical one arrives from the left. Two copies of the same masked
   glyph do it, the second parked a box-width off to the left where the button's
   own overflow hides it.

   The markup's own arrow is suppressed here rather than removed, so a browser
   without masks keeps a single static arrow instead of losing the button. */
@supports (mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E")) {
	.mf-tile__go-arrow {
		display: none;
	}

	.mf-tile__go::before,
	.mf-tile__go::after {
		content: "";
		position: absolute;
		inset: 0;
		background-color: var(--mf-tile-go-fg);
		-webkit-mask-image: var(--mf-tile-arrow);
		-webkit-mask-repeat: no-repeat;
		-webkit-mask-position: 0 0;
		-webkit-mask-size: 100% 100%;
		mask-image: var(--mf-tile-arrow);
		mask-repeat: no-repeat;
		mask-position: 0 0;
		mask-size: 100% 100%;
	}

	.mf-tile__go::after {
		transform: translateX(calc(-1 * var(--mf-tile-go) - 10px));
	}
}

@media (prefers-reduced-motion: no-preference) {

	.mf-tile__body,
	.mf-tile__arc,
	.mf-tile__go {
		transition: transform var(--mf-tile-dur) var(--mf-tile-ease);
	}

	/* transform, not the translate property: mf-rise animates translate with
	   fill-mode both, and its final value would beat a hover written that way.

	   The arc and the button are siblings of the body rather than children of
	   it, so they have to be lifted along with it or the whole corner treatment
	   detaches from the card on hover. The lift carries the mask with it, so the
	   cut moves; the button makes the same move and then the reference's own:
	   it shrinks to .875 about its centre, settling into the crook with air
	   opening around it rather than sliding out of the shape that holds it. */
	.mf-tile:hover .mf-tile__body,
	.mf-tile:hover .mf-tile__arc {
		transform: translateY(-3px);
	}

	.mf-tile:hover .mf-tile__go {
		transform: translateY(-3px) scale(var(--mf-tile-go-scale));
	}

	.mf-tile__go-shape {
		transition: fill var(--mf-tile-dur) var(--mf-tile-ease);
	}

	.mf-tile:hover .mf-tile__go-shape {
		fill: var(--mf-tile-go-bg-hover);
	}

	.mf-tile__go::before,
	.mf-tile__go::after {
		transition:
			transform var(--mf-tile-dur) var(--mf-tile-ease),
			background-color var(--mf-tile-dur) var(--mf-tile-ease);
	}

	.mf-tile:hover .mf-tile__go::before {
		transform: translateX(calc(var(--mf-tile-go) + 10px));
	}

	.mf-tile:hover .mf-tile__go::after {
		transform: translateX(0);
	}

	.mf-tile:hover .mf-tile__go::before,
	.mf-tile:hover .mf-tile__go::after {
		background-color: var(--mf-tile-go-fg-hover);
	}
}

/* --- tones --- */

/* Ink: the brand navy, which the first card carries. */
.mf-tile--ink {
	--mf-tile-bg: var(--mf-ink);
	--mf-tile-line: color-mix(in srgb, var(--mf-base) 16%, transparent);
	--mf-tile-title: var(--mf-base);
	--mf-tile-meta: color-mix(in srgb, var(--mf-base) 62%, transparent);
	--mf-tile-more: var(--mf-on-dark);
	--mf-tile-pill-bg: color-mix(in srgb, var(--mf-base) 13%, transparent);
	--mf-tile-pill-fg: var(--mf-base);
	--mf-tile-media-bg: color-mix(in srgb, var(--mf-base) 8%, transparent);
	--mf-tile-media-line: color-mix(in srgb, var(--mf-base) 14%, transparent);
	--mf-tile-media-label: color-mix(in srgb, var(--mf-base) 46%, transparent);
}

/* Deep: the same navy carried down, so the set reads as a ramp rather than
   three unrelated colours. */
.mf-tile--deep {
	--mf-tile-bg: color-mix(in srgb, var(--mf-ink) 62%, #061020);
	--mf-tile-line: color-mix(in srgb, var(--mf-base) 16%, transparent);
	--mf-tile-title: var(--mf-base);
	--mf-tile-meta: color-mix(in srgb, var(--mf-base) 62%, transparent);
	--mf-tile-more: var(--mf-on-dark);
	--mf-tile-pill-bg: color-mix(in srgb, var(--mf-base) 13%, transparent);
	--mf-tile-pill-fg: var(--mf-base);
	--mf-tile-media-bg: color-mix(in srgb, var(--mf-base) 8%, transparent);
	--mf-tile-media-line: color-mix(in srgb, var(--mf-base) 14%, transparent);
	--mf-tile-media-label: color-mix(in srgb, var(--mf-base) 46%, transparent);
}

/* Accent: the fourth step in the ramp, kept as the proof the tone modifier is
   a real axis. Not used by the industries section. The arrow inverts here — an
   accent button on an accent card would disappear. */
.mf-tile--accent {
	--mf-tile-bg: var(--mf-accent);
	--mf-tile-line: color-mix(in srgb, var(--mf-base) 24%, transparent);
	--mf-tile-title: var(--mf-base);
	--mf-tile-meta: color-mix(in srgb, var(--mf-base) 72%, transparent);
	--mf-tile-more: color-mix(in srgb, var(--mf-base) 82%, transparent);
	--mf-tile-pill-bg: color-mix(in srgb, var(--mf-base) 18%, transparent);
	--mf-tile-pill-fg: var(--mf-base);
	--mf-tile-swatch: var(--mf-base);
	--mf-tile-media-bg: color-mix(in srgb, var(--mf-base) 12%, transparent);
	--mf-tile-media-line: color-mix(in srgb, var(--mf-base) 20%, transparent);
	--mf-tile-media-label: color-mix(in srgb, var(--mf-base) 62%, transparent);
	--mf-tile-go-bg: var(--mf-base);
	--mf-tile-go-fg: var(--mf-accent-dk);
	/* The hover pair turns over too: the button cannot take the ink here without
	   disappearing into the ink's own family of colours, so it goes the other
	   way, to the darker accent, and its arrow inverts with it. */
	--mf-tile-go-bg-hover: var(--mf-accent-dk);
	--mf-tile-go-fg-hover: var(--mf-base);
}

@media (max-width: 420px) {
	.mf-tile {
		/* The hook follows the button through the ratios above; only the button
		   itself has to be named here. */
		--mf-tile-go: 40px;
		--mf-tile-pad: 22px;
		--mf-tile-gap: 15px;
	}

	.mf-tile__body {
		min-height: 340px;
	}

	.mf-tile__title {
		font-size: 19px;
	}
}

/* --------------------------------------------------------------------------
   23. Solutions page
   --------------------------------------------------------------------------
   Rendered through templates/page-solutions.html, which is the page template
   the block hierarchy picks for the /solutions/ slug. That template renders
   post-content straight into <main>, the same way front-page.html does, so the
   sections here can be full-bleed bands like the homepage rather than being
   nested inside another padded wrapper.
   -------------------------------------------------------------------------- */

.mf-sol__hero {
	padding-block: 80px 56px;
}

.mf-sol__hero h1 {
	max-width: 22ch;
}

/* The submenu. Sticky directly under the header so the four areas stay
   reachable down a long page, and scrolling sideways rather than wrapping on a
   phone — four labels do not fit 390px in one row. */
.mf-sol__nav {
	position: sticky;
	top: var(--mf-header-h);
	z-index: 20;
	padding-inline: var(--mf-gutter);
	background: color-mix(in srgb, var(--mf-base) 94%, transparent);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
	border-bottom: 1px solid var(--mf-line);
}

.mf-sol__nav-inner {
	display: flex;
	gap: 8px;
	overflow-x: auto;
	scrollbar-width: none;
	padding-block: 12px;
	max-width: 1180px;
	margin-inline: auto;
}

.mf-sol__nav-inner::-webkit-scrollbar {
	display: none;
}

.mf-sol__nav-inner a {
	flex: none;
	padding: 9px 16px;
	border: 1px solid var(--mf-line);
	border-radius: 999px;
	font-size: 14.5px;
	font-weight: 500;
	color: var(--mf-body);
	white-space: nowrap;
	transition: border-color .15s ease, color .15s ease;
}

.mf-sol__nav-inner a:hover {
	border-color: var(--mf-ink);
	color: var(--mf-ink);
}

/* The sticky submenu is about 50px tall, so an anchor landing needs more
   clearance than the plain --mf-header-h the global :target rule allows. */
.mf-sol__area {
	scroll-margin-top: calc(var(--mf-header-h) + 66px);
}

.mf-sol__head {
	max-width: 700px;
}

.mf-sol__cols {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 30px 48px;
	margin-top: 48px;
}

.mf-sol__item {
	padding-top: 22px;
	border-top: 1px solid var(--mf-line);
}

.mf-sol__col--soft .mf-sol__item {
	border-top-color: var(--mf-frame-line);
}

.mf-sol__item h3 {
	margin: 0 0 8px;
	font-size: var(--wp--preset--font-size--heading-3);
	color: var(--mf-ink);
}

.mf-sol__item p {
	font-size: var(--wp--preset--font-size--text-sm);
	color: var(--mf-body);
}

/* On the dark band the hairlines and the muted meta have to change or they
   vanish into the background. */
.mf-sec--ink .mf-sol__item {
	border-top-color: color-mix(in srgb, var(--mf-base) 22%, transparent);
}

.mf-sec--ink .mf-sol__item h3 {
	color: var(--mf-base);
}

.mf-sec--ink .mf-sol__item p {
	color: var(--mf-on-dark);
}

.mf-sec--ink .mf-sol__head .mf-lede {
	color: var(--mf-on-dark);
}

@container (max-width: 760px) {
	.mf-sol__cols {
		grid-template-columns: 1fr;
		gap: 26px;
	}
}

/* --------------------------------------------------------------------------
   24. Content pages
   --------------------------------------------------------------------------
   The thirty pages built from the content catalog reuse the solutions-page
   components wholesale — .mf-sol__hero, .mf-sol__nav, .mf-sol__area,
   .mf-sol__head, .mf-sol__cols and .mf-sol__item carry the whole page body —
   so this section is only the handful of things that page shape needs and the
   theme had no answer for. Everything here is additive: no rule in §1–§23 is
   modified, and these selectors are new names.

   Rendered through templates/page-landing.html, which the block hierarchy
   picks up from each page's `_wp_page_template` meta.
   -------------------------------------------------------------------------- */

/* The onwards link that closes a feature item — "Learn more", "See AI
   planning in full". There is no inline arrow-link in the theme to borrow:
   .mf-bar__link is scoped inside the impact band and would drag the band's
   tokens with it.

   The class goes on the *paragraph* and the anchor inside it carries the
   styling, so the arrow is a pseudo-element of the link and the whole label
   plus its arrow is one hit target. Written the other way round — class on the
   anchor — the arrow would sit outside the link and vertical margins on an
   inline-level box would not push the line box down. */
.mf-more {
	margin-top: 12px;
	font-size: var(--wp--preset--font-size--text-sm);
	font-weight: 600;
}

.mf-more a {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	color: var(--mf-accent-dk);
}

.mf-more a::after {
	content: "\2192";
	transition: transform .15s ease;
}

.mf-more a:hover {
	color: var(--mf-ink);
}

.mf-more a:hover::after {
	transform: translateX(3px);
}

.mf-sec--ink .mf-more a {
	color: var(--mf-accent);
}

.mf-sec--ink .mf-more a:hover {
	color: var(--mf-base);
}

@media (prefers-reduced-motion: reduce) {
	.mf-more a::after {
		transition: none;
	}

	.mf-more a:hover::after {
		transform: none;
	}
}

/* Breadcrumbs. Rendered by the [moonfoos_breadcrumbs] shortcode, which the
   page template places above post-content so it sits ahead of the first band.
   It therefore owns both its gutter and its 1180px column — the sections it
   precedes each carry their own, and nothing here is inside a .mf-cq. */
.mf-crumb {
	padding: 20px var(--mf-gutter) 0;
	font-size: var(--wp--preset--font-size--text-sm);
	color: var(--mf-muted);
}

.mf-crumb__inner {
	max-width: 1180px;
	margin-inline: auto;
}

.mf-crumb a {
	color: var(--mf-body);
}

.mf-crumb a:hover {
	color: var(--mf-ink);
	text-decoration: underline;
}

.mf-crumb__sep {
	margin-inline: 9px;
	opacity: .5;
}

.mf-crumb__here {
	color: var(--mf-ink);
	font-weight: 500;
}

/* FAQ. Core ships the details block but no styling for it, and the theme has
   no disclosure component to reuse, so the marker, the rhythm and the open
   state are all declared here. `display: flex` on the summary is what lets the
   marker sit hard right regardless of how many lines the question runs to. */
.mf-faq {
	margin-top: 48px;
	max-width: 880px;
}

.mf-faq details {
	border-top: 1px solid var(--mf-line);
}

.mf-faq details:last-of-type {
	border-bottom: 1px solid var(--mf-line);
}

.mf-faq summary {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 28px;
	padding: 20px 0;
	font-size: var(--wp--preset--font-size--heading-3);
	font-weight: 600;
	color: var(--mf-ink);
	cursor: pointer;
	list-style: none;
}

/* The native triangle. WebKit needs the pseudo-element, Gecko the property. */
.mf-faq summary::-webkit-details-marker {
	display: none;
}

.mf-faq summary::after {
	content: "+";
	flex: none;
	font-size: 22px;
	font-weight: 400;
	line-height: 1;
	color: var(--mf-accent-dk);
	transition: transform .18s ease;
}

.mf-faq details[open] summary::after {
	transform: rotate(45deg);
}

/* Everything that is not the summary, so a wrapped answer paragraph or a list
   is styled without needing to know which core block produced it. */
.mf-faq details>*:not(summary) {
	margin: 0 0 22px;
	max-width: 74ch;
	font-size: var(--wp--preset--font-size--text-sm);
	color: var(--mf-body);
}

.mf-faq details>p:last-child {
	margin-bottom: 24px;
}

@media (prefers-reduced-motion: reduce) {
	.mf-faq summary::after {
		transition: none;
	}

	.mf-faq details[open] summary::after {
		transform: none;
	}
}

/* The "Goes well with" band at the foot of a module page. Pills rather than
   cards, and deliberately the same treatment as the sticky submenu's pills —
   both are onward routes, not content, and a card would out-weigh the section
   it follows. */
.mf-rel {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin-top: 32px;
}

.mf-rel a {
	padding: 10px 18px;
	border: 1px solid var(--mf-line);
	border-radius: 999px;
	font-size: 14.5px;
	font-weight: 500;
	color: var(--mf-body);
	transition: border-color .15s ease, color .15s ease;
}

.mf-rel a:hover {
	border-color: var(--mf-ink);
	color: var(--mf-ink);
}

/* The topic currently being filtered, on the archive. Marked with aria-current
   by the chips shortcode and styled like the hover state, which is the only
   "selected" treatment this component has. */
.mf-rel a[aria-current] {
	border-color: var(--mf-ink);
	color: var(--mf-ink);
}

/* The row is flex, so it starts at the left edge — which is right under a
   left-aligned heading and wrong inside the closing band, where everything
   around it is centred. */
.mf-band .mf-rel {
	justify-content: center;
}

/* The comparison matrix on /compare/. Nothing in the theme styles a table —
   core's block-library table rules are not enqueued for a hand-written block
   theme beyond `wp-block-styles`, and that only covers the block's own
   borders. Scrolled horizontally rather than squeezed, because four columns of
   prose do not survive a 390px viewport. */
.mf-cmp {
	margin-top: 48px;
	overflow-x: auto;
}

.mf-cmp table {
	width: 100%;
	min-width: 720px;
	border-collapse: collapse;
	font-size: var(--wp--preset--font-size--text-sm);
}

.mf-cmp :is(th, td) {
	padding: 16px 18px;
	text-align: left;
	vertical-align: top;
	border-top: 1px solid var(--mf-line);
}

.mf-cmp thead th {
	padding-top: 0;
	border-top: 0;
	border-bottom: 1px solid var(--mf-ink);
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: .06em;
	color: var(--mf-muted);
}

.mf-cmp tbody th {
	font-weight: 600;
	color: var(--mf-ink);
}

.mf-cmp td {
	color: var(--mf-body);
}

/* Moonfoos is always the last column, and tinting it is the whole point of
   the table — without it the eye has to count columns to find the answer. */
.mf-cmp :is(th, td):last-child {
	background: var(--mf-soft);
}

/* Placeholders for content the catalog flags [VERIFY] and nobody has supplied
   yet — customer logos, aggregate stats, case-study metrics, quotes, the
   integration list, the pricing model. They ship *visible* on purpose, so the
   client can see exactly what is missing and the page doubles as the brief;
   the body names the flag code from the catalog's own register.

   The dashed outline is what stops one being mistaken for finished copy. To
   hide the whole set at once, return false from the `moonfoos_show_placeholders`
   filter — see functions.php. */
.mf-todo {
	margin-top: 40px;
	padding: 22px 24px;
	border: 1px dashed var(--mf-accent);
	border-radius: var(--mf-r);
	background: color-mix(in srgb, var(--mf-accent-soft) 45%, var(--mf-base));
}

.mf-todo p {
	margin: 0;
	font-size: var(--wp--preset--font-size--text-sm);
	color: var(--mf-body);
}

.mf-todo p+p {
	margin-top: 8px;
}

/* (0,2,0) and after the rule above, so the tag keeps its own colour and
   spacing instead of losing both to the paragraph rule on specificity. */
.mf-todo .mf-todo__tag {
	margin: 0 0 10px;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: .06em;
	color: var(--mf-accent-dk);
}

/* On a narrow viewport the comparison table's min-width would push the page
   sideways rather than scrolling inside the wrapper, so the wrapper is told it
   may shrink. */
@container (max-width: 760px) {
	.mf-cmp {
		margin-inline: calc(var(--mf-gutter) * -1);
		padding-inline: var(--mf-gutter);
	}
}

/* The article body on a guide. Every other page in this theme is built from
   mf-sol__* items — headline, two-line description, link — so there has been no
   long-form prose anywhere to style. The measure comes from the template's own
   constrained layout (720px); what is here is the two things prose needs that
   the items do not.

   First, inline links. theme.json sets elements.link to currentColor with no
   underline, which is right for the card, nav and footer links the rest of the
   site is made of, and wrong inside a paragraph: a link in running text
   distinguished from its neighbours by colour alone fails WCAG 1.4.1, and on
   this palette the accent sits close enough to the body colour to read as
   emphasis rather than as something clickable. So links in an article get the
   underline back — and only here, so nothing about the existing components
   changes.

   Second, the space above a heading. Core's block gap (24px) is a paragraph
   rhythm; a section heading needs more than the paragraph before it to read as
   a section. Only the top margin is touched, and the first heading in the body
   is exempt, because the section above it already carries 104px of padding. */
.mf-article a {
	color: var(--mf-accent-dk);
	text-decoration: underline;
	text-underline-offset: 2px;
	text-decoration-thickness: 1px;
}

.mf-article a:hover {
	color: var(--mf-ink);
}

.mf-article>h2 {
	margin-block-start: 56px;
}

.mf-article>h3 {
	margin-block-start: 40px;
}

.mf-article> :is(h2, h3):first-child {
	margin-block-start: 0;
}

/* --------------------------------------------------------------------------
   25. Surfacia page
   --------------------------------------------------------------------------
   The /surfacia/ page. It reuses §23 and §24 wholesale — .mf-sol__hero,
   .mf-sol__nav, .mf-sol__area, .mf-sol__head, .mf-sol__cols, .mf-sol__item,
   .mf-card, .mf-more, .mf-rel and .mf-todo carry most of it — so what is here is
   only what this page needs and the theme had no answer for. Additive again: no
   rule in §1–§24 is modified and every selector is a new name.

   THE TWO BRAND COLOURS. Surfacia is a different product with its own mark —
   a yellow square over a slate bar — and its palette is neither of Moonfoos's
   (ink #2A4979, accent #DE502A). They are declared here rather than added to
   theme.json on purpose: theme.json's palette is offered to the editor on
   *every* page, so putting a second product's brand into it would put Surfacia's
   yellow in the colour picker for Moonfoos's own homepage. The two variables are
   set on .mf-main, which every page template renders, but nothing outside this
   section consumes them, so they are inert elsewhere.

   The yellow is never used for text. On white it fails contrast by a wide
   margin, so it carries weight instead — as the 3px rule that opens a claim or
   a feature block, and as a filled marker in the proof lists. The slate is used
   for small marks, and neither is used inside .mf-sec--ink, where the slate
   would disappear and yellow text would be the only legible option but the
   wrong one; the dark bands are carried by the existing ink overrides in §23.

   WHY THE FIGURES PRINT AS DASHES. This page could not have charts. The content
   pack supplies no worked example, and a marketing page that invents the
   numbers in its own product's screenshots is worse than one that shows none.
   So .mf-ba, .mf-ladder and .mf-cal are built structurally — from the pack's own
   description of each figure — and every amount renders as an em dash. That
   reads as unfinished only until you meet the feature it illustrates: "a figure
   with no price prints as a dash, never as a zero, so nothing unpriced can hide
   as free". The dash is the product's own behaviour, captioned on the page.
   -------------------------------------------------------------------------- */

.mf-main {
	--mf-surf: #FFCC07;
	--mf-surf-ink: #353D47;
}

/* The wordmark in the hero. The lockup is a 2000x309 PNG — a raster, because
   the client supplied the vector only for the mark without the wordmark, and
   redrawing a wordmark in a near-miss typeface would be worse than a raster at
   420px. The mark alone is the SVG, in a format that needed no conversion. */
.mf-sur__logo {
	margin: 0 0 28px;
}

.mf-sur__logo img {
	width: 420px;
	max-width: 78%;
	height: auto;
}

/* The four trust claims under the hero. .mf-claim is .mf-sol__item's argument —
   a hairline and a label — with the brand rule in place of the hairline and the
   label doing the work of a heading, so there is no heading block to mis-level. */
.mf-claims {
	margin-top: 64px;
}

.mf-claim {
	padding-top: 20px;
	border-top: 3px solid var(--mf-surf);
}

.mf-claim p {
	margin: 0;
	font-size: var(--wp--preset--font-size--text-sm);
	font-weight: 500;
	color: var(--mf-ink);
}

/* The promise band. One sentence, set at the stat size, which is the largest
   step theme.json has below the H1 — it is the page's thesis and is deliberately
   louder than the H2s around it. */
.mf-promise {
	font-size: var(--wp--preset--font-size--stat);
	max-width: 26ch;
}

/* The deep-dive feature blocks. Two columns, because twelve of these stacked in
   one would make a page nobody scrolls to the end of, and the prose is short
   enough to hold a 560px measure. */
.mf-feats {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 56px;
	margin-top: 56px;
}

.mf-feat {
	padding-top: 26px;
	border-top: 3px solid var(--mf-surf);
}

.mf-feat__num {
	margin: 0 0 10px;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	letter-spacing: .09em;
	color: var(--mf-muted);
}

.mf-feat h3 {
	margin: 0 0 10px;
}

/* The hook. It is the pack's own one-line argument for the feature and reads as
   a pull-quote, so it is set apart from the explanatory paragraphs below it
   without being promoted to a heading — it is not a level in the outline. */
.mf-hook {
	margin-top: 0;
	font-size: var(--wp--preset--font-size--text-sm);
	font-style: italic;
	color: var(--mf-ink);
}

.mf-feat p {
	font-size: var(--wp--preset--font-size--text-sm);
	color: var(--mf-body);
}

.mf-feat code {
	padding: 1px 5px;
	border: 1px solid var(--mf-line);
	border-radius: 4px;
	font-size: .92em;
	color: var(--mf-ink);
}

/* "Best for / sells to" — the pack's audience note. Small and muted, because it
   is a note to the sales team reading the page, not a claim to the buyer. */
.mf-note {
	margin-top: 20px;
	font-size: var(--wp--preset--font-size--eyebrow);
	color: var(--mf-muted);
}

.mf-note em {
	color: var(--mf-accent-dk);
}

/* The proof-point lists. Two-border bullets would fight the 3px brand rules on
   the blocks above, so the marker is the brand's own shape — a square, never a
   circle, which is the same correction the tile corner carries. */
.mf-proof {
	margin: 22px 0 0;
	padding: 0;
	list-style: none;
}

.mf-proof li {
	position: relative;
	padding-inline-start: 24px;
	font-size: var(--wp--preset--font-size--text-sm);
	color: var(--mf-body);
}

.mf-proof li+li {
	margin-top: 11px;
}

.mf-proof li::before {
	content: "";
	position: absolute;
	inset-inline-start: 2px;
	top: .62em;
	width: 7px;
	height: 7px;
	background: var(--mf-surf);
}

.mf-proof code {
	padding: 1px 5px;
	border: 1px solid var(--mf-line);
	border-radius: 4px;
	font-size: .92em;
	color: var(--mf-ink);
}

/* The buyer motivations sit full-width under a four-column audience grid, and
   each item is two clauses long — so it takes a measure of its own. */
.mf-pull {
	max-width: 900px;
	margin-top: 30px;
}

.mf-pull li strong {
	color: var(--mf-ink);
}

/* A caption under a figure. Distinct from .mf-note: this one describes the
   drawing above it, so it is muted and deliberately part of the figure. */
.mf-cap {
	margin-top: 26px;
	max-width: 74ch;
	font-size: var(--wp--preset--font-size--text-sm);
	color: var(--mf-muted);
}

/* Deep-dive A — the spreadsheet-versus-traced comparison. Two panels, the traced
   one carrying the brand rule across its top so the eye knows which side wins
   before it has read either. Both are structurally identical rows; only the
   number of them and the em dashes differ, which is the whole argument. */
.mf-ba {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 26px;
	margin-top: 56px;
}

.mf-ba__col {
	padding: 28px;
	border: 1px solid var(--mf-line);
	border-radius: var(--mf-r);
	background: var(--mf-base);
}

.mf-ba__col--trace {
	border-color: var(--mf-frame-line);
	box-shadow: inset 0 3px 0 var(--mf-surf);
}

.mf-ba__cap {
	margin: 0 0 18px;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--mf-muted);
}

.mf-ba__col--trace .mf-ba__cap {
	color: var(--mf-ink);
}

.mf-ba__row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 18px;
	padding: 11px 0;
	border-top: 1px solid var(--mf-line);
	font-size: var(--wp--preset--font-size--text-sm);
}

.mf-ba__k {
	color: var(--mf-body);
}

/* Tabular so the dashes stack into a column rather than drifting with the
   label lengths above them. */
.mf-ba__v {
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	color: var(--mf-muted);
}

.mf-ba__note {
	margin: 18px 0 0;
	font-size: var(--wp--preset--font-size--text-sm);
	color: var(--mf-muted);
}

.mf-ba__col--trace .mf-ba__note {
	color: var(--mf-body);
}

/* Deep-dive B — the cost-to-selling ladder. The bars are deliberately hatched
   rather than filled to a length: a filled bar of arbitrary width is a data
   claim, and this page has no data to make one with. The hatched bar says "a
   rung, priced in your numbers" and nothing more. The base rung is solid,
   because it is the only rung that exists before any markup is applied. */
.mf-ladder {
	margin-top: 56px;
	padding: 28px;
	border: 1px solid var(--mf-line);
	border-radius: var(--mf-r);
	background: var(--mf-base);
}

.mf-ladder__cap {
	margin: 0 0 18px;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--mf-muted);
}

.mf-ladder__row {
	display: grid;
	grid-template-columns: 150px 1fr 56px;
	align-items: center;
	gap: 18px;
	padding: 9px 0;
}

.mf-ladder__k {
	font-size: var(--wp--preset--font-size--text-sm);
	font-weight: 500;
	color: var(--mf-body);
}

/* Each rung is clipped to an inline `--w`, so the bar climbs from the real cost to
   the selling price instead of drawing six identical bricks. The widths are
   illustrative shape, exactly like .mf-cal's month heights, and captioned as such;
   the figures beside them stay em dashes. background-size does the clipping, which
   keeps the hatch pattern anchored to the left edge rather than stretched. */
.mf-ladder__bar {
	height: 10px;
	border: 1px solid var(--mf-frame-line);
	background-color: color-mix(in srgb, var(--mf-ink) 8%, transparent);
	background-image: repeating-linear-gradient(135deg,
			color-mix(in srgb, var(--mf-surf) 70%, transparent) 0 5px,
			transparent 5px 10px);
	background-repeat: no-repeat;
	background-size: var(--w, 100%) 100%;
}

.mf-ladder__row--base .mf-ladder__bar {
	border-color: var(--mf-ink);
	background-image: linear-gradient(var(--mf-ink), var(--mf-ink));
	background-size: var(--w, 100%) 100%;
}

.mf-ladder__v {
	text-align: right;
	font-size: var(--wp--preset--font-size--text-sm);
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	color: var(--mf-muted);
}

/* Deep-dive C — the two calendars. This is the one figure on the page that can
   honestly be drawn rather than tabled, because its whole content is a *shape*:
   the lumpy month and the flat one. The heights are decorative and captioned as
   such; they are not anyone's data. */
.mf-cal {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 26px;
	margin-top: 56px;
}

.mf-cal__col {
	padding: 28px;
	border: 1px solid var(--mf-line);
	border-radius: var(--mf-r);
	background: var(--mf-base);
}

.mf-cal__col:last-child {
	border-color: var(--mf-frame-line);
	box-shadow: inset 0 3px 0 var(--mf-surf);
}

.mf-cal__cap {
	margin: 0 0 22px;
	font-size: var(--wp--preset--font-size--eyebrow);
	font-weight: 600;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--mf-muted);
}

.mf-cal__bars {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	align-items: end;
	gap: 6px;
	height: 150px;
}

.mf-cal__bars span {
	display: block;
	height: var(--h, 50%);
	border-radius: 3px 3px 0 0;
	background: var(--mf-ink);
}

.mf-cal__bars--flat span {
	background: var(--mf-surf);
}

.mf-cal__axis {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: 6px;
	margin-top: 10px;
	font-size: 11px;
	text-align: center;
	color: var(--mf-placeholder);
}

@container (max-width: 940px) {
	.mf-feats {
		grid-template-columns: 1fr;
		gap: 44px;
	}
}

@container (max-width: 760px) {

	.mf-ba,
	.mf-cal {
		grid-template-columns: 1fr;
	}
}

/* The ladder's label column is fixed at 150px, which at 390px leaves the bar
   about 90px wide — narrow enough that the hatch reads as a texture rather than
   a rung. The label moves above the bar instead.
   Rows are pinned explicitly: the bar spans the full width and comes before the
   value in source order, so with sparse auto-placement the value is pushed past
   the bar onto a row of its own and the dash drifts below the rung. */
@media (max-width: 560px) {
	.mf-ladder__row {
		grid-template-columns: 1fr auto;
		gap: 6px 14px;
	}

	.mf-ladder__k {
		grid-column: 1;
		grid-row: 1;
	}

	.mf-ladder__v {
		grid-column: 2;
		grid-row: 1;
	}

	.mf-ladder__bar {
		grid-column: 1 / -1;
		grid-row: 2;
	}
}

/* The six "what it does" cards on the concise /surfacia/ page reuse .mf-card,
   but .mf-card__icon carries Moonfoos's orange — the wrong product's accent on
   the wrong page, and it shows against the yellow rules everywhere else in the
   hero above it. Scoped by a new wrapper class rather than by editing §11, so
   the Moonfoos pages keep that rule exactly as it was.
   The tile becomes a tint of Surfacia's yellow and the glyph its slate: yellow
   is a surface here, never a stroke, which is the same rule the rest of §25
   follows — on white it fails contrast by a wide margin. */
.mf-sur__does .mf-card__icon {
	background: color-mix(in srgb, var(--mf-surf) 26%, transparent);
}

.mf-sur__does .mf-card__icon svg {
	stroke: var(--mf-surf-ink);
}
/* --------------------------------------------------------------------------
   26. Hero animation stage
   --------------------------------------------------------------------------

   The product stage shared by the three app heroes: a grid of ghosted UI
   cards (five columns on Moonschat, three on the homepage and Surfacia),
   deliberately wider than the stage so the outer columns bleed off both edges
   and are clipped, with that app's Lottie in the centre.
   Edge fades melt the bleed into the page. The idea is Pigment's
   `.c_hero_animation`; the cards, the animation and the palette are ours.

   Each app supplies its own `--mf-hero-stage-*` values. The Lottie files already
   carry the product palette — these only have to agree at the edges: the glow
   when a ghost card lights up, the phone's shadow, the resting tint. Warmer
   accents (Surfacia's yellow) are surfaces here, never text or thin strokes,
   which is the same contrast rule §25 follows. */

.mf-hero-stage {
	--mf-hero-stage-ink: var(--mf-ink);
	--mf-hero-stage-accent: var(--mf-accent);
	--mf-hero-stage-accent-soft: var(--mf-accent-soft);

	position: relative;
	display: flex;
	justify-content: center;
	align-items: center;
	overflow: hidden;
	margin-top: 56px;
	aspect-ratio: 1200 / 640;
	background: linear-gradient(180deg, var(--mf-soft), var(--mf-base) 26%);
}

.mf-hero-stage--moonschat {
	--mf-hero-stage-ink: #353C47;
	--mf-hero-stage-accent: #FFCC00;
	--mf-hero-stage-accent-soft: #FFF6CC;
}

.mf-hero-stage--surfacia {
	--mf-hero-stage-ink: var(--mf-surf-ink);
	--mf-hero-stage-accent: var(--mf-surf);
	--mf-hero-stage-accent-soft: #FFF6CC;
}

/* 120%, not 100%: the outer columns must run past the clip so the grid reads
   as a fragment of something larger, not as five tidy columns. */
.mf-hero-stage__grid {
	display: grid;
	grid-template-columns: 1fr 1fr 1.5fr 1fr 1fr;
	gap: 24px;
	place-items: center;
	width: 120%;
	flex: none;
}

.mf-hero-stage__col {
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	gap: 24px;
	width: 100%;
}

.mf-hero-stage__col--main {
	position: relative;
	z-index: 2;
}

/* The homepage and Surfacia stages run three columns, not five: one wall of
   two large ghost cards either side of the phone. The middle column is 1.3fr
   so the phone is deliberately larger than the walls flanking it (~539px at
   1440 instead of ~360px on the five-column stages). The cards step in
   slightly from their column so the pair fits the taller stage without the
   top and bottom edges being cut off. */
.mf-hero-stage--hero,
.mf-hero-stage--surfacia {
	aspect-ratio: 1200 / 780;
}

.mf-hero-stage--hero .mf-hero-stage__grid,
.mf-hero-stage--surfacia .mf-hero-stage__grid {
	grid-template-columns: 1fr 1.3fr 1fr;
}

.mf-hero-stage--hero .mf-ghost,
.mf-hero-stage--surfacia .mf-ghost {
	width: 88%;
}

.mf-hero-stage--hero .mf-hero-stage__phone,
.mf-hero-stage--surfacia .mf-hero-stage__phone {
	margin-top: 1.5rem;
}

.mf-hero-stage__phone {
	position: relative;
	width: 100%;
	aspect-ratio: 400 / 520;
	margin-top: 3.5rem;
	overflow: hidden;
	border-radius: 16px;
	background: var(--mf-base);
	box-shadow:
		0 0 0 4px color-mix(in srgb, var(--mf-base) 60%, transparent),
		0 0 0 5px color-mix(in srgb, var(--mf-hero-stage-ink) 6%, transparent),
		0 36px 130px -24px color-mix(in srgb, var(--mf-hero-stage-ink) 55%, transparent);
}

/* The homepage hero's centrepiece: a landscape operations recording in a white
   app window, laid over the ghost wall. It is absolutely positioned so it can
   span the side columns without disturbing the grid underneath. */
.mf-hero-stage__screen {
	position: absolute;
	z-index: 2;
	top: 50%;
	left: 50%;
	width: min(84%, 1000px);
	aspect-ratio: 1592 / 702;
	transform: translate(-50%, -50%);
	overflow: hidden;
	border-radius: 14px;
	background: var(--mf-base);
	box-shadow:
		0 0 0 4px color-mix(in srgb, var(--mf-base) 60%, transparent),
		0 0 0 5px color-mix(in srgb, var(--mf-hero-stage-ink) 6%, transparent),
		0 36px 130px -24px color-mix(in srgb, var(--mf-hero-stage-ink) 55%, transparent);
}

.mf-hero-stage__video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* The recording ends on a close-up and starts on the wide view, so the loop
   point is a hard cut. A white dip (the video's own background) hides it. */
.mf-hero-stage__video-fade {
	position: absolute;
	inset: 0;
	background: var(--mf-base);
	opacity: 0;
	transition: opacity .35s ease;
	pointer-events: none;
}

.mf-hero-stage__video-fade.is-on {
	opacity: 1;
}

.mf-hero-stage__video-toggle {
	position: absolute;
	right: 12px;
	bottom: 12px;
	z-index: 3;
	display: grid;
	place-items: center;
	width: 34px;
	height: 34px;
	padding: 0;
	border: 1px solid color-mix(in srgb, var(--mf-hero-stage-ink) 12%, transparent);
	border-radius: 50%;
	background: color-mix(in srgb, var(--mf-base) 86%, transparent);
	color: var(--mf-hero-stage-ink);
	cursor: pointer;
	opacity: .55;
	transition: opacity .2s ease, background .2s ease;
}

.mf-hero-stage__screen:hover .mf-hero-stage__video-toggle,
.mf-hero-stage__video-toggle:focus-visible,
.mf-hero-stage__screen.is-paused .mf-hero-stage__video-toggle {
	opacity: 1;
}

.mf-hero-stage__video-toggle:hover {
	background: var(--mf-base);
}

.mf-hero-stage__icon {
	width: 14px;
	height: 14px;
	fill: currentColor;
}

.mf-hero-stage__screen.is-paused .mf-hero-stage__icon.is--pause,
.mf-hero-stage__screen:not(.is-paused) .mf-hero-stage__icon.is--play {
	display: none;
}

.mf-hero-stage__caption {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 9px;
	margin: 20px 0 0;
	font-size: 12px;
	font-weight: 600;
	letter-spacing: .09em;
	text-transform: uppercase;
	color: var(--mf-muted);
}

.mf-hero-stage__live {
	width: 7px;
	height: 7px;
	flex: none;
	border-radius: 50%;
	background: #2F8F63;
	box-shadow: 0 0 0 4px color-mix(in srgb, #2F8F63 14%, transparent);
}

.mf-hero-stage__notes {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 20px 30px;
	max-width: 1000px;
	margin: 24px auto 0;
	padding: 0;
	list-style: none;
}

.mf-hero-stage__notes li {
	display: grid;
	gap: 4px;
	padding-top: 13px;
	border-top: 1px solid var(--mf-line);
}

.mf-hero-stage__notes strong {
	font-size: 14px;
	font-weight: 600;
	line-height: 1.3;
	color: var(--mf-ink);
}

.mf-hero-stage__notes span {
	font-size: 13px;
	line-height: 1.45;
	color: var(--mf-muted);
}

.mf-lottie,
.mf-lottie svg {
	display: block;
	width: 100%;
	height: 100%;
}

/* The ghost cards. Resting state is very faint — the light-up sequence in
   assets/js/hero.js is what makes them readable one at a time. */
.mf-ghost {
	width: 100%;
	aspect-ratio: 4 / 3;
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: 14px;
	background: var(--mf-base);
	border: 1px solid var(--mf-line);
	border-radius: 10px;
	box-shadow: 0 10px 34px -14px color-mix(in srgb, var(--mf-hero-stage-ink) 28%, transparent);
	opacity: .16;
	transition: opacity .45s ease, box-shadow .45s ease, border-color .45s ease;
}

/* Real screenshots, supplied through the shortcode's `images` attribute,
   sit in the same cards as the drawn placeholders. */
.mf-ghost.has-image {
	padding: 0;
	overflow: hidden;
}

.mf-ghost__img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.mf-ghost span {
	display: block;
	height: 7px;
	flex: none;
	border-radius: 4px;
	background: var(--mf-frame);
}

.mf-ghost span:nth-child(1) {
	width: 46%;
	background: var(--mf-frame-line);
}

.mf-ghost span:nth-child(2) {
	width: 82%;
}

.mf-ghost span:nth-child(3) {
	width: 68%;
}

.mf-ghost span:nth-child(4) {
	width: 74%;
	margin-top: auto;
}

/* The chart variant turns the same four spans into bars. */
.mf-ghost.is--chart {
	flex-direction: row;
	align-items: flex-end;
	gap: 7px;
}

.mf-ghost.is--chart span {
	width: auto;
	height: 34%;
	flex: 1;
	margin-top: 0;
	border-radius: 4px 4px 2px 2px;
	background: var(--mf-frame);
}

.mf-ghost.is--chart span:nth-child(1) {
	width: auto;
	height: 44%;
}

.mf-ghost.is--chart span:nth-child(2) {
	width: auto;
	height: 72%;
	background: var(--mf-frame-line);
}

.mf-ghost.is--chart span:nth-child(3) {
	width: auto;
	height: 56%;
}

.mf-ghost.is--chart span:nth-child(4) {
	width: auto;
	height: 88%;
	background: color-mix(in srgb, var(--mf-hero-stage-accent) 42%, var(--mf-frame));
}

/* Surfacia's wall is floor plans, charts and a signed proposal. The two
   drawn variants carry an inline SVG instead of the four spans. */
.mf-ghost.is--plan,
.mf-ghost.is--signed,
.mf-ghost.is--invoice,
.mf-ghost.is--planning,
.mf-ghost.is--finance,
.mf-ghost.is--building,
.mf-ghost.is--crew {
	padding: 12px;
}

.mf-ghost__art {
	display: block;
	width: 100%;
	height: 100%;
	color: var(--mf-frame-line);
}

.mf-ghost__accent {
	fill: color-mix(in srgb, var(--mf-hero-stage-accent) 45%, transparent);
}

.mf-ghost__accent-stroke {
	stroke: var(--mf-hero-stage-accent);
}

.mf-ghost.is-lit {
	opacity: 1;
	border-color: color-mix(in srgb, var(--mf-hero-stage-accent) 42%, var(--mf-line));
	box-shadow:
		0 0 0 1px color-mix(in srgb, var(--mf-hero-stage-accent) 34%, transparent),
		0 22px 60px -22px color-mix(in srgb, var(--mf-hero-stage-accent) 62%, transparent);
}

/* A photograph carries far more contrast than a drawn UI card, so a lit photo
   stops short of full opacity: the light-up still picks it out, but a black
   invoice or a deep-blue plan never becomes the loudest thing on the stage. */
.mf-ghost.has-image.is-lit {
	opacity: .62;
}

.mf-hero-stage__fade {
	position: absolute;
	z-index: 5;
	pointer-events: none;
}

.mf-hero-stage__fade.is--left,
.mf-hero-stage__fade.is--right {
	inset-block: 0;
	width: 6rem;
}

.mf-hero-stage__fade.is--left {
	left: 0;
	background: linear-gradient(90deg, var(--mf-base), transparent);
}

.mf-hero-stage__fade.is--right {
	right: 0;
	background: linear-gradient(270deg, var(--mf-base), transparent);
}

.mf-hero-stage__fade.is--top,
.mf-hero-stage__fade.is--bottom {
	inset-inline: 0;
	height: 5rem;
}

.mf-hero-stage__fade.is--top {
	top: 0;
	background: linear-gradient(180deg, var(--mf-base), transparent);
}

.mf-hero-stage__fade.is--bottom {
	bottom: 0;
	background: linear-gradient(0deg, var(--mf-base), transparent);
}

@media (max-width: 900px) {
	.mf-hero-stage {
		aspect-ratio: 1200 / 800;
	}
}

@media (max-width: 780px) {
	.mf-hero-stage {
		margin-top: 40px;
		/* A little taller than the phone needs, so the bottom fade does not
		   wash out the clock-in pill at the foot of the animation. */
		aspect-ratio: 1200 / 1080;
	}

	/* More bleed, not less: at 390px a 120% grid leaves the centre column too
	   narrow to hold the phone. 230% keeps the phone ~206px wide while the
	   outer columns stay mostly off-screen. */
	.mf-hero-stage__grid {
		width: 230%;
		gap: 12px;
	}

	.mf-hero-stage__col {
		gap: 12px;
	}

	.mf-hero-stage__phone {
		margin-top: 1rem;
		border-radius: 12px;
	}

	/* The video runs nearly edge to edge, so the side fades would wash out the
	   first and last columns of the dashboard. The top and bottom fades stay. */
	.mf-hero-stage--video .mf-hero-stage__fade.is--left,
	.mf-hero-stage--video .mf-hero-stage__fade.is--right {
		display: none;
	}

	.mf-hero-stage__screen {
		width: 92%;
		border-radius: 12px;
	}

	.mf-hero-stage__video-toggle {
		right: 8px;
		bottom: 8px;
		width: 30px;
		height: 30px;
	}

	.mf-hero-stage__caption {
		margin-top: 16px;
		font-size: 11px;
	}

	.mf-hero-stage__notes {
		grid-template-columns: 1fr;
		gap: 12px;
		margin-top: 20px;
	}

	.mf-ghost {
		padding: 10px;
		gap: 6px;
		border-radius: 8px;
	}

	.mf-ghost span {
		height: 5px;
	}

	.mf-hero-stage__fade.is--left,
	.mf-hero-stage__fade.is--right {
		width: 1.5rem;
	}

	.mf-hero-stage__fade.is--top,
	.mf-hero-stage__fade.is--bottom {
		height: 1.5rem;
	}
}

/* On a phone the homepage and Surfacia stages are a single column: the
   animation itself, without the ghost wall. The artboard ratio keeps the
   phone's own margins, so the fades only graze its edges. */
@media (max-width: 780px) {
	.mf-hero-stage--hero,
	.mf-hero-stage--surfacia {
		aspect-ratio: 400 / 520;
	}

	.mf-hero-stage--hero .mf-hero-stage__grid,
	.mf-hero-stage--surfacia .mf-hero-stage__grid {
		grid-template-columns: 1fr;
		width: 90%;
	}

	.mf-hero-stage--hero .mf-hero-stage__col:not(.mf-hero-stage__col--main),
	.mf-hero-stage--surfacia .mf-hero-stage__col:not(.mf-hero-stage__col--main) {
		display: none;
	}

	.mf-hero-stage--hero .mf-hero-stage__phone,
	.mf-hero-stage--surfacia .mf-hero-stage__phone {
		margin-top: 0;
	}
}
