/**
 * borrego/board-directory — flip cards for the Board of Directors.
 *
 * Declared as the block's `style` in block.json, so WP_Block::render() enqueues
 * it only on pages that actually contain the block. That is why none of this
 * lives in style.css (global, every page) or art-direction.css (loaded only on
 * Home and About).
 *
 * READ THIS BEFORE TOUCHING .bb-board-card__inner.
 * It is the only element carrying `transform-style: preserve-3d`, and the spec
 * flattens that to `flat` if the SAME element also has any of: overflow other
 * than visible, clip-path, mask-image, filter, opacity below 1, mix-blend-mode,
 * contain: paint, or isolation: isolate. This theme's decorative helpers set
 * three of those — .bb-shape-blob-card sets overflow, .bb-shape-wave-* set
 * mask-image, and style.css blanket-applies isolation: isolate to all of them.
 * Putting any bb-shape-* class on the inner, or on an ancestor of the grid,
 * silently kills the flip and leaves the back face showing through mirrored.
 * Clipping belongs on the faces; decoration belongs on a sibling.
 */

/* --------------------------------------------------------------------------
   Intro chrome (rendered by patterns/board-directory.php)
   -------------------------------------------------------------------------- */

/* The intro lives in the pattern, but its CSS lives here because this file is
   the only stylesheet the board page loads. The alternative was style.css,
   which every page on the site pays for, to serve one page.
   .bb-botanical and .bb-section-brush have base rules in art-direction.css —
   deliberately loaded only on Home and About — so the handful this page needs
   are restated rather than pulling in 1600 lines for two helpers. Keep them in
   sync if the originals change. */

.bb-board-intro {
	background-color: var( --wp--preset--color--base );
	overflow-x: clip;
	padding-block: var( --wp--preset--spacing--70 ) var( --wp--preset--spacing--60 );
	position: relative;
}

.bb-board-intro__inner {
	margin-inline: auto;
	max-width: var( --wp--style--global--content-size, 760px );
	padding-inline: var( --wp--preset--spacing--40 );
	position: relative;
	z-index: 2;
}

.bb-board-intro__eyebrow {
	color: var( --wp--preset--color--terracotta-text );
	margin: 0 0 var( --wp--preset--spacing--20 );
}

.bb-board-intro__title {
	color: var( --wp--preset--color--forest );
	font-family: var( --wp--preset--font-family--display );
	font-size: var( --wp--preset--font-size--xx-large );
	font-weight: 500;
	line-height: 1.1;
	margin: 0 0 var( --wp--preset--spacing--20 );
}

.bb-board-intro__lead {
	color: var( --wp--preset--color--contrast );
	margin: 0;
	max-width: 46rem;
}

.bb-board-intro .bb-section-brush {
	color: var( --wp--preset--color--terracotta );
	display: block;
	height: 0.55rem;
	margin-bottom: 1rem;
	width: 7.5rem;
}

.bb-board-intro .bb-botanical {
	color: var( --wp--preset--color--gold );
	max-width: 100%;
	pointer-events: none;
	position: absolute;
	user-select: none;
	z-index: 1;
}

.bb-board-intro .bb-botanical--board {
	bottom: -6%;
	opacity: 0.45;
	right: 2%;
	width: clamp( 4rem, 9vw, 8rem );
}

@media ( max-width: 899px ) {
	.bb-board-intro .bb-botanical--board { display: none; }
}

/* --------------------------------------------------------------------------
   Section and grid
   -------------------------------------------------------------------------- */

.bb-board {
	background-color: var( --wp--preset--color--base-alt );
	padding-block: var( --wp--preset--spacing--60 );
	position: relative;
}

.bb-board__grid {
	display: grid;
	gap: var( --wp--preset--spacing--40 );
	grid-template-columns: 1fr;
	margin-inline: auto;
	max-width: var( --wp--style--global--wide-size, 1200px );
	padding-inline: var( --wp--preset--spacing--40 );
}

@media ( min-width: 640px ) {
	.bb-board__grid { grid-template-columns: repeat( 2, 1fr ); }
}

@media ( min-width: 1024px ) {
	.bb-board__grid { grid-template-columns: repeat( 3, 1fr ); }
}

@media ( min-width: 1280px ) {
	.bb-board__grid { grid-template-columns: repeat( 4, 1fr ); }
}

