/*
Theme Name: Redwood Vihara
Theme URI: https://redwoodvihara.org
Author: Redwood Vihara
Description: A block theme for Redwood Vihara — a Buddhist monastery and training center in the Chan lineage of Venerable Master Hsuan Hua. Forest green, bone, and seal red; quiet serif typography and full-bleed imagery.
Requires at least: 6.7
Tested up to: 7.0
Requires PHP: 8.1
Version: 1.2.3
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: redwood-vihara
Tags: full-site-editing, one-column, custom-colors, custom-menu, editor-style, featured-images, translation-ready
*/

/* --------------------------------------------------------------------------
   Supplemental styles. The design system lives in theme.json — this file is
   only for things theme.json cannot express.
   -------------------------------------------------------------------------- */

/* Smooth anchor scrolling, offset for the sticky header. */
html {
	scroll-behavior: smooth;
	scroll-padding-top: 6rem;
}

@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	*, *::before, *::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
	}
}

/* Optical refinements for the serif stack. */
body {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-rendering: optimizeLegibility;
	font-variant-ligatures: common-ligatures;
}

/* Long-form reading measure: slightly looser leading inside post content. */
.rv-prose p {
	max-width: 38rem;
}

/* Underlines on content links: offset and thinned so they sit clear of the
   serif descenders instead of crowding the text (theme.json sets the color/
   text-decoration; navigation and the site title opt out there). */
.entry-content a,
.wp-block-post-content a,
.rv-prose a {
	text-decoration-thickness: 1px;
	text-underline-offset: 0.15em;
}

/* --- Sticky header ------------------------------------------------------- */
/* No backdrop-filter here. It would make the header a containing block for
   position:fixed descendants, which collapses the mobile navigation overlay
   into the header's own box instead of covering the viewport. The header is a
   solid colour anyway, so there was nothing to blur. */
.rv-header {
	position: sticky;
	top: 0;
	z-index: 100;
	transition: background-color 0.3s ease, box-shadow 0.3s ease;
}

/* The wordmark is two words but reads as one lockup — never break it. */
.rv-header .wp-block-site-title {
	white-space: nowrap;
	line-height: 1.25;
}

.rv-header .wp-block-site-logo img {
	display: block;
}

/* Below the nav's natural breakpoint the wordmark competes for room; the seal
   alone carries the identity. */
@media (max-width: 60rem) {
	.rv-header .wp-block-site-title {
		display: none;
	}
}

/* --- Navigation: understated underline on hover -------------------------- */
.wp-block-navigation .wp-block-navigation-item__content {
	position: relative;
	padding-block: 0.35em;
}

.wp-block-navigation .wp-block-navigation-item__content::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 1px;
	background: currentColor;
	transform: scaleX(0);
	transform-origin: left;
	transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
	opacity: 0.7;
}

.wp-block-navigation .wp-block-navigation-item__content:hover::after,
.wp-block-navigation .current-menu-item > .wp-block-navigation-item__content::after {
	transform: scaleX(1);
}

/* --- Mobile navigation overlay ------------------------------------------- */
/* Cover the whole viewport, centre the links, and give them touch-sized
   targets. Uses dvh so mobile browser chrome doesn't clip the bottom. */
.wp-block-navigation__responsive-container.is-menu-open {
	position: fixed;
	inset: 0;
	/* No explicit width: `inset: 0` already spans the viewport, and 100vw plus
	   padding overflows horizontally (and ignores the scrollbar). */
	box-sizing: border-box;
	height: 100dvh;
	min-height: 100dvh;
	padding: calc(var(--wp--preset--spacing--40) + 1.5rem) var(--wp--preset--spacing--20) var(--wp--preset--spacing--40);
	overflow-y: auto;
	overscroll-behavior: contain;
	background-color: var(--wp--preset--color--forest-deep) !important;
	z-index: 200;
}

.wp-block-navigation__responsive-container.is-menu-open *,
.wp-block-navigation__responsive-container.is-menu-open *::before,
.wp-block-navigation__responsive-container.is-menu-open *::after {
	box-sizing: border-box;
}

/* The desktop nav is justified right, which WordPress expresses as an
   `items-justified-right` class on the list. In the overlay that pins every
   link to the right edge, so centring has to be forced back on. */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container,
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container.items-justified-right,
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container.items-justified-left {
	align-items: center;
	justify-content: flex-start;
	width: 100%;
	text-align: center;
}

.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-content {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--wp--preset--spacing--10);
	min-height: 100%;
	width: 100%;
}

