/*
Theme Name: Wakeford Photography
Author: Joe
Description: An editorial block theme for photography studios, with a scroll-driven hero, motion-aware sections and a Template Shop.
Version: 0.2.0
Requires at least: 6.7
Tested up to: 7.1
Requires PHP: 7.4
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: wakefordphotography
Tags: photography, portfolio, block-theme, full-site-editing, editorial
*/

/**
 * Design tokens, palette and block styling live in theme.json.
 *
 * This file holds only what theme.json cannot express: a handful of composable
 * `wf-` utility classes used by the theme's own patterns, plus a few
 * document-level manners. Every rule is prefixed or scoped, so nothing here
 * leaks into a block's own styling.
 */

/* ---------------------------------------------------------------- document */

html {
	scroll-behavior: smooth;

	/*
	 * The header can float above the page, so an in-page anchor would otherwise
	 * scroll its target underneath the bar. The block publishes its own measured
	 * height; the fallback covers the moment before that lands.
	 */
	scroll-padding-top: calc(var(--wf-hdr-height, 5rem) + 1rem);
}

::selection {
	background: var(--wp--preset--color--contrast);
	color: var(--wp--preset--color--base);
}

body {
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizelegibility;
}

/* A visible, high-contrast focus ring everywhere, since the palette is soft. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: 2px solid var(--wp--preset--color--contrast);
	outline-offset: 3px;
}

/* Headings set tight; long display lines read better balanced. */
:where(h1, h2, h3) {
	text-wrap: balance;
}

/* ------------------------------------------------------------- typography */

/* Mono micro-label: the theme's recurring section marker. */
.wf-label {
	color: var(--wp--preset--color--muted);
	font-family: var(--wp--preset--font-family--mono);
	font-size: var(--wp--preset--font-size--micro);
	letter-spacing: 0.2em;
	line-height: 1;
	text-transform: uppercase;
}

/*
 * The muted palette colour is designed against the light background and is too
 * dark to sit on the contrast colour, so inverted labels derive their tone from
 * the two palette colours instead of introducing a fifth.
 */
.wf-label.is-inverse {
	color: #aca8a3;
	color: color-mix(in srgb, var(--wp--preset--color--base) 68%, var(--wp--preset--color--contrast));
}

.wf-label.is-ruled.is-inverse::after {
	background: color-mix(in srgb, var(--wp--preset--color--base) 22%, var(--wp--preset--color--contrast));
}

/* A label with a hairline running out from it, used to open a section. */
.wf-label.is-ruled {
	align-items: center;
	display: flex;
	gap: 1.25em;
}

.wf-label.is-ruled::after {
	background: var(--wp--preset--color--line);
	content: "";
	flex: 1;
	height: 1px;
}

/* Standfirst paragraph: larger, quieter, narrower than body copy. */
.wf-lead {
	color: var(--wp--preset--color--muted);
	font-size: clamp(1.125rem, 1.6vw, 1.375rem);
	line-height: 1.5;
	max-width: 34em;
}

.wf-measure {
	max-width: 38em;
}

/* Display line used for the manifesto: larger than a heading, set as body. */
.wf-statement {
	font-family: var(--wp--preset--font-family--display);
	font-size: clamp(1.75rem, 4.2vw, 3.25rem);
	letter-spacing: -0.02em;
	line-height: 1.14;
	margin: 0;
	text-wrap: balance;
}

.wf-statement em {
	color: var(--wp--preset--color--muted);
	font-style: italic;
}

/* ------------------------------------------------------------------ links */

/* An underline that draws in from the left, for links inside prose. */
.wf-underline a,
a.wf-underline {
	background-image: linear-gradient(currentcolor, currentcolor);
	background-position: 0 100%;
	background-repeat: no-repeat;
	background-size: 0% 1px;
	padding-bottom: 0.08em;
	transition: background-size 320ms cubic-bezier(0.22, 1, 0.36, 1);
}

.wf-underline a:hover,
.wf-underline a:focus-visible,
a.wf-underline:hover,
a.wf-underline:focus-visible {
	background-size: 100% 1px;
}

/* -------------------------------------------------------------- structure */

.wf-rule {
	background: var(--wp--preset--color--line);
	border: 0;
	height: 1px;
	margin: 0;
}

/* Link columns read as an index, not a bulleted list. */
.wf-underline .wp-block-page-list,
.wp-block-page-list.wf-underline {
	list-style: none;
	margin: 0;
	padding: 0;
}

.wf-underline .wp-block-page-list li {
	list-style: none;
}

/* A definition row: mono key left, value right, with a hairline above. */
.wf-facts {
	border-top: 1px solid var(--wp--preset--color--line);
}