.bb-board__empty,
.bb-board__notice {
	margin-inline: auto;
	max-width: var( --wp--style--global--content-size, 760px );
	padding-inline: var( --wp--preset--spacing--40 );
	text-align: center;
}

.bb-board__notice {
	background-color: var( --wp--preset--color--base );
	border-left: 3px solid var( --wp--preset--color--terracotta-text );
	color: var( --wp--preset--color--contrast );
	font-size: var( --wp--preset--font-size--small );
	margin-block-end: var( --wp--preset--spacing--40 );
	max-width: var( --wp--style--global--wide-size, 1200px );
	padding: var( --wp--preset--spacing--30 ) var( --wp--preset--spacing--40 );
	text-align: start;
}

/* --------------------------------------------------------------------------
   The card, and the 3D rig
   -------------------------------------------------------------------------- */

.bb-board-card {
	display: flex;
	perspective: 1600px;
	position: relative;
	transition: transform 0.3s cubic-bezier( 0.2, 0.7, 0.2, 1 );
}

/* The lift is suppressed while this card is open — a flipped card that also
   floats reads as two separate things happening to one object. */
.bb-board-card:hover:not( :has( .is-flipped ) ):not( :has( .bb-board-card__face--back:target ) ) {
	transform: translateY( -6px );
}

.bb-board-card__inner {
	display: flex;
	flex-direction: column;
	position: relative;
	transform-style: preserve-3d;
	-webkit-transform-style: preserve-3d;
	transition: transform 0.7s cubic-bezier( 0.2, 0.8, 0.2, 1 );
	width: 100%;
}

/* TWO MECHANISMS, ONLY ONE ACTIVE AT A TIME.
 * `:target` is the no-JavaScript flip. view.js stamps data-bb-hydrated on the
 * section as soon as it takes over, and every :target rule below is scoped to
 * its absence — so once the store is running, the URL fragment cannot rotate
 * anything. They must not both be live: a fragment that outlived the store's
 * state used to hold a card open that Escape and the close button could not
 * shut, because CSS went on matching something the store had already cleared. */
.bb-board-card__inner.is-flipped,
.bb-board:not( [data-bb-hydrated] ) .bb-board-card__inner:has( .bb-board-card__face--back:target ) {
	transform: rotateY( 180deg );
}

.bb-board-card__face {
	backface-visibility: hidden;
	-webkit-backface-visibility: hidden;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	/* Safari lets absolutely-positioned children of a hidden-backface element
	   bleed through without its own compositing layer. */
	transform: translateZ( 0 );
	transition: box-shadow 0.3s;
	width: 100%;
}

/* The front is in normal flow and therefore defines the card's height; the back
   is absolutely positioned and simply matches it. That is what makes every card
   in a grid row line up without a fixed height. */
.bb-board-card__face--front {
	background-color: var( --wp--preset--color--base );
	border-radius: 6px 26px 6px 26px;
	box-shadow: rgb( 42 42 36 / 0.12 ) 0 12px 30px;
	flex: 1;
	position: relative;
}

/* Mirrored radius, not the same radius. rotateY(180deg) flips the box
   horizontally, so an asymmetric corner set would visibly change the card's
   silhouette halfway through the flip. */
.bb-board-card__face--back {
	background: linear-gradient( 160deg, var( --wp--preset--color--forest-deep ) 0%, var( --wp--preset--color--forest ) 100% );
	border-radius: 26px 6px 26px 6px;
	box-shadow: rgb( 42 42 36 / 0.12 ) 0 12px 30px;
	inset: 0;
	position: absolute;
	transform: rotateY( 180deg ) translateZ( 0 );
}

.bb-board-card:hover:not( :has( .is-flipped ) ):not( :has( .bb-board-card__face--back:target ) ) .bb-board-card__face {
	box-shadow: rgb( 42 42 36 / 0.2 ) 0 22px 46px;
}

/* backface-visibility hides pixels but leaves the content in the accessibility
   tree and the tab order. `visibility` hides both — and unlike `inert`, CSS can
   toggle it, so the no-JS :target path gets correct semantics for free.
 *
 * THE DELAY IS ASYMMETRIC, AND THAT IS THE WHOLE TRICK.
 * Hiding is delayed to the midpoint of the rotation, so the outgoing face stays
 * painted while it is still facing the viewer. Revealing is IMMEDIATE, because
 * a `visibility: hidden` element cannot take focus — with a delay on the way in,
 * the script's focus() call lands on a still-hidden panel, silently does
 * nothing, and leaves focus stranded on <body>. Since backface-visibility hides
 * the incoming face's pixels until it rotates around anyway, making it visible
 * at once costs nothing visually.
 *
 * A transition uses the delay declared on the state it is moving TO, so each
 * rule below carries the delay for the direction it represents. */
