/*
Theme Name: DEVORA
Theme URI: https://weave.co.nz
Author: Weave Digital Studio
Author URI: https://weave.co.nz
Description: DEVORA block theme. Presentation only, site behaviour lives in the weave-site-functions plugin.
Requires at least: 7.0
Tested up to: 7.1
Requires PHP: 8.1
Version: 0.4.158
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: weave-blocks
Tags: block-patterns, full-site-editing, one-column, custom-colors, custom-logo, editor-style, wide-blocks
*/

/*
 * Custom CSS policy (CLAUDE.md hard rule 5): everything that can be a token or a block support lives in theme.json.
 * Each rule below names the block limitation it works around so it can be counted as FSE friction. Rule codes
 * (H1, Q1, CS3 and so on) match the weave-blocks reference theme, where each was measured and logged.
 */

/* ------------------------------------------------------------------
 * Header (stage 4): the floating bar, the Menu Designer panels and the mobile menu card.
 * Spec docs/stage-4-spec.md, sections 3 to 5; tags as in its step 9. The header scrolls away
 * (Gareth, 30 Sep), so there is no sticky or scrolled state.
 * ------------------------------------------------------------------ */

/* "Top row" below means the header nav's own list (`...-content > ul > li`), never the panel or
 * overlay navs that render inside it. */

/* HD10. The header spans the page with a transparent 20px inset over the hero, so only the bar takes
 *      the pointer. The wrapper sits above main for the hero pull-up (HD2). No overflow, transform
 *      or filter here: they would clip the absolute panels and trap core's fixed overlay. */
header.wp-block-template-part:has(> .devora-header) { position: relative; z-index: 20; }
.devora-header { pointer-events: none; }
.devora-header__bar { pointer-events: auto; }

/* HD2. A page that opens with a hero (stage 5 heroes carry .devora-hero) slides it up under the bar;
 *      markup cannot know what the page leads with. Without :has() the hero starts below the bar. */
.wp-site-blocks:has(> main > .devora-hero:first-child, > main > .wp-block-post-content > .devora-hero:first-child) > header.wp-block-template-part { margin-block-end: calc(-1 * var(--wp--custom--header--height)); }

/* HD11. With no hero (legal pages, 404, search) the white bar would sit on white with no edge, so the
 *      header gets a surface-subtle band with the inset below it too (spec 10a question 13). */
.wp-site-blocks:not(:has(> main > .devora-hero:first-child, > main > .wp-block-post-content > .devora-hero:first-child)) .devora-header {
	background-color: var(--wp--preset--color--surface-subtle);
	padding-bottom: var(--wp--custom--header--inset);
}

/* HD8. Top row items are 48 tall (12 above and below the label) and the menu has 12 inside each end;
 *      core Navigation has no item or list padding control. (Their Bold 16 comes from theme.json:
 *      core/navigation links inherit the block's 700 rather than the global link element's 600.) */
.devora-header .wp-block-navigation__responsive-container-content > ul > li > .wp-block-navigation-item__content { padding-block: var(--wp--custom--nav--item-padding-y); }
.devora-header .wp-block-navigation__responsive-container-content > ul { padding-inline: var(--wp--custom--nav--padding-x); }

/* HD3. Top row hover, open and current: label accent-strong (Figma's accent is 2.92:1 on white) with a
 *      1px accent rule at the item's bottom edge. Core pins nav link colour to inherit at (0,3,0), Menu
 *      Designer gives a toggle no ancestor class, and core marks no section on a custom post type
 *      single, so the panel's content and the body class stand in. */
.devora-header .wp-block-navigation__responsive-container-content > ul > li > .wp-block-navigation-item__content:is(:hover, :focus-visible, [aria-expanded="true"]),
.devora-header .wp-block-navigation__responsive-container-content > ul > li:is(.current-menu-item, :has(.devora-mega .current-menu-item)) > .wp-block-navigation-item__content,
.single-volcano_region .devora-header .wp-block-navigation__responsive-container-content > ul > li:has(.devora-regions) > .wp-block-navigation-item__content,
.single-resource .devora-header .wp-block-navigation__responsive-container-content > ul > li:has(> a[href$="/resources/"]) > .wp-block-navigation-item__content,
:is(.single-post, .single-event) .devora-header .wp-block-navigation__responsive-container-content > ul > li:has(> a[href$="/news-and-events/"]) > .wp-block-navigation-item__content {
	color: var(--wp--preset--color--accent-strong);
	box-shadow: inset 0 -1px 0 var(--wp--preset--color--accent);
}

/* HD4. Mega menu toggle arrow: Menu Designer hard-codes a 12px stroked chevron that turns over when
 *      open. Figma draws the 16px filled arrow 8 after the label, never rotated. */
.devora-header .wp-block-ollie-mega-menu__toggle.wp-block-navigation-item__content { display: inline-flex; align-items: center; }
.devora-header .wp-block-ollie-mega-menu__toggle .wp-block-ollie-mega-menu__toggle-icon {
	width: var(--wp--custom--nav--arrow);
	height: var(--wp--custom--nav--arrow);
	margin-left: var(--wp--custom--nav--arrow-gap);
	background-color: currentColor;
	-webkit-mask: url("assets/icons/arrow-down-fill.svg") center / contain no-repeat;
	mask: url("assets/icons/arrow-down-fill.svg") center / contain no-repeat;
}
.devora-header .wp-block-ollie-mega-menu__toggle .wp-block-ollie-mega-menu__toggle-icon svg { display: none; }

/* HD5. The desktop row needs about 1036px, so the menu card takes over below 1080. Core's switch is
 *      hard-coded at 600; its two rules are restated for 600 to 1079. */
@media (min-width: 600px) and (max-width: 1079px) {
	.devora-header__nav .wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open) { display: none; }
	.devora-header__nav .wp-block-navigation__responsive-container-open:not(.always-shown) { display: flex; }
}

/* HD6. While a panel is open the bar squares its bottom corners so bar and panel read as one card
 *      (Figma grows the navbar box). Two boxes in the build, and the bar's radius is an inline style. */
.devora-header__bar:has(.wp-block-ollie-mega-menu__toggle[aria-expanded="true"]) {
	border-bottom-left-radius: 0 !important;
	border-bottom-right-radius: 0 !important;
}

/* HD12. The panel hangs from the header, not from its menu item. Menu Designer places a full-width panel
 *      with inline top, left and width that it measures at page load and on resize; when that goes
 *      stale (Gareth, 30 Sep: Chrome's device mode on dev left it 360px to the right, the width the
 *      window had grown by) the panel slides off the page. With the item, the list and the navigation's
 *      wrappers static on desktop, the containing block is the full-width .devora-header, so the panel
 *      sits at left 0, full width, and at the bar's bottom edge (the header height: inset plus bar),
 *      whatever the script measured. Its root's padding already centres it on the 1400 bar. */
@media (min-width: 1080px) {
	.devora-header .devora-header__bar :is(.wp-block-navigation, .wp-block-navigation__responsive-container:not(.is-menu-open), .wp-block-navigation__responsive-dialog, .wp-block-navigation__container, .wp-block-ollie-mega-menu) { position: static; }
	.devora-header .wp-block-ollie-mega-menu__menu-container { top: var(--wp--custom--header--height) !important; left: 0 !important; width: 100% !important; max-width: none !important; }
}

/* HD7. Menu Designer's panel close button reads --wp--preset--spacing--medium, undefined here, and
 *      fades in over a blurred white square with a hard-coded black X whenever the pointer is over
 *      the panel. Figma draws none, so it shows for keyboard focus only, at the panel's top right. */
.devora-header .wp-block-ollie-mega-menu__menu-container .menu-container__close-button {
	top: var(--wp--preset--spacing--10);
	right: calc(max(var(--wp--custom--header--inset), (100% - var(--wp--custom--header--max-width)) / 2) + var(--wp--preset--spacing--10));
	background-color: var(--wp--preset--color--surface);
	backdrop-filter: none;
}
.devora-header .wp-block-ollie-mega-menu__menu-container .menu-container__close-button:not(:focus-visible) { opacity: 0; }
.devora-header .wp-block-ollie-mega-menu__menu-container .menu-container__close-button svg path { fill: var(--wp--preset--color--accent-text-strong); }

/* HD13. Reduced motion: Menu Designer's own reduced-motion rule drops only the slide transform and keeps
 *      its 0.1s opacity fade, so the panels appear at once here (spec 9b). Plugin CSS, (0,4,0). */
@media (prefers-reduced-motion: reduce) {
	.devora-header .wp-block-ollie-mega-menu .wp-block-ollie-mega-menu__toggle[aria-expanded] ~ .wp-block-ollie-mega-menu__menu-container { transition: none; }
}

/* HD9. Menu and close buttons: Figma's 36px glyphs (longer, rounded bars than core's `menu` icon) in
 *      a 44 target, as masks of the theme icons. The overlay close block shows its "Close menu"
 *      text to screen readers only, because its icon mode is named just "Close". */
.devora-header__nav .wp-block-navigation__responsive-container-open,
.devora-overlay__close.wp-block-navigation-overlay-close {
	width: var(--wp--custom--overlay--button);
	height: var(--wp--custom--overlay--button);
	align-items: center;
	justify-content: center;
	color: var(--wp--preset--color--accent-text-strong);
}
.devora-header__nav .wp-block-navigation__responsive-container-open svg { display: none; }
.devora-header__nav .wp-block-navigation__responsive-container-open::before,
.devora-overlay__close::before {
	content: "";
	width: var(--wp--custom--overlay--icon);
	height: var(--wp--custom--overlay--icon);
	background-color: currentColor;
	-webkit-mask: url("assets/icons/functional-menu.svg") center / contain no-repeat;
	mask: url("assets/icons/functional-menu.svg") center / contain no-repeat;
}
.devora-overlay__close::before { -webkit-mask-image: url("assets/icons/functional-close.svg"); mask-image: url("assets/icons/functional-close.svg"); }
.devora-overlay__close .wp-block-navigation-overlay-close__text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* MP1. Panel link rows (Related Link Item 817:24211): an 8px dot centred in a 20px box, 8 before the
 *      label, top-aligned when a label wraps; rows 32 tall. The dot is drawn at 40% opacity in decor-8,
 *      and in accent-text on hover (icon/arrow-circle/main in 817:24211 and 817:24208); decorative.
 *      Navigation links and Post Template items have no marker or row padding control. */
