/*
Theme Name: André Ester
Theme URI: https://andreester.com/
Author: André Ester
Author URI: https://andreester.com/
Description: A block theme for an artist's site. The page is an exhibition wall: works are mounted and framed, sections are rooms announced by a rule and a number, and catalogue data is set as a wall label. Content lives in the companion plugins, so a redesign never risks the catalogue.
Requires at least: 6.5
Tested up to: 7.0
Requires PHP: 8.1
Version: 2.0.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: andre-ester
Tags: portfolio, blog, one-column, block-patterns, full-site-editing, accessibility-ready, translation-ready, custom-colors, editor-style, wide-blocks
*/

/*
 * This file exists for the theme header above and for the few rules that
 * genuinely cannot live in theme.json. Everything about colour, spacing,
 * typography and layout belongs there, where the block editor can read it and
 * the artist can change it without touching CSS.
 *
 * The art direction this implements is documented in
 * .claude/skills/museographic-art-direction/. Read it before changing a value.
 */

/* ------------------------------------------------------------------ *
 * Aliases
 *
 * The preset variables are correct but unreadable at the point of use.
 * These are the same values under the names the design system uses.
 * ------------------------------------------------------------------ */

:root {
	--ae-gutter: var(--wp--custom--gutter);
	--ae-rule: 1px solid var(--wp--preset--color--contrast);
	--ae-hairline: 1px solid var(--wp--preset--color--line);
	--ae-ease: cubic-bezier(0.2, 0.6, 0.2, 1);
	--ae-label: var(--wp--preset--font-family--mono);
}

/* ------------------------------------------------------------------ *
 * Skip link
 * ------------------------------------------------------------------ */

.skip-link {
	position: absolute;
	top: -100px;
	left: 0;
	z-index: 100000;
	padding: 0.9em 1.4em;
	color: var(--wp--preset--color--base);
	background-color: var(--wp--preset--color--contrast);
	font-family: var(--ae-label);
	font-size: var(--wp--preset--font-size--x-small);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	text-decoration: none;
}

.skip-link:focus {
	top: 0;
}

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

/*
 * A visible focus ring everywhere, on the element that actually has focus.
 * Removing outlines is the single most common accessibility failure on
 * portfolio sites, and there is no design reason for it.
 */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 3px;
}

::selection {
	color: #fff;
	background-color: var(--wp--preset--color--accent);
}

/* ------------------------------------------------------------------ *
 * Typographic detail
 * ------------------------------------------------------------------ */

body {
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
	text-wrap: balance;
}

p, li, dd, figcaption {
	text-wrap: pretty;
}

/*
 * Anything set in the data face is data, and data lines up in columns rather
 * than dancing. Applied to the face itself so no component has to remember.
 */
[class*="has-mono-font-family"],
.ae-card__year,
.ae-artwork-meta__value,
.ae-project-meta__value,
.ae-scrolly__year,
.ae-marker,
time {
	font-variant-numeric: tabular-nums;
	font-feature-settings: "tnum" 1;
}

/*
 * Nothing in this system is round and nothing floats. Block supports and the
 * core stylesheet both reintroduce radii; this is the single place that says no.
 */
:where(.wp-block-button__link, .wp-block-search__button, input, textarea, select, img, .wp-block-image img) {
	border-radius: 0;
	box-shadow: none;
}

/* ------------------------------------------------------------------ *
 * Images and mounts
 *
 * An artwork is never cropped. Uniform proportion in a grid comes from the
 * mount around the work, exactly as it does on a wall, not from cutting the
 * work to fit — which is an editorial decision the theme has no business
 * making.
 * ------------------------------------------------------------------ */

img {
	max-width: 100%;
	height: auto;
}

.wp-block-post-featured-image {
	margin-bottom: var(--wp--preset--spacing--30);
	padding: clamp(0.75rem, 2vw, 1.5rem);
	background-color: var(--wp--preset--color--mount);
	border: var(--ae-hairline);
}

.wp-block-post-featured-image img {
	width: 100%;
	height: auto;
	object-fit: contain;
}

/*
 * Query loops render their own cards from core blocks, so they cannot reuse
 * the plugin's `.ae-card`. Without this the archives would show a ragged wall
 * of raw images while the home page showed mounted works — the same catalogue
 * in two visual languages.
 */
.wp-block-query .wp-block-post-featured-image {
	display: grid;
	place-items: center;
	aspect-ratio: 1 / 1;
	overflow: hidden;
	margin-bottom: var(--wp--preset--spacing--20);
	padding: clamp(0.75rem, 1.6vw, 1.25rem);
	background-color: var(--wp--preset--color--mount);
	border: var(--ae-hairline);
	transition: border-color 200ms var(--ae-ease);
}

.wp-block-query .wp-block-post-featured-image a {
	display: grid;
	place-items: center;
	width: 100%;
	height: 100%;
}

.wp-block-query .wp-block-post-featured-image img {
	max-width: 100%;
	max-height: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
}