.bb-board-card__face--front {
	transition: box-shadow 0.3s, visibility 0s linear 0s;
	visibility: visible;
}

.bb-board-card__face--back {
	transition: box-shadow 0.3s, visibility 0s linear 0.35s;
	visibility: hidden;
}

.bb-board-card__inner.is-flipped .bb-board-card__face--back,
.bb-board:not( [data-bb-hydrated] ) .bb-board-card__inner:has( .bb-board-card__face--back:target ) .bb-board-card__face--back {
	transition: box-shadow 0.3s, visibility 0s linear 0s;
	visibility: visible;
}

.bb-board-card__inner.is-flipped .bb-board-card__face--front,
.bb-board:not( [data-bb-hydrated] ) .bb-board-card__inner:has( .bb-board-card__face--back:target ) .bb-board-card__face--front {
	transition: box-shadow 0.3s, visibility 0s linear 0.35s;
	visibility: hidden;
}

/* --------------------------------------------------------------------------
   Front face
   -------------------------------------------------------------------------- */

/* Portraits arrive at whatever shape Airtable holds — of the eleven current
   members, five are LANDSCAPE and one is a 224px square; none is 4:5. So the
   well is a fixed 4:5 box and the image is CONTAINED inside it, letterboxed
   against the forest gradient. object-fit: cover would crop the tops off half
   the board. The fixed ratio also pins layout, so no width/height attributes
   are needed to avoid a layout shift. */
.bb-board-card__well {
	aspect-ratio: 4 / 5;
	background: linear-gradient( 135deg, var( --wp--preset--color--forest-deep ) 0%, var( --wp--preset--color--forest ) 100% );
	border-radius: 6px 26px 0 0;
	display: grid;
	flex-shrink: 0;
	overflow: hidden;
	place-items: center;
	position: relative;
	width: 100%;
}

.bb-board-card__photo {
	height: 100%;
	inset: 0;
	object-fit: contain;
	position: absolute;
	width: 100%;

	/* Carrier for the low-quality placeholder, when the feed sends one. Inert
	   until then. `contain` here is not a guess: it fits the blur into the SAME
	   box object-fit fits the photo into, so the two are pixel-aligned by
	   construction and the sharp image simply paints over its own blur. No
	   fade, no observer, no flash — the browser has the data URI the instant it
	   parses the attribute. */
	background-position: center;
	background-repeat: no-repeat;
	background-size: contain;
}

/* A background paints BEHIND replaced content, so an LQIP whose aspect ratio
   ever disagreed with the photo would fringe the letterbox bars permanently
   rather than briefly. Retire it once the real thing has painted. */
.bb-board-card__well.is-loaded .bb-board-card__photo { background-image: none; }

/* The bust and the monogram share ONE cell rather than stacking into two rows.
   The well is a grid, and until now it held exactly one in-flow item — the
   <img> is absolutely positioned and so is not a grid item at all. Adding the
   bust as a second in-flow child would create two implicit rows and split the
   free space between them, pushing the monogram below centre and resolving the
   bust's height against a half-height track. That misfires precisely on the
   two cards this feature exists for: a member with no photograph, and one
   whose photograph failed. */
.bb-board-card__silhouette,
.bb-board-card__initials {
	grid-area: 1 / 1;
}

.bb-board-card__silhouette {
	color: var( --wp--preset--color--terracotta-on-dark );
	height: 42%;
	opacity: 0.35;
	transition: opacity 200ms ease;
	width: auto;
}

/* Once the portrait has painted the bust would only ever show in the letterbox
   bars, so it goes. Without JavaScript this class never lands and the bust
   stays there: mildly odd, never broken. */
.bb-board-card__well.is-loaded .bb-board-card__silhouette { opacity: 0; }