/* Top-level items read as a quiet display list, not a nav bar. */
.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation-item__content {
	font-family: var(--wp--preset--font-family--display);
	font-size: 1.75rem;
	font-weight: 300;
	letter-spacing: 0.04em;
	color: var(--wp--preset--color--bone);
	padding: 0.5rem 0.75rem;
	text-align: center;
}

/* The hover underline is a desktop affordance; it only flickers on touch. */
.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation-item__content::after {
	content: none;
}

/* Each top-level entry stacks its label above its children and fills the row,
   so both centre on the same axis. */
/* Core's `items-justified-right` class sets align-items: flex-end on both the
   list and each item with higher specificity than a plain class selector can
   reach. The overlay deliberately overrides that layout, so force it. */
.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__container > li {
	display: flex;
	flex-direction: column;
	align-items: center !important;
	justify-content: center;
	width: 100%;
	text-align: center;
}

.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__container {
	align-items: center !important;
}

/* Submenu children: smaller, in the interface face, visually subordinate.
   box-shadow must be cleared explicitly — the desktop dropdown rule gives it a
   soft lift shadow, which over the dark overlay reads as a stray pale card. */
.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__submenu-container {
	background-color: transparent !important;
	box-shadow: none !important;
	padding-block: 0.25rem;
	border: none;
	align-items: center;
	width: 100%;
	min-width: 0;
	position: static;
	opacity: 1;
	visibility: visible;
}

.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__submenu-container .wp-block-navigation-item__content {
	font-family: var(--wp--preset--font-family--ui);
	font-size: 1rem;
	letter-spacing: 0.08em;
	color: #cfd6c6;
	padding: 0.6rem 0.75rem;
}

/* Generous, obvious close control. */
.wp-block-navigation__responsive-container-close {
	top: var(--wp--preset--spacing--20);
	right: var(--wp--preset--spacing--20);
	padding: 0.5rem;
	color: var(--wp--preset--color--bone);
}

.wp-block-navigation__responsive-container-close svg {
	width: 1.75rem;
	height: 1.75rem;
}

/* The hamburger sits in the dark header, so it must be light. */
.rv-header .wp-block-navigation__responsive-container-open {
	color: var(--wp--preset--color--bone);
	padding: 0.4rem;
}

.rv-header .wp-block-navigation__responsive-container-open svg {
	width: 1.6rem;
	height: 1.6rem;
}

/* Stop the page behind the overlay from scrolling. */
.has-modal-open body,
body.has-modal-open {
	overflow: hidden;
}

/* Submenus: a quiet card rather than a hard dropdown.
   Desktop only — below the overlay breakpoint the submenu is rendered inline
   inside the full-screen menu, where a pale card and drop shadow are wrong. */
@media (min-width: 600px) {
	.wp-block-navigation .wp-block-navigation-submenu__toggle + .wp-block-navigation__submenu-container,
	.wp-block-navigation .wp-block-navigation__submenu-container {
		border: none;
		box-shadow: var(--wp--preset--shadow--lift);
		background-color: var(--wp--preset--color--bone);
		padding-block: 0.5rem;
		min-width: 15rem;
	}

	.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
		padding: 0.6rem 1.25rem;
	}
}

/* --- Hero ---------------------------------------------------------------- */
.rv-hero .wp-block-cover__inner-container {
	max-width: 52rem;
}

.rv-hero h1 {
	text-wrap: balance;
}

/* Eyebrow label above headings. */
.rv-eyebrow {
	font-family: var(--wp--preset--font-family--ui);
	font-size: var(--wp--preset--font-size--x-small);
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--wp--preset--color--gold);
	margin-bottom: var(--wp--preset--spacing--10);
}

/* Over photography the gold eyebrow loses contrast against bright foliage;
   a soft shadow anchors it without introducing a visible plate. */
.rv-hero .rv-eyebrow,
.rv-hero-edit .rv-eyebrow {
	color: #e8d5a3;
	text-shadow: 0 1px 12px rgba(20, 32, 18, 0.85);
}

.rv-hero h1,
.rv-hero p,
.rv-hero-edit h1,
.rv-hero-edit p {
	text-shadow: 0 2px 24px rgba(20, 32, 18, 0.5);
}

/* --- Hero, in the editor -------------------------------------------------- */
/* The front end's cover shell is built by render.php, so the editor cannot
   reuse it. These rules stand in for it: same photograph, same overlay, same
   measure, so the wording can be judged against the real thing while it is
   being typed. */
.rv-hero-edit {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 60vh;
	padding: var(--wp--preset--spacing--50) var(--wp--preset--spacing--30);
	background-color: var(--wp--preset--color--forest-deep);
	background-position: center;
	background-size: cover;
}

