/*
Theme Name: MSWC
Theme URI: https://mswconline.org
Author: Alex Ramirez Blonski
Description: A lean block theme for Management Skills for Wildlife Conservation. Built for MSWC to own: the whole design lives in theme.json and a handful of readable template files. Replaces the classic wildlifemgmt theme and the WonderBlocks page builder.
Version: 0.21.4
Requires at least: 6.6
Tested up to: 7.0
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: mswc
Tags: block-theme, one-column, accessibility-ready, custom-colors, custom-menu
*/

/*
 * Design decisions live in theme.json, not here. This file carries the theme
 * header (WordPress requires it) plus the few rules that genuinely cannot be
 * expressed as block styles. Keep it short. If a rule can go in theme.json,
 * it goes in theme.json.
 */

/*
 * Screen-reader-only text. The blog index carries a hidden "Blog" h1 the way
 * production does — without it that page is the only one on the site with no
 * h1 at all. Kept here rather than relying on core shipping the class.
 */
.screen-reader-text {
	border: 0;
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	white-space: nowrap;
	width: 1px;
}

.screen-reader-text:focus {
	clip-path: none;
	height: auto;
	margin: 0;
	padding: 1rem;
	position: static;
	white-space: normal;
	width: auto;
}

/*
 * Post navigation. Core emits an EMPTY link container when there is no
 * adjacent post (so the newest post shows a stray gap where "next" would be);
 * production renders nothing at all. Core also butts the arrow straight
 * against the title — "←Light for Wildlife" — where production separates them.
 */
.wp-block-post-navigation-link:empty {
	display: none;
}

.wp-block-post-navigation-link__arrow-previous {
	margin-right: 0.35em;
}

.wp-block-post-navigation-link__arrow-next {
	margin-left: 0.35em;
}

/*
 * Ordinary content — the privacy page, blog post bodies, anything Guy writes
 * later — uses production's DEFAULT heading sizes, and those differ from the
 * bespoke sizes the migrated pages state for themselves. Production's default
 * h2 is 36px/1.2 with 1px tracking and its h3 is 24px/1.2/1px BOLD, whereas
 * theme.json's presets (35px/1.1, 25px/1.1/400) exist because the homepage's
 * hand-tuned headings need them.
 *
 * The two :not() guards are what make this safe: any heading carrying its own
 * font-size — inline (the migrated pages) or as a preset class such as
 * has-xx-large-font-size (the homepage) — is left completely alone.
 */
:where(.wp-block-post-content, .entry-content) h2:not([style*="font-size"]):not([class*="-font-size"]) {
	font-size: 36px;
	line-height: 1.2;
	letter-spacing: 1px;
}

:where(.wp-block-post-content, .entry-content) h3:not([style*="font-size"]):not([class*="-font-size"]) {
	font-size: 24px;
	line-height: 1.2;
	letter-spacing: 1px;
	font-weight: 700;
}

/*
 * EYEBROW. A short label that names the job a band or an article does, set
 * small, spaced and muted so it reads as a signpost rather than as content.
 * Used two ways and deliberately identical in both: the redesigned homepage
 * labels each band, and the blog prints a post's category above its title.
 *
 * It takes `body` #535353 rather than the `muted` #939c93 that every other
 * secondary thing on this site uses. Muted is 2.4:1 on white — fine as a wash
 * behind 44px display type, unreadable at 13px. The tracking and the caps do
 * the signposting; the colour does not have to.
 *
 * Open Sans 600 is safe to specify here where it would not have been on the
 * migrated pages: production loads only static 400 and 700 faces, so its
 * 500-spec text renders 400 — but we self-host the variable font at 300-800,
 * so 600 is a real face and actually renders. (Handoff lesson 11, inverted.)
 */
.mswc-eyebrow,
.mswc-eyebrow a {
	color: var(--wp--preset--color--body);
	font-family: var(--wp--preset--font-family--body);
	font-size: 0.8125rem;
	font-weight: 600;
	letter-spacing: 0.14em;
	line-height: 1.4;
	text-transform: uppercase;
	text-decoration: none;
}

.mswc-eyebrow a:hover,
.mswc-eyebrow a:focus-visible {
	color: var(--wp--preset--color--accent);
	text-decoration: underline;
}

/*
 * On a band that sets its own text colour — the accent-blue Mayanai band is
 * the only one today — the eyebrow inherits instead of holding its #535353.
 * Left alone it measured 1.64:1 on that blue, which is not "a bit low", it is
 * invisible. The a11y probe found it; nothing else would have.
 */