.devora-link-list li {
	position: relative;
	padding-block: calc(var(--wp--preset--spacing--10) / 2);
	padding-left: calc(var(--wp--custom--panel--bullet-box) + var(--wp--custom--panel--bullet-gap));
}
.devora-link-list li::before {
	content: "";
	position: absolute;
	left: calc((var(--wp--custom--panel--bullet-box) - var(--wp--custom--panel--bullet-dot)) / 2);
	top: calc(var(--wp--preset--spacing--10) / 2 + (1.5em - var(--wp--custom--panel--bullet-dot)) / 2);
	width: var(--wp--custom--panel--bullet-dot);
	height: var(--wp--custom--panel--bullet-dot);
	border-radius: 50%;
	background-color: var(--wp--preset--color--decor-8);
	opacity: 0.4;
}
.devora-link-list li:has(a:hover)::before { background-color: var(--wp--preset--color--accent-text); }
:is(.devora-link-list, .devora-overlay__regions) .wp-block-post-title { margin: 0; } /* Post Title carries heading block margins */

/* MP2. Panel link colour and underline (DL1's border-strong rule, at Figma's 50% offset, so it sits just
 *      under the 24 line box), hover decor-6 with the rule in currentColor (Figma's accent hover is 2.56:1
 *      on accent-subtle). theme.json sets nav links to no decoration and core pins their colour at (0,3,0). */
.devora-link-list .wp-block-navigation-item__content.wp-block-navigation-item__content,
.devora-link-list .wp-block-post-title a {
	color: var(--wp--preset--color--accent-text-strong);
	text-decoration: underline 1px var(--wp--preset--color--border-strong);
	text-underline-offset: 0.5em;
}
.devora-link-list .wp-block-navigation-item__content.wp-block-navigation-item__content:hover,
.devora-link-list .wp-block-post-title a:hover { color: var(--wp--preset--color--decor-6); text-decoration-color: currentColor; }
/* MP2b. A link that wraps (Potential impact on people, buildings, economy and the environment) gets
 *       room between lines: at Figma's 24 line the first line's rule touches the second line's letters
 *       (Gareth, 30 Sep). The line is 2em with a -0.25em margin above and below, so a one-line link keeps
 *       its box, baseline and rule exactly where they were and only wrapped lines move apart. */
.devora-link-list .wp-block-navigation-item__content.wp-block-navigation-item__content,
.devora-link-list .wp-block-post-title {
	line-height: 2;
	margin-block: -0.25em;
}

/* MP3. The 12 regions fill down each of the three columns (Figma 817:24545 to 817:24555); Post
 *      Template's grid fills across rows. */
.devora-mega .devora-regions .wp-block-post-template { grid-auto-flow: column; grid-template-rows: repeat(4, auto); }

/* MP8. Saved mega-menu panels have no shadow attribute. Apply the shared dropdown token to
 *      their class so every existing panel gets a fine inset edge and soft lower shadow without
 *      changing navigation content or the panel dimensions. */
@media (min-width: 1080px) {
	.devora-header .devora-mega__panel { box-shadow: var(--wp--preset--shadow--dropdown); }
}

/* MP7. Our Volcanic Story columns (903:31480 to 903:31542): each column is its tile then its card, and the
 *      three share the panel's rows, so the cards stay equal height and the lists start level even when one
 *      kicker wraps (1080 to about 1150). Group has no subgrid control. */
.devora-mega__panel > .devora-mega__column { display: grid; grid-template-rows: subgrid; grid-row: span 3; }
.devora-mega__column > .devora-mega__card { display: grid; grid-template-rows: subgrid; grid-row: span 2; align-content: start; }

/* MP4. Story hub links are stored in sentence case and drawn in capitals in the desktop panel only
 *      (the mobile list shows them in sentence case, 903:34268). */
.devora-mega .devora-panel-hub > .wp-block-navigation-item__content { text-transform: uppercase; }

/* MP9. Core Navigation submenus are floating flyouts by default. Story child pages need an
 *      always-visible, indented hierarchy in both the mega menu and mobile overlay. */
.devora-header :is(.devora-mega, .devora-overlay) .wp-block-navigation .devora-panel-branch.wp-block-navigation-item {
	flex-direction: column;
	align-items: flex-start;
}
.devora-header :is(.devora-mega, .devora-overlay) .wp-block-navigation .devora-panel-branch.wp-block-navigation-item > .wp-block-navigation__submenu-icon.wp-block-navigation-submenu__toggle { display: none; }
.devora-header :is(.devora-mega, .devora-overlay) .wp-block-navigation .devora-panel-branch.wp-block-navigation-item > .wp-block-navigation__submenu-container.wp-block-navigation-submenu {
	position: static;
	visibility: visible;
	opacity: 1;
	width: auto;
	min-width: 0;
	height: auto;
	overflow: visible;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--wp--preset--spacing--10);
	margin-block-start: var(--wp--preset--spacing--10);
	padding: 0;
	padding-inline-start: var(--wp--preset--spacing--10);
	border: 0;
	background: transparent;
	color: inherit;
}
/* MP9b. Core adds flyout link padding and MP1 adds a dot gutter. Child links need neither;
 *       keep a small nesting inset and give long page labels the full remaining row width. */
.devora-header :is(.devora-mega, .devora-overlay) .wp-block-navigation .devora-panel-branch > ul > li,
.devora-header :is(.devora-mega, .devora-overlay) .wp-block-navigation .devora-panel-branch > ul > li > .wp-block-navigation-item__content {
	padding-inline: 0;
}
.devora-link-list .devora-panel-branch > ul > li::before { content: none; }
/* MP9c. Core flyout links retain vertical padding inside the story hierarchy. Match the
 *       surrounding mega-menu rows; the overlay keeps its larger touch targets. */
.devora-header .devora-mega .wp-block-navigation .devora-panel-branch.wp-block-navigation-item {
	padding-block-end: 0;
}
.devora-header .devora-mega .wp-block-navigation .devora-panel-branch > ul > li > .wp-block-navigation-item__content {
	padding-block: 0;
	margin-block: -0.25em;
}
.devora-header .devora-mega .wp-block-navigation .devora-panel-branch.wp-block-navigation-item > .wp-block-navigation__submenu-container.wp-block-navigation-submenu {
	gap: var(--wp--preset--spacing--20);
	margin-block-start: var(--wp--preset--spacing--30);
}

/* MP5. Hero text shadow on the tile and promo titles; text shadow has no block support. (Their -2%
 *      heading tracking is a block attribute: they are paragraphs, so the heading element's does not reach.) */
.devora-tile-title { text-shadow: var(--wp--custom--text-shadow--hero); }

/* MP6. Promo cards: heading at the top, "Explore the map" at the foot with the arrow at the far right
 *      (Figma 817:24658), the label underlined in border-strong (currentColor on hover); Cover has no
 *      space-between control, Paragraph no icon slot, and DL1 sets no colour on a link inside Cover. White
 *      ring on the image (A1). */
.devora-promo { --devora-ring: var(--wp--preset--color--white); }
.devora-promo .wp-block-cover__inner-container { display: flex; flex-direction: column; justify-content: space-between; align-self: stretch; }
.devora-promo-link a {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--wp--custom--panel--icon-gap);
	padding-block: calc(var(--wp--preset--spacing--10) / 2);
	color: var(--wp--preset--color--text-inverse);
	text-decoration: underline 1px var(--wp--preset--color--border-strong);
	text-underline-offset: 0.5em;
}
.devora-promo-link a::after {
	content: "";
	flex: none;
	width: var(--wp--custom--panel--icon);
	height: var(--wp--custom--panel--icon);
	background-color: var(--wp--preset--color--border);
	-webkit-mask: url("assets/icons/arrow-right.svg") center / contain no-repeat;
	mask: url("assets/icons/arrow-right.svg") center / contain no-repeat;
	transition: transform 0.15s ease-out;
}
.devora-promo-link a:hover { color: var(--wp--preset--color--text-inverse); text-decoration-color: currentColor; }
.devora-promo-link a:hover::after { transform: translateX(var(--wp--preset--spacing--10)); }
@media (prefers-reduced-motion: reduce) { .devora-promo-link a::after { transition: none; } }

/* OV1. Mobile menu accordion rows (532:1898): Kicker/Default Mb in Manrope with 5% tracking, and the
 *      16px line chevron in accent-text (Figma's decor-8 is 2.59:1), down when closed, up when open.
 *      Core draws a "+" that turns 45 degrees, and heading elements take the Geist heading family. */
.devora-overlay .wp-block-accordion-heading {
	font-family: var(--wp--preset--font-family--body);
	line-height: var(--wp--custom--line-height--kicker);
	letter-spacing: var(--wp--custom--letter-spacing--kicker);
}
.devora-overlay .wp-block-accordion-heading__toggle-icon {
	width: var(--wp--custom--nav--arrow);
	height: var(--wp--custom--nav--arrow);
	font-size: 0;
	background-color: var(--wp--preset--color--accent-text);
	-webkit-mask: url("assets/icons/arrow-down-line.svg") center / contain no-repeat;
	mask: url("assets/icons/arrow-down-line.svg") center / contain no-repeat;
}
.devora-overlay .wp-block-accordion-item.is-open > .wp-block-accordion-heading .wp-block-accordion-heading__toggle-icon {
	transform: none;
	-webkit-mask-image: url("assets/icons/arrow-up-line.svg");
	mask-image: url("assets/icons/arrow-up-line.svg");
}

/* OV2. Overlay links: no underline (Figma draws none; each row is its own target), hover underlines.
 *      The current page is accent-strong and ExtraBold, since accent-strong beside accent-text rows is
 *      only 1.22:1 and colour cannot carry the state alone. */
.devora-overlay a:where(:not(.wp-element-button)) { text-decoration: none; }
.devora-overlay a:where(:not(.wp-element-button)):hover { text-decoration: underline; }
.devora-overlay .current-menu-item > .wp-block-navigation-item__content.wp-block-navigation-item__content { color: var(--wp--preset--color--accent-strong); font-weight: 800; }
/* The three direct links share the uppercase treatment of the three top-level accordion headings. */
.devora-overlay__list > .devora-overlay__link a { text-transform: uppercase; }