.wp-block-query .wp-block-post-featured-image:hover,
.wp-block-query .wp-block-post-featured-image:focus-within {
	border-color: var(--wp--preset--color--accent);
}

/* A project is documented rather than mounted, so its image fills the frame. */
.wp-block-query.is-projects .wp-block-post-featured-image {
	aspect-ratio: 3 / 2;
	padding: 0;
	background-color: var(--wp--preset--color--line);
}

.wp-block-query.is-projects .wp-block-post-featured-image img {
	width: 100%;
	height: 100%;
	max-height: none;
	object-fit: cover;
}

.wp-block-query .wp-block-post-title {
	line-height: 1.25;
}

.wp-block-query .wp-block-post-title a {
	text-decoration: none;
}

.wp-block-query .wp-block-post-title a:hover,
.wp-block-query .wp-block-post-title a:focus-visible {
	text-decoration: underline;
	text-underline-offset: 0.22em;
}

/* ------------------------------------------------------------------ *
 * Pagination and search
 * ------------------------------------------------------------------ */

.wp-block-query-pagination {
	padding-top: var(--wp--preset--spacing--20);
	border-top: var(--ae-rule);
	font-family: var(--ae-label);
	font-size: var(--wp--preset--font-size--cartela);
	letter-spacing: 0.12em;
	text-transform: uppercase;
}

.wp-block-query-pagination a {
	text-decoration: none;
	border-bottom: 1px solid transparent;
}

.wp-block-query-pagination a:hover,
.wp-block-query-pagination a:focus-visible {
	border-bottom-color: currentColor;
}

.wp-block-query-pagination .current {
	color: var(--wp--preset--color--accent);
	font-weight: 700;
}

.wp-block-search__input {
	min-height: 48px;
	padding: 0.7em 0.85em;
	font: inherit;
	font-size: var(--wp--preset--font-size--small);
	color: inherit;
	background-color: var(--wp--preset--color--mount);
	border: 1px solid var(--wp--preset--color--contrast);
}

.wp-block-search__button {
	min-height: 48px;
	margin-inline-start: -1px;
	padding-inline: 1.4em;
	font-family: var(--ae-label);
	font-size: var(--wp--preset--font-size--cartela);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--wp--preset--color--base);
	background-color: var(--wp--preset--color--contrast);
	border: 1px solid var(--wp--preset--color--contrast);
	cursor: pointer;
}

.wp-block-search__button:hover,
.wp-block-search__button:focus-visible {
	color: #fff;
	background-color: var(--wp--preset--color--accent);
	border-color: var(--wp--preset--color--accent);
}

/* ------------------------------------------------------------------ *
 * Breadcrumbs
 *
 * Owned by the SEO plugin, which cannot know the type system. Set as data,
 * like every other piece of wayfinding on the site.
 * ------------------------------------------------------------------ */

.ae-breadcrumbs {
	font-family: var(--ae-label);
	font-size: var(--wp--preset--font-size--cartela);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--wp--preset--color--muted);
}

.ae-breadcrumbs a {
	color: inherit;
	text-decoration: none;
	border-bottom: 1px solid transparent;
}

.ae-breadcrumbs a:hover,
.ae-breadcrumbs a:focus-visible {
	border-bottom-color: currentColor;
}

/* ------------------------------------------------------------------ *
 * Site header
 *
 * Flush to the page gutter rather than centred in a container: the header is
 * the top edge of the wall, and an edge that stops short of the edge is a box.
 * ------------------------------------------------------------------ */

.site-header {
	border-bottom: var(--ae-rule);
}

.wp-block-site-title a {
	text-decoration: none;
}

.wp-block-navigation {
	--navigation-layout-gap: var(--wp--preset--spacing--30);
}

.wp-block-navigation .wp-block-navigation-item__content {
	padding-block: 0.4em;
	border-bottom: 1px solid transparent;
	transition: border-color 160ms var(--ae-ease);
}

.wp-block-navigation .wp-block-navigation-item__content:hover,
.wp-block-navigation .wp-block-navigation-item__content:focus-visible {
	border-bottom-color: currentColor;
	text-decoration: none;
}

/*
 * The current page is marked by the red square, not by colour alone — the
 * square survives greyscale, low vision and a printed page.
 */
.wp-block-navigation .current-menu-item > .wp-block-navigation-item__content,
.wp-block-navigation .current-menu-ancestor > .wp-block-navigation-item__content,
.wp-block-navigation .current-menu-parent > .wp-block-navigation-item__content {
	position: relative;
	border-bottom-color: currentColor;
}

.wp-block-navigation .current-menu-item > .wp-block-navigation-item__content::before,
.wp-block-navigation .current-menu-ancestor > .wp-block-navigation-item__content::before,
.wp-block-navigation .current-menu-parent > .wp-block-navigation-item__content::before {
	content: "";
	position: absolute;
	inset-block-start: -0.25em;
	inset-inline-start: -0.85em;
	width: 0.4em;
	height: 0.4em;
	background-color: var(--wp--preset--color--accent);
}