.wp-block-group.has-text-color > .mswc-eyebrow,
.wp-block-group.has-text-color > .mswc-eyebrow a {
	color: inherit;
}

/*
 * ARTICLE TYPE. The blog runs on the redesign's reading scale — 17px over a
 * 42rem measure — rather than the 16px/1.625 default across the full 65rem
 * container. Guy will be publishing conference material here from November, so
 * this is the part of the site most likely to be read at length, and it was
 * the one place still set at the old size.
 *
 * Scoped to `.mswc-article` (the single-post content) rather than applied to
 * body copy generally: the four migrated pages state their own sizes inline
 * and must keep measuring zero against production.
 */
.mswc-article p,
.mswc-article li {
	font-size: 1.0625rem;
	line-height: 1.7;
}

/*
 * Category archive intro. The term description explains what the category
 * collects; it is a subtitle, not body copy, so it takes the lede size and the
 * body ink rather than the muted grey.
 */
.mswc-term-description {
	font-size: 1.0625rem;
	line-height: 1.7;
	margin-inline: auto;
	max-width: 42rem;
}

/*
 * Ninja Forms (the contact form). The plugin ships its own look and the old
 * theme overrode parts of it; without these the form is inset 5px from the
 * container (1030 wide instead of 1040) and its labels render body-grey
 * instead of production's accent blue.
 */
.nf-form-content {
	padding: 0;
	max-width: none;
}

.nf-form-content .nf-field-label label {
	color: var(--wp--preset--color--accent);
}

.nf-form-content input[type="text"],
.nf-form-content input[type="email"],
.nf-form-content textarea {
	padding-left: 3px;
}

/*
 * Captions. Core's block library sets .wp-element-caption to #555 and centres
 * it; production renders captions in the body ink, left-aligned (measured on
 * the Mayanai video credits and the What We Do giraffe).
 */
figcaption.wp-element-caption {
	color: var(--wp--preset--color--body);
	text-align: start;
}

/* Visible focus ring everywhere. The old theme suppressed this in places. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
	outline: 2px solid var(--wp--preset--color--accent);
	outline-offset: 2px;
}

/* Media never overflows its container on small screens. */
:where(img, video, iframe, embed, object) {
	max-width: 100%;
	height: auto;
}

/* Embeds (Vimeo, YouTube, TED) keep their aspect ratio without wrapper divs. */
:where(.wp-block-embed__wrapper) iframe {
	width: 100%;
	aspect-ratio: 16 / 9;
	height: auto;
}

/*
 * The About page's board band is a 1100px box — wider than the 1040 content
 * container, so the grey extends 30px past the cards on each side. `alignwide`
 * can only give 1040, so this states the width directly. The selector carries
 * one extra class over WordPress's `.is-layout-constrained > .alignwide`
 * (0,3,0 vs 0,2,0) so it wins on specificity rather than on stylesheet order.
 */
.wp-block-post-content > .wp-block-group.mswc-band-wide {
	max-width: 1100px;
}

/*
 * What We Do, LAMP band: production overlaps its two portrait images by 55px
 * using a 12-column grid (spans 1-7 and 6-12 on one row; the second column
 * carries its own 100px top padding). Block columns are flexbox and cannot
 * overlap, so this restates the grid. DOM order paints the second image on
 * top, exactly as production does (both are static / z-index auto there).
 * Gap is 32px, matching the blockGap the columns block declares.
 */
.wp-block-columns.mswc-grid12 {
	display: grid;
	grid-template-columns: repeat(12, minmax(0, 1fr));
	gap: 32px;
}

.wp-block-columns.mswc-grid12 > .wp-block-column:first-child {
	grid-column: 1 / 8;
	grid-row: 1;
}

.wp-block-columns.mswc-grid12 > .wp-block-column:last-child {
	grid-column: 6 / 13;
	grid-row: 1;
}

/*
 * What We Do, LAMP band: the band's bottom edge is slanted - an 80px wedge in
 * the band's own lime hanging below it (WonderBlocks' nfd-divider-slant,
 * re-measured 2026-08-01: bottom -79px so it overlaps the band 1px, z-index
 * 10, clip-path triangle tapering to bottom-left).
 */
.mswc-divider-slant {
	position: relative;
}

.mswc-divider-slant::before {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: -79px;
	height: 80px;
	z-index: 10;
	background: var(--wp--preset--color--lime-tint);
	clip-path: polygon(0 0, 100% 0, 0 100%);
}