/* OV3. The card hugs its content up to the viewport less the inset, then the list scrolls inside it
 *      (Explore open is 842 tall against an 852 frame); Group has no max-height or overflow control. */
.devora-overlay__card { max-height: calc(100dvh - 2 * var(--wp--custom--header--inset)); overflow: hidden; }
.devora-overlay__list { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
/* On tablets (600 to 1079) the card stays as wide as the bar it opens from, but its rows stop at 560 so the
 * chevrons sit near their labels (Liz, 30 Sep: no need for full width). No effect on a phone. */
.devora-overlay__list > * { max-width: var(--wp--custom--overlay--list-max-width); }

/* OV4. Rows are drawn 22 to 24 tall with 24 between: each top-level row carries the 24 above and below
 *      it and a surface-subtle rule between rows (none inside an open section), and every link and
 *      toggle is padded out to a 44 target with the padding taken back by a negative margin, so the
 *      drawn spacing stands. No per-row control in core. */
.devora-overlay__list > :not(.wp-block-accordion),
.devora-overlay__list > .wp-block-accordion > .wp-block-accordion-item { padding-block: var(--wp--preset--spacing--40); }
.devora-overlay__list > * + *,
.devora-overlay__list > .wp-block-accordion > .wp-block-accordion-item + .wp-block-accordion-item { border-top: 1px solid var(--wp--preset--color--surface-subtle); }
.devora-overlay :is(.devora-overlay__link a, .wp-block-navigation-item__content, .wp-block-post-title a) {
	display: block;
	padding-block: var(--wp--custom--overlay--row-pad);
	margin-block: calc(-1 * var(--wp--custom--overlay--row-pad));
}
.devora-overlay .wp-block-accordion-heading__toggle {
	padding-block: var(--wp--custom--overlay--row-pad);
	margin-block: calc(-1 * var(--wp--custom--overlay--row-pad));
}
/* The heading holds its toggle's negative margins, so they never collapse through a level 1 heading into
 * the first level 2 toggle below it, where only one of the two was taken back (10px too low, 30 Sep review). */
.devora-overlay .wp-block-accordion-heading { display: flow-root; }

/* OV5. The mobile region list keeps Figma's two columns (820:41050) at every width; core drops every
 *      Post Template grid to one column below 600 with a (0,4,0) selector, hence the repeated class. */
.devora-overlay .devora-overlay__regions.wp-block-post-template-is-layout-grid.wp-block-post-template-is-layout-grid.wp-block-post-template-is-layout-grid.wp-block-post-template-is-layout-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* T2. Live balances heading line breaks (text-wrap: balance on the H1, so "Transforming" sits
 *     alone on line one). theme.json has no text-wrap property. */
h1, h2, h3 { text-wrap: balance; }

/* T3. Live sets text-wrap: pretty on paragraphs (fewer orphans, slightly different line breaks in
 *     the quotes and the CTA prompt). theme.json has no text-wrap property. */
p { text-wrap: pretty; }

/* DT1. Mobile heading line heights. Figma's H4 Bold is 28/36 on desktop and 22/28 on mobile, and
 *     H6 20/24 and 18/20: the only two headings that do not follow size plus 4px at both widths.
 *     theme.json line-height takes one value, so one media rule for the two. */
@media (max-width: 768px) {
	h4 { line-height: calc(1em + 0.375rem); }
	h6 { line-height: calc(1em + 0.125rem); }
}

/* DT2. Bold is ExtraBold. Figma's Body/Bold and Caption/Bold are Manrope 800; theme.json has no
 *     strong element and the browser's "bolder" stops at 700. */
:where(strong, b) { font-weight: 800; }

/* ------------------------------------------------------------------
 * Testimonials .
 * ------------------------------------------------------------------ */

/* Q1. Scroller. Core has no carousel block, so the track is a nowrap flex Group of 100%-wide
 *     slide Groups (child sizing from block attributes) that scrolls horizontally with scroll
 *     snap; Group has no overflow or scroll-snap controls. Works with no script (swipe, trackpad,
 *     keyboard scroll); the buttons and counter are wired by assets/js/testimonials.js, and the
 *     nav stays hidden until that script marks the section ready so a no-JS page shows no dead
 *     buttons. Live hides the scrollbar too. Slides carry selfStretch "fixed" / flexSize 100%
 *     from the block, but core's fixed child sizing sets flex-basis without flex-shrink: 0, so
 *     three slides still squeezed into one row; the flex shorthand here makes fixed mean fixed. */
.weave-testimonials__track {
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	overscroll-behavior-x: contain;
	scrollbar-width: none;
}
.weave-testimonials__track::-webkit-scrollbar { display: none; }
.weave-testimonials__track > * { flex: 0 0 100%; scroll-snap-align: start; }
.weave-testimonials:not(.is-ready) .weave-testimonials__nav { visibility: hidden; }

/* Q2. Icon-only prev / next. Core Button carries text only and the Icon block cannot be a
 *     button, so each control is a Button block (tagName button, outline style) whose visible
 *     label is pushed out of a 38px round box (text stays in the accessibility tree) and whose
 *     arrow is the icon set glyph as a currentColor mask, so the outline style's hover and
 *     focus (navy fill, white text) invert the arrow too. Reference: 38px glyph, 28px at 768 and below. */
.weave-testimonials__nav .wp-block-button__link {
	position: relative;
	width: 2.375rem;
	height: 2.375rem;
	padding: 0;
	overflow: hidden;
	white-space: nowrap;
	text-indent: 100%;
	border-radius: 50%;
}
.weave-testimonials__nav .wp-block-button__link::before {
	content: "";
	position: absolute;
	inset: 25%;
	background-color: currentColor;
	-webkit-mask: url("assets/images/icons/arrow-left.svg") center / contain no-repeat;
	mask: url("assets/images/icons/arrow-left.svg") center / contain no-repeat;
}
.weave-testimonials__next .wp-block-button__link::before {
	-webkit-mask-image: url("assets/images/icons/arrow-right.svg");
	mask-image: url("assets/images/icons/arrow-right.svg");
}
@media (max-width: 768px) {
	.weave-testimonials__nav .wp-block-button__link { width: 1.75rem; height: 1.75rem; }
}

/* ------------------------------------------------------------------
 * CTA .
 * ------------------------------------------------------------------ */

/* C1. Button arrow. Core Button carries text only (no icon slot; the Icon block cannot sit inside
 *     a button), so the "White with arrow" style draws the icon set arrow-up-right after the
 *     label as a currentColor mask; it inverts with the style's hover / focus from theme.json.
 *     Reference: 22px glyph 8px after a 20px label, label never wraps. */
.is-style-white-arrow .wp-block-button__link { white-space: nowrap; }
.is-style-white-arrow .wp-block-button__link::after {
	content: "";
	display: inline-block;
	width: 0.85em;
	height: 1.1em;
	margin-left: 0.4em;
	vertical-align: -0.2em;
	background-color: currentColor;
	-webkit-mask: url("assets/images/icons/arrow-up-right.svg") center / contain no-repeat;
	mask: url("assets/images/icons/arrow-up-right.svg") center / contain no-repeat;
}

/* ------------------------------------------------------------------
 * Service pages .
 * ------------------------------------------------------------------ */

/* C2. CTA text column. The column must shrink beside the button at desktop (so a long display line
 *     wraps to two lines rather than pushing the button under it) and drop below the button on
 *     mobile. Core's "fill" child sizing is flex: 1 1 auto, whose auto basis makes a wrapping row
 *     wrap first and shrink never; basis 0 plus a 600px floor (the prompt's width) does both. */
.weave-cta__panel > .wp-block-group:first-child { flex: 1 1 0; min-width: min(100%, 600px); }

/* SV1. FAQ accordion icon. Core renders the "+" at the heading's size and colour (32px Parchment);
 *      the reference shows a 20px white glyph. Two declarations; the glyph itself is core's. */
.weave-service__faq-list .wp-block-accordion-heading__toggle-icon { font-size: var(--wp--preset--font-size--body); color: var(--wp--preset--color--text-inverse); }

/* A1. Focus ring. Browsers' default focus-visible ring (blue, 1px) is 2.9:1 on the navy bands,
 *     under WCAG 1.4.11. theme.json has no focus-visible outline, so: a 2px ring (custom focus.ring) in primary, and
 *     white on dark surfaces. The colour is a custom property set by the nearest painted surface,
 *     so a light panel inside a navy band gets the dark ring back (DEVORA, 29 Sep: the old swap
 *     listed only three light classes). */
:root { --devora-ring: var(--wp--preset--color--primary); }
.has-surface-inverse-background-color, .has-primary-background-color, .has-primary-strong-background-color,
.has-text-body-background-color, .has-black-background-color, .has-text-on-accent-background-color { --devora-ring: var(--wp--preset--color--white); }
.has-surface-background-color, .has-white-background-color, .has-surface-subtle-background-color,
.has-surface-raised-background-color, .has-surface-panel-background-color, .has-accent-subtle-background-color,
.has-accent-background-color, .has-accent-inverse-background-color, .has-border-background-color { --devora-ring: var(--wp--preset--color--primary); }
:focus-visible { outline: var(--wp--custom--focus--ring) solid var(--devora-ring); outline-offset: 3px; }

/* descendants only: the ring sits on the surface behind the element, so a navy button on a light band keeps the navy ring */
.has-surface-inverse-background-color .has-white-background-color :focus-visible,
.has-surface-inverse-background-color .has-surface-background-color :focus-visible,
.has-surface-inverse-background-color .has-accent-background-color :focus-visible { outline-color: var(--wp--preset--color--primary); } /* a light panel inside a navy band */

/* ------------------------------------------------------------------
 * Case studies : archive, taxonomy archives, single project.
 * ------------------------------------------------------------------ */

/* ------------------------------------------------------------------
 * Insights : posts index, archives, single post.
 * ------------------------------------------------------------------ */

/* I1. The first card spans two of the three grid columns (live's featured treatment is
 *     positional). Post Template's grid is uniform, as on the homepage (W1). */
@media (min-width: 769px) { .weave-insights-grid .wp-block-post-template > li:first-child { grid-column: span 2; } }

/* I2. Category row. The Categories block renders a bulleted <ul>; the reference shows a centred wrapping
 *     row of links with no markers. No layout controls on the block. */
.weave-term-row { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--wp--preset--spacing--10) var(--wp--preset--spacing--40); padding: 0; margin: 0; list-style: none; }
.weave-term-row li { margin: 0; }
.weave-term-row a { color: inherit; text-decoration: none; }
.weave-term-row a:hover, .weave-term-row a:focus-visible, .weave-term-row .current-cat > a { text-decoration: underline; }

