/**
 * mhm-* block style variations — the selector layer.
 *
 * theme.json carries the VALUES for these variations (colour, typography,
 * spacing, border) under styles.blocks.<block>.variations.<name>. It does not
 * carry their SELECTORS.
 *
 * Why the split: `styles.blocks.*.variations.*.css` in this WordPress version
 * mangles anything past a flat declaration list. Measured in the served
 * global-styles CSS:
 *
 *   `&:hover, &:focus-visible { … }`  ->  `…is-style-mhm-ghost--7 …){:hover,}`
 *   `@media (min-width: 1367px) { … }` -> the rail's whole declaration list is
 *                                        swallowed into the selector
 *   `& > a, & > span { … }`           ->  `…){> a,}` plus a second broken rule
 *
 * A single nested selector beginning with `&` survives; a comma-separated
 * selector list or an at-rule does not. So every hover state, every
 * prefers-reduced-motion branch and every breakpoint in the mhm-* set was
 * silently absent from the page. They live here instead.
 *
 * Specificity: `.wp-block-x.is-style-y` is (0,2,0), above the (0,1,0) that
 * `:root :where(…)` gives theme.json's own output, so these rules win
 * regardless of load order. Every value is still a token — nothing here is a
 * literal that belongs in theme.json.
 *
 * @package Plinthworks
 * @license GPL-2.0-or-later
 */

/* ==========================================================================
   Hero — frame, band, rail, panel
   ========================================================================== */

.wp-block-group.is-style-mhm-hero-frame {
	position:  relative;
	isolation: isolate;
	overflow:  clip;
}

/* The header sits over the hero and the band sits across its foot; the slide's
   content needs to clear both. Slide padding stays available in the editor and
   applies outside this box.

   Both are ADDITIVE to the slider's intrinsic height — the band is absolutely
   positioned and reserves nothing of its own — so between them they put 250px
   under every hero. That is fine while the band is there and wrong when it is
   not. */
.wp-block-group.is-style-mhm-hero-frame .pw-slide-content {
	padding-block-start: var(--wp--custom--hero--headroom);
	padding-block-end:   var(--wp--custom--hero--band--height);
}

/* The band carries plinthworks' "Hide on mobile" control on this page, which is
   `.pw-hide-mobile{display:none!important}` below PLINTHWORKS_VIS_BREAKPOINT
   (781px, inc/block-extras.php:26). The reservation above did not follow it, so
   a hero whose band was hidden still carried 130px of dead photograph at its
   foot at every mobile width — measured at 390: band rect 0x0, slide padding
   still 130px. The reservation now tracks the band.

   The breakpoint is the plugin's, restated rather than shared: a media query
   cannot read a PHP constant and `:has()` cannot ask whether a hidden sibling
   is hidden. Recorded as an open item rather than left silent. */
@media (max-width: 781px) {

	.wp-block-group.is-style-mhm-hero-frame:has( > .is-style-mhm-hero-band.pw-hide-mobile ) .pw-slide-content {
		padding-block-end: 0;
	}
}

/* Heading margins inside a slide belong to the blocks, not to the browser.

   Measured on the front end before this rule: the display line is an h2 and took
   the UA's `margin-block: .83em` = 74.7px above it at 1440; the script line is
   an h3 and took `margin-block: 1em` = 130px below it. 204.7px that no rule in
   theme.json, this theme or the plugin ever asked for, added to a box whose
   height is content-driven — and the reference wants 10px between the script
   line and the promo paragraph (SECTION-SPEC §5, reference CSS L5421), not 130.

   This is a RESET, not a design value: it sets the unowned UA numbers to zero so
   that whatever the pattern or an editor puts on the block's own margin control
   is the only number in play. Block spacing emits an inline style, which beats
   this selector, so every heading in a slide stays fully adjustable. */
.wp-block-group.is-style-mhm-hero-frame .pw-slide-content > :is(h1, h2, h3, h4, h5, h6) {
	margin-block: 0;
}

/* The slider's own height is NOT set here. It used to be — a min-block-size
   pinned to --wp--custom--hero--height at >=1025px — and because that is
   (0,2,0) on the same element the block writes --pw-slider-height to, it beat
   the block's own rule and the editor's Height control did nothing above
   1024px. The block owns its height: plinthworks/slider writes
   --pw-slider-height (and --pw-slider-height-sm below 1025px) from its own
   attributes, over min-height plus the grid stacking that stops a tall slide
   clipping. The hero's 900 / 800 step is set on the block in the pattern. */

.wp-block-group.is-style-mhm-hero-band {
	position:        absolute;
	inset-block-end: 0;
	inset-inline-start: 0;
	z-index:         var(--wp--custom--z--header);
	block-size:      var(--wp--custom--hero--band--height);
	display:         flex;
	flex-direction:  row;
	flex-wrap:       nowrap;
	align-items:     stretch;
	gap:             0;
	padding:         0;
	margin:          0;
}

.wp-block-group.is-style-mhm-hero-band > * { margin-block: 0; }