/* The mobile overlay is a room you walk into, not a dropdown. */
.wp-block-navigation__responsive-container.is-menu-open {
	padding: var(--wp--preset--spacing--40) var(--ae-gutter);
	color: var(--wp--preset--color--base);
	background-color: var(--wp--preset--color--contrast);
}

.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content {
	font-size: var(--wp--preset--font-size--large);
	letter-spacing: 0.04em;
}

.wp-block-navigation__responsive-container-open,
.wp-block-navigation__responsive-container-close {
	/* 44px is the smallest target most people can hit reliably. */
	min-width: 44px;
	min-height: 44px;
}

/* ------------------------------------------------------------------ *
 * Block styles
 * ------------------------------------------------------------------ */

/*
 * Filete — the sign at the entrance to a room. A rule the full width of the
 * section, the red square, and the label set in the data face. This is the
 * single device that gives the page its structure, so it is worth the specificity.
 */
.is-style-eyebrow {
	display: flex;
	align-items: baseline;
	gap: 0.9em;
	margin-block: 0 var(--wp--preset--spacing--40);
	padding-block-start: 0.85em;
	border-block-start: var(--ae-rule);
	font-family: var(--ae-label);
	font-size: var(--wp--preset--font-size--cartela);
	font-weight: 500;
	line-height: 1.2;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--wp--preset--color--contrast);
}

.is-style-eyebrow::before {
	content: "";
	flex: none;
	width: 0.55em;
	height: 0.55em;
	background-color: var(--wp--preset--color--accent);
}

/* A quieter variant for column headings inside the footer. */
.is-style-label {
	margin-block: 0 var(--wp--preset--spacing--20);
	font-family: var(--ae-label);
	font-size: var(--wp--preset--font-size--cartela);
	font-weight: 400;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--wp--preset--color--muted);
}

/* A label that happens to link still reads as a label, not as prose. */
.is-style-label a {
	text-decoration: none;
	border-bottom: 1px solid transparent;
}

.is-style-label a:hover,
.is-style-label a:focus-visible {
	border-bottom-color: currentColor;
}

/*
 * Poster — the one place the type is allowed to be loud. Uppercase, tight to
 * the point of touching, and sized off the viewport rather than off the text.
 */
.is-style-poster {
	margin: 0;
	font-size: var(--wp--preset--font-size--poster);
	font-weight: 700;
	line-height: 0.85;
	letter-spacing: -0.04em;
	text-transform: uppercase;
	text-wrap: balance;
}

.is-style-plain {
	list-style: none;
	padding-left: 0;
}

/*
 * A field inside a stretched column has to be told to fill it. Columns stretch;
 * the group inside a column does not, and a red rectangle that stops short of
 * the column it lives in reads as a mistake rather than as a composition.
 */
.ae-fill {
	height: 100%;
}

/*
 * "See everything" links. Set as data rather than as prose, because they are
 * navigation: the reader is scanning for the exit from the section, not reading
 * a sentence.
 */
.ae-more a {
	display: inline-block;
	padding-block: 0.5em;
	font-family: var(--ae-label);
	font-size: var(--wp--preset--font-size--cartela);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	text-decoration: none;
	border-bottom: 1px solid currentColor;
}

.ae-more a:hover,
.ae-more a:focus-visible {
	border-bottom-color: var(--wp--preset--color--accent);
}

.is-style-plain li {
	margin-bottom: 0.5em;
}

.is-style-inverted {
	color: var(--wp--preset--color--base);
	background-color: var(--wp--preset--color--contrast);
}

.is-style-inverted a {
	color: inherit;
}

.is-style-inverted .is-style-eyebrow {
	border-block-start-color: currentColor;
	color: inherit;
}

/* Separator as a structural rule rather than a decorative flourish. */
.wp-block-separator:not(.is-style-dots) {
	height: 0;
	margin-block: var(--wp--preset--spacing--50);
	border: 0;
	border-block-start: var(--ae-rule);
	opacity: 1;
}

.wp-block-separator.is-style-hairline {
	border-block-start: var(--ae-hairline);
}

/*
 * The section navigation is styled by the blocks plugin, which owns the block.
 * Duplicating it here would mean two sources of truth for one component.
 */

/* ------------------------------------------------------------------ *
 * Prose
 * ------------------------------------------------------------------ */

.entry-content > * + * {
	margin-block-start: var(--wp--preset--spacing--30);
}

.entry-content h2,
.entry-content h3 {
	margin-block-start: var(--wp--preset--spacing--50);
}

/* ------------------------------------------------------------------ *
 * Motion
 * ------------------------------------------------------------------ */

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

/* ------------------------------------------------------------------ *
 * Print
 * ------------------------------------------------------------------ */

@media print {
	.wp-block-navigation,
	.ae-consent,
	.ae-breadcrumbs,
	.wp-block-template-part.site-footer {
		display: none !important;
	}

	/* A printed page cannot be clicked, so the destination has to be written. */
	.entry-content a[href^="http"]::after {
		content: " (" attr(href) ")";
		font-size: 0.85em;
		word-break: break-all;
	}
}