/* I3. Article body headings. Post Content on a post uses live's article scale (h2 at the subheading size, h3 at the small-heading size) while the global h2 / h3 are the homepage display sizes.
 *     theme.json's post-content element styles would also resize the homepage headings, so the
 *     override is scoped to the single post template. */
.weave-single-post .wp-block-post-content h2 { font-size: var(--wp--preset--font-size--subheading); color: var(--wp--preset--color--primary-strong); }
.weave-single-post .wp-block-post-content h3 { font-size: var(--wp--preset--font-size--small-heading); font-weight: 300; color: var(--wp--preset--color--primary); }
.weave-single-post .wp-block-post-content ul, .weave-single-post .wp-block-post-content ol { padding-left: var(--wp--preset--spacing--50); }
.weave-single-post .wp-block-post-content .wp-block-quote { font-size: var(--wp--preset--font-size--headline); font-weight: 500; color: var(--wp--preset--color--text-muted); line-height: 1.4; }

/* I4. Single header categories: Post Terms centres through textAlign but the separator is a space;
 *     give the links live's 40px visual gap. */
.weave-post-categories a { margin: 0 var(--wp--preset--spacing--20); }

/* S1: Sector feature list. Core List cannot hold a heading and a paragraph per item and has no icon
   marker control, so the ticked info list is a Group of "Feature" Groups (h3 + p) and the tick is a
   masked SVG pseudo-element in a 22px column, 22px from the text (live: icon set glyph, 22 x 22). */
.weave-tick-list__item {
	display: grid;
	grid-template-columns: 22px 1fr;
	column-gap: 22px;
}
.weave-tick-list__item > * { grid-column: 2; }
.weave-tick-list__item::before {
	content: "";
	grid-column: 1;
	grid-row: 1;
	width: 22px;
	height: 22px;
	margin-top: 0.25em;
	background-color: currentColor;
	-webkit-mask: url("assets/images/icons/check.svg") no-repeat center / contain;
	mask: url("assets/images/icons/check.svg") no-repeat center / contain;
}

/* L1. Prose pages (legal): space above headings. Core's flow layout sets every child's margin to the
 *     block gap (20 here), so headings sit flush on the paragraph before them; the reference has 136 / 108
 *     above H2 / H3 at 1440 and 51 / 40 at 390. theme.json heading margins lose to that layout rule. */
.weave-page .entry-content > h2.wp-block-heading { margin-block-start: var(--wp--preset--spacing--grid-row); }
.weave-page .entry-content > h3.wp-block-heading { margin-block-start: var(--wp--preset--spacing--card-row); }

/* S2. Sector showcase panel: the one band that stops widening. Live caps it at 1400 and centres it at
 *     2000 while every other band follows the gutter; a Group has no max-width control of its own. */
.weave-sector-showcase.alignwide { max-width: min(100%, 1400px); margin-inline: auto; }

/* ============ DEVORA (build 2026-09-29) ============ */

/* DL1. Link underline. theme.json's link element sets the colour and the decoration but cannot set
 *     the decoration colour, thickness or offset. Figma draws inline links with a very pale rule
 *     (sea glass/mist, 1.2:1 on white); the link text is only 2:1 against body text, so the rule is
 *     the cue that is not colour (WCAG 1.4.1) and uses sea glass/tint, as the Copy Citation link
 *     does. It darkens on hover. */
:where(.wp-site-blocks) a:where(:not(.wp-element-button)) {
	text-decoration-color: var(--wp--preset--color--border-strong);
	text-decoration-thickness: 1px;
	text-underline-offset: 0.3em;
}
:where(.wp-site-blocks) a:where(:not(.wp-element-button)):hover { text-decoration-color: currentColor; }

/* DL2. Links on dark bands follow the Figma dark mode tokens: Blue tint text with the 60% teal
 *     underline, Teal mid on hover. theme.json has no per-background element styles. */
.has-surface-inverse-background-color a:where(:not(.wp-element-button)) {
	color: var(--wp--preset--color--text-inverse-muted);
	text-decoration-color: var(--wp--custom--color--link-underline-inverse);
}
.has-surface-inverse-background-color a:where(:not(.wp-element-button)):hover { color: var(--wp--preset--color--accent-inverse); }

/* DQ1. Quote mark. Figma's Quote (692:41211) opens with a 36px mark above the text; core Quote and
 *     Pullquote have no icon slot, so the mark is a mask of assets/icons/quote.svg in decor-8
 *     (sea glass/mid, decorative). Pullquote is centred and bordered by core; it takes the same panel, left
 *     aligned, so either block gives the design. */
.wp-block-quote::before,
.wp-block-pullquote blockquote::before {
	content: "";
	display: block;
	width: var(--wp--custom--quote--mark-size);
	height: var(--wp--custom--quote--mark-size);
	margin-bottom: var(--wp--preset--spacing--40);
	background-color: var(--wp--custom--quote--mark-colour);
	-webkit-mask: url("assets/icons/quote.svg") center / contain no-repeat;
	mask: url("assets/icons/quote.svg") center / contain no-repeat;
}
.wp-block-pullquote { text-align: left; }
.wp-block-pullquote blockquote { margin: 0; }

/* DN1. Note label. The Figma Note leads with a bold "Note:" in a colour of its own; theme.json
 *     variations have no strong element, so the label colour is one rule. */
:where(.is-style-note) :where(strong, b) { color: var(--wp--preset--color--accent-text); }

/* === Footer (stage 4) === */

/* F0. Footer measures (Footer Desktop 533:2737, Mobile 533:2736, tokens custom.footer.*) gathered into local
 *     properties, so F3 swaps in the mobile values below 1024 in one place; theme.json has no breakpoints. */
.devora-footer {
	--devora-footer-edge: var(--wp--custom--footer--edge);             /* Shape 525:10304, 80 */
	--devora-footer-tear: var(--wp--custom--footer--tear);             /* drawn width of both torn vectors */
	--devora-footer-pad-y: var(--wp--preset--spacing--section);        /* Block 525:10308, 80 at 1440 */
	--devora-footer-gap: var(--wp--custom--footer--gap);               /* Block gap, 60 */
	--devora-footer-rule: var(--wp--custom--footer--rule);             /* Line 533:2734, 20 */
	--devora-footer-list-gap: var(--wp--custom--footer--list-gap);     /* lists 525:10428, 525:10435, 525:10588, 20 */
	--devora-footer-social: var(--wp--custom--footer--social);         /* button 525:10436, 40 */
	--devora-footer-social-icon: var(--wp--custom--footer--social-icon); /* its icon, 24 */
	--devora-footer-social-gap: var(--devora-footer-list-gap);
}

/* F1. Torn top edge (Shape 525:10304, vector 525:10306). Decorative, and its colour has to follow surface-inverse, so
 *     it is a mask over a painted box rather than an image; above the tear the section before shows through. Drawn
 *     1440 wide at the foot of the 80 box, centred and cropped below 1440, stretched above so the sides never open
 *     (1px past each side, or the vector's anti-aliased ends show as a pale column at the viewport edge).
 *     The footer's own root block gap would put a strip of page colour between the last section and the tear. */
footer.wp-block-template-part:has(> .devora-footer) { margin-block-start: 0; }
.devora-footer::before {
	content: "";
	display: block;
	height: var(--devora-footer-edge);
	background-color: var(--wp--preset--color--surface-inverse);
	-webkit-mask: url("assets/images/dividers/paper-torn-edge-top.svg") bottom center / max(100% + 0.125rem, var(--devora-footer-tear)) auto no-repeat;
	mask: url("assets/images/dividers/paper-torn-edge-top.svg") bottom center / max(100% + 0.125rem, var(--devora-footer-tear)) auto no-repeat;
}
.devora-footer__body { padding-block: var(--devora-footer-pad-y); }

/* F2. Torn line (Line 533:2734, vector 525:10447) between the two rows, full bleed through the side padding, a
 *     text-body mask for the same reason as F1. A pseudo-element of the bottom row, so the editor shows no empty
 *     block and nothing reaches the accessibility tree; the row's top margin makes its room. It spans the viewport
 *     from the row's centre, and the band clips the scrollbar's width so it never scrolls sideways. */
.devora-footer__body { overflow-x: clip; }
.devora-footer__body > .devora-footer__bottom { position: relative; margin-block-start: calc(2 * var(--devora-footer-gap) + var(--devora-footer-rule)); }
.devora-footer__bottom::before {
	content: "";
	position: absolute;
	top: calc(-1 * (var(--devora-footer-gap) + var(--devora-footer-rule)));
	left: calc(50% - 50vw);
	width: 100vw;
	height: var(--devora-footer-rule);
	background-color: var(--wp--preset--color--text-body);
	-webkit-mask: url("assets/images/dividers/paper-torn-line.svg") center / max(100% + 0.125rem, var(--devora-footer-tear)) 100% no-repeat;
	mask: url("assets/images/dividers/paper-torn-line.svg") center / max(100% + 0.125rem, var(--devora-footer-tear)) 100% no-repeat;
}

/* F3. Layout. Grid layout has no vertical alignment control (Bottom Info 525:10448 centres its row) and, in
 *     column-count mode, no breakpoints: below 1024 (533:2736) the columns go 2 x 2 with 40 between rows and the
 *     bottom row stacks, centred, 36 under the logo and 16 between the lines. */
.devora-footer .devora-footer__bottom { align-items: center; }
/* The legal menu and the credit share the last two columns (Gareth, 30 Sep), so the three legal links sit on one
 * line under Keep reading and the credit holds the right edge, even when it wraps below the links. */