/* The rail shows at every width.

   It used to be display:none with a `@media (min-width: 1367px)` gate turning
   it on, which is the reference's own behaviour — and it meant four social
   links were simply absent from the page on every laptop, tablet and phone.
   They are navigation, not decoration, so the gate is gone and the rail is
   `display: block` from the base rule down.

   What changes across the breakpoints is only WHERE it sits, and each placement
   was picked by measuring what the hero actually leaves free, at three slider
   heights, rather than by scaling one position down:

   >= 1367px  vertical column, bottom-left, 200px up. The reference's placement,
              unchanged — at this width the copy is centred well inside the
              frame and the left gutter is empty.

   601-1366   horizontal row, bottom-left, above the band. The copy runs the
              full width here (measured at 1024: the promo paragraph spans
              x 30-994), so a column tall enough for four icons cannot avoid it.
              The band's top edge and the copy's last line leave a shallow
              strip, and a 36px row is what fits in it.

   <= 600     the same row, moved to the TOP, into the strip the headroom
              reserves under the header. The hero's foot is full at this width:
              the band takes the bottom 130px, the controls sit at bottom:160px
              and the copy runs down to the button, leaving a 25px gap above the
              band that a 36px row cannot use.

              The bottom-right corner is not an alternative. The site's
              dark-mode toggle is position:fixed there — 44px at bottom:28px
              right:28px, z-index 9999, on every page — so anything the hero
              puts in its own bottom-right corner meets it as soon as the two
              corners coincide. Measured at 390x667 with the hero at its saved
              height: the toggle lands at y 595-639 and a rail in that corner
              sat underneath it. The headroom strip has the opposite property —
              scrolling only ever moves the hero UP, so the hero's head can
              never arrive at the viewport's foot.

   Every offset is anchored to an edge of the hero that does not move with the
   copy — its foot above 600px, its head below — so each placement holds when
   the Height control changes. Verified clear of the header, the copy, the
   band, the controls and the dark-mode toggle at 1440 / 1024 / 390 x four
   heights. */
.wp-block-group.is-style-mhm-hero-rail {
	position:           absolute;
	inset-block-end:    var(--wp--custom--hero--rail--bottom);
	inset-inline-start: var(--wp--custom--hero--rail--inset);
	z-index:            var(--wp--custom--z--header);
	margin:             0;
	display:            block;
}

/* 601-1366 — a horizontal row in the strip above the band. */
@media (max-width: 1366px) {

	.wp-block-group.is-style-mhm-hero-rail {
		inset-block-end:  var(--wp--custom--hero--rail--bottom-md);
		inset-inline-end: auto;
	}

	/* The block is saved `is-vertical`; the orientation belongs to the
	   placement, not to the block, so it is turned here and stays turned for
	   every width below this one. The gap is left alone — it remains whatever
	   the social-links block's own spacing control says. */
	.wp-block-group.is-style-mhm-hero-rail .wp-block-social-links.is-vertical {
		flex-direction: row;
	}
}

/* <= 600 — the same row, re-anchored to the hero's head. --hero--rail--top-sm
   clears the header's own foot (measured 73px at 390) and still sits above the
   headroom's 120px edge, which is the highest the copy can ever start. It is
   the one strip in the hero whose size does not move with the Height control:
   the copy centres inside the padding, so it can only ever start lower. */
@media (max-width: 600px) {

	.wp-block-group.is-style-mhm-hero-rail {
		inset-block-start: var(--wp--custom--hero--rail--top-sm);
		inset-block-end:   auto;
	}
}

.wp-block-group.is-style-mhm-hero-panel,
.wp-block-cover.is-style-mhm-hero-panel {
	inline-size:    var(--wp--custom--hero--band--panel-width);
	block-size:     100%;
	min-block-size: 100%;
	flex:           0 0 auto;
	overflow:       clip;
	padding:        0;
}

@media (min-width: 576px) {
	.wp-block-group.is-style-mhm-hero-panel,
	.wp-block-cover.is-style-mhm-hero-panel {
		inline-size: var(--wp--custom--hero--band--panel-width-sm);
	}
}

/* The panel holds one control and the reference centres it with margin:auto.
   The block itself is layout-agnostic, so the placement belongs here. */
.wp-block-cover.is-style-mhm-hero-panel .wp-block-cover__inner-container {
	text-align: center;
}

.wp-block-cover.is-style-mhm-hero-panel .wp-block-cover__image-background {
	transition: transform var(--wp--custom--motion--duration--zoom) var(--wp--custom--motion--easing--default);
}

.wp-block-cover.is-style-mhm-hero-panel:hover .wp-block-cover__image-background,
.wp-block-cover.is-style-mhm-hero-panel:focus-within .wp-block-cover__image-background {
	transform: scale(var(--wp--custom--motion--image-zoom));
}

/* ==========================================================================
   Vertical label — the rotated "Scroll Down"
   ========================================================================== */

.is-style-mhm-vertical-label {
	display:         none;
	align-items:     center;
	justify-content: center;
	margin:          0;
	inline-size:     var(--wp--custom--hero--rail--label-width);
	block-size:      100%;
	flex:            0 0 auto;
}

.is-style-mhm-vertical-label > a,
.is-style-mhm-vertical-label > span {
	rotate:          -90deg;
	white-space:     nowrap;
	color:           inherit;
	text-decoration: none;
}

.is-style-mhm-vertical-label > a:hover,
.is-style-mhm-vertical-label > a:focus-visible {
	opacity: var(--wp--custom--motion--hover-opacity);
}

@media (min-width: 992px) {
	.is-style-mhm-vertical-label { display: flex; }
}

/* ==========================================================================
   Media zoom — group, image, cover and the news mosaic share the behaviour

   .pw-mosaic-media is plinthworks/news-mosaic's image frame. It is named here
   rather than given its own rule in the block so the zoom stays ONE
   declaration set: distance, duration and easing are the same tokens for
   every consumer, and changing them changes them everywhere.
   ========================================================================== */

:is(.wp-block-group, .wp-block-image, .wp-block-post-featured-image, .pw-mosaic-media).is-style-mhm-media-zoom { overflow: hidden; }

:is(.wp-block-group, .wp-block-image, .wp-block-post-featured-image, .pw-mosaic-media).is-style-mhm-media-zoom img {
	transition: transform var(--wp--custom--motion--duration--zoom) var(--wp--custom--motion--easing--default);
}

.wp-block-image.is-style-mhm-media-zoom img,
.wp-block-post-featured-image.is-style-mhm-media-zoom img { display: block; width: 100%; }