.rv-hero-edit__overlay {
	position: absolute;
	inset: 0;
	background-color: var(--wp--preset--color--forest-deep);
	pointer-events: none;
}

.rv-hero-edit__inner {
	position: relative;
	width: 100%;
	max-width: 52rem;
}

.rv-hero-edit h1 {
	text-wrap: balance;
}

.rv-hero-edit__note {
	color: var(--wp--preset--color--ink-soft);
	font-size: 0.75rem;
	margin: 0.5rem 0 0;
}

/* --- Cards --------------------------------------------------------------- */
/* No `height: 100%` here — the parent row is auto-height, so the percentage
   resolves against nothing and collapses the card to its content instead.
   Flexbox stretch already equalises the heights. */
.rv-card {
	transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.35s ease;
}

/* Cards in a columns row share a baseline height regardless of how much copy
   each one carries, and the trailing link sits on the bottom edge. */
.wp-block-columns > .wp-block-column.rv-card {
	display: flex;
	flex-direction: column;
	align-self: stretch;
}

.wp-block-columns > .wp-block-column.rv-card > :last-child {
	margin-top: auto;
	padding-top: var(--wp--preset--spacing--10);
}

.rv-card:hover {
	transform: translateY(-3px);
	box-shadow: var(--wp--preset--shadow--lift);
}

.rv-card img {
	transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.rv-card:hover img {
	transform: scale(1.03);
}

.rv-card .wp-block-post-featured-image {
	overflow: hidden;
}

/* --- Events -------------------------------------------------------------- */
.rv-event-date {
	font-family: var(--wp--preset--font-family--ui);
	font-size: var(--wp--preset--font-size--x-small);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--wp--preset--color--seal);
}

.rv-event-location {
	font-family: var(--wp--preset--font-family--ui);
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--ink-soft);
}

/* The events archive reads as a list rather than a grid: photo on the left,
   date/title/summary on the right. The image keeps its declared width instead
   of being squeezed by the flex row, and the copy takes whatever is left. */
.rv-event-row {
	position: relative;
}

.rv-event-row > .wp-block-post-featured-image {
	flex: 0 0 auto;
	overflow: hidden;
	margin-bottom: 0;
}

.rv-event-row__body {
	flex: 1 1 auto;
	min-width: 0;
}

/* An event with no featured image would otherwise start its copy at the far
   left, breaking the column the rest of the list shares. The block renders
   nothing at all when there is no thumbnail, so the body being :first-child is
   exactly the "no photograph" case — hold the space open. */
.rv-event-row > .rv-event-row__body:first-child {
	padding-left: calc(18rem + var(--wp--preset--spacing--30));
}

/* The whole row is the click target. Rather than wrap the row in a second
   anchor — which would nest a link around the title's own link — the title's
   anchor is stretched over the row, so there is still exactly one link per
   event for keyboards and screen readers. The featured image drops its link
   in the template for the same reason. */
.rv-event-row .wp-block-post-title a::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
}

/* The stretched anchor swallows the focus ring, so the row wears it instead. */
.rv-event-row:focus-within {
	outline: 2px solid var(--wp--preset--color--seal);
	outline-offset: 4px;
}

.rv-event-row .wp-block-post-title a:focus {
	outline: none;
}

/* Page numbers run together without a gap of their own — the pagination block
   only spaces its three top-level children, not the numbers inside. */
.wp-block-query-pagination-numbers {
	display: inline-flex;
	gap: 0.85rem;
}

/* The link between the upcoming archive and the past-events page. Quiet — it is
   a way out of the list, not a call to action. */
.rv-events-switch {
	font-size: var(--wp--preset--font-size--small);
	letter-spacing: 0.04em;
}

.rv-events-switch a {
	color: var(--wp--preset--color--ink-soft);
	text-decoration: none;
	border-bottom: 1px solid currentColor;
	padding-bottom: 0.15em;
}

.rv-events-switch a:hover,
.rv-events-switch a:focus {
	color: var(--wp--preset--color--seal);
}

/* The date sits at the very top of the column, level with the photo's top
   edge; a little padding drops it off that line. */
.rv-event-row .rv-event-date {
	padding-top: 0.5rem;
}

/* Summaries are trimmed to a couple of lines so every row stays scannable;
   a clamp keeps that true even when an excerpt is written by hand. */
.rv-event-row__summary p {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	line-clamp: 3;
	overflow: hidden;
}