.devora-footer .devora-footer__end > :last-child { margin-inline-start: auto; }
@media (max-width: 1023.98px) {
	.devora-footer {
		--devora-footer-edge: var(--wp--preset--spacing--60);
		--devora-footer-tear: var(--wp--custom--footer--tear-mobile);
		--devora-footer-pad-y: var(--wp--custom--width--gutter);
		--devora-footer-gap: var(--wp--preset--spacing--60);
		--devora-footer-rule: var(--wp--custom--footer--rule-mobile);
		--devora-footer-social: var(--wp--custom--footer--social-mobile);
		--devora-footer-social-icon: var(--wp--custom--footer--social-icon-mobile);
		--devora-footer-social-gap: var(--wp--preset--spacing--30);
	}
	.devora-footer .devora-footer__top { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--devora-footer-gap); }
	.devora-footer .devora-footer__bottom { grid-template-columns: minmax(0, 1fr); justify-items: center; row-gap: var(--wp--preset--spacing--30); text-align: center; }
	.devora-footer .devora-footer__bottom > .devora-logo + * { margin-block-start: calc(var(--wp--custom--footer--logo-gap) - var(--wp--preset--spacing--30)); }
	.devora-footer .devora-footer__legal { --navigation-layout-justify: center; --navigation-layout-justification-setting: center; }
	.devora-footer .devora-footer__bottom > .devora-footer__end { grid-column: auto; flex-direction: column; justify-content: center; }
	.devora-footer .devora-footer__end > :last-child { margin-inline-start: 0; }
}

/* F4. Social buttons (525:10436, 533:2151): 40 squares (36 below 1024), the faint 1px text-body border as drawn,
 *     radius small, icon 24 (20) in decor-4. Figma draws no hover: only the icon turns accent-inverse, the square
 *     stays faint (Gareth, 30 Sep). Core's logos-only style paints brand colours, sizes the icon from the list's
 *     font size, scales the button on hover and drops the bottom border at (0,3,1), hence the (0,4,0) anchor rule. */
.devora-footer .devora-footer__social { gap: var(--devora-footer-social-gap); }
.devora-footer .devora-footer__social .wp-social-link { color: var(--wp--preset--color--decor-4); border-radius: var(--wp--preset--border-radius--small); transform: none; }
.devora-footer .devora-footer__social .wp-social-link:is(:hover, :focus-within) { color: var(--wp--preset--color--accent-inverse); }
.devora-footer .devora-footer__social .wp-social-link .wp-block-social-link-anchor { box-sizing: border-box; justify-content: center; width: var(--devora-footer-social); height: var(--devora-footer-social); border: 1px solid var(--wp--preset--color--text-body); border-radius: inherit; }
.devora-footer .devora-footer__social .wp-social-link svg { width: var(--devora-footer-social-icon); height: var(--devora-footer-social-icon); }

/* F5. Footer links on the DL2 colours: the two menus underlined in the 60% teal, every footer link turning
 *     accent-inverse with a currentColor underline on hover. Navigation has no decoration control and core pins its
 *     link colour to inherit at (0,3,0), past DL2; its 20 list gap has no spacing preset, and the legal row wraps
 *     8 apart when it runs out of room (below about 1300, and on mobile). */
.devora-footer .wp-block-navigation.devora-footer__nav { gap: var(--devora-footer-list-gap); }
.devora-footer .wp-block-navigation.devora-footer__legal { gap: var(--wp--preset--spacing--10) var(--devora-footer-list-gap); }
/* The credit link at regular weight like the legal links (Gareth, 30 Sep); the global link element sets 600. */
.devora-footer .devora-footer__end p a { font-weight: 400; }
.devora-footer .wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content { text-decoration: underline; text-decoration-color: var(--wp--custom--color--link-underline-inverse); }
.devora-footer .wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content:hover { color: var(--wp--preset--color--accent-inverse); }
.devora-footer a:where(:not(.wp-element-button)):hover,
.devora-footer .wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content:hover { text-decoration-color: currentColor; }

/* F6. Partner logos (525:10312): each at its natural size, centred in a 48 row. Post Template has no row height and
 *     the figure brings its own bottom margin. */
.devora-footer__logos .wp-block-post-template > li { display: flex; align-items: center; min-height: var(--wp--custom--footer--logo-row); }
.devora-footer__logos .devora-partner-logo { margin: 0; }

/* === end footer === */

/* === Home: shared (stage 5) === */
/* Home (stage 5), docs/stage-5-spec.md 5.5: rules more than one section group uses. Tags here: HM5 (credit
 * rows: AVF figure 2.3 and video 2.10) and the devora-show-desktop / devora-show-mobile pair of HM13 (the Why
 * images 2.4; the carousel button half of HM13 is in "news and fact sheets"). The bands that hold several
 * sections (.devora-band--featured, .devora-band--content.is-style-torn-top) come from patterns/home-band.php
 * through the builder. Theme assets for the section rules: assets/images/dividers/skyline.svg and
 * skyline-mobile.svg (HM2), assets/images/masks/brush-a.webp and brush-b.webp (HM6, lossless: alpha only),
 * assets/images/masks/paper-tear-left.svg and paper-tear-top.svg with assets/images/paper-texture.webp (HM7),
 * assets/images/illustrations/{term illustration meta}.svg (2.13). */

/* HM5. Credit rows (907:11096, I703:43882;683:29197): an Image with the class devora-credit shows the photo-credit
 *      icon 8 before its caption, the row 12 under the image. The caption has no icon slot; the icon file draws
 *      its own 40% (DI1), so the mask in the caption colour (text-muted) matches Figma. Positioned rather than
 *      flex, so a link inside the caption stays in the line. */
.devora-credit .wp-element-caption {
	position: relative;
	min-height: var(--wp--custom--credit--icon);
	margin-block: var(--wp--preset--spacing--20) 0;
	padding-inline-start: calc(var(--wp--custom--credit--icon) + var(--wp--preset--spacing--10));
}
.devora-credit .wp-element-caption::before {
	content: "";
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: 0;
	width: var(--wp--custom--credit--icon);
	height: var(--wp--custom--credit--icon);
	background-color: currentColor;
	-webkit-mask: url("assets/icons/photo-credit.svg") center / contain no-repeat;
	mask: url("assets/icons/photo-credit.svg") center / contain no-repeat;
}

/* HM13 (show pair). One of two blocks per width, where a design draws a different composition on mobile (the Why
 *      panel 2.4). Blocks have no per-width visibility in 7.1.2 stock core; 782 is where core Columns stack. */
@media (width < 782px) { :root .devora-show-desktop { display: none; } }
@media (width >= 782px) { :root .devora-show-mobile { display: none; } }

/* === end === */

/* === Home: hero and where to go === */
/* Spec 2.1 and 2.2 (patterns home-hero.php, link-cards.php). Tags: HM1 (hero text shadow), HM2 (skyline),
 * HM4 (stats list), HM15 (hero Cover height and inner column), HM18 (link card leads). The hero root carries
 * devora-hero for HD2. */

/* HM1. Hero text shadow (Figma effect on I682:27343;525:9656): the H1 and lead carry devora-text-shadow; text
 *      shadow has no block support. The A1 focus ring turns white over the texture: an image is no painted surface,
 *      so no A1 class catches it, and the navy ring was near invisible on the darkened texture. */
.devora-text-shadow { text-shadow: var(--wp--custom--text-shadow--hero); }
.devora-home-hero { --devora-ring: var(--wp--preset--color--white); }

/* HM15. Hero height and foot (spec 2.1 "Cover limits"). Cover's minHeight is a number, so the fluid token
 *      custom.hero.home.minHeight cannot go in the block, and core centres the inner container at its content
 *      height; the inner container stretches into a column here, so the Content group's space-between has the
 *      full height to share and the stats sit at the foot however long the H1 runs. */
.devora-home-hero__cover { min-height: var(--wp--custom--hero--home--min-height); }
.devora-home-hero__cover > .wp-block-cover__inner-container { display: flex; flex-direction: column; align-self: stretch; z-index: 2; }
.devora-home-hero__inner { flex: 1; width: 100%; }
/* HM15a. Cover supports one image source; the responsive picture needs its own full-Cover box. */
.devora-home-hero__picture { position: absolute; inset: 0; width: 100%; height: 100%; }
.devora-home-hero__picture > .wp-block-cover__image-background { position: absolute; inset: 0; width: 100%; height: 100%; }

/* HM2. Skyline along the hero's foot (Skyline 1243:13441, 1440 x 200; mobile 1243:13440 393 x 100 below 600): one
 *      flat white vector, so it is the surface colour painted through a mask and meets the band below without a
 *      seam. Cover has no edge control and the drawing is decoration; 1px past each side so the vector's
 *      anti-aliased ends never show a pale line at the viewport edge. Above the image and overlay, under the text. */
.devora-home-hero__cover::after {
	content: "";
	position: absolute;
	z-index: 1;
	inset-inline: -0.0625rem;
	bottom: 0;
	aspect-ratio: 1440 / 200;
	background-color: var(--wp--preset--color--surface);
	-webkit-mask: url("assets/images/dividers/skyline.svg") bottom center / 100% 100% no-repeat;
	mask: url("assets/images/dividers/skyline.svg") bottom center / 100% 100% no-repeat;
	pointer-events: none;
}
/* Editor only: selecting or hovering the Cover gives it core's outline on ::after (top 0), which moved the skyline to
 * the hero's top. The skyline keeps its foot; the outline is lost inside the mask, and List View shows the selection. */
.block-editor-block-list__layout .devora-home-hero__cover.block-editor-block-list__block.wp-block-cover::after { top: auto; }
@media (width < 600px) {
	/* HM2a. Cover has no responsive focal point or dimming control. The darker left crop and mobile
	 * scrim keep white text legible after the image replacement; desktop retains its saved appearance. */
	.devora-home-hero__cover .wp-block-cover__image-background { object-position: left center; }
	.devora-home-hero__cover > .wp-block-cover__background {
		background: var(--wp--preset--color--black);
		/* Core explicitly sets opacity to zero for the saved has-background-dim-0 class. */
		opacity: var(--wp--custom--hero--home--mobile-overlay-opacity) !important;
	}
	.devora-home-hero__cover::after {
		aspect-ratio: 393 / 100;
		-webkit-mask-image: url("assets/images/dividers/skyline-mobile.svg");
		mask-image: url("assets/images/dividers/skyline-mobile.svg");
	}
}