:is(.wp-block-group, .wp-block-image, .wp-block-post-featured-image, .pw-mosaic-media).is-style-mhm-media-zoom:hover img,
:is(.wp-block-group, .wp-block-image, .wp-block-post-featured-image, .pw-mosaic-media).is-style-mhm-media-zoom:focus-within img {
	transform: scale(var(--wp--custom--motion--image-zoom));
}

.wp-block-cover.is-style-mhm-media-zoom .wp-block-cover__image-background {
	transition: transform var(--wp--custom--motion--duration--zoom) var(--wp--custom--motion--easing--default);
}

.wp-block-cover.is-style-mhm-media-zoom:hover .wp-block-cover__image-background,
.wp-block-cover.is-style-mhm-media-zoom:focus-within .wp-block-cover__image-background {
	transform: scale(var(--wp--custom--motion--image-zoom));
}

/* ==========================================================================
   Icon frame and the bloom behind it
   ========================================================================== */

.wp-block-group.is-style-mhm-icon-frame {
	position:        relative;
	display:         flex;
	align-items:     center;
	justify-content: center;
}

.wp-block-group.is-style-mhm-icon-frame > *:not(.is-style-mhm-bloom) {
	position: relative;
	z-index:  5;
}

.wp-block-image.is-style-mhm-bloom {
	position:           absolute;
	inset-block-start:  50%;
	inset-inline-start: 50%;
	translate:          -50% -50%;
	z-index:            1;
	margin:             0;
	pointer-events:     none;
}

.wp-block-image.is-style-mhm-bloom img { width: auto; max-width: none; }

/* ==========================================================================
   Buttons — ghost
   ========================================================================== */

.wp-block-button.is-style-mhm-ghost .wp-block-button__link {
	min-width:      var(--wp--custom--button--min-width);
	letter-spacing: 0.05em;
	transition:
		transform        var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--easing--default),
		box-shadow       var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--easing--default),
		background-color var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--easing--default),
		color            var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--easing--default);
}

.wp-block-button.is-style-mhm-ghost .wp-block-button__link:hover,
.wp-block-button.is-style-mhm-ghost .wp-block-button__link:focus-visible {
	transform:        translateY(var(--wp--custom--motion--lift--ghost));
	box-shadow:       var(--wp--preset--shadow--deep);
	background-color: var(--wp--preset--color--accent);
	color:            var(--wp--preset--color--contrast);
}


/* ==========================================================================
   Cause card, and the photo frame whose pill straddles its top edge
   ========================================================================== */

/* The card fills the height of the carousel cell it sits in, so a short
   excerpt does not leave the donate button floating halfway up a taller
   neighbour. The white ground and the panel shadow are theme.json's. */
.wp-block-group.is-style-mhm-cause-card {
	display:        flex;
	flex-direction: column;
	height:         100%;
}

/* The donate button is pushed to the foot for the same reason. `auto` on the
   last child rather than a fixed gap, so it tracks whatever the card holds.

   The gap above it belongs to the block ABOVE, not to the button - a
   margin-top set on the button from the editor is an inline style, and an
   inline margin-top beats this one outright, which silently un-bottom-aligns
   every card whose content is short. The pattern therefore carries the
   spacing as the progress bar's margin-bottom, which is a floor this auto
   margin then grows past. */
.wp-block-group.is-style-mhm-cause-card > :last-child { margin-top: auto; }

/* The frame the photo and the category pill share. Both are ordinary core
   blocks - core/post-featured-image and core/post-terms - and this is the
   positioning context that lets the second sit on the first's top edge. */
.wp-block-group.is-style-mhm-media-pill {
	position: relative;
}

.wp-block-group.is-style-mhm-media-pill > .wp-block-post-featured-image {
	margin: 0;
}

.wp-block-group.is-style-mhm-media-pill > .wp-block-post-featured-image img {
	display: block;
	width:   100%;
}

/* Half above the edge, half below it - the reference's translateY(-50%) on a
   pill anchored to the image's top. 15px in from the left, matching the card's
   own inset so the pill lines up with the copy beneath it. */
.wp-block-group.is-style-mhm-media-pill > .wp-block-post-terms {
	position:  absolute;
	top:       0;
	left:      var(--wp--preset--spacing--30);
	transform: translateY(-50%);
	z-index:   2;
	margin:    0;
	/* A cause with two categories shows two pills side by side. Core would
	   otherwise put its separator between them and break the pill in two. */
	display:   flex;
	gap:       var(--wp--preset--spacing--10);

	/* The pill's ink lives HERE, on the container, not on the anchor.

	   Inside a carousel the section-wide prose-link reset in ngo-brand.css
	   sets `color: inherit` on every card anchor at (0,6,1), which beats any
	   colour this file could reasonably put on the anchor itself - measured:
	   the pill came out #727272 on #F36F8F, a contrast ratio of 1.73:1.
	   Setting it on the parent works WITH that reset instead of against it,
	   and gives the same result outside a carousel, where the anchor has no
	   colour rule and inherits from here anyway.

	   Ink, not white. The reference sets white on this pink, which measures
	   2.81:1 - below AA for 14px bold, which is not large text. #333333 on
	   the same pink is 4.50:1 and passes. The pill still reads as the
	   reference's pink badge; only its ink differs. */
	color:     var(--wp--preset--color--on-action);
}

.wp-block-group.is-style-mhm-media-pill > .wp-block-post-terms a {
	display:         inline-block;
	padding:         2px var(--wp--preset--spacing--30) 3px;
	border-radius:   var(--wp--custom--radius--chip);
	background:      var(--wp--preset--color--accent-3);
	font-size:       var(--wp--preset--font-size--small);
	line-height:     27px;
	font-weight:     700;
	text-align:      center;
	text-decoration: none;
}


.wp-block-group.is-style-mhm-media-pill > .wp-block-post-terms .wp-block-post-terms__separator {
	display: none;
}

/* ==========================================================================
   Headings, pill, social rail icons
   ========================================================================== */