/* Below the row's comfortable width the photo would be a thumbnail; stack. */
@media (max-width: 600px) {
	.rv-event-row {
		flex-direction: column;
		align-items: stretch;
	}

	.rv-event-row > .wp-block-post-featured-image {
		width: 100% !important;
	}

	/* Stacked, there is no column to hold open. */
	.rv-event-row > .rv-event-row__body:first-child {
		padding-left: 0;
	}
}

/* Bindings resolve to an empty string when an event has no location; collapse
   the block rather than leaving a gap in the layout. */
.rv-event-date:empty,
.rv-event-location:empty {
	display: none;
}

/* --- Images -------------------------------------------------------------- */
figure.wp-block-image img,
.wp-block-post-featured-image img {
	display: block;
}

/* Hero featured images come in both orientations — landscape monastery shots
   and portrait photographs of teachers. Rather than force one aspect ratio
   (which crops through a subject's head), cap the height and bias the crop
   toward the upper third, where faces sit. */
.wp-block-post-featured-image.alignwide img {
	max-height: min(34rem, 68vh);
	width: 100%;
	object-fit: cover;
	object-position: center 28%;
}

/* Inline images within page copy should breathe but never dominate.
   Scoped to DIRECT children: gallery tiles are also figure.wp-block-image, and
   overriding their width breaks the gallery's flex sizing. */
.rv-prose > figure.wp-block-image {
	margin-block: var(--wp--preset--spacing--30);
	text-align: center;
}

.rv-prose > figure.wp-block-image img {
	max-height: 40rem;
	width: auto;
	max-width: 100%;
	margin-inline: auto;
}

/* --- Gallery ------------------------------------------------------------- */
/* Three even columns that collapse gracefully. The block's own flex sizing
   rounds up past the container at 3-up, so the columns are set explicitly. */
.wp-block-gallery.has-nested-images.columns-3 {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: var(--wp--preset--spacing--20);
}

/* Core sizes gallery tiles with a `:not(#individual-image)` specificity hack,
   and prints that rule inline in <head> — i.e. AFTER this linked stylesheet.
   Matching its specificity therefore loses on source order, so the selectors
   below add a class to outrank it outright. Grid handles the sizing; the tiles
   only need to fill their cell. */
.rv-prose .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) {
	width: 100%;
	max-width: 100%;
	margin: 0;
	flex: none;
}

.rv-prose .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) > a {
	display: block;
	width: 100%;
	height: 100%;
}

.rv-prose .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image) img {
	width: 100%;
	height: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	max-height: none;
	flex: none;
}