/* Whenever the MONOGRAM is on screen — a member with no photograph at all, or
   one whose photograph failed — the bust steps back to a watermark. The two
   share a single grid cell, and at their natural sizes they are close enough
   to the same height that they read as one muddy shape rather than as initials
   over a silhouette. Bigger and much fainter separates them: the letters stay
   the thing you read, the bust becomes texture behind them.

   Both selectors use :has() on the WELL deliberately. The bust is a PRECEDING
   sibling of the <img>, so a `~` combinator would silently never match — and
   it has to stay in front, because style.css pairs the image with the monogram
   using an ADJACENT combinator further down.

   Order matters: these come after the .is-loaded rule at equal specificity, so
   a portrait that loads and only later fails still brings the bust back. */
.bb-board-card__well:has( > .bb-board-card__initials:not( .bb-board-card__initials--fallback ) ) .bb-board-card__silhouette,
.bb-board-card__well:has( .bb-board-card__photo.is-hidden ) .bb-board-card__silhouette {
	height: 72%;
	opacity: 0.16;
}

.bb-board-card__initials {
	color: var( --wp--preset--color--terracotta-on-dark );
	font-family: var( --wp--preset--font-family--display );
	font-size: 3.2rem;
	font-weight: 600;
	letter-spacing: 0.04em;
	line-height: 1;
}

/* Rendered but idle whenever a photo exists, so a portrait that fails to load
   can be swapped for the monogram without re-rendering anything. */
.bb-board-card__initials--fallback { display: none; }
.bb-board-card__photo.is-hidden { display: none; }
.bb-board-card__photo.is-hidden + .bb-board-card__initials--fallback { display: block; }

.bb-board-card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	padding: var( --wp--preset--spacing--40 );
}

.bb-board-card__name {
	color: var( --wp--preset--color--forest );
	font-family: var( --wp--preset--font-family--display );
	font-size: var( --wp--preset--font-size--large );
	font-weight: 600;
	line-height: 1.2;
	margin: 0;
}

.bb-board-card__accent {
	background-color: var( --wp--preset--color--terracotta );
	border-radius: 2px;
	display: block;
	height: 3px;
	margin-block: var( --wp--preset--spacing--20 );
	width: 36px;
}

.bb-board-card__excerpt {
	-webkit-box-orient: vertical;
	color: var( --wp--preset--color--contrast );
	display: -webkit-box;
	font-size: var( --wp--preset--font-size--small );
	-webkit-line-clamp: 3;
	line-clamp: 3;
	margin: 0 0 var( --wp--preset--spacing--40 );
	overflow: hidden;
}

.bb-board-card__more {
	align-items: center;
	align-self: flex-start;
	background-color: var( --wp--preset--color--terracotta-text );
	border-radius: 999px;
	color: var( --wp--preset--color--base );
	display: inline-flex;
	font-size: var( --wp--preset--font-size--small );
	font-weight: 600;
	gap: 0.5em;
	/* Pins the control to the bottom regardless of how much bio sits above it,
	   so every card in a row has its button on the same line. */
	margin-top: auto;
	min-height: 44px;
	padding: 0.6em 1.3em;
	text-decoration: none;
	transition: transform 0.2s, box-shadow 0.2s, background-color 0.2s;
}

.bb-board-card__more:hover,
.bb-board-card__more:focus-visible {
	background-color: var( --wp--preset--color--forest );
	box-shadow: rgb( 143 69 41 / 0.35 ) 0 10px 22px;
	color: var( --wp--preset--color--base );
	transform: translateY( -2px );
}

.bb-board-card__arrow {
	display: inline-block;
	transition: transform 0.2s;
}

.bb-board-card__more:hover .bb-board-card__arrow,
.bb-board-card__more:focus-visible .bb-board-card__arrow {
	transform: translateX( 3px );
}

/* --------------------------------------------------------------------------
   Back face
   -------------------------------------------------------------------------- */

/* Solid, not the gradient. terracotta-on-dark measures 4.87:1 on forest-deep
   but only 4.25:1 on forest — below the 4.5 needed for normal-size text. Laying
   the office label on the gradient would drift it under the threshold as the
   panel lightens, so the header strip stays at the dark stop and the gradient
   begins below it. scripts/check-contrast.mjs asserts the pairing that survives. */
.bb-board-card__backhead {
	align-items: center;
	background-color: var( --wp--preset--color--forest-deep );
	border-bottom: 1px solid rgb( 227 217 198 / 0.16 );
	display: flex;
	flex-shrink: 0;
	gap: var( --wp--preset--spacing--30 );
	padding: var( --wp--preset--spacing--40 ) var( --wp--preset--spacing--40 ) var( --wp--preset--spacing--30 );
	border-radius: 26px 0 0 0;
}