/* HM4. Stats (525:9644, mobile 533:1361): a core List, each item "<strong>53</strong> volcanoes across the field",
 *      so the client adds a stat with Enter. List has no layout control and no per-part type: the number is the
 *      strong, on its own line at the Heading/H3 size; the items run in a row 48 apart from 600, stacked 12 apart
 *      below, with no bullets. The row stops at 80% of the viewport, where the skyline's Sky Tower rises (x 1180 of
 *      1440), so a long stat wraps under the others rather than running into the white (it did at 1024). */
.wp-block-list.devora-stats {
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--20);
	margin-block: 0;
	padding: 0;
	list-style: none;
}
.wp-block-list.devora-stats > li { margin: 0; }
.devora-stats strong {
	display: block;
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--subheading);
	font-weight: 600;
	line-height: var(--wp--custom--line-height--heading);
	letter-spacing: var(--wp--custom--letter-spacing--heading);
}
@media (width >= 600px) {
	.wp-block-list.devora-stats { flex-flow: row wrap; column-gap: var(--wp--preset--spacing--70); max-width: calc(80vw - var(--wp--custom--width--gutter)); }
}

/* HM18. Where to go leads (2.2): the leads are the copy master's lines word for word, and GeoNet's carries its full
 *      URL as text, one unbroken word wider than the card at every width (the card grew past the viewport, 1515 at
 *      1440). The lead may break inside a word, and only where nothing else fits. */
.devora-link-card .is-style-lead { overflow-wrap: anywhere; }
/* HM18b. The three View website buttons need a visible rule before hover; core's link button style
 *        uses the pale border-accent token. These are saved page blocks, so scope the colour to Home. */
body.home .devora-link-card .is-style-link .wp-block-button__link {
	border-bottom-color: var(--wp--preset--color--accent-text);
}
body.home .devora-link-card .is-style-link .wp-block-button__link:is(:hover, :focus) {
	border-bottom-color: var(--wp--preset--color--accent-strong);
}
/* === end === */

/* === Home: featured and standard === */
/* Spec 2.3 to 2.6 and 2.8 (patterns standard.php, featured-media.php). Tags: HM3 (is-style-torn-top on the Content
 * band, from 768, and the extra bottom padding on the band before it), HM6 (is-style-brush-a and -b), HM9
 * (.devora-icon-list), HM17 (the Why card's media panel and clip). */

/* HM3. Torn top edge (414:6119, 2.5): the band's own colour painted above it through the F1 torn vector, 80 tall,
 *      from 768 up (mobile draws a straight edge). A block style, so there is no empty divider block and nothing in
 *      the accessibility tree. The mask is drawn wider than the box and centred, so the vector's ends never show. The
 *      band before it gets the 80 as an empty ::after, because its padding is inline and a stylesheet cannot add to it
 *      (a band: an unanchored :has() subject made every element on the page a candidate). */
@media (width >= 768px) {
	.is-style-torn-top { position: relative; }
	.is-style-torn-top::before {
		content: "";
		position: absolute;
		inset-inline: 0;
		bottom: 100%;
		height: var(--wp--custom--edge--paper);
		background-color: inherit;
		-webkit-mask: url("assets/images/dividers/paper-torn-edge-top.svg") bottom center / max(100% + 0.125rem, var(--wp--custom--footer--tear)) auto no-repeat;
		mask: url("assets/images/dividers/paper-torn-edge-top.svg") bottom center / max(100% + 0.125rem, var(--wp--custom--footer--tear)) auto no-repeat;
		pointer-events: none;
	}
	.devora-band:has(+ .is-style-torn-top)::after {
		content: "";
		display: block;
		height: var(--wp--custom--edge--paper);
	}
}

/* HM6. Brush masks (427:1569, 427:1570): the photo's edge is a soft painted brush, so the Image is cut by an alpha
 *      mask stretched to its 3:4 box. Image has no mask support. */
.wp-block-image:is(.is-style-brush-a, .is-style-brush-b) img {
	-webkit-mask: url("assets/images/masks/brush-a.webp") center / 100% 100% no-repeat;
	mask: url("assets/images/masks/brush-a.webp") center / 100% 100% no-repeat;
}
.wp-block-image.is-style-brush-b img {
	-webkit-mask-image: url("assets/images/masks/brush-b.webp");
	mask-image: url("assets/images/masks/brush-b.webp");
}

/* HM9. Icon lists (414:6132, 414:6199; mobile 448:2830, 448:2899): icon 36 (24 on mobile), 20 (16) to the text, the
 *      text padded to centre its first line on the icon, items 12 apart (16 on mobile), the list frame padded 8 (4).
 *      The icon style generator is fixed at 1.1em and 1.75em, and the Icon block has no fluid size. Two shapes share
 *      it: Row Groups of an Icon block and a Lead (small) paragraph, and a core List with the feature-check style. */
.devora-icon-list {
	--devora-icon: var(--wp--custom--list--icon);
	--devora-icon-pad: max(0rem, (var(--devora-icon) - var(--wp--custom--line-height--lead-small)) / 2);
}
.devora-icon-list__row.devora-icon-list__row { gap: var(--wp--custom--list--icon-gap); }
.devora-icon-list__row .wp-block-icon { flex: none; line-height: 0; }
.devora-icon-list__row .wp-block-icon svg { width: var(--devora-icon); height: var(--devora-icon); }
.devora-icon-list__row > p { padding-block: var(--devora-icon-pad); }
.devora-icon-list.devora-icon-list { padding-block: var(--wp--preset--spacing--10); }
ul.wp-block-list.devora-icon-list {
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 500;
	line-height: var(--wp--custom--line-height--lead-small);
}
ul.wp-block-list.devora-icon-list > li {
	min-height: var(--devora-icon);
	padding-block: var(--devora-icon-pad);
	padding-inline-start: calc(var(--devora-icon) + var(--wp--custom--list--icon-gap));
}
ul.wp-block-list.devora-icon-list > li + li { margin-top: var(--wp--preset--spacing--20); }
ul.wp-block-list.devora-icon-list > li::before {
	top: 0;
	width: var(--devora-icon);
	height: var(--devora-icon);
	background-color: var(--wp--preset--color--error);
}
@media (width < 768px) {
	.devora-icon-list.devora-icon-list { padding-block: calc(var(--wp--preset--spacing--10) / 2); }
	.devora-icon-list.devora-icon-list > * + * { margin-block-start: var(--wp--preset--spacing--30); }
	ul.wp-block-list.devora-icon-list > li + li { margin-top: var(--wp--preset--spacing--30); }
}

/* HM17. Why card (414:6096): the card clips its panel to the radius, and from 782 the decorative panel fills the right
 *       column at the card's full height, whatever the text's height (the panel is absolute in Figma). The text's
 *       right padding is 12 beside the panel (1284 less 108, 528 and the 636 panel) and the side padding once the card
 *       stacks. Group has no overflow control, Image cannot fill a column's height, and padding has no breakpoints.
 *       Below 1400 the column narrows while the text makes the card taller, so a cover crop would cut into the map
 *       circle (10% in from each side of the art; sliced from about 1340, only its right half left at 800). There
 *       the panel is the column's width at its own ratio, centred, its top and bottom 12% faded into the white card
 *       (the left edge is white torn paper already), and it is capped at the column height, where cover takes over. */
.devora-feature { overflow: clip; }
.devora-feature .devora-feature__media .wp-block-image { margin: 0; }
.devora-feature .devora-feature__text { padding-right: var(--wp--custom--feature--pad-x); }
@media (width >= 782px) {
	.devora-feature .devora-feature__text { padding-right: var(--wp--preset--spacing--20); }
	.devora-feature .devora-feature__media { position: relative; }
	.devora-feature .devora-feature__media > .devora-show-desktop { position: absolute; inset: 0; display: flex; align-items: center; }
	.devora-feature .devora-feature__media > .devora-show-desktop img { width: 100%; height: 100%; }
}
@media (782px <= width < 1400px) {
	.devora-feature .devora-feature__media > .devora-show-desktop img {
		height: auto;
		max-height: 100%;
		-webkit-mask: linear-gradient(transparent, var(--wp--preset--color--black) 12%, var(--wp--preset--color--black) 88%, transparent);
		mask: linear-gradient(transparent, var(--wp--preset--color--black) 12%, var(--wp--preset--color--black) 88%, transparent);
	}
}
/* === end === */

/* === Home: panels, cards and video === */
/* Spec 2.7, 2.9 and 2.10 (patterns featured-cards.php, media-feature.php). Tags: HM7 (.devora-paper-panel texture
 * and tear), HM8 (is-style-numbered-cards), HM10 (.devora-media and the play badge), HM7b (links in a panel H2).
 * The halves, the stacking below 1024 and the image frame are block supports (grid layout with @tablet and
 * @mobile states, border), not CSS. */

/* HM7. The paper half of a featured-cards panel (450:4573, 703:43733): the paper texture (bd9d431a, 636 x 1284,
 *      1:1 at 1440, anchored top left) under a torn edge, down the left while the halves sit side by side
 *      (450:4588 turned a quarter, 7 to 30 deep) and across the top once the panel stacks below 1024, as mobile's
 *      strip 450:4952 draws it. The masks are used at their drawn size so the tear keeps its depth at any width.
 *      A Group has no mask support; painted on a pseudo-element behind the cards so the mask never clips them. */
.devora-paper-panel { position: relative; isolation: isolate; }
.devora-paper-panel::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	border-radius: inherit;
	background: url("assets/images/paper-texture.webp") left top / cover no-repeat;
	-webkit-mask: url("assets/images/masks/paper-tear-left.svg") left center / auto no-repeat;
	mask: url("assets/images/masks/paper-tear-left.svg") left center / auto no-repeat;
}
@media (width < 1024px) {
	.devora-paper-panel::before {
		-webkit-mask: url("assets/images/masks/paper-tear-top.svg") center top / auto no-repeat;
		mask: url("assets/images/masks/paper-tear-top.svg") center top / auto no-repeat;
	}
}