@media (max-width: 48rem) {
	.wp-block-gallery.has-nested-images.columns-3 {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 30rem) {
	.wp-block-gallery.has-nested-images.columns-3 {
		grid-template-columns: 1fr;
	}
}

/* --- Home mosaic --------------------------------------------------------- */
/* The homepage photograph grid (patterns/home-gallery.php).
 *
 * Six columns. The row unit is half a column's width, so a tile's proportions
 * come out of its span: `n` columns by `m` rows is an aspect ratio of 2n/m. The
 * row height cannot be a percentage — percentages in `grid-auto-rows` resolve
 * against the grid's own height, which is what we are trying to derive — so it
 * is recomputed from the same expression WordPress uses for an `alignwide`
 * child under root-padding-aware alignments.
 *
 * Which tile a photograph gets is decided by the pattern from the photograph
 * itself, so the sequence of shapes is whatever an editor happened to upload.
 * Two properties keep that from tearing holes in the grid:
 *
 *   - every span is 2 or 4 columns, both divisors of six; and
 *   - every span is 3 or 6 rows, so two short tiles stack into the height of
 *     one tall one.
 *
 * Together those mean the grid resolves into 6-row bands of three 2-column
 * stacks, and any mix of shapes closes. This is why the short tile is 4:3 and
 * not square: a square tile would be 4 rows, which shares no rhythm with 6 and
 * strands a 2-row gap under every portrait.
 *
 * `dense` then backfills what is left, at the cost of letting the packer
 * reorder slightly — fine here, where this is one field of photographs rather
 * than a list whose order carries meaning.
 */
.rv-mosaic {
	--rv-mosaic-gap: 0.5rem;
	--rv-mosaic-width: min(76rem, 100vw - 2 * var(--wp--preset--spacing--20));
	--rv-mosaic-column: calc((var(--rv-mosaic-width) - 5 * var(--rv-mosaic-gap)) / 6);

	display: grid;
	grid-template-columns: repeat(6, minmax(0, 1fr));
	grid-auto-rows: calc((var(--rv-mosaic-column) - var(--rv-mosaic-gap)) / 2);
	grid-auto-flow: row dense;
	gap: var(--rv-mosaic-gap);
}

.rv-mosaic__tile {
	position: relative;
	margin: 0;
	overflow: hidden;
	background-color: var(--wp--preset--color--forest);
}

/* 2 x 6 — 2:3 upright, for photographs shot in portrait. */
.rv-mosaic__tile.is-portrait {
	grid-column: span 2;
	grid-row: span 6;
}

/* 4 x 6 — 4:3 at double size. The occasional large tile that gives the grid
   its rhythm; the pattern promotes every fifth wide photograph into one. */
.rv-mosaic__tile.is-feature {
	grid-column: span 4;
	grid-row: span 6;
}

/* 2 x 3 — 4:3, the default. Two of these stack into one tall tile. */
.rv-mosaic__tile.is-wide {
	grid-column: span 2;
	grid-row: span 3;
}

/* 6 x 6 and 3 x 6 — the one- and two-photograph cases. Neither combination of
   the shapes above can fill a band on its own, so these divide one exactly:
   whole, or half each. */
.rv-mosaic__tile.is-solo {
	grid-column: span 6;
	grid-row: span 6;
}

.rv-mosaic__tile.is-duo {
	grid-column: span 3;
	grid-row: span 6;
}

/* 4 x 3 — a panoramic band, and the grid's adjustment screw. It covers the
   same area as a portrait tile while occupying half the height across double
   the width, which is what lets the pattern top the total area up to a whole
   number of bands without distorting anything: see the tail correction in
   patterns/home-gallery.php. */
.rv-mosaic__tile.is-banner {
	grid-column: span 4;
	grid-row: span 3;
}

/* Absolute rather than in-flow so the image never contributes its intrinsic
   size back to the grid: the cell is sized by the row unit alone, and the
   layout is settled before any photograph arrives.

   The photographs are candid and vary a lot in exposure, so they are held
   slightly back by default: the grid reads as one field rather than a set of
   competing images, and the hover becomes a genuine lift. */
.rv-mosaic__tile img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	filter: saturate(0.92) brightness(0.94);
	transition: transform 0.7s ease, filter 0.7s ease;
}

@media (hover: hover) {
	.rv-mosaic__tile:hover img {
		transform: scale(1.04);
		filter: saturate(1) brightness(1.02);
	}
}

/* Below the breakpoint six columns would put the smallest tiles under ~100px.
   Two even columns of squares instead: it stays flush for any number of
   photographs, and the portrait and landscape shots centre-crop acceptably.
   Uniform tiles are also the only arrangement that cannot strand a hole. */