.wf-facts .wp-block-column {
	border-bottom: 1px solid var(--wp--preset--color--line);
	padding-block: var(--wp--preset--spacing--m);
}

/* --------------------------------------------------------------- captions */

/* Figure captions sit tight under their image and stay quiet. */
:where(.wp-block-image) figcaption {
	margin-top: var(--wp--preset--spacing--xs);
}

/* ---------------------------------------------------------- touch targets */

/*
 * A link inside a sentence is exempt from minimum target sizes — it is running
 * text, and enlarging it would be meaningless. A link that acts as a control is
 * not exempt, and this theme has several: the footer index, the page list, the
 * pagination numbers. Measured on a phone they were 15 to 22 pixels tall,
 * against the 44 that Apple's guidelines ask for and the 48 that Google's do.
 *
 * They are grown with a positioned pseudo-element rather than with padding, so
 * the layout, the baseline grid and the sliding underline all stay exactly as
 * designed; only the region that answers to a finger changes.
 *
 * `any-pointer`, not `pointer`. `pointer` describes only the *primary* device,
 * so a touchscreen laptop with its keyboard attached reports `fine` and would
 * get none of this — which is exactly the machine where a 15px link is hardest
 * to hit. Growing a target costs a mouse user nothing, so the wider query is
 * the right one here.
 */
@media (any-pointer: coarse) {

	.wf-underline > p,
	:is(.wf-underline .wp-block-page-list, .wp-block-page-list.wf-underline) li {
		position: relative;
	}

	/*
	 * The gap has to be at least the growth, or neighbouring targets overlap
	 * and the lower one wins the contested strip — a worse defect than the one
	 * being fixed. A 44px target around a ~22px line needs 22px between rows,
	 * which is what this is. `:is()` because the page list can carry the
	 * utility class itself or inherit it from a wrapper, depending on how the
	 * footer was assembled.
	 */
	.wf-underline > p + p,
	:is(.wf-underline .wp-block-page-list, .wp-block-page-list.wf-underline) li + li {
		margin-top: var(--wp--custom--touch-gap, 1.375rem);
	}

	/*
	 * A fixed 44px box centred on the link, rather than a guessed inset either
	 * side of it: the height of the line box depends on the reader's own text
	 * size, so a constant offset lands somewhere different for everyone.
	 */
	.wf-underline > p > a::after,
	:is(.wf-underline .wp-block-page-list, .wp-block-page-list.wf-underline) li > a::after {
		block-size: max(44px, 100%);
		content: "";
		inset-block-start: 50%;
		inset-inline: 0;
		position: absolute;
		transform: translateY(-50%);
	}
}

/*
 * Making a whole card tappable replaces its other controls, so unlike the
 * additive growth above this must not fire on a machine with a mouse: `hover:
 * none` is what separates a phone from a touchscreen laptop.
 *
 * Scoped with `:has()` to cards that carry an image. A query loop of titles and
 * excerpts is a list, not a set of cards; blanketing it would swallow the
 * excerpt text — unselectable, with any link inside it untappable. Browsers
 * without `:has()` simply keep the plain title link.
 */
@media (hover: none) and (pointer: coarse) {

	.wp-block-post-template > li:has(.wp-block-post-featured-image) {
		position: relative;
	}

	.wp-block-post-template > li:has(.wp-block-post-featured-image)
	.wp-block-post-title a::after {
		content: "";
		inset: 0;
		position: absolute;
	}

	/* Anything that has to stay reachable inside a card opts back out. */
	.wp-block-post-template > li .wf-above-card-link {
		position: relative;
		z-index: 1;
	}
}

/*
 * Core's outline button variation ships its own `padding: 0.667em 1.333em` at a
 * specificity theme.json's element styles cannot reach, so the theme's padding
 * silently applies to filled buttons only. The two then disagree by ten pixels
 * and every outline button lands under the minimum touch target — which is most
 * of the secondary calls to action on the site.
 *
 * Restated here at one class higher than core's rule, which resolves it by
 * specificity rather than by `!important`. The value itself is the same
 * theme.json token the element style uses, so the two cannot drift.
 */
.wp-block-button.is-style-outline > .wp-block-button__link.wp-element-button {
	padding:
		var(--wp--custom--button-padding--block, 1.1em)
		var(--wp--custom--button-padding--inline, 2em);
}

/*
 * Pagination is a control at every width, not only on a phone: single-digit
 * page numbers are 7 pixels wide whatever is pointing at them.
 */
.wp-block-query-pagination :where(a, a.page-numbers) {
	align-items: center;
	display: inline-flex;
	justify-content: center;
	min-height: 44px;
	min-width: 44px;
}