/* HM8. Numbered cards (450:4574 to 450:4586, 703:43734 to 703:43742): a core ordered List shown as white cards 12
 *      apart, each number before its text on the first baseline, in the kicker type and accent-text (decor-8 as
 *      drawn is 2.59:1). List has no per-item box or marker style. Tabular figures keep every text column at the
 *      same x (Figma's 322 and 324). The number is generated with an empty alternative text, so assistive tech
 *      hears only the list's own numbering. */
.wp-block-list.is-style-numbered-cards {
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--card-row);
	margin: 0;
	padding: 0;
	list-style: none;
	counter-reset: devora-card;
}
.wp-block-list.is-style-numbered-cards > li {
	display: flex;
	align-items: baseline;
	gap: var(--wp--custom--card--mini--gap);
	margin: 0;
	padding: var(--wp--custom--card--mini--pad-y) var(--wp--custom--card--mini--pad-x);
	background-color: var(--wp--preset--color--surface);
	border-radius: var(--wp--preset--border-radius--panel);
	counter-increment: devora-card;
}
.wp-block-list.is-style-numbered-cards > li::before {
	content: counter(devora-card); /* browsers without the alternative text syntax keep the number */
	content: counter(devora-card) / "";
	flex: none;
	color: var(--wp--preset--color--accent-text);
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--eyebrow);
	font-variant-numeric: tabular-nums;
	font-weight: 700;
	letter-spacing: var(--wp--custom--letter-spacing--kicker);
	line-height: var(--wp--custom--line-height--kicker);
	text-transform: uppercase;
}

/* HM7b. Links in a panel's H2 (703:43731, the About H2 names two organisations): the site link style, as in the
 *      body, DL1's pale rule included. core/heading's theme.json style gives heading links the heading colour and
 *      no underline (for linked titles), which also resets DL1's rule colour, and the section heading helper
 *      writes no per-block link colour. */
.devora-featured-cards .devora-section-heading a:where(:not(.wp-element-button)) {
	color: var(--wp--preset--color--accent-text);
	text-decoration: underline 1px var(--wp--preset--color--border-strong);
}
.devora-featured-cards .devora-section-heading a:where(:not(.wp-element-button)):is(:hover, :focus) { color: var(--wp--preset--color--accent-text-strong); text-decoration-color: currentColor; }

/* HM10. The play badge on a linked media image (I703:43882;684:31120): a solid white 76 circle (48 on mobile)
 *       with its drop shadow and a dark play icon 36 (24), centred on the poster; the circle fills
 *       aqua on hover, keyboard focus and press. Image has no overlay slot and an Icon block cannot sit over a link. Both
 *       layers are generated and empty, so the link's name stays the image alt. The class sits on a wrapper (Home) or
 *       on the Image itself (stage 6 article media, devora_media_image()); :is() keeps the specificity. */
:is(.devora-media--play .wp-block-image, .wp-block-image.devora-media--play) a { position: relative; display: block; }
/* Editor only: the editor writes display inline on the image link, so the badge centred on a 22px line box at the
 * image's foot, over the caption. */
:is(.devora-media--play .wp-block-image, .wp-block-image.devora-media--play).block-editor-block-list__block > a { display: block !important; }
:is(.devora-media--play .wp-block-image, .wp-block-image.devora-media--play) a::before,
:is(.devora-media--play .wp-block-image, .wp-block-image.devora-media--play) a::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	translate: -50% -50%;
	aspect-ratio: 1;
	pointer-events: none;
}
:is(.devora-media--play .wp-block-image, .wp-block-image.devora-media--play) a::before {
	width: var(--wp--custom--media--play);
	border-radius: 50%;
	background-color: var(--wp--preset--color--white);
	box-shadow: var(--wp--custom--media--play-shadow);
	transition: var(--wp--custom--motion--fast);
}
:is(.devora-media--play .wp-block-image, .wp-block-image.devora-media--play) a::after {
	width: var(--wp--custom--media--play-icon);
	background-color: var(--wp--preset--color--text-on-accent);
	-webkit-mask: url("assets/icons/play.svg") center / contain no-repeat;
	mask: url("assets/icons/play.svg") center / contain no-repeat;
}
:is(.devora-media--play .wp-block-image, .wp-block-image.devora-media--play) a:is(:hover, :focus-visible, :active)::before { background-color: var(--wp--preset--color--accent); }
@media (prefers-reduced-motion: reduce) { :is(.devora-media--play .wp-block-image, .wp-block-image.devora-media--play) a::before { transition: none; } }
/* HM10b. The plugin video block has no colour controls for its generated badge. Theme rules give
 * it the same presentation as linked posters, including the plugin's no-poster state. The frame
 * selector keeps these theme colours stronger than the plugin defaults regardless of load order. */
:root .devora-player .devora-player__frame .devora-player__play:not(:hover, :focus-visible, :active)::before {
	background-color: var(--wp--preset--color--white);
}
:root .devora-player .devora-player__frame .devora-player__play::after {
	background-color: var(--wp--preset--color--text-on-accent);
}
:root .devora-player .devora-player__frame .devora-player__play:is(:hover, :focus-visible, :active)::before {
	background-color: var(--wp--preset--color--accent);
}
/* === end === */

/* === Home: news and fact sheets === */
/* Spec 2.11 and 2.12 (patterns news-latest.php, news-card.php, fact-sheet-carousel.php, fact-sheet-card.php).
 * Tags: HM11 (carousel track and news row), HM12 (cards: stretched link, scrim, hover, focus), HM13 (carousel
 * buttons; the show pair is in "shared"). The scrim on the fact sheet card is the Featured Image's own overlay
 * (gradient scrim, dim 100), so it needs no rule here. Script: assets/js/carousel.js, enqueued by functions.php
 * (Home: news and fact sheets) only where one of the two Query blocks renders. */

/* HM11. Card rows. Post Template has no horizontal layout (Nursing Research IM4): the fact sheets are always a
 *       scroll-snap row, the news only below 1024 (a 3-column grid from there, core's grid layout). Cards are 262
 *       wide below 768 and 312 from there (custom.width), 12 apart, and the row bleeds past the container to the
 *       window's right edge (414:6463, 450:5168), its left edge on the container. Core caps a Post Template at max-width 100%, which would stop the bleed. The row keeps 8 of padding
 *       round the cards so a focused card's ring is not cut by the scroller, and gives it back in its margins;
 *       the Query is a flow root so the negative top margin cannot collapse into the heading gap. The band that
 *       holds a row clips sideways, because 100vw counts a classic scrollbar and would scroll the page. */
.devora-band:has(.devora-carousel, .devora-news-row) { overflow-x: clip; }
.devora-carousel__query,
.devora-news-row { display: flow-root; }
.devora-carousel__query > .devora-carousel__track.wp-block-post-template {
	display: flex;
	flex-wrap: nowrap;
	gap: var(--wp--preset--spacing--card-row);
	margin-block: calc(-1 * var(--wp--preset--spacing--10));
	margin-inline: calc(-1 * var(--wp--preset--spacing--10)) calc(50% - 50vw);
	padding: var(--wp--preset--spacing--10);
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scroll-snap-type: x mandatory;
	scroll-padding-inline: var(--wp--preset--spacing--10);
	scrollbar-width: none;
	max-width: none;
}
.devora-carousel__track::-webkit-scrollbar { display: none; }
.devora-carousel__track.wp-block-post-template > li { flex: 0 0 var(--wp--custom--width--card); margin: 0; scroll-snap-align: start; }
@media (width < 1024px) {
	.devora-news-row > .devora-news-row__track.wp-block-post-template {
		display: flex;
		flex-wrap: nowrap;
		gap: var(--wp--preset--spacing--card-row);
		margin-block: calc(-1 * var(--wp--preset--spacing--10));
		margin-inline: calc(-1 * var(--wp--preset--spacing--10)) calc(50% - 50vw);
		padding: var(--wp--preset--spacing--10);
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scroll-snap-type: x mandatory;
		scroll-padding-inline: var(--wp--preset--spacing--10);
		scrollbar-width: none;
		max-width: none;
	}
	.devora-news-row__track::-webkit-scrollbar { display: none; }
	.devora-news-row__track.wp-block-post-template > li { flex: 0 0 var(--wp--custom--width--card); margin: 0; scroll-snap-align: start; }
}
@media (width < 768px) {
	.devora-carousel__track.wp-block-post-template > li,
	.devora-news-row__track.wp-block-post-template > li { flex-basis: var(--wp--custom--width--card-mobile); }
}

/* HM12. Cards. One link per card, its click area stretched over the card (Read more on a news card, the title on a
 *       fact sheet), the ring of that link drawn round the whole card, and the title underlined on hover: core
 *       has no card link, hover or focus control. News cards fill their grid cell or row height with Read more at
 *       the foot (425 at 1440 whatever the title's lines). The fact sheet card stacks its cover and title in one
 *       grid cell; the title group's z-index lifts it over the cover (the Featured Image is positioned) without
 *       positioning it, so the link's stretched area still measures from the card. */
.devora-card { position: relative; }
.devora-card .devora-card__link a::after,
.devora-card a.devora-card__link::after { content: ""; position: absolute; inset: 0; }
.devora-card:has(a:focus-visible) { outline: var(--wp--custom--focus--ring) solid var(--devora-ring); outline-offset: 3px; }
.devora-card a:focus-visible { outline: none; }
.devora-news-row__track > li { display: flex; }
.devora-news-card { flex: 1 1 auto; }
.devora-news-card > .wp-block-read-more { margin-block-start: auto; }
/* HM12b. Home's Read more rule is too faint on surface-raised. The saved Read More block writes
 *       border-bottom-color inline, so this scoped override needs !important until that content is edited. */