@media (max-width: 52rem) {
	.rv-mosaic {
		--rv-mosaic-width: calc(100vw - 2 * var(--wp--preset--spacing--20));
		--rv-mosaic-column: calc((var(--rv-mosaic-width) - var(--rv-mosaic-gap)) / 2);

		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	/* One shape for everything: at this width the size differences stop
	   reading as composition and only make the small tiles too small, and
	   uniform tiles are the one arrangement that cannot strand a gap. */
	.rv-mosaic__tile.is-portrait,
	.rv-mosaic__tile.is-feature,
	.rv-mosaic__tile.is-banner,
	.rv-mosaic__tile.is-wide,
	.rv-mosaic__tile.is-duo {
		grid-column: span 1;
		grid-row: span 3;
	}

	/* A lone photograph still spans the width, as it does on desktop. */
	.rv-mosaic__tile.is-solo {
		grid-column: span 2;
		grid-row: span 3;
	}
}

/* Held out of the grid rather than never rendered: the lightbox script finds
   every `.rv-mosaic__open` button in the mosaic regardless of visibility, so
   the hidden tiles are still there for it to step through once "View More
   Photos" opens the viewer. `display: none` removes a grid item from layout
   entirely, so none of the band-closing math above has to account for them. */
.rv-mosaic__tile--hidden {
	display: none;
}

.rv-mosaic__more-wrap {
	margin: var(--wp--preset--spacing--40) 0 0;
	text-align: center;
}

.rv-mosaic__more {
	cursor: pointer;
}

/* --- Gallery lightbox ---------------------------------------------------- */
/* The full-screen viewer for a mosaic photograph (assets/js/lightbox.js).
 *
 * The dialog is in the browser's top layer, so none of this needs a z-index or
 * has to reckon with the sticky header.
 */

/* The tile's button is invisible: the photograph is the affordance. It only
   fills its figure and takes the pointer. */
.rv-mosaic__open {
	position: absolute;
	inset: 0;
	display: block;
	padding: 0;
	border: 0;
	background: none;
	cursor: zoom-in;
	-webkit-appearance: none;
	appearance: none;
}

/* Inside, so the focus ring frames the photograph rather than being clipped by
   the tile's own overflow. */
.rv-mosaic__open:focus-visible {
	outline: 2px solid var(--wp--preset--color--bone);
	outline-offset: -4px;
}

.rv-lightbox {
	width: 100vw;
	max-width: 100vw;
	height: 100dvh; /* dvh, so the photograph is not cut by mobile browser chrome. */
	max-height: 100dvh;
	margin: 0;
	padding: 0;
	border: 0;
	overflow: hidden;
	background: transparent;
	color: var(--wp--preset--color--bone);
}

.rv-lightbox::backdrop {
	/* Nearly opaque rather than black: a hint of the page behind keeps the
	   viewer feeling like a layer over the site and not a new document. The
	   blur is what makes that hint atmosphere instead of legible text
	   competing with the photograph. */
	background-color: rgba(10, 15, 11, 0.96);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
}

/* The photograph's frame. Padded clear of the controls at every size, and of
   the notch and home indicator on a phone. */
.rv-lightbox__stage {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: max(3.5rem, env(safe-area-inset-top) + 3.5rem)
		max(1rem, env(safe-area-inset-right))
		max(4.5rem, env(safe-area-inset-bottom) + 3.5rem)
		max(1rem, env(safe-area-inset-left));
	/* The drag gestures need every pointermove; without this the browser
	   claims the vertical ones for scrolling and the swipe dies mid-way. */
	touch-action: none;
	transition: transform 0.25s ease, opacity 0.25s ease;
}

/* While a finger is down the stage tracks it exactly — any transition here
   would lag the gesture. */
.rv-lightbox__stage.is-dragging {
	transition: none;
}

.rv-lightbox__image {
	max-width: 100%;
	max-height: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
	/* The mosaic holds its photographs back; here they are the subject. */
	filter: none;
	user-select: none;
	-webkit-user-drag: none;
}

.rv-lightbox__caption,
.rv-lightbox__count {
	position: absolute;
	left: 50%;
	transform: translateX(-50%);
	margin: 0;
	max-width: min(40rem, 90vw);
	text-align: center;
	color: #cfd6c6;
}

.rv-lightbox__caption {
	bottom: max(2.5rem, env(safe-area-inset-bottom) + 2.25rem);
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.5;
}

.rv-lightbox__count {
	bottom: max(1rem, env(safe-area-inset-bottom) + 0.75rem);
	font-size: 0.75rem;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	opacity: 0.7;
}

/* Controls. Circular, 44px minimum so they are a comfortable touch target,
   and held at low contrast until hovered or focused so they sit behind the
   photograph rather than competing with it. */
.rv-lightbox__close,
.rv-lightbox__nav {
	position: absolute;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2.75rem;
	height: 2.75rem;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background-color: rgba(255, 255, 255, 0.08);
	color: var(--wp--preset--color--bone);
	cursor: pointer;
	transition: background-color 0.2s ease, opacity 0.2s ease;
	-webkit-appearance: none;
	appearance: none;
}

.rv-lightbox__close[hidden],
.rv-lightbox__nav[hidden] {
	display: none;
}

.rv-lightbox__close svg,
.rv-lightbox__nav svg {
	width: 1.25rem;
	height: 1.25rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.rv-lightbox__close:hover,
.rv-lightbox__nav:hover,
.rv-lightbox__close:focus-visible,
.rv-lightbox__nav:focus-visible {
	background-color: rgba(255, 255, 255, 0.2);
}

.rv-lightbox__close:focus-visible,
.rv-lightbox__nav:focus-visible {
	outline: 2px solid var(--wp--preset--color--bone);
	outline-offset: 2px;
}

.rv-lightbox__close {
	top: max(1rem, env(safe-area-inset-top) + 0.5rem);
	right: max(1rem, env(safe-area-inset-right) + 0.5rem);
}

.rv-lightbox__nav {
	top: 50%;
	transform: translateY(-50%);
}

.rv-lightbox__nav.is-prev {
	left: max(0.75rem, env(safe-area-inset-left));
}

.rv-lightbox__nav.is-next {
	right: max(0.75rem, env(safe-area-inset-right));
}

/* On a touch screen the swipe is the primary way through the gallery and the
   arrows would sit on top of the photograph, so they step out of the way —
   but they stay in the DOM, because a phone with a keyboard or a switch
   control still needs a button to press. The width clause catches the same
   problem on a narrow desktop window, where the edges get too tight to put a
   control over the photograph whatever the pointer is. */
@media (hover: none), (max-width: 34rem) {
	.rv-lightbox__nav {
		top: auto;
		bottom: max(0.75rem, env(safe-area-inset-bottom) + 0.5rem);
		transform: none;
		background-color: rgba(255, 255, 255, 0.06);
	}

	.rv-lightbox__count {
		bottom: max(1.6rem, env(safe-area-inset-bottom) + 1.35rem);
	}
}

/* Stop the page behind from scrolling under the viewer. iOS still scrolls the
   document on a drag that begins over a modal dialog's backdrop otherwise. */
html.rv-lightbox-open,
html.rv-lightbox-open body {
	overflow: hidden;
	overscroll-behavior: none;
}

/* --- Contact form (Fluent Forms) ----------------------------------------- *
 * The plugin ships a generic blue-and-rounded look. These rules pull it into
 * the theme's typography and palette so it reads as part of the site rather
 * than an embedded widget. Selectors stay on the plugin's own classes, which
 * are stable across its releases.
 * ------------------------------------------------------------------------- */

.rv-contact-form .fluentform .ff-el-group {
	margin-bottom: var(--wp--preset--spacing--20);
}

.rv-contact-form .fluentform .ff-el-input--label label {
	font-family: var(--wp--preset--font-family--ui);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--forest-soft);
	margin-bottom: 0.5rem;
	display: inline-block;
}

/* Required marker in the seal red rather than the default alarm red. */
.rv-contact-form .fluentform .ff-el-is-required.asterisk-right label::after {
	color: var(--wp--preset--color--seal);
}

.rv-contact-form .fluentform .ff-el-form-control {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	color: var(--wp--preset--color--ink);
	background-color: transparent;
	border: 1px solid rgba(58, 79, 46, 0.28);
	border-radius: 0;
	padding: 0.85rem 1rem;
	width: 100%;
	transition: border-color 0.2s ease, background-color 0.2s ease;
	box-shadow: none;
}

.rv-contact-form .fluentform .ff-el-form-control::placeholder {
	color: var(--wp--preset--color--ink-soft);
	opacity: 0.6;
}

.rv-contact-form .fluentform .ff-el-form-control:focus {
	border-color: var(--wp--preset--color--forest);
	background-color: rgba(255, 255, 255, 0.5);
	outline: 2px solid var(--wp--preset--color--gold);
	outline-offset: 2px;
}

.rv-contact-form .fluentform textarea.ff-el-form-control {
	min-height: 11rem;
	resize: vertical;
}

/* Submit button: match the theme's button element styling exactly. */
.rv-contact-form .fluentform .ff-btn-submit,
.rv-contact-form .fluentform .ff-btn.ff-btn-submit {
	font-family: var(--wp--preset--font-family--ui);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	background-color: var(--wp--preset--color--forest);
	color: var(--wp--preset--color--bone);
	border: none;
	border-radius: 0;
	padding: 1.05em 2.25em;
	cursor: pointer;
	transition: background-color 0.2s ease;
	box-shadow: none;
}

.rv-contact-form .fluentform .ff-btn-submit:hover,
.rv-contact-form .fluentform .ff-btn-submit:focus {
	background-color: var(--wp--preset--color--forest-deep);
	color: var(--wp--preset--color--bone);
}

/* Validation and success messaging in the theme's voice. */
.rv-contact-form .fluentform .error-text,
.rv-contact-form .fluentform .ff-el-is-error .text-danger {
	font-family: var(--wp--preset--font-family--ui);
	font-size: var(--wp--preset--font-size--x-small);
	color: var(--wp--preset--color--seal);
	letter-spacing: 0.04em;
}

.rv-contact-form .fluentform .ff-el-is-error .ff-el-form-control {
	border-color: var(--wp--preset--color--seal);
}

.rv-contact-form .ff-message-success {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--large);
	line-height: 1.75;
	color: var(--wp--preset--color--forest-deep);
	background-color: var(--wp--preset--color--bone-deep);
	border: none;
	border-left: 2px solid var(--wp--preset--color--gold);
	padding: var(--wp--preset--spacing--30);
}