.bb-board-card__avatar {
	align-items: center;
	background: linear-gradient( 135deg, var( --wp--preset--color--forest ), var( --wp--preset--color--forest-deep ) );
	border: 2px solid var( --wp--preset--color--terracotta-on-dark );
	border-radius: 50%;
	color: var( --wp--preset--color--terracotta-on-dark );
	display: flex;
	flex-shrink: 0;
	font-family: var( --wp--preset--font-family--display );
	font-size: var( --wp--preset--font-size--medium );
	height: 3.5rem;
	justify-content: center;
	overflow: hidden;
	width: 3.5rem;
}

.bb-board-card__avatar img {
	height: 100%;
	object-fit: cover;
	width: 100%;
}

.bb-board-card__backhead-text {
	display: flex;
	flex-direction: column;
	gap: 0.15em;
	min-width: 0;
}

.bb-board-card__backname {
	color: var( --wp--preset--color--base );
	font-family: var( --wp--preset--font-family--display );
	font-size: var( --wp--preset--font-size--large );
	font-weight: 600;
	line-height: 1.15;
}

.bb-board-card__office {
	color: var( --wp--preset--color--terracotta-on-dark );
	font-size: var( --wp--preset--font-size--x-small );
}

/* The bio scrolls inside the card rather than growing it: the back is
   absolutely positioned to the front's height, and bios range from 436 to 1291
   characters. */
.bb-board-card__bio {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: var( --wp--preset--spacing--30 ) var( --wp--preset--spacing--40 ) var( --wp--preset--spacing--40 );
}

.bb-board-card__bio p {
	color: var( --wp--preset--color--base-alt );
	font-size: var( --wp--preset--font-size--small );
	line-height: 1.6;
	margin: 0;
	/* Biographies arrive with \n\n paragraph breaks and no markup. */
	white-space: pre-line;
}

.bb-board-card__bio-missing {
	font-style: italic;
	opacity: 0.75;
}

.bb-board-card__close {
	align-items: center;
	background-color: var( --wp--preset--color--forest );
	border-radius: 50%;
	color: var( --wp--preset--color--base );
	display: flex;
	font-size: 1.25rem;
	height: 2rem;
	inset-block-start: var( --wp--preset--spacing--20 );
	inset-inline-end: var( --wp--preset--spacing--20 );
	justify-content: center;
	line-height: 1;
	position: absolute;
	text-decoration: none;
	transition: background-color 0.2s;
	width: 2rem;
	z-index: 2;
}

.bb-board-card__close:hover,
.bb-board-card__close:focus-visible {
	background-color: var( --wp--preset--color--terracotta-text );
	color: var( --wp--preset--color--base );
}

/* --------------------------------------------------------------------------
   Focus
   -------------------------------------------------------------------------- */

/* The back panel is tabindex="-1" and receives focus only because the script
   moves it there, so a pointer user who clicked "Read full bio" would otherwise
   get the browser's default ring drawn around the whole card — an outline they
   did not ask for, in a colour the palette does not contain. :focus-visible
   still fires for keyboard users, who DO need to see where focus went, and they
   get the theme's own ring. */
.bb-board-card__face--back:focus {
	outline: none;
}

.bb-board-card__face--back:focus-visible {
	outline: 2px solid var( --wp--preset--color--terracotta-on-dark );
	outline-offset: -4px;
}

.bb-board-card__more:focus-visible,
.bb-board-card__close:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   Fallbacks
   -------------------------------------------------------------------------- */

/* Without :has(), the no-JS :target flip cannot be expressed at all. Rather
   than leave a card that half-rotates, drop out of 3D entirely and let the back
   face sit in flow beneath the front — every bio still readable, just not
   animated. */
@supports not ( selector( :has( * ) ) ) {
	.bb-board-card__inner {
		transform: none !important;
		transform-style: flat;
	}

	.bb-board-card__face--back {
		position: static;
		transform: none;
		visibility: visible;
	}

	.bb-board-card__face { backface-visibility: visible; }
}

@media ( prefers-reduced-motion: reduce ) {
	.bb-board-card,
	.bb-board-card__inner,
	.bb-board-card__face,
	.bb-board-card__more,
	.bb-board-card__arrow,
	.bb-board-card__silhouette {
		transition: none !important;
	}

	.bb-board-card:hover,
	.bb-board-card__more:hover {
		transform: none;
	}
}