/*
 * Flex-shrink guard (production's nfd-shrink-0). The LAMP session listings
 * put a fixed accent box next to a flex-grow text column; without this the
 * text column's natural basis squeezes the box 14px narrower and "SESSION"
 * wraps letter by letter.
 */
.mswc-shrink0 {
	flex-shrink: 0;
}

/*
 * What We Do, LAMP band separators: 2px white at 10% on the lime band.
 * Production computes opacity 1 on these (its stylesheet never loads core's
 * .has-alpha-channel-opacity rule, ours does - hence the explicit opacity).
 * Total height is 3px: 1px transparent content + the 2px top border,
 * content-box like production. The hr element in the selector outranks the
 * theme.json separator colour, which prints later at equal specificity.
 */
hr.mswc-sep-lamp {
	box-sizing: content-box;
	height: 1px;
	border-top: 2px solid rgba(255, 255, 255, 0.1);
	border-bottom: none;
	background: none;
	opacity: 1;
	margin: 0;
}

/*
 * Faint perspective grid over the Mayanai band. Replicates production's
 * WonderBlocks theme-style ::after exactly (re-measured 2026-07-29): 100px
 * white 1px grid at 12%, radial mask, opacity .6.
 *
 * The `perspective` on the parent is load-bearing and easy to miss: without
 * it, rotateX only squashes the grid flat (and both rotation signs render
 * identically). With it, the plane recedes. Production puts perspective on
 * the band, keeps its own perspective-origin centred, and anchors the
 * pseudo-element's transform-origin at the TOP edge so the grid falls away
 * toward the horizon. Overflow stays visible, as production's does.
 */
.mswc-pattern-grid {
	position: relative;
	isolation: isolate;
	perspective: 1000px;
}