/* The two name fields sit side by side on desktop, stacked on mobile. */
@media (max-width: 40rem) {
	.rv-contact-form .fluentform .ff-t-container {
		flex-direction: column;
	}

	.rv-contact-form .fluentform .ff-t-cell {
		width: 100%;
		flex: 0 0 100%;
	}
}

/* --- Newsletter sign-up (Mailchimp for WordPress) ------------------------ *
 * MC4WP renders whatever markup the form holds, wrapped in its own <form>, so
 * unlike the contact form there is no plugin look to override — every
 * selector below but the response one is our own markup, written in the
 * plugin's form editor. The field and button treatments deliberately match
 * the contact form's above so the two read as one family; they are written
 * out rather than shared because the elements they hang off differ.
 * ------------------------------------------------------------------------- */

.rv-subscribe-form .rv-subscribe-fields {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	align-items: stretch;
}

.rv-subscribe-form input[type="email"] {
	flex: 1 1 14rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	color: var(--wp--preset--color--ink);
	background-color: transparent;
	border: 1px solid rgba(58, 79, 46, 0.28);
	border-radius: 0;
	padding: 0.85rem 1rem;
	transition: border-color 0.2s ease, background-color 0.2s ease;
	box-shadow: none;
}

.rv-subscribe-form input[type="email"]::placeholder {
	color: var(--wp--preset--color--ink-soft);
	opacity: 0.6;
}