.wp-block-heading.is-style-mhm-heading-mixed strong,
.wp-block-heading.is-style-mhm-heading-mixed b { font-weight: 700; }

.is-style-mhm-pill { display: inline-block; width: auto; text-align: center; }

/* The rail sits on the hero photograph, so the icons need a light ink and no
   per-network chip. Core paints .wp-social-link per service (white on the brand
   chip, #444 on anything without a rule); with the chip gone that default is a
   coin toss against a dark photo, so the variation states one.

   This is a DEFAULT, not a decision baked in ink: core's Icon color control
   emits an inline `style="color:…"` on the same <li> (wp-includes/blocks/
   social-link.php:376), and an inline style beats this selector, so the block's
   own control still owns the colour. The anchor and its svg take it by
   inheritance through core's `color:currentcolor` — which is why ngo-brand.css
   must not intercept .wp-block-social-link-anchor. */
.wp-block-social-links.is-style-mhm-hero-icons .wp-social-link {
	background: none;
	color:      var(--wp--preset--color--base);
}

.wp-block-social-links.is-style-mhm-hero-icons .wp-social-link a {
	opacity:    1;
	transition: opacity var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--easing--default);
}

.wp-block-social-links.is-style-mhm-hero-icons .wp-social-link a:hover,
.wp-block-social-links.is-style-mhm-hero-icons .wp-social-link a:focus-visible {
	opacity: var(--wp--custom--motion--hover-opacity);
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {

	:is(.wp-block-group, .wp-block-image, .pw-mosaic-media).is-style-mhm-media-zoom img,
	.wp-block-cover.is-style-mhm-media-zoom .wp-block-cover__image-background,
	.wp-block-cover.is-style-mhm-hero-panel .wp-block-cover__image-background {
		transition-duration: 1ms;
	}

	:is(.wp-block-group, .wp-block-image, .pw-mosaic-media).is-style-mhm-media-zoom:hover img,
	:is(.wp-block-group, .wp-block-image, .pw-mosaic-media).is-style-mhm-media-zoom:focus-within img,
	.wp-block-cover.is-style-mhm-media-zoom:hover .wp-block-cover__image-background,
	.wp-block-cover.is-style-mhm-media-zoom:focus-within .wp-block-cover__image-background,
	.wp-block-cover.is-style-mhm-hero-panel:hover .wp-block-cover__image-background,
	.wp-block-cover.is-style-mhm-hero-panel:focus-within .wp-block-cover__image-background {
		transform: none;
	}

	.wp-block-button.is-style-mhm-ghost .wp-block-button__link { transition-duration: 1ms; }

	.wp-block-button.is-style-mhm-ghost .wp-block-button__link:hover,
	.wp-block-button.is-style-mhm-ghost .wp-block-button__link:focus-visible {
		transform: none;
	}
}

/* ==========================================================================
   Hero — ink inheritance
   ========================================================================== */

/* The slider's counter and arrows sit over the slide photograph, so they take
   the hero's ink. Set on the slider element itself, not on the controls, so
   the block's own Text colour control (an inline style on this element) still
   wins when an editor sets one. */
.wp-block-group.is-style-mhm-hero-frame .wp-block-plinthworks-slider {
	color: var(--wp--preset--color--base);
}

/* theme.json gives every heading an explicit colour, which beats inheriting
   the slide's Text colour. Headings inside the hero inherit unless the editor
   has picked a colour for that heading, in which case has-text-color is
   present and this rule stands aside. */
.wp-block-group.is-style-mhm-hero-frame .pw-slide-content :is(h1, h2, h3, h4, h5, h6):not(.has-text-color):not(.is-style-mhm-script) {
	color: inherit;
}

/* The category pill's hover, kept apart from the rest of the pill rules above.

   Ordering, not taste. This selector is (0,4,1); the resting pill rule is
   (0,3,1) and has to stay ABOVE the social rail's own `a` rule, which is also
   (0,3,1). If the hover sat with it, a (0,4,1) rule would precede a (0,3,1)
   one for the same element and stylelint's no-descending-specificity would be
   right to object. Split, every `a` rule in this file runs least-specific
   first. */
.wp-block-group.is-style-mhm-media-pill > .wp-block-post-terms a:hover,
.wp-block-group.is-style-mhm-media-pill > .wp-block-post-terms a:focus-visible {
	/* Background only - the ink is the container's, for the reason above. */
	background: var(--wp--preset--color--accent-2);
}

/* ==========================================================================
   Volunteer band — the giant script word and the block pulled up over it
   ========================================================================== */

/* The word never causes a horizontal scrollbar.

   It is live text in a script face at up to 400px, and how wide "Volunteer"
   renders depends on the font, the word and the language — none of which this
   rule can know. `clip` rather than `hidden` so the band never becomes a
   scroll container of its own. */
.wp-block-group.mhm-band {
	position: relative;
	overflow: clip;
}

/* Tracking and wrapping for a word set at Display 3.

   Attached to the SIZE, not to the script style: is-style-mhm-script is shared
   with the hero.s script line, which is one rung of a heading and wants its
   own tracking. At 400px the variation.s 0.05em would add 20px to every gap;
   the reference sets none.

   The LEADING is not here. The reference tightens it as the word grows - a
   400px word in a 250px box - which wants a viewport-relative value, and this
   repo.s stylelint config allows line-height to be unitless only. That is the
   right rule, so the leading is a plain number on the block instead, where it
   is also a control the author can drag. */
.wp-block-heading.is-style-mhm-script.has-display-3-font-size {
	letter-spacing: 0;
	white-space:    nowrap;
}

/* The block sits over the word's lower half.

   The overlap itself is NOT here - it is the block's own top margin, set in
   the pattern, so it shows up in the Dimensions panel and an author can
   change it. It has to be: a margin-top written here would be an ordinary
   stylesheet rule, and the block's spacing control emits an inline style that
   beats it outright, so the CSS value would apply only until somebody touched
   the panel and then never again. (That is not hypothetical - the same trap
   ate the donate button's bottom alignment on the cause card one section
   over.) All that is left here is the stacking. */
.wp-block-columns.mhm-band__over {
	position: relative;
	z-index:  1;
}

/* The reference holds the copy off the column's right edge at full width and
   lets it run to the edge once the columns stack. */
@media (min-width: 782px) {

	.mhm-band__copy { padding-right: var(--wp--preset--spacing--80); }
}

/* Stacked, the 33% spacer column is an empty box that would still take the
   grid's row. */
@media (max-width: 781px) {

	.wp-block-columns.mhm-band__over > .wp-block-column:empty { display: none; }
}

/* ==========================================================================
   Page hero — reusable template-part frame, mask and title

   Structure lives in parts/page-hero.html: a core/cover keyed to the page's
   featured image, holding (in order) the mask, a static eyebrow placeholder,
   core/post-title and a two-rule divider. Per-page eyebrow/subtitle content
   binding is session 2 — see STATE.md.
   ========================================================================== */

/* Ratio measured on the reference at 1353x466 (2.90:1). Set on the class
   rather than the block's own native aspectRatio style: that style attribute
   would print as an INLINE style, and an inline style beats any media query
   in this file outright — there would be no way to relax it at all below the
   breakpoint. A plain class carries no such penalty. */
/* min-block-size:0 unsets core's own `min-height:430px` on .wp-block-cover,
   and it is load-bearing, not tidying (open item 43). With both in play the
   ratio and the floor fight, and Chrome resolves the fight by deriving the
   WIDTH from the height: measured on /about/, 390px and 480px viewports both
   produced a 573.3px-wide hero (430 x 4/3) and 1024px produced 1248.5px
   (430 x 1353/466). The excess was silently cut off by the cover's own
   overflow:hidden, so it never showed as a scrollbar — it showed as a hero
   whose mask, title and photograph were all cropped at the viewport edge.
   With the floor gone the ratio is the only thing setting height, and the
   width is the viewport at every size. Heights become 292.5 at 390, 360 at
   480, 450 at 600, 576 at 768, 352.7 at 1024, 496 at 1440 — i.e. the ratios
   below now actually apply, which they never did under 573px.

   overflow:hidden is core's default on .wp-block-cover and is restated here
   because the subtitle depends on it: the script word is MEANT to run off the
   right and bottom edges (DESIGN-SYSTEM.md §9.6), so this is the rule that
   makes the clipping happen rather than an incidental inheritance. */
.wp-block-cover.mhm-page-hero {
	aspect-ratio:    1353 / 466;
	min-block-size:  0;
	overflow:        hidden;
}

/* Relaxed, not measured: a straight 2.90:1 box gets too short for the title
   to clear two lines once it wraps on a narrow phone. 4:3 is a judgment
   call, not a reference value — revisit if a real device screenshot says
   otherwise. */
@media (max-width: 768px) {

	.wp-block-cover.mhm-page-hero {
		aspect-ratio: 4 / 3;
	}
}

/* THE ARCHIVE HERO'S PHOTOGRAPH.

   parts/page-hero-archive.html is parts/page-hero.html with a query-title in
   place of the post-title, and it carries the same `useFeaturedImage:true`
   cover — which resolves to nothing on an archive, because an archive is not a
   post and has no featured image to use. Core still renders the cover, so the
   hero would be a 70% dim over nothing.

   inc/archive-hero-image.php borrows one instead: the newest entry in the
   current query that HAS a featured image lends it, which makes /news/ show its
   latest story, a category archive show something from that category and a CPT
   archive show its own newest item. It arrives as an inline background-image on
   the cover (there is no core/cover attribute that would take a URL), so these
   three properties are what turn that URL into the same edge-to-edge crop the
   real <img class="wp-block-cover__image-background"> gets on a page. */
.wp-block-cover.mhm-page-hero.has-pw-hero-image {
	background-size:     cover;
	background-position: center;
	background-repeat:   no-repeat;
}

/* NO PHOTOGRAPH ANYWHERE — the flat hero. Two ways in: a page or CPT single
   with no featured image set (tagged in inc/featured-image-fallback.php), and
   an archive, search page or 404 whose whole query turned up nothing with a
   thumbnail (tagged in inc/archive-hero-image.php). Both land on the same
   pw-hero-fallback class.

   The site-wide treatment for that class is a gold/coral brand gradient
   (plinthworks.css) with the overlay lightened to 0.42 so the gradient reads
   through. The page hero overrides it to flat surface-deep, and the override is
   deliberate: this hero's whole composition is light text sitting bottom-left
   with a gold script word crossing it, and that word disappears against a gold
   gradient. Specificity (0,3,0 against 0,2,0) is what carries it, so this does
   not depend on which stylesheet loads last.

   The colour goes on the DIM SPAN at full opacity, not just on the cover, so
   there is nothing for the 70% dim to darken — a 70% black wash over #343877
   would land near-black and lose the hero's identity entirely. The cover gets
   the same colour underneath so no seam shows if the span is ever suppressed. */
.wp-block-cover.mhm-page-hero.pw-hero-fallback {
	background: var(--wp--preset--color--surface-deep, #343877);
}

.wp-block-cover.mhm-page-hero.pw-hero-fallback > .wp-block-cover__background {
	background-color: var(--wp--preset--color--surface-deep, #343877);
	opacity:          1;
}

/* Isolated so the mask's negative z-index below has something to escape TO
   only as far as this box — it lands above the cover's own background image
   (painted outside this stacking context, so still further back) and below
   every ordinary in-flow child (the eyebrow, title and divider stay static,
   and static in-flow boxes paint before any positioned descendant of an
   isolated ancestor, negative z-index or not).

   width/height:100% overrides core cover CSS, not a fresh rule: a non-centred
   contentPosition ("center left" here) makes core set the inner container to
   width:auto (shrink-to-fit its own text) and its height is content-driven at
   every content position. The mask is a child of this container and sizes
   itself with inset:0, so a shrink-wrapped containing block meant "inset:0"
   was measuring the text's own box, not the hero's — the mask rendered as a
   small rectangle hugging the copy instead of a full-bleed diagonal. Restored
   to 100/100 so the mask's containing block is the whole hero again; content
   position still works because that is the OUTER cover's flex alignment, not
   this container's size. */
/* Stretching this box to full height/width moves vertical centring OUT of
   core's own flex-on-.wp-block-cover mechanism (which centred the box itself,
   back when the box was content-sized) and onto this rule instead — the box
   IS the full height now, so centring it does nothing; these three
   properties are what actually centre the eyebrow/title/divider inside it.
   The mask stays unaffected: it is position:absolute, so it does not
   participate in flex alignment at all. */
/* The 6.5% content inset lives HERE, not on the cover's own padding control,
   and that placement is load-bearing.

   The mask is position:absolute with inset:0, so its containing block is the
   padding box of the nearest POSITIONED ancestor — this container, which core
   makes position:relative. While the 6.5% sat on the cover, this container was
   inset by it, so the mask was too: measured, a 93.6px strip down the left
   edge of a 1440px hero carried no panel at all and showed overlay-over-photo
   (rgb(51,54,106) at the top, rgb(79,74,126) at the foot — the gradient that
   looked like a translucent panel but was its absence).

   It also silently mis-scaled the diagonal. clip-path percentages resolve
   against the clipped box, so 35.52% of an inset 1252.8px box put the edge at
   37.4% of the hero, not the measured 35.52%. One cause, two symptoms.

   With the inset here instead, this container spans the hero edge to edge, the
   mask's inset:0 spans it too, and the clip-path percentages resolve against
   the full hero width the measurements in §9.1 were taken from. The cover's
   own padding control is left at 0 and still works — an author's value adds to
   this inset rather than replacing it. */
.wp-block-cover.mhm-page-hero > .wp-block-cover__inner-container {
	isolation:      isolate;
	block-size:     100%;
	padding-inline: 6.5%;
	display:        flex;
	flex-direction: column;
	justify-content: center;
	align-items:    flex-start;
	/* Core's own has-custom-content-position rule is a 4-class compound
	   selector (0,4,1) — this plain descendant selector (0,2,1) cannot beat it
	   on specificity alone. */
	inline-size: 100% !important;
}

/* THE MASK — shape only. Colour is the group's own native Background colour
   control (theme.json is not involved: nothing here is a design token,
   it is geometry). Straight diagonal from 35.52% of the width at the top
   edge to 11.27% at the bottom, per the reference measurement; anchored to
   the left so the panel holds the eyebrow/title/divider (also left-anchored
   via contentPosition) and narrows going down, revealing the photo on the
   right at every height.

   35.52% / 11.27% are MEASURED off the reference shot, not eyeballed — see
   DESIGN-SYSTEM.md §9.1. Two independent line fits (Theil-Sen and
   iterative-rejection OLS) agree to 0.13% of width, R² 0.99933, residual
   stdev 2.82px. They supersede the 34.92% / 9.10% this rule shipped with,
   which were a working guess. The edge sits 35.28° off vertical. */
.wp-block-cover.mhm-page-hero .is-style-mhm-hero-mask {
	position:       absolute;
	inset:          0;
	z-index:        -1;
	pointer-events: none;
	clip-path:      polygon(0 0, 35.52% 0, 11.27% 100%, 0 100%);
}

/* THE RIGHT MASK — the other side of the same aperture.

   STRUCTURAL CORRECTION, not an addition. The navy is not a left panel sitting
   on a photograph; it covers the WHOLE hero, and the photograph shows through
   an aperture cut between two boundaries. The reference carries a navy band
   down the right edge too — 8% to 15.5% of the width through the middle rows —
   which the original spec missed entirely. DESIGN-SYSTEM.md §9.1 now records
   both boundaries.

   Same mechanism as the left mask above, mirrored: inset:0 makes this box the
   whole hero (the inner container's 6.5% is padding, so its padding box — the
   containing block for an absolutely positioned child — still spans the hero
   edge to edge; see the container's own rule for why that placement is
   load-bearing), and the clip-path percentages therefore resolve against the
   hero, which is the box §9.1's percentages were measured from.

   z-index:-1 is what puts it above the photograph and the overlay (both
   painted outside this isolated container) but below the eyebrow, title and
   divider, which are static in flow, and below the script subtitle, which is
   positioned with z-index:auto and so paints later still. The subtitle starts
   at 44.73% and runs off the right edge, so it crosses this band and must stay
   on top of it — verified, not assumed.

   THE EDGE IS STRAIGHT BY CHOICE. The reference's right boundary is a torn
   brush edge: a straight-line fit gives R2 0.7103 with a residual sigma of
   29.2px, against 0.99933 and 2.82px for the left edge. That is a texture, and
   it is theirs. A straight edge through the fitted endpoints is the intended
   re-implementation.

   97.78% / 81.40% ARE RE-MEASURED, and they replace a first attempt at
   92.91% / 85.06% that was wrong by enough to see. Two things make the numbers
   below trustworthy where those were not:

   1. The tinted photograph is not the mask. Under this overlay a dark part of
      the photograph lands within a few units of the tint colour, so any
      "is this pixel navy?" test picks up photo as mask. The mask is FLAT —
      DESIGN-SYSTEM.md §9.2 measures sigma 0.000 across 32,744 panel px — so
      the boundary has to be found by requiring an exactly-equal run at least
      24px long that reaches the image edge. Colour-distance alone put the
      right band at 11.6% of the width at y=40, where the flat test puts it at
      0.9%: the rest was photograph.
   2. White text truncates the scan. The eyebrow and title start at the 6.53%
      content inset, so on rows 105-270 a left-to-right walk stops there and
      reports a 6.5% panel. Those rows are excluded from the LEFT fit; they are
      harmless for the right one.

   Left fit over the 21 uncontaminated rows: 35.46% at the top, 11.30% at the
   bottom — which is §9.1's recorded 35.52 / 11.27 to within a third of a
   pixel, so the left edge was right all along and is untouched. Right fit over
   rows 40-400: 97.78% / 81.40%, monotone from 98.22% at y=45 to 85.61% at
   y=360.

   The shape this produces is the point. Both boundaries lean left going down
   at a similar rate (-0.052 and -0.040 % of width per row), so the aperture is
   a slanted band of roughly constant width — measured 65-66% at the top and
   70-72% lower down. The first attempt made the right edge nearly vertical,
   which gave a 57% aperture at the top instead of 65% and a uniform strip
   instead of a wedge. */
.wp-block-cover.mhm-page-hero .is-style-mhm-hero-mask-right {
	position:       absolute;
	inset:          0;
	z-index:        -1;
	pointer-events: none;
	clip-path:      polygon(97.78% 0, 100% 0, 100% 100%, 81.4% 100%);
}

/* theme.json gives every heading an explicit colour, which otherwise beats
   inheriting the cover's own light text colour — same mechanism already
   fixed for the slider hero above (see "Hero — ink inheritance"). */
.wp-block-cover.mhm-page-hero :is(h1, h2, h3, h4, h5, h6):not(.has-text-color) {
	color: inherit;
}

/* TITLE — Two-tone. ::first-line is a raw pseudo-element selector, so it
   cannot live in theme.json's variation `css` (trap 1); the variation's own
   base weight (light, 300) is a theme.json value and stays there.

   Single-line failure mode: ::first-line matches the whole title when it
   never wraps, so a short page title renders entirely bold with no light
   weight visible at all. Not a bug to fix here — it is the correct
   behaviour of a "first line is bold" rule applied to one line. */
.wp-block-post-title.is-style-mhm-two-tone::first-line,
.wp-block-heading.is-style-mhm-two-tone::first-line,
.wp-block-query-title.is-style-mhm-two-tone::first-line {
	font-weight: 500;
}

/* SUBTITLE — the gold script word over the photograph (DESIGN-SYSTEM.md §9.6).
   Family, colour and size are theme.json values on the same variation; only
   the geometry is here, per trap 1.

   ANCHORED TO THE HERO, NOT TO THE 6.5% INSET. The containing block for an
   absolutely positioned box is the PADDING box of the nearest positioned
   ancestor. That ancestor is .wp-block-cover__inner-container, which carries
   `padding-inline: 6.5%` — but padding is inside the padding box, so the box
   these percentages resolve against is the full hero, edge to edge, exactly
   as it is for the mask's own inset:0 a few rules up. Measured, not assumed:
   the ink lands at 44.73% / 57.67% of the cover at 1440. Putting the 6.5% on
   the COVER instead would inset this box and shift both percentages — that is
   the failure this same file already documents for the clip-path, and it
   would land here in exactly the same way.

   Out of flow, so it is not a flex item of the column above it and does not
   move the eyebrow, title or divider — the hero's height is unchanged by it.

   The two em corrections turn "box top/left" into "INK top/left", which is
   what §9.6 measured. Caveat at line-height:1 puts its ink 0.15em below the
   line box top (font ascent 0.96em, half-leading (1 - 1.26)/2 = -0.13em, ink
   ascent 0.68em) and 0.116em right of the inline start. Both are proportions
   of the font size, so they stay correct at every viewport as the vw-based
   size scales.

   THE SIZE (theme.json, 20vw) IS TUNED TO THE GOLD PIXEL COUNT, not to a cap
   height — §9.6 records the cap height as unmeasurable, because the reference
   word's baseline is below the frame. The one acceptance number it does give is
   28,659 gold pixels. Sizing instead to §9.6's ascender-to-x-height span (100px
   of 463, i.e. 21.6% of the hero) gives 23.24vw and 33,611 gold pixels, over
   the band: the reference word is ~7 letterforms and this one is ten, so the
   same size packs more ink into the same clipped window. Measured at 1440:
   23.24vw -> 33,611, 21 -> 32,243, 20 -> 31,308 (+9.2%), 19 -> 29,393,
   18 -> 27,737, 17 -> 26,653 but it stops clipping the bottom edge, which §9.6
   requires. 20vw sits inside the band with both edges still cut.

   Clipping right and bottom is the design (§9.6: the last content row still
   carries 280 gold pixels, which a word ending inside the frame cannot do).
   nowrap is what makes it clip rather than wrap out of position. */
.wp-block-cover.mhm-page-hero .is-style-mhm-hero-subtitle {
	position:            absolute;
	inset-block-start:   57.67%;
	inset-inline-start:  44.73%;
   inset-inline-end:    0;
	margin-block:        -0.15em 0;
	margin-inline-start: -0.116em;
	max-inline-size:     none;
	white-space:         nowrap;
	pointer-events:      none;
}

/* ==========================================================================
   News Mosaic tiles

   Each grid position is a real core/query block (one post, an offset), so
   the alternation the old plinthworks/news-mosaic block computed in PHP is
   now just "which Style pill is selected on this query" and the wrapping
   group's native Grid column-span (1 for Card, 2 for Feature). Everything
   inside — border, background/gradient, padding, font size, excerpt length —
   is a native control on core/group, core/post-title, core/post-excerpt,
   core/post-terms; nothing here duplicates a sidebar setting as a literal.
   ========================================================================== */

.wp-block-group.is-style-mhm-mosaic-card,
.wp-block-group.is-style-mhm-mosaic-feature {
	display:        flex;
	flex-direction: column;
	block-size:     100%;
	overflow:       hidden;
}

/* The tile's block-size:100% above only has something to be 100% OF if every
   box between it and the grid cell also has a real height — a plain <ul>/<li>
   does not inherit a stretched ancestor's height on its own. Without this, a
   Card tile (whose own content happens to be tall) set each row's height, and
   the Feature tile next to it — genuinely shorter until block-size:100% can
   take effect — sat at only its min-block-size, leaving a gap under it. */
.mhm-mosaic-grid > .wp-block-query,
.mhm-mosaic-grid .wp-block-post-template,
.mhm-mosaic-grid .wp-block-post-template > li {
	block-size: 100%;
}

/* Plain block flow, not flex: its children (the pill, title, excerpt, date)
   are already block-level and stack fine on their own. A flex column here
   was blockifying AND stretching the pill to the full tile width — a flex
   item cannot stay inline-block, and "stretch" is align-items' default —
   turning the small pill into a full-width bar. */
.mhm-mosaic-tile__info { display: block; }

/* ── Card — white, photo above the text ──────────────────────────────────── */

/* Default look matching the retired render.php output. Still a plain class
   rule, not an attribute, so it stays in effect only until an author picks
   their own border/background/padding from the sidebar — a sidebar choice
   always compiles to an inline style, which beats this by origin, not
   specificity trickery. */
.wp-block-group.is-style-mhm-mosaic-card {
	background: var(--wp--preset--color--base, #fff);
	box-shadow: var(--wp--preset--shadow--natural, 0 3px 15px 2px rgba(0, 0, 0, 0.06));
	padding:    var(--wp--preset--spacing--30, 15px);
}

.wp-block-group.is-style-mhm-mosaic-card .wp-block-post-featured-image {
	margin:       0;
	aspect-ratio: 16 / 9;
}

.wp-block-group.is-style-mhm-mosaic-card .wp-block-post-featured-image img {
	block-size: 100%;
	object-fit: cover;
}

.wp-block-group.is-style-mhm-mosaic-card .mhm-mosaic-tile__info {
	flex: 1 1 auto;
}

/* ── Feature — the photo IS the tile ─────────────────────────────────────── */

.wp-block-group.is-style-mhm-mosaic-feature {
	position:       relative;
	min-block-size: 330px;
	color:          var(--wp--preset--color--base);
}

.wp-block-group.is-style-mhm-mosaic-feature .wp-block-post-featured-image {
	position: absolute;
	inset:    0;
	margin:   0;
	z-index:  0;
}

.wp-block-group.is-style-mhm-mosaic-feature .wp-block-post-featured-image img {
	inline-size: 100%;
	block-size:  100%;
	object-fit:  cover;
}

/* The scrim is the theme's own card-scrim gradient preset, read rather than
   re-declared, and kept as a pseudo-element rather than a block: it is a
   fixed part of the Feature treatment, not a setting an author picks per
   tile — same call the old render.php made for the same element. */
.wp-block-group.is-style-mhm-mosaic-feature::after {
	content:        "";
	position:       absolute;
	inset:          0;
	z-index:        1;
	background:     var(--wp--preset--gradient--card-scrim);
	pointer-events: none;
}

.wp-block-group.is-style-mhm-mosaic-feature .mhm-mosaic-tile__info {
	position:           relative;
	z-index:            2;
	margin-block-start: auto;
	padding:            var(--wp--preset--spacing--50, 30px);
}

/* theme.json sets an explicit colour on every heading, and an explicit colour
   beats an inherited one, so the feature tile's white has to be stated on
   each text block rather than left to cascade down from the tile. */
.wp-block-group.is-style-mhm-mosaic-feature .wp-block-post-title,
.wp-block-group.is-style-mhm-mosaic-feature .wp-block-post-title a,
.wp-block-group.is-style-mhm-mosaic-feature .wp-block-post-excerpt,
.wp-block-group.is-style-mhm-mosaic-feature .wp-block-post-date {
	color: inherit;
}

/* ── Grid ─────────────────────────────────────────────────────────────────
   A plain 3-column CSS grid on the wrapping group, not the block editor's
   native Grid layout type: that layout writes each child's column-span as a
   generated class plus a page-scoped <style> tag keyed to a global block
   counter, which cannot be predicted or hand-written into a pattern file.
   Column span is read off the Style pill instead, via :has() — the same
   pill that already picks the treatment also decides the width, exactly as
   `units` did in the old render.php, and it works whether the query block
   was inserted from the pattern or from its own block-variation entry. */
.mhm-mosaic-grid {
	display:               grid;
	grid-template-columns: repeat(3, 1fr);
	gap:                   var(--wp--preset--spacing--50, 30px);
}

/* The group's default Flow layout puts a top margin on every child but the
   first (a global :where() rule, so this normal-specificity reset always
   wins) — harmless in a stacked column, but it would push three of the four
   grid rows down out of alignment with their row partner. */
.mhm-mosaic-grid > * {
	margin-block: 0;
}

.mhm-mosaic-grid > .wp-block-query:has(.is-style-mhm-mosaic-feature) {
	grid-column: span 2;
}

@media (max-width: 1024px) {

	.mhm-mosaic-grid { grid-template-columns: repeat(2, 1fr); }
	.mhm-mosaic-grid > .wp-block-query:has(.is-style-mhm-mosaic-feature) { grid-column: span 2; }
	.wp-block-group.is-style-mhm-mosaic-feature { min-block-size: 300px; }
}

@media (max-width: 767px) {

	.mhm-mosaic-grid { grid-template-columns: 1fr; }
	.mhm-mosaic-grid > .wp-block-query { grid-column: span 1; }
	.wp-block-group.is-style-mhm-mosaic-feature { min-block-size: 280px; }
}