.mswc-pattern-grid::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	opacity: 0.6;
	background-image:
		linear-gradient(to right, rgba(255, 255, 255, 0.12) 1px, transparent 1px),
		linear-gradient(rgba(255, 255, 255, 0.12) 1px, transparent 1px);
	background-size: 100px 100px;
	transform-origin: 50% 0;
	transform: rotateX(-60deg);
	-webkit-mask-image: radial-gradient(50% 50% at 50% 20%, #000 70%, rgba(0, 0, 0, 0.3) 100%);
	mask-image: radial-gradient(50% 50% at 50% 20%, #000 70%, rgba(0, 0, 0, 0.3) 100%);
}

/*
 * ============================================================== THE PHONE
 *
 * Everything below this line is inside a max-width query and changes nothing
 * on a desktop. It is here rather than in home-v4.css because the header, the
 * overlay menu and the tap targets are the SITE's, not the homepage's:
 * home-v4.css is enqueued front-page-only, so a fix written there would leave
 * About, What We Do, Testimonials, Blog and Contact exactly as they were.
 */

/*
 * TAP TARGETS ON THE MENU BUTTONS.
 *
 * Core renders both the hamburger and its close as bare 24x24 SVG buttons.
 * That is under every published minimum (44px on iOS, 48dp on Android) and it
 * is the one control a phone visitor cannot do without.
 *
 * The padding is NOT pulled back with a negative margin, which was the first
 * attempt: these buttons are flex items in a right-justified row, so a
 * negative right margin drags the glyph back out toward the screen edge and
 * undoes the gutter it was just given. Growing the box inward costs 10px of
 * optical inset and is the honest trade.
 */
@media (max-width: 781px) {
	.wp-block-navigation__responsive-container-open,
	.wp-block-navigation__responsive-container-close {
		min-width: 44px;
		min-height: 44px;
		padding: 10px;
		align-items: center;
		justify-content: center;
	}

	/*
	 * THE OVERLAY IS A MENU, NOT A LIST OF LINKS.
	 *
	 * Left as core ships it, it is 15px black type in rows 24px tall, and on
	 * the homepage the nav block's `justifyContent: right` pushes those rows
	 * against the right edge, which reads as a layout accident rather than a
	 * choice. Full-width rows with a hairline between them give each link a
	 * 52px target and make the panel look built.
	 *
	 * The left alignment is bought by stretching the rows to full width rather
	 * than by setting `text-align`: the right-justification comes from
	 * `items-justified-right` on the <ul>, a class the block emits from its
	 * own attributes and which the desktop bar still wants. Stretched items
	 * have no free space left for it to distribute, so it stops mattering
	 * here without being overridden there.
	 */
	.wp-block-navigation__responsive-container.is-menu-open {
		padding: 24px;
	}

	/*
	 * ⚠️ THIS SELECTOR IS LONG ON PURPOSE. Core's is:
	 *
	 *   .wp-block-navigation__responsive-container.is-menu-open
	 *   :where(:not(.disable-default-overlay))
	 *   .wp-block-navigation__responsive-container-content
	 *   .wp-block-navigation__container { gap: inherit }
	 *
	 * `:where()` contributes nothing, so that is FOUR classes, (0,4,0), and
	 * `gap: inherit` on the <ul> resolves to the 2rem the desktop bar sets on
	 * the <nav>. Two shorter forms lost to it before this one landed — the
	 * plain three-class version, and the same plus `ul` (0,3,1), because a
	 * fourth class outranks any number of element selectors. Naming the
	 * `-content` wrapper is what makes this (0,4,1).
	 *
	 * The symptom was not a missing gap: it was every hairline separator
	 * floating 33px BELOW the label it belonged to, which reads as a stray
	 * rule rather than as a list.
	 */
	.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-content
	ul.wp-block-navigation__container {
		align-items: stretch;
		gap: 0;
		width: 100%;
	}

	.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item {
		width: 100%;
		border-bottom: 1px solid rgba(20, 24, 26, 0.1);
	}

	.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item:last-child {
		border-bottom: 0;
	}

	.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item a {
		display: flex;
		align-items: center;
		width: 100%;
		min-height: 52px;
		font-family: var(--wp--preset--font-family--body);
		font-size: 1.0625rem;
		color: var(--wp--preset--color--body);
		text-decoration: none;
	}

	.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item .current-menu-item > a,
	.wp-block-navigation__responsive-container.is-menu-open .current-menu-item a {
		color: var(--wp--preset--color--accent);
	}

	/*
	 * ANY FORM CONTROL UNDER 16px MAKES iOS SAFARI ZOOM THE WHOLE PAGE when
	 * it takes focus, and it does not zoom back out afterwards — the visitor
	 * is left on a page they now have to pan sideways to read.
	 *
	 * The contact form is already 16px and unaffected. What this catches is
	 * the Mailchimp newsletter embed at the foot of the Contact page, whose
	 * email field is a third-party 15px and is not ours to edit at source.
	 * `max()` raises only what is below the threshold, so nothing set larger
	 * on purpose is touched.
	 */
	input[type="text"],
	input[type="email"],
	input[type="url"],
	input[type="tel"],
	input[type="number"],
	input[type="search"],
	input[type="password"],
	textarea,
	select {
		font-size: max(16px, 1em);
	}

	/* Same embed: its subscribe button is 32px tall. */
	input[type="submit"],
	button[type="submit"] {
		min-height: 44px;
	}

	/*
	 * WHAT WE DO — the LAMP band's overlap grid, which does not survive a
	 * phone. This is the one place on the site that still scrolled sideways.
	 *
	 * `.mswc-grid12` restates production's 12-column grid so the two portraits
	 * can overlap by 55px, and its gap is 32px. Twelve tracks need eleven gaps
	 * — 352px of gap alone — inside a 302px container, so every track computed
	 * to 0px, the item placed at track 6 started at x=204 and took its
	 * min-content width of 192, and the document ended up 396px wide against a
	 * 390px viewport. On screen: two photographs on top of each other, the
	 * second one running off the edge.
	 *
	 * Stacked, the overlap has nothing left to express — it exists to relate
	 * two images side by side — so both columns take the full row.
	 *
	 * ⚠️ `!important` on the padding for the same reason as the header below:
	 * the 100px is the second column's own block attribute, emitted inline.
	 * It is the offset that produces the overlap at full width and it is 100px
	 * of dead white between two stacked photographs at this one.
	 */
	.wp-block-columns.mswc-grid12 {
		grid-template-columns: 1fr;
		gap: 24px;
	}

	.wp-block-columns.mswc-grid12 > .wp-block-column:first-child,
	.wp-block-columns.mswc-grid12 > .wp-block-column:last-child {
		grid-column: 1 / -1;
		grid-row: auto;
		padding-top: 0 !important;
	}

	/*
	 * ⚠️ THE SAGE HEADER IS GONE (0.16.0) AND SO ARE THE RULES THAT TAMED IT.
	 *
	 * parts/header.html carried a 340px centred logo over a 30px-padded sage
	 * band with a second group beneath it for the nav: 433px of chrome on a
	 * 390px phone, which four `!important` declarations here cut to 241px. The
	 * part is the v4 topbar on every template now, so those selectors matched
	 * nothing and have been deleted rather than left to look load-bearing.
	 * The topbar's own phone rules live with the rest of it in home-v4.css.
	 */
}