.rv-subscribe-form input[type="email"]:focus {
	border-color: var(--wp--preset--color--forest);
	background-color: rgba(255, 255, 255, 0.5);
	outline: 2px solid var(--wp--preset--color--gold);
	outline-offset: 2px;
}

.rv-subscribe-form input[type="submit"] {
	flex: 0 0 auto;
	font-family: var(--wp--preset--font-family--ui);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	background-color: var(--wp--preset--color--forest);
	color: var(--wp--preset--color--bone);
	border: none;
	border-radius: 0;
	padding: 1.05em 2.25em;
	cursor: pointer;
	transition: background-color 0.2s ease;
	box-shadow: none;
}

.rv-subscribe-form input[type="submit"]:hover,
.rv-subscribe-form input[type="submit"]:focus {
	background-color: var(--wp--preset--color--forest-deep);
	color: var(--wp--preset--color--bone);
}

/* The field and the button stack below the width where they stop fitting
   side by side without the button crowding the address. */
@media (max-width: 30rem) {
	.rv-subscribe-form .rv-subscribe-fields {
		flex-direction: column;
	}

	/* flex-basis follows the main axis, so the 14rem that keeps the field a
	   sensible width in a row would be its *height* in a column — a 14rem-tall
	   email box. Stacked, the field only needs its natural height; the column
	   stretches both it and the button to full width on its own. */
	.rv-subscribe-form input[type="email"] {
		flex: 0 0 auto;
	}
}

/* Sign-up responses. MC4WP always prints `.mc4wp-response`, empty until a
   submission, so the spacing lives on the alert inside it rather than on the
   wrapper — otherwise every unsubmitted form would carry a blank gap. */
.rv-subscribe-form .mc4wp-alert {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.75;
	margin-top: var(--wp--preset--spacing--20);
	padding: var(--wp--preset--spacing--20);
	border-left: 2px solid var(--wp--preset--color--gold);
	/* Translucent white rather than a palette tint: this form sits on bone on
	   the Newsletter page and on bone-deep in the homepage band, and a fixed
	   tint would vanish against one of them. */
	background-color: rgba(255, 255, 255, 0.55);
	color: var(--wp--preset--color--forest-deep);
}

.rv-subscribe-form .mc4wp-alert p {
	margin: 0;
}

.rv-subscribe-form .mc4wp-error {
	border-left-color: var(--wp--preset--color--seal);
	color: var(--wp--preset--color--seal);
}

/* --- Footer -------------------------------------------------------------- */
.rv-footer a {
	color: inherit;
	opacity: 0.85;
	transition: opacity 0.2s ease;
}

.rv-footer a:hover {
	opacity: 1;
	color: var(--wp--preset--color--gold);
}

/* --- Accessibility ------------------------------------------------------- */
:where(a, button, input, select, textarea, summary):focus-visible {
	outline: 2px solid var(--wp--preset--color--gold);
	outline-offset: 3px;
}

.skip-link:focus {
	background-color: var(--wp--preset--color--bone);
	color: var(--wp--preset--color--forest-deep);
	padding: 0.75rem 1.25rem;
	z-index: 999;
}

/* Screen-reader text (WP core convention). */
.screen-reader-text {
	border: 0;
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	width: 1px;
	word-wrap: normal !important;
}

/* --- Print --------------------------------------------------------------- */
@media print {
	.rv-header, .rv-footer, .wp-block-navigation { display: none !important; }
	body { background: #fff; color: #000; }
}