body.home .devora-news-row .devora-news-card > .wp-block-read-more {
	border-bottom-color: var(--wp--preset--color--accent-text) !important;
}
body.home .devora-news-row .devora-news-card > .wp-block-read-more:is(:hover, :focus) {
	border-bottom-color: var(--wp--preset--color--accent-strong) !important;
}
.devora-news-card:hover .wp-block-post-title,
.devora-fact-card:hover .wp-block-post-title a { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.2em; }
.devora-fact-card { display: grid; overflow: hidden; }
.devora-fact-card > * { grid-area: 1 / 1; }
.devora-fact-card > .devora-fact-card__text { align-self: end; z-index: 1; }
.devora-fact-card .wp-block-post-title a { text-decoration: none; }
/* A resource with no cover: Post Featured Image prints nothing, so the card keeps its A4 shape on its own primary fill
 * (a block colour; the cover hides it otherwise). The fill stops inside the translucent border, which would otherwise
 * read navy on every card. It would also turn A1's ring white, but the ring is drawn outside the card, on the white
 * band, so it keeps primary. */
.devora-fact-card:not(:has(.wp-block-post-featured-image)) { aspect-ratio: 210 / 297; }
.devora-fact-card.has-primary-background-color { --devora-ring: var(--wp--preset--color--primary); background-clip: padding-box; }

/* HM13. Carousel buttons (1195:24428, 1195:24434): 48 squares with the arrow-left and arrow-right glyphs, the outline
 *       look with its border and icon in accent-strong (Figma's teal/base is 2.92:1). Core Button carries text only,
 *       so the label is pushed out of the box and stays the button's name (the Q2 technique). At either end the
 *       script sets aria-disabled: the button dims to Figma's 0.3 and stays focusable. Hidden until the script marks
 *       the carousel ready, so a page without JavaScript shows no dead controls, while every card fits (is-static),
 *       and below 768 (448:3159 draws none). */
.devora-carousel:not(.is-ready) .devora-carousel__nav { visibility: hidden; }
.devora-carousel.is-static .devora-carousel__nav { display: none; } /* every card fits: no dead controls */
.devora-carousel__nav .wp-block-button__link {
	position: relative;
	width: var(--wp--custom--carousel--button);
	height: var(--wp--custom--carousel--button);
	padding: 0;
	overflow: hidden;
	white-space: nowrap;
	text-indent: 100%;
	color: var(--wp--preset--color--accent-strong);
}
.devora-carousel__nav .wp-block-button__link::before {
	content: "";
	position: absolute;
	inset: 0;
	width: var(--wp--custom--button--icon-size);
	height: var(--wp--custom--button--icon-size);
	margin: auto;
	background-color: currentColor;
	-webkit-mask: url("assets/icons/arrow-left.svg") center / contain no-repeat;
	mask: url("assets/icons/arrow-left.svg") center / contain no-repeat;
}
.devora-carousel__next .wp-block-button__link::before {
	-webkit-mask-image: url("assets/icons/arrow-right.svg");
	mask-image: url("assets/icons/arrow-right.svg");
}
.devora-carousel__nav .wp-block-button__link:where(:hover, :focus-visible):not([aria-disabled="true"]) { color: var(--wp--preset--color--accent-text-strong); }
.devora-carousel__nav .wp-block-button__link[aria-disabled="true"] { opacity: 0.3; cursor: default; background-color: transparent; }
/* Core has no rotation control. Keep the script-added Pause/Play label readable beside the arrow buttons. */
.devora-carousel__pause .wp-block-button__link {
	width: auto;
	padding-inline: var(--wp--preset--spacing--30);
	text-indent: 0;
}
.devora-carousel__pause .wp-block-button__link::before { content: none; }
@media (width < 768px) {
	.devora-carousel .devora-carousel__nav.wp-block-buttons { display: none; }
	.devora-carousel .devora-carousel__nav.wp-block-buttons:has(.devora-carousel__pause) { display: flex; }
	.devora-carousel__nav:has(.devora-carousel__pause) > :not(.devora-carousel__pause) { display: none; }
}
/* === end === */

/* === Home: tiles === */
/* Spec 2.13 and 2.14 (patterns topic-tiles.php, output-type-tiles.php). Tags: HM14 (topic and output type
 * tiles, rows below 1340), HM16 (output type icon masks from --devora-type-icon). */

/* HM14. Library tiles (Library B 1286:41532; the output types borrow its look, 2.14). Each tile is a Group in a
 *       Term Template item, and core's Grid layout has one column count at every width: tiles 240 tall from 1340
 *       (6 across, 5 for output types), below that the rows of mobile 448:2945 (illustration left, text, arrow
 *       right), two columns to 1339 and one below 600, 8 apart. 1340, not 1200: 6 across below it leaves a topic
 *       title about 120 wide, so "Communities & Infrastructure" took three lines with the "&" alone (and ran 5 past
 *       the padding at 1200); both blocks switch together so the two libraries never show two looks. The padding lives here because it changes with
 *       the mode and an inline padding could not. */
.devora-library .devora-library-tile {
	position: relative;
	box-sizing: border-box;
	height: 100%;
	padding: var(--wp--preset--spacing--40);
}
.devora-library .devora-library-tile__icon { flex: none; margin: 0; }
.devora-library .devora-library-tile__icon img { display: block; width: var(--wp--custom--tile--icon); height: var(--wp--custom--tile--icon); }
.devora-library .devora-library-tile__text { gap: var(--wp--custom--tile--text-gap); } /* 4 has no spacing preset */
/* One link per tile, named by the term: Term Name has no link decoration control, so the link takes the title's
 * colour and weight (the link element's 600 would beat the title's 500, Heading/H6), drops the underline and
 * stretches over the tile; the focus ring goes on the stretched box (A1). */
.devora-library .devora-library-tile__title a { color: inherit; font-weight: inherit; text-decoration: none; }
.devora-library .devora-library-tile__title a::after { content: ""; position: absolute; inset: 0; border-radius: var(--wp--preset--border-radius--panel); }
.devora-library .devora-library-tile__title a:focus-visible { outline: none; }
.devora-library .devora-library-tile__title a:focus-visible::after { outline: var(--wp--custom--focus--ring) solid var(--devora-ring); outline-offset: 3px; }
/* The "Explore topic" cue (1286:41665): aria-hidden text and a 16 arrow over a border-accent rule, 8 under the count
 * (the 4 gap plus 4), in accent-text rather than Figma's teal/base (2.56:1 on accent-subtle, spec 2.13). Never
 * clickable itself, so a click on it reaches the stretched link underneath. */
.devora-library .devora-library-tile__cue {
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--10);
	margin-block-start: var(--wp--custom--tile--text-gap);
	padding-block: var(--wp--preset--spacing--10);
	border-bottom: 1px solid var(--wp--preset--color--border-accent);
	pointer-events: none;
}
.devora-library .devora-library-tile__cue::after {
	content: "";
	flex: none;
	width: var(--wp--custom--tile--cue-arrow);
	height: var(--wp--custom--tile--cue-arrow);
	background-color: currentColor;
	-webkit-mask: url("assets/icons/arrow-right.svg") center / contain no-repeat;
	mask: url("assets/icons/arrow-right.svg") center / contain no-repeat;
}
/* Core has no parent-hover cue colour control. Override the saved colour utility for homepage topics. */
body.home .devora-library--topics .devora-library-tile:is(:hover, :focus-within) .devora-library-tile__cue {
	color: var(--wp--preset--color--accent) !important;
}
@media (width >= 1340px) {
	.devora-library .devora-library-tile { min-height: var(--wp--custom--tile--height); }
	.devora-library .devora-library-tile:not(:hover, :focus-within) .devora-library-tile__cue { display: none; }
}
@media (width < 1340px) {
	.devora-library .devora-library__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.devora-library .devora-library-tile {
		flex-direction: row;
		align-items: center;
		justify-content: flex-start;
		gap: var(--wp--preset--spacing--30);
		padding: var(--wp--custom--tile--row-pad-y) var(--wp--custom--tile--row-pad-x);
	}
	.devora-library .devora-library-tile__text { flex: 1; padding-inline-end: calc(var(--wp--custom--tile--row-arrow) + var(--wp--custom--tile--text-gap)); }
	/* In a row the cue is only its arrow (24, 448:3630), centred at the right. */
	.devora-library .devora-library-tile__cue {
		position: absolute;
		inset-block-start: 50%;
		inset-inline-end: var(--wp--custom--tile--row-pad-x);
		translate: 0 -50%;
		margin: 0;
		padding: 0;
		border: 0;
	}
	.devora-library .devora-library-tile__cue > span { display: none; }
	.devora-library .devora-library-tile__cue::after { width: var(--wp--custom--tile--row-arrow); height: var(--wp--custom--tile--row-arrow); }
}
/* Below 600 the list is one column 8 apart, and All resources moves under it, where 448:3153 draws its row: the
 * header lets its two parts join the section's column so the button can be ordered last. */
@media (width < 600px) {
	.devora-library .devora-library__grid { grid-template-columns: minmax(0, 1fr); gap: var(--wp--preset--spacing--10); }
	.devora-library .devora-library__header { display: contents; }
	.devora-library .devora-library__header > .wp-block-buttons { order: 1; }
}

/* HM16. Output type icons (2.14). The files are single-colour currentColor drawings, which an img paints black, so
 *       each is a mask at 48 in its chip family's icon colour. The plugin (devora-site inc/cards.php) writes the
 *       icon file into --devora-type-icon on each Term Template item (from the term's icon meta, when
 *       assets/icons/output-type-{icon}.svg exists) and devora-chip--{chip_colour}; without them (the editor
 *       preview, or an icon with no file) the placeholder icon shows in accent-text. Decorative: the name is the text. */
.devora-library .devora-type-tile::before {
	content: "";
	flex: none;
	width: var(--wp--custom--tile--icon);
	height: var(--wp--custom--tile--icon);
	background-color: var(--devora-type-colour, var(--wp--preset--color--accent-text));
	-webkit-mask: var(--devora-type-icon, url("assets/icons/output-type-placeholder.svg")) center / contain no-repeat;
	mask: var(--devora-type-icon, url("assets/icons/output-type-placeholder.svg")) center / contain no-repeat;
}
.devora-library .devora-chip--category-1 { --devora-type-colour: var(--wp--custom--chip--category-1--icon); }
.devora-library .devora-chip--category-2 { --devora-type-colour: var(--wp--custom--chip--category-2--icon); }
.devora-library .devora-chip--category-3 { --devora-type-colour: var(--wp--custom--chip--category-3--icon); }
.devora-library .devora-chip--category-4 { --devora-type-colour: var(--wp--custom--chip--category-4--icon); }
/* === end === */