/*
 * A Site Title can be a single unbreakable token -- a domain name, which is
 * exactly what this site is called. Normal wrapping has nowhere to break
 * "wakefordphotography.com", so at the display size the footer sets it ran
 * 504px wide inside a 305px column and dragged the whole document 179px past
 * a 360px viewport: the page, not the title, got the horizontal scrollbar.
 *
 * Unconditional, because the overflow is a function of title length against
 * column width, not of any one breakpoint -- it was still 69px over at 768px.
 * `break-word` only breaks a word that would otherwise overflow, so a title
 * made of ordinary words still wraps between them and is untouched here.
 */
.wp-block-site-title {
	overflow-wrap: break-word;
}

/* ---------------------------------------------------------- small screens */

/*
 * Cards in a catalogue grid share one aspect ratio so the index reads as a set,
 * and the ratio is chosen for a grid several columns wide. On a phone the
 * column count collapses to one, the shared box becomes 350px wide by 470 tall,
 * and a 16:9 welcome screen letterboxed inside it leaves most of a screen empty
 * between one product and the next.
 *
 * Keeping two columns is the fix rather than abandoning the ratio: the set
 * still reads as a set, each card is a comfortable thumbnail, and the artwork
 * is shown whole. Core writes the column rule from the block's own settings, so
 * this only has to out-specify it — no `!important`. Site Editor changes to the
 * ratio or scale still takes effect.
 *
 * The column count and gap are theme.json tokens, so they can be changed in
 * Global Styles rather than by editing this rule.
 */
@media (max-width: 599.98px) {

	.wp-block-query .wp-block-post-template.is-layout-grid {
		grid-template-columns: repeat(var(--wp--custom--card-columns-sm, 2), minmax(0, 1fr));

		/* The authored block gap sets the rhythm between rows, which is still
		   wanted; between two narrow columns the same value is most of the
		   screen. Only the horizontal half is capped. */
		column-gap: var(--wp--custom--card-gap-sm, 1rem);
	}

	/*
	 * Buttons keep their 44px height but give up side padding, so a pair of
	 * calls to action sits on one line instead of stacking into two unequal
	 * blocks with a ragged right edge.
	 */
	.wp-block-button__link.wp-element-button,
	.wp-block-button.is-style-outline > .wp-block-button__link.wp-element-button {
		padding-inline: 1.25em;
	}

	/*
	 * A wordmark set on one line at desktop width can be given generous
	 * leading; the same value on a phone, where it wraps to two lines, reads
	 * as two unrelated headings 88 pixels apart. The line height is an inline
	 * style written by the editor, so `!important` is the only way to reach
	 * it — the size and the letterspacing are left exactly as set.
	 */
	.wp-block-site-title {
		/* stylelint-disable-next-line declaration-no-important */
		line-height: 1.05 !important;
	}
}

/*
 * Core stacks every column to full width below 782px, which is right for a
 * phone and wasteful on a tablet: the footer's three short link lists became
 * three rows nearly 700px tall in a 768px-wide frame, against 219px on a
 * desktop. Between the phone breakpoint and core's own, they pair up instead.
 *
 * `!important` is required because core's stacking rule uses it; the override
 * is scoped to the footer so no other column layout is affected.
 */
@media (min-width: 600px) and (max-width: 781.98px) {

	.wp-site-blocks footer .wp-block-columns > .wp-block-column {
		/* stylelint-disable-next-line declaration-no-important */
		flex-basis: calc(50% - var(--wp--style--block-gap, 1.5rem)) !important;
	}
}

/* -------------------------------------------------------------------- print */

/*
 * A fixed header prints on top of the first page, and a hero built to occupy a
 * screen has no meaning on paper. What a reader wants from a printed page here
 * is the words and the pictures.
 */
@media print {

	.wp-block-wakeford-site-header {
		position: static !important;
	}

	.wp-block-wakeford-scroll-sequence .wf-seq__track,
	.wp-block-wakeford-scroll-sequence .wf-seq__stage {
		height: auto !important;
		position: static !important;
	}

	.wp-block-wakeford-scroll-sequence .wf-seq__cue,
	.wp-block-wakeford-scroll-sequence .wf-seq__progress,
	.wp-block-wakeford-marquee,
	.wp-block-navigation__responsive-container-open {
		display: none;
	}

	/* Reveal animations never run without a viewport to scroll, so anything
	   still waiting for one would print blank. */
	[data-wf-motion],
	[data-wf-motion] .wf-m-unit > span {
		opacity: 1 !important;
		transform: none !important;
	}
}

/* --------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {

	html {
		scroll-behavior: auto;
	}

	.wf-underline a,
	a.wf-underline {
		transition: none;
	}
}
