/*
Theme Name: DIGISTARTER
Theme URI: https://www.digistarter.de/
Author: DIGISTALTER GmbH
Author URI: https://www.digistalter.de/
Description: A block theme built to launch fast and build complete sites with. Over forty section patterns and six style variations in light and dark mean there's barely a page you can't put together, right out of the box - and when you want something more extravagant, a child theme extends it without ever touching a template.
Requires at least: 6.9
Tested up to: 7.1
Requires PHP: 7.4
Version: 1.1.0
License: GNU General Public License v3
License URI: https://www.gnu.org/licenses/gpl-3.0.html
Text Domain: digistarter
Tags: full-site-editing, block-patterns, block-styles, style-variations, custom-colors, custom-logo, custom-menu, editor-style, featured-images, one-column, threaded-comments, translation-ready, wide-blocks, rtl-language-support
*/

/* -------------------------------------------------------------------------
 * 1. Base
 * ---------------------------------------------------------------------- */

*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	scroll-behavior: smooth;
}

/* Classic-editor content the block editor never touches: no theme.json equivalent to lean on. */
pre {
	overflow-x: auto;
}

.wp-caption,
.wp-caption img {
	max-width: 100%;
}

/*
 * Targets <main> rather than a theme class: plugins that render a custom post
 * type archive supply their own <main> without it. Zeroed on every side,
 * deliberately: see the note below on why no block in this theme gets space
 * it did not ask for.
 */
main {
	margin-bottom: 0;
	margin-top: 0;
	padding-bottom: 0;
	padding-top: 0;
}

/*
 * Every pattern in this theme states its own top and bottom spacing; content
 * this theme did not author (a plugin's archive markup) does not, and would
 * otherwise run straight into the header. The fallback skips an alignfull
 * first/last child and a core/spacer, both already an explicit request for
 * this space, and skips a leading post-title too - see inc/page-title.php.
 */
main:not(:has(> :is(.alignfull, .wp-block-spacer):first-child)):not(:has(> .wp-block-post-title:first-child + :is(.alignfull, .wp-block-spacer))) {
	padding-top: var(--wp--preset--spacing--large);
}

main:not(:has(> :last-child.alignfull)):not(:has(> :last-child.wp-block-spacer)) {
	padding-bottom: var(--wp--preset--spacing--large);
}

/*
 * Core's block gap gives every block a margin-block-start - wrong for two
 * stacked full width bands, which left a 30px seam of bare page between them.
 * Only alignfull is targeted: it reads as a deliberate edge-to-edge choice, so
 * two in a row are meant to meet. Wide keeps the ordinary gap. Space between
 * two full width bands now takes an explicit core/spacer.
 */
main > .alignfull.has-background,
main .entry-content > .alignfull.has-background {
	margin-block-start: 0;
}

/*
 * A band switched from Full to Wide width loses its inset: core gives a
 * nested .alignwide only max-width, no padding. has-background is the
 * deciding condition, not is-layout-constrained alone, since a transparent
 * group has no colour edge to protect, and padding would narrow the
 * alignfull tiles nested inside it (numbered-columns).
 */
.alignwide.is-layout-constrained.has-background {
	padding-left: var(--wp--custom--spacing--gap);
	padding-right: var(--wp--custom--spacing--gap);
}

a,
button,
input,
select,
textarea,
.wp-element-button {
	transition:
		background-color var(--wp--custom--transition--duration) var(--wp--custom--transition--timing),
		border-color var(--wp--custom--transition--duration) var(--wp--custom--transition--timing),
		color var(--wp--custom--transition--duration) var(--wp--custom--transition--timing),
		opacity var(--wp--custom--transition--duration) var(--wp--custom--transition--timing);
}

@media (prefers-reduced-motion: reduce) {

	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		scroll-behavior: auto !important;
		transition-duration: 0.01ms !important;
	}
}

b,
strong,
th {
	font-weight: var(--wp--custom--font-weight--medium);
}

/*
 * Surface pairing: every palette surface carries its own text colour. Each
 * background slug has an on-* role beside it in theme.json, so a surface can
 * be switched - in the editor or through a section style - without the text
 * colour needing to change with it. :not(.has-text-color) keeps an explicit
 * editor choice ahead of this.
 */
.has-accent-background-color:not(.has-text-color) {
	color: var(--wp--custom--on-accent);
}

.has-neutral-background-color:not(.has-text-color) {
	color: var(--wp--custom--on-neutral);
}

.has-primary-background-color:not(.has-text-color) {
	color: var(--wp--custom--on-primary);
}

.has-secondary-background-color:not(.has-text-color) {
	color: var(--wp--custom--on-secondary);
}

/*
 * The same pairing for a link in running text, written with real classes
 * rather than left to the block's own elements.link setting - a nested
 * surface (e.g. a numbered-columns tile) sets its own link colour at the same
 * specificity as a section style like "On accent", so source order alone
 * decided the winner and left tile links measuring as low as 1.05:1.
 *
 * Scoped to the text elements a link sits directly inside, not every
 * descendant link, so a category pill (a link on its own fill) and a card's
 * links (grandchildren of core/post-template's <li>) stay untouched - an
 * earlier version without the child combinator caught both and handed a
 * card's white text to a pill's green, 1.40:1.
 *
 * This outranks a link colour picked by hand on the same block: a surface
 * without a readable foreground is a broken surface, and an author who wants
 * a different colour can still set it on the link itself.
 */
:is(.has-accent-background-color, .has-neutral-background-color, .has-primary-background-color, .has-secondary-background-color)
	:is(p, li, cite, dd, dt, figcaption, h1, h2, h3, h4, h5, h6, td, th, strong, em) > a:not(.wp-element-button) {
	color: currentColor;
}

.has-accent-background-color :is(p, li, cite, dd, dt, figcaption, h1, h2, h3, h4, h5, h6, td, th, strong, em) > a:not(.wp-element-button):hover,
.has-neutral-background-color :is(p, li, cite, dd, dt, figcaption, h1, h2, h3, h4, h5, h6, td, th, strong, em) > a:not(.wp-element-button):hover,
.has-primary-background-color :is(p, li, cite, dd, dt, figcaption, h1, h2, h3, h4, h5, h6, td, th, strong, em) > a:not(.wp-element-button):hover,
.has-secondary-background-color :is(p, li, cite, dd, dt, figcaption, h1, h2, h3, h4, h5, h6, td, th, strong, em) > a:not(.wp-element-button):hover {
	color: var(--wp--custom--link--hover-color);
}

/* -------------------------------------------------------------------------
 * 2. Typography
 *
 * The size a piece of text takes is decided by the role it plays, never by the
 * pattern it happens to sit in. Two patterns doing the same job have to look
 * like they do. The roles:
 *
 *   Opener headline (hero, search prompt)        h1, 4X large
 *   Document title (blog, archive, search, post) h1/h2, theme.json default
 *   Section headline                             h2, 3X large
 *   Oversized statement (text reveal)            h2, 4X large - the pattern is
 *                                                the size
 *   Headline inside a card (hero with tabs)      h2, X large
 *   Panel or step headline (tabs, sticky quote)  h3, X large
 *   Card title (pricing tier, timeline entry)    h3, large
 *   Tile title (feature grids, demos)            h3, medium
 *   Display numeral (statistic, tile number)     h3, 2X large
 *   Price                                        p,  4X large - the loudest
 *                                                thing in a pricing card, and
 *                                                the one place a number
 *                                                outranks the headline above it
 *   Lead paragraph in an opener                  p,  large
 *   Body                                         theme.json default (medium)
 *   Meta, labels, pills                          small / extra small
 *
 * Spacing follows the same idea. Inside a section opener - eyebrow pill,
 * headline, lead - the gap is extra small (20px) in every pattern. Inside a
 * card or a grid tile it is tiny (10px): the component is smaller, so its
 * rhythm is tighter. A section's own padding is large, or extra large for a
 * page opener. A card's padding is small.
 * ---------------------------------------------------------------------- */

/*
 * theme.json cannot attach a line height to a font size preset, so the scale
 * hangs off the preset classes. The values live in custom.lineHeight.scale:
 * the larger the type, the tighter the leading.
 */
.has-x-small-font-size { line-height: var(--wp--custom--line-height--scale--x-small); }
.has-small-font-size { line-height: var(--wp--custom--line-height--scale--small); }
.has-medium-font-size { line-height: var(--wp--custom--line-height--scale--medium); }
.has-large-font-size { line-height: var(--wp--custom--line-height--scale--large); }
.has-x-large-font-size { line-height: var(--wp--custom--line-height--scale--x-large); }
.has-xx-large-font-size { line-height: var(--wp--custom--line-height--scale--xx-large); }
.has-xxx-large-font-size { line-height: var(--wp--custom--line-height--scale--xxx-large); }
.has-xxxx-large-font-size { line-height: var(--wp--custom--line-height--scale--xxxx-large); }
.has-xxxxx-large-font-size { line-height: var(--wp--custom--line-height--scale--xxxxx-large); }

/* -------------------------------------------------------------------------
 * 3. Links
 * ---------------------------------------------------------------------- */

/*
 * Links come in two roles: a link in running text is underlined, a
 * structural link (navigation, site title, reply link) is not. The role is
 * carried by an inherited custom property, so a child theme adds a context
 * by naming it here rather than writing a new rule.
 *
 * A pill sets the property itself rather than inheriting it, so the same
 * category label is not underlined in a term list but plain inside a card
 * that happens to carry "Subtle links" - a component must not depend on
 * where it was placed. Hovering never adds an underline; the colour change
 * from theme.json carries the state.
 */
.badge,
.badge-accent,
.term-list,
.wp-block-comment-reply-link,
.wp-block-navigation,
.wp-block-site-title {
	--link-decoration: none;
}

a:not(.wp-element-button) {
	text-decoration: var(--link-decoration, underline);
	text-decoration-thickness: 1px;
}

a:not(.wp-element-button):hover {
	text-decoration: none;
}

/*
 * The inline logo in the footer is drawn from currentColor, so the mark would
 * take on the hover colour together with the label beside it. Redefining the
 * token on the link itself keeps both at the surrounding text colour, without a
 * specificity fight.
 */
.brand-mark {
	--wp--custom--link--hover-color: currentColor;
}

/*
 * core/social-links in its "Logos only" style paints each mark in that
 * service's own brand colour - the only thing on the page not answering to
 * the palette. They follow the surrounding text instead. Core sets those
 * colours through :where(), so a plain two-class selector wins without a
 * flag; :not(.has-icon-color) keeps a colour picked by hand in the editor
 * ahead of this default.
 */
.wp-block-social-links.is-style-logos-only:not(.has-icon-color) .wp-social-link {
	color: currentColor;
}

/* -------------------------------------------------------------------------
 * 4. Focus
 * ---------------------------------------------------------------------- */

/*
 * currentColor rather than a palette entry: the ring has to stay visible on
 * every surface the theme offers, including the dark variations.
 */
a:focus-visible,
button:focus-visible,
input:focus,
select:focus,
textarea:focus,
.wp-element-button:focus-visible {
	outline: var(--wp--custom--focus-ring--width) solid currentColor;
	outline-offset: var(--wp--custom--focus-ring--offset);
}

/* -------------------------------------------------------------------------
 * 5. Form controls
 * ---------------------------------------------------------------------- */

/*
 * The border derives from the field's own text colour at low opacity, so it
 * stays visible on any background without competing with the content.
 */
input,
select,
textarea {
	background-color: var(--wp--preset--color--base);
	border: 1px solid color-mix(in srgb, currentColor 25%, transparent);
	border-radius: var(--wp--custom--border-radius--standard);
	color: var(--wp--preset--color--contrast);
	font-family: var(--wp--preset--font-family--primary);
	font-size: var(--wp--preset--font-size--medium);
	font-weight: var(--wp--custom--font-weight--light);
	line-height: var(--wp--custom--line-height--body);
	padding: 0.6em 0.8em;
	width: 100%;
}

/*
 * Only the border changes on focus. Browsers paint their own autofill
 * background over fields carrying an autocomplete attribute, which cannot be
 * overridden, so a background change would reach just part of a form.
 */
input:focus,
select:focus,
textarea:focus {
	border-color: currentColor;
}

input[type="image"] {
	width: auto;
}

/*
 * The rules above are meant for text inputs - a range control has to keep its
 * native shape. The attribute is repeated to raise specificity above a plugin
 * that colours its own sliders from a palette slug.
 */
input[type="range"][type="range"] {
	accent-color: var(--wp--preset--color--accent);
	background-color: transparent;
	border: 0;
	padding: 0;
}

/* iOS and Safari otherwise replace border and radius with native styling. */
input[type="button"],
input[type="email"],
input[type="number"],
input[type="password"],
input[type="search"],
input[type="submit"],
input[type="tel"],
input[type="text"],
input[type="url"],
textarea {
	-webkit-appearance: none;
}

/*
 * The arrow is drawn from two gradients rather than an SVG data URI so that it
 * follows the text colour through currentColor.
 */
select {
	-webkit-appearance: none;
	appearance: none;
	background-image:
		linear-gradient(45deg, transparent 50%, currentColor 50%),
		linear-gradient(135deg, currentColor 50%, transparent 50%);
	background-position:
		calc(100% - 20px) calc(50% - 2px),
		calc(100% - 15px) calc(50% - 2px);
	background-repeat: no-repeat;
	background-size: 5px 5px;
	padding-inline-end: 45px;
}

/*
 * Drawn from scratch because the native control ignores border colour and
 * radius. Uses the control radius token, not the standard one: this box is
 * 20px square, so "Extra rounded" variations would resolve it to a full
 * circle - a radio button's shape, which a checkbox may never take.
 */
input[type="checkbox"],
input[type="radio"] {
	-webkit-appearance: none;
	appearance: none;
	background-color: var(--wp--preset--color--base);
	border: 1px solid var(--wp--preset--color--contrast);
	border-radius: var(--wp--custom--border-radius--control);
	display: inline-block;
	flex-shrink: 0;
	height: 20px;
	margin: 0;
	padding: 0;
	position: relative;
	vertical-align: middle;
	width: 20px;
}

input[type="radio"] {
	border-radius: 50%;
}

input[type="checkbox"]:checked,
input[type="radio"]:checked {
	background-color: var(--wp--preset--color--accent);
	border-color: var(--wp--preset--color--accent);
}

input[type="checkbox"]:checked::after {
	border: solid var(--wp--custom--on-accent);
	border-width: 0 2px 2px 0;
	content: "";
	height: 10px;
	inset-inline-start: 6px;
	position: absolute;
	top: 2px;
	transform: rotate(45deg);
	width: 5px;
}

input[type="radio"]:checked::after {
	background-color: var(--wp--custom--on-accent);
	border-radius: 50%;
	content: "";
	height: 8px;
	inset-inline-start: 5px;
	position: absolute;
	top: 5px;
	width: 8px;
}

/* Core outputs the consent checkbox and its label as siblings without a wrapper. */
.comment-form-cookies-consent {
	align-items: baseline;
	display: flex;
	gap: var(--wp--preset--spacing--xx-small);
}

.comment-form-cookies-consent input[type="checkbox"] {
	margin-top: 2px;
}

/* -------------------------------------------------------------------------
 * 6. Writing direction
 *
 * The theme carries no style-rtl.css: everything directional uses logical
 * properties, so the browser mirrors it on its own.
 *
 * The rules below exist because their source cannot express the logical form.
 * theme.json only knows "left" and "right", never "inline-start", and
 * background-position has no logical counterpart in CSS at all.
 * ---------------------------------------------------------------------- */

.wp-block-details {
	border-inline-start: 4px solid var(--wp--preset--color--accent);
}

.wp-block-list {
	padding-inline-start: var(--wp--custom--spacing--gap);
}

[dir="rtl"] select {
	background-position:
		20px calc(50% - 2px),
		15px calc(50% - 2px);
}

/* -------------------------------------------------------------------------
 * 7. Components
 *
 * Classes handed out in patterns. A component knows nothing about where it is
 * used - the same card works in a grid, a column or a sidebar.
 * ---------------------------------------------------------------------- */

/*
 * Real text that only a screen reader (and a search engine crawler, which
 * reads the DOM the same way) ever meets - never display:none or
 * visibility:hidden, both of which take the text out of the accessibility
 * tree as well as off the screen. Used by the footer credit's brand name,
 * carried elsewhere as an SVG mark rather than as text a browser can render.
 */
.screen-reader-text {
	border: 0;
	clip: rect(1px, 1px, 1px, 1px);
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute !important;
	width: 1px;
	word-wrap: normal !important;
}

/*
 * A surface whose children may sit flush against its edge. overflow: hidden is
 * the point: a featured image is clipped by the card and picks up exactly its
 * corners, including after columns stack on small screens. There is no block
 * support for overflow.
 */
.card {
	border-radius: var(--wp--custom--border-radius--standard);
	overflow: hidden;
}

/*
 * No pattern in this theme puts .card on an alignfull block today, but the
 * class can land on core/group or core/columns, and both support alignfull -
 * so the corner has to be squared off there too, same reasoning as section 12
 * below. Kept here rather than there since .card is a component, not a block
 * exception.
 */
.card.alignfull {
	border-radius: 0;
}

/*
 * Card colour on the pricing and testimonials patterns, keyed to the band's
 * own section style so the two stay coordinated when a site owner switches
 * "On accent" / "On neutral" / "On primary" in the block sidebar - a card is a
 * fixed attribute on its own block and would otherwise not follow.
 *
 * A card can never read as a card on a page surface of the same colour, so
 * each band picks whichever surface contrasts against it. The default (no
 * style variation, the page's own surface) is the one exception the others
 * are not: neutral steps in for a plain card, the same surface
 * digistarter_post_card() already uses for the same reason.
 */
.pricing .card,
.testimonials .card {
	background-color: var(--wp--preset--color--neutral);
	color: var(--wp--custom--on-neutral);
}

.pricing .card.is-featured {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--custom--on-primary);
}

/*
 * :is(), not :where(): the latter carries no specificity of its own and would
 * lose to the lower, default rule above for the same property. :is() keeps
 * the specificity of whichever of its arguments is highest, which is enough
 * here since every argument already matches two classes against the default
 * rule's one.
 */
:is(.pricing.is-style-on-accent, .pricing.is-style-on-neutral, .testimonials.is-style-on-accent, .testimonials.is-style-on-neutral, .testimonials.is-style-on-primary) .card {
	background-color: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
}

.pricing.is-style-on-accent .card.is-featured {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--custom--on-primary);
}

.pricing.is-style-on-neutral .card.is-featured {
	background-color: var(--wp--preset--color--accent);
	color: var(--wp--custom--on-accent);
}

.pricing.is-style-on-primary .card {
	background-color: var(--wp--preset--color--secondary);
	color: var(--wp--custom--on-secondary);
}

/*
 * An outline button's resting fill (theme.json, core/button variations) is
 * base - right for a card that is itself light, wrong once the card or the
 * band underneath the button turns dark or saturated. Scoped to precisely the
 * combinations where that happens: a featured pricing card on any band, every
 * card once the whole pricing band is primary, and the testimonials button
 * once its band is primary - the only three cases with no light surface left
 * between the button and whatever sits behind it.
 */
:where(.pricing .card.is-featured, .pricing.is-style-on-primary .card) .wp-block-buttons .wp-block-button.is-style-outline > .wp-block-button__link,
.testimonials.is-style-on-primary .wp-block-buttons .wp-block-button.is-style-outline > .wp-block-button__link {
	background-color: transparent;
}

:where(.pricing .card.is-featured, .pricing.is-style-on-primary .card) .wp-block-buttons .wp-block-button.is-style-outline > .wp-block-button__link:hover,
.testimonials.is-style-on-primary .wp-block-buttons .wp-block-button.is-style-outline > .wp-block-button__link:hover {
	border-color: currentColor;
	color: var(--wp--custom--muted);
}

/*
 * The dash before a name reads as accent rather than following the muted
 * paragraph it sits in - the same small brand touch as a badge. Steps down
 * to primary once the card's own band is accent, same reasoning as every
 * other accent-on-accent collision the section styles can now reach.
 */
.testimonials__dash {
	color: var(--wp--preset--color--accent);
}

.testimonials.is-style-on-accent .testimonials__dash,
.testimonials.has-accent-background-color .testimonials__dash {
	color: var(--wp--preset--color--primary);
}

/*
 * Labels come in two roles: taxonomy in the accent colour, metadata such as a
 * date in the primary colour. The geometry is shared so both sit at the same
 * height. fit-content because blocks such as core/post-date output a full width
 * <div>; the line height is set rather than inherited, since labels reach their
 * font size through a preset class or through theme.json.
 */
.badge,
.term-list a {
	border-radius: var(--wp--custom--border-radius--standard);
	display: inline-block;
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: var(--wp--custom--font-weight--semi-bold);
	line-height: var(--wp--custom--line-height--body);
	max-width: 100%;
	overflow-wrap: anywhere;
	padding: 0.2em 0.7em;
	width: fit-content;
}

.badge:not(.badge-accent) {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--custom--on-primary);
}

/*
 * On a primary surface a primary fill would disappear, so it steps down a
 * shade. Two different ancestor contexts reach a primary surface: a block
 * with the literal backgroundColor attribute (has-primary-background-color),
 * and a band recoloured through the "On primary" section style instead
 * (is-style-on-primary) - a pattern such as cta-banner.php keeps an explicit
 * primary button regardless of which surface it ships on, and only clashes
 * once that surface is switched to primary itself.
 */
:is(.has-primary-background-color, .is-style-on-primary) .badge:not(.badge-accent) {
	background-color: var(--wp--preset--color--secondary);
	color: var(--wp--custom--on-secondary);
}

/*
 * !important to beat core's own colour support, which renders every
 * has-*-background-color preset class the same way - see the note further
 * down at .wp-block-button__link.has-primary-background-color:hover.
 */
.is-style-on-primary .wp-block-button__link.has-primary-background-color {
	background-color: var(--wp--preset--color--secondary) !important;
	color: var(--wp--custom--on-secondary) !important;
}

/*
 * The default, undecorated button fills with accent at rest and hovers to
 * secondary, so it clashes wherever the band underneath is that same colour -
 * the same problem as the badge above, for the one button style with no
 * colour class of its own to key off. Excludes the outline variation, handled
 * below, and a button that already names its own surface explicitly.
 */
:is(.is-style-on-accent, .has-accent-background-color)
	.wp-block-button__link.wp-element-button:not(.is-style-outline):not([class*="has-"][class*="-background-color"]) {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--custom--on-primary);
}

:is(.is-style-on-secondary, .has-secondary-background-color)
	.wp-block-button__link.wp-element-button:not(.is-style-outline):not([class*="has-"][class*="-background-color"]):hover {
	background-color: var(--wp--preset--color--accent);
	color: var(--wp--custom--on-accent);
}

/*
 * The outline button's resting fill is base, with no text colour of its own -
 * it inherits the surrounding text colour. On primary or secondary that text
 * is light, and light text on a light fill measured as invisible. Clearing
 * the fill lets the button's own text show against the band behind it
 * instead, automating the fix pricing.php and testimonials.php used to apply
 * by hand.
 */
:is(.is-style-on-primary, .is-style-on-secondary, .has-primary-background-color, .has-secondary-background-color)
	.wp-block-button.is-style-outline > .wp-block-button__link {
	background-color: transparent;
}

:is(.is-style-on-primary, .is-style-on-secondary, .has-primary-background-color, .has-secondary-background-color)
	.wp-block-button.is-style-outline > .wp-block-button__link:hover {
	border-color: currentColor;
	color: var(--wp--custom--muted);
}

.badge-accent,
.term-list a {
	background-color: var(--wp--preset--color--accent);
	color: var(--wp--custom--on-accent);
}

/*
 * The same step-down as .badge:not(.badge-accent) above, mirrored for the
 * accent-filled role, so the pill doesn't disappear on an accent surface.
 * Primary is the step-down rather than secondary since primary already pairs
 * with accent everywhere else in the theme (the cta-banner button, for one).
 */
:is(.has-accent-background-color, .is-style-on-accent) :is(.badge-accent, .term-list a) {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--custom--on-primary);
}

/* core/post-terms and core/post-date wrap their content in a link that would otherwise take the global link colour. */
.badge a {
	color: inherit;
}

/*
 * core/tab-list's active tab is handled in theme.json alone: currentColor
 * marks the selected tab (text and underline) against the muted colour the
 * other tabs carry - chosen over a fixed palette colour like primary, which
 * failed on some surfaces (measured 1.08:1 under "Dark") that no per-surface
 * exception list could fully enumerate.
 */

/*
 * Without an underline on hover a clickable label needs another cue. The :has()
 * puts it on the pill rather than on the link inside, which would paint a square
 * block over the rounded corners.
 */
.badge:has(a:hover),
.term-list a:hover {
	background-color: var(--wp--preset--color--secondary);
	color: var(--wp--custom--on-secondary);
}

/* Same reasoning as the resting-state step-downs above: hovering to secondary clashes once the band itself already is secondary. */
:is(.is-style-on-secondary, .has-secondary-background-color) :is(.badge:has(a:hover), .term-list a:hover) {
	background-color: var(--wp--preset--color--accent);
	color: var(--wp--custom--on-accent);
}

/*
 * A list of terms rendered as individual labels. core/post-terms emits all terms
 * in one container with no way to address the individual links, hence the
 * descendant selector.
 */
.term-list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--xx-small);
}

/* Core replaces an empty separator attribute with a space, so it is hidden here. */
.term-list .wp-block-post-terms__separator {
	display: none;
}

/*
 * A comment as a speech bubble. The surface is base rather than a fixed white:
 * the bubble has to lift off whatever the comments section stands on, and in
 * the dark variations that is a dark bubble on a slightly lighter ground.
 */
.comment-bubble {
	background-color: var(--wp--preset--color--base);
	border-radius: var(--wp--custom--border-radius--standard);
	color: var(--wp--preset--color--contrast);
	padding: var(--wp--preset--spacing--x-small);
	position: relative;
}

/*
 * The tip, drawn from three borders: two transparent, one carrying the colour.
 * It points back at the avatar beside it. Logical properties, so it moves to
 * the other side by itself in a right to left language.
 */
.comment-bubble::before {
	border-block: 8px solid transparent;
	border-inline-end: 10px solid var(--wp--preset--color--base);
	content: "";
	height: 0;
	inset-block-start: var(--wp--preset--spacing--xx-small);
	inset-inline-start: -10px;
	position: absolute;
	width: 0;
}

/*
 * Places a label over a featured image, kept apart from .badge so the label
 * itself knows nothing about where it is used. width and min-height stand in
 * for the image: core/post-featured-image renders nothing on a post with no
 * featured image, and an absolutely positioned badge with no containing
 * block to size against wraps one letter per line instead. min-height fits
 * two wrapped pill rows as a backstop, even though inc/post-card.php already
 * caps the category list at two pills.
 */
.media-frame {
	min-height: 4.5em;
	position: relative;
	width: 100%;
}

/*
 * .wp-block-group.media-frame rather than plain .media-frame: in the editor,
 * core's own two-class layout rule tied this selector's specificity, and load
 * order let core win, leaving the pill in flow instead of floating over the
 * image. The extra class (true to the markup) settles the tie without a flag.
 */
.wp-block-group.media-frame .media-badge {
	inset-inline-start: var(--wp--preset--spacing--xx-small);
	margin: 0;
	position: absolute;
	top: var(--wp--preset--spacing--xx-small);
}

/*
 * core/quote's opening mark is fixed to accent in theme.json - wrong on the
 * two surfaces where accent is too close in luminance to read against
 * (measured 1:1 on accent, 1.21:1 on neutral). Same fix as the badge's
 * surface flip above: steps the mark to the surface's own on-* role instead.
 * Specificity (0,2,1) beats the "css" key's (0,1,0) without !important.
 */
.has-accent-background-color .wp-block-quote::before {
	color: var(--wp--custom--on-accent);
}

.has-neutral-background-color .wp-block-quote::before {
	color: var(--wp--custom--on-neutral);
}

/*
 * Three cards side by side, as plain CSS rather than core/post-template's own
 * "layout":{"type":"grid"} attribute: opening a page holding one of these
 * patterns silently rewrites that attribute to {"type":"default"}, and saving
 * flattens the grid to one column permanently - specific to core/post-template,
 * core/group's grid survives the same step. A className is never affected,
 * and also gives the grid a responsive step-down core's fixed columnCount
 * never had.
 */
.post-grid {
	display: grid;
	gap: var(--wp--preset--spacing--x-small);
	grid-template-columns: repeat(3, minmax(0, 1fr));
}

/*
 * post-template's layout type is "default" (see above), which core spaces
 * with a margin on every child but the first - colliding with this grid's own
 * gap and shorting every tile but the first by 20px. Two class names match
 * core's own generated container rule's specificity, so this wins without a
 * flag.
 */
.wp-block-post-template.post-grid > * {
	margin-block: 0;
}

/*
 * Same fix as .post-grid above, for a query loop presented as a divided list:
 * the row's own padding is meant to be the only space around it, so the flow
 * layout's margin is zeroed. Not the theme's "Divided rows" block style -
 * that compiles to ".wp-block-group.is-style-rows-divided" and never reaches
 * core/post-template, only core/group. Same visual formula, written directly.
 */
.wp-block-post-template.post-rows > * {
	margin-block: 0;
}

/*
 * Padding on every row rather than only padding-top on the ones after the
 * first: with only one side set, the rule sat flush against the row above and
 * all the air fell on the far side of it. Split evenly, the first row loses
 * its own top padding (it has no rule above to clear) and the same happens in
 * reverse at the bottom, so the list takes no more outer space than a single
 * row's worth of padding beyond what the section around it already sets.
 */
.post-rows > * {
	padding-block: var(--wp--preset--spacing--small);
}

.post-rows > *:first-child {
	padding-top: 0;
}

.post-rows > *:last-child {
	padding-bottom: 0;
}

.post-rows > * + * {
	border-top: 1px solid color-mix(in srgb, currentColor 15%, transparent);
}

@media (max-width: 781px) {

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

@media (max-width: 599px) {

	.post-grid {
		grid-template-columns: minmax(0, 1fr);
	}
}

/*
 * The carousel pattern's own chrome - the slides are genuine wp:image blocks
 * styled by core; assets/js/carousel.js does the actual switching, toggling
 * each slide's inline display style and each dot's is-active class. .carousel
 * carries the positioning context the controls are placed against.
 */
.carousel {
	position: relative;
}

.carousel__viewport {
	border-radius: var(--wp--custom--border-radius--standard);
	overflow: hidden;
}

.carousel__viewport .wp-block-image {
	margin: 0;
}

/*
 * Everything that makes this a carousel waits for the script. is-enhanced is
 * set by assets/js/carousel.js once it has taken over, so a browser that never
 * runs it keeps all the slides stacked and shows no controls - rather than one
 * slide, two unreachable ones, and two buttons that do nothing.
 *
 * Both the controls and the slides beyond the first are therefore hidden from
 * the enhanced state only, never by default.
 */
.carousel.is-enhanced .carousel__viewport .wp-block-image + .wp-block-image {
	display: none;
}

.carousel__control {
	align-items: center;
	background-color: var(--wp--preset--color--base);
	border: 1px solid color-mix(in srgb, currentColor 15%, transparent);
	border-radius: var(--wp--custom--border-radius--standard);
	color: var(--wp--preset--color--contrast);
	cursor: pointer;
	display: none;
	height: 3rem;
	justify-content: center;
	padding: 0;
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: 3rem;
}

.carousel.is-enhanced .carousel__control {
	display: flex;
}

.carousel__control--prev {
	left: var(--wp--preset--spacing--x-small);
}

.carousel__control--next {
	right: var(--wp--preset--spacing--x-small);
}

.carousel__control:hover {
	border-color: var(--wp--preset--color--accent);
}

.carousel__control svg {
	height: 1.25rem;
	width: 1.25rem;
}

.carousel__dots {
	display: none;
	gap: var(--wp--preset--spacing--xx-small);
	justify-content: center;
	margin-top: var(--wp--preset--spacing--small);
}

.carousel.is-enhanced .carousel__dots {
	display: flex;
}

/*
 * The same marker the timeline and the sticky quote already draw (sections 7
 * and 15): currentColor at low opacity until it is the current one, and the
 * theme radius token rather than a fixed pill shape. A fixed 9999px was the
 * one progress marker in the theme that stayed round through "Standard",
 * where the other two are squares - three dots doing the same job, one of
 * them ignoring the active style variation.
 */
.carousel__dot {
	background-color: color-mix(in srgb, currentColor 25%, transparent);
	border: 0;
	border-radius: var(--wp--custom--border-radius--standard);
	cursor: pointer;
	height: 0.6rem;
	padding: 0;
	width: 0.6rem;
}

.carousel__dot.is-active {
	background-color: var(--wp--preset--color--accent);
}

@media (max-width: 599px) {

	.carousel__control {
		display: none;
	}
}

/*
 * The progress pill's track and fill, patterns/progress-bars.php. Accent is
 * the fill on purpose, reserved theme-wide for a marker showing where things
 * stand, never running text. The fill rises from the bottom like a
 * thermometer, reading as "how much" on its own; the dashed border marks it
 * as a gauge rather than a solid card. Both round through the theme's own
 * radius token rather than a fixed stadium shape.
 */
.progress-pill {
	border: 1px dashed color-mix(in srgb, currentColor 30%, transparent);
	border-radius: var(--wp--custom--border-radius--standard);
	display: flex;
	align-items: flex-end;
	height: clamp(140px, 20vw, 190px);
	overflow: hidden;
	width: 3rem;
}

.progress-pill__fill {
	background-color: var(--wp--preset--color--accent);
	width: 100%;
}

/*
 * The pill row's own grid layout reflows on its own as it narrows, but "auto"
 * answers to the row's width, not a device, and on a phone it never settled
 * on two columns - the one count that keeps every pill wide enough for its
 * track and label. Two class names beat core's own single-class generated
 * rule, so this wins without a flag.
 */
@media (max-width: 599px) {

	.wp-block-group.progress-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/*
 * patterns/menu-list-media.php's thumbnail: fixed at 56x56 by the image
 * block's own width and height, but a flex item shrinks by default the
 * moment its row runs out of room, squashing that square into whatever is
 * left. flex-shrink: 0 keeps the figure at its real size and lets the
 * text beside it wrap instead.
 */
.menu-item > .wp-block-image {
	flex-shrink: 0;
}

/*
 * logo-strip.php sizes its logos by height alone, so each keeps its own
 * proportions - true on the front end, but the editor resolves the real file
 * and adds its own width/height attributes to the img, which won against no
 * width in the CSS and stretched the logo to its full file width. A declared
 * width beats the attribute; auto is the value the front end already used.
 */
.logo-strip .wp-block-image img {
	width: auto;
}

/*
 * An outline button on a coloured surface clears its resting fill to
 * transparent (patterns do this inline), since the "Outlined" variation fills
 * with base and a white slab on a dark band is not an outline button. That
 * broke the hover state, since the inline fill outweighs the variation's own
 * :hover fill - border and label now dim together with currentColor and the
 * muted role instead, which resolves correctly on both light and dark
 * palettes.
 */
.button-on-surface > .wp-block-button__link:hover {
	border-color: currentColor;
	color: var(--wp--custom--muted);
}

/*
 * The timeline pattern's rule and markers: all pseudo-elements, since none is
 * content anybody would edit. Positioned with inset-inline-start alone rather
 * than a 50% offset pulled back by translateX, since a transform moves the
 * same way in both writing directions and would land the marker off-centre
 * in a right-to-left language. The rule and fill are currentColor, the same
 * --scroll-progress mechanism as sticky-quote.php's line (assets/js/scroll-progress.js).
 */
.timeline {
	--scroll-progress: 0;

	position: relative;
}

.timeline::before {
	background-color: color-mix(in srgb, currentColor 15%, transparent);
	content: "";
	inset-block: 0;
	inset-inline-start: calc(50% - 1px);
	position: absolute;
	width: 2px;
}

.timeline::after {
	background-color: currentColor;
	content: "";
	height: calc( var(--scroll-progress) * 100% );
	inset-block-start: 0;
	inset-inline-start: calc(50% - 1px);
	position: absolute;
	width: 2px;
}

@media (prefers-reduced-motion: no-preference) {

	.timeline::after {
		transition: height 0.1s linear;
	}
}

.timeline__item {
	position: relative;
}

/*
 * Sits level with the first line of the date opposite - the offset is in em
 * so it follows that paragraph's own font size. Same size and colours as the
 * sticky quote's marker, currentColor throughout, muted until passed.
 */
.timeline__item::before {
	background-color: transparent;
	border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
	border-radius: var(--wp--custom--border-radius--standard);
	content: "";
	height: 10px;
	inset-inline-start: calc(50% - 5px);
	position: absolute;
	top: 0.4em;
	width: 10px;
}

.timeline__item.is-passed::before {
	background-color: currentColor;
	border-color: currentColor;
}

/*
 * 781px is where core stacks columns - the same reasoning as section 9.
 * Above it, every second entry is flipped so the cards alternate sides;
 * the date opposite always hugs the rule, so it aligns to whichever edge
 * faces the middle.
 */
@media (min-width: 782px) {

	.timeline__item--right {
		flex-direction: row-reverse;
	}

	.timeline__item--right .timeline__meta {
		text-align: end;
	}
}

/*
 * Stacked, there is no middle left for a centre rule, so the whole thing
 * moves to one edge: the rule runs down the inline start, entries indent
 * past it, and markers line up along it. The date moves above its card here,
 * since a marker has to sit level with the date, not whatever image the card
 * opens with - only the visual order changes, and a screen reader still
 * meets the entry before its date.
 */
@media (max-width: 781px) {

	.timeline::before,
	.timeline::after {
		inset-inline-start: 4px;
	}

	.timeline__item {
		padding-inline-start: var(--wp--preset--spacing--small);
	}

	.timeline__item::before {
		inset-inline-start: 0;
	}

	.timeline__meta {
		order: -1;
	}
}

/* -------------------------------------------------------------------------
 * 8. Core workarounds
 *
 * Rules that only exist because a core rule has to be outweighed. Check this
 * section after a WordPress update: if core changes its own declaration, the
 * rule below may become unnecessary - or insufficient.
 *
 * All of them would belong in the "css" key of theme.json by subject. They
 * cannot live there: WordPress wraps that key in ":root :where(...)", fixing it
 * at specificity (0,1,0) so that user styles keep winning. Core's own block
 * rules are (0,1,1). The key therefore only works for properties core does not
 * set itself.
 * ---------------------------------------------------------------------- */

/*
 * Core: ".wp-block-table td, .wp-block-table th {border: 1px solid;}" - the
 * colour follows the text and turns the grid into a black cage. Naming the
 * table element lifts the rule past core without a flag.
 */
.wp-block-table table th,
.wp-block-table table td {
	border-color: var(--wp--preset--color--neutral);
}

/*
 * Core: ".wp-block-table thead {border-bottom: 3px solid;}" - the heavier rule
 * under the head is welcome, its text colour is not.
 */
.wp-block-table table thead {
	border-bottom-color: var(--wp--preset--color--neutral);
}

/*
 * A closed accordion panel still took up its own bottom padding. Core marks
 * it hidden="until-found", which hides content via content-visibility but
 * leaves the box (padding included) in the flow. Zeroed while the attribute
 * is there; core removes it on opening, so padding returns exactly when
 * there's something to pad.
 */
.wp-block-accordion-panel[hidden] {
	padding-block-end: 0;
}

/*
 * The row's breathing room sits on the toggle, not the heading around it -
 * measured, the heading was 103px tall while the button inside it was only
 * 43, so two thirds of what looks like a clickable row did nothing. Two
 * class names outweigh core's own single-class rule on the toggle.
 */
.wp-block-accordion-heading .wp-block-accordion-heading__toggle {
	padding-block: var(--wp--custom--spacing--gap);
}

/*
 * Anchored on the section rather than "tr:first-child", which counts per
 * thead/tbody/tfoot and would also strip the first body row's own border.
 * Only the width is zeroed; the shorthand would reset the colour set above.
 */
.wp-block-table table tr > :first-child {
	border-inline-start-width: 0;
}

.wp-block-table table tr > :last-child {
	border-inline-end-width: 0;
}

.wp-block-table table > :first-child > tr:first-child > * {
	border-top-width: 0;
}

.wp-block-table table > :last-child > tr:last-child > * {
	border-bottom-width: 0;
}

/*
 * Core: ".wp-block-code code {white-space: break-spaces; overflow-wrap:
 * break-word;}" - which wraps code mid-token. Code keeps its line breaks and
 * scrolls sideways instead.
 */
.wp-block-code > code {
	overflow-wrap: normal;
	overflow-x: auto;
	tab-size: 4;
	white-space: pre;
}

/*
 * Core renders every "has-*-background-color" preset class with !important,
 * while theme.json's styles.elements.button:hover compiles behind :where(),
 * which carries no specificity - so a primary button stayed primary on hover
 * whatever theme.json said. Matching the !important settles it.
 *
 * Scoped to buttons: a primary background elsewhere has no hover state to
 * correct.
 */
.wp-block-button__link.has-primary-background-color:hover {
	background-color: var(--wp--preset--color--secondary) !important;
	color: var(--wp--custom--on-secondary) !important;
}

/* -------------------------------------------------------------------------
 * 9. Viewport dependent block styles
 *
 * The three rules here are the only block styles of this theme written in CSS.
 * Every other one is a block-*.json partial in /styles/ and needs no code.
 *
 * They cannot follow: theme.json drops an @media wrapper in its "css" key
 * without warning and applies the declaration unconditionally - measured, not
 * assumed. Their registration lives in inc/block-styles.php.
 *
 * The class is matched without the counter WordPress appends when rendering
 * (is-style-x--1): the authored class stays on the element as well.
 * ---------------------------------------------------------------------- */

@media (max-width: 599px) {

	.is-style-hidden-mobile {
		display: none;
	}
}

/*
 * 781px is where core stacks columns. Both rules below react to the stack, not
 * to a device: a reversal only means anything once the columns sit on top of
 * each other, and the divider seam moves from the side to the top at the same
 * moment. Reversing at a narrower width than core stacks at would leave a range
 * in which the columns are stacked but still in source order.
 */
@media (max-width: 781px) {

	.is-style-columns-reverse {
		flex-direction: column-reverse;
	}

	.is-style-columns-divided > .wp-block-column + .wp-block-column {
		border-inline-start-width: 0;
		border-block-start: 1px solid color-mix(in srgb, currentColor 15%, transparent);
		padding-inline-start: 0;
		padding-block-start: var(--wp--preset--spacing--x-small);
	}
}

/* -------------------------------------------------------------------------
 * 10. Comments
 * ---------------------------------------------------------------------- */

/*
 * core/comments renders nothing when comments are closed and none were ever
 * left. The band around it in patterns/single-post.php is a plain group and
 * knows nothing of that, so without this it stayed behind as an empty strip
 * of colour. Can't drop the band in favour of styling core/comments itself,
 * since that block has no layout support and would lose the constrained width.
 */
.comments-band:not(:has(.wp-block-comments)) {
	display: none;
}

/* core/categories and the comment list output a <ul>/<ol> with browser defaults. */
.wp-block-categories,
.wp-block-categories-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.wp-block-comment-template ol {
	padding-inline-start: var(--wp--preset--spacing--small);
}

/*
 * core/comment-reply-link outputs a plain link that never receives the
 * wp-element-button class, so the outline button look is repeated here.
 */
.wp-block-comment-reply-link a {
	background-color: transparent;
	border: 1px solid currentColor;
	border-radius: var(--wp--custom--border-radius--standard);
	display: inline-block;
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: var(--wp--custom--font-weight--semi-bold);
	line-height: var(--wp--custom--line-height--medium);
	padding:
		calc(var(--wp--custom--button--padding-block) - 1px)
		calc(var(--wp--custom--button--padding-inline) - 1px);
}

.wp-block-comment-reply-link a:hover {
	background-color: var(--wp--preset--color--secondary);
	border-color: var(--wp--preset--color--secondary);
	color: var(--wp--custom--on-secondary);
}

/* -------------------------------------------------------------------------
 * 11. Navigation submenus and overlay
 *
 * A submenu looks different depending on where it opens: as a panel below the
 * bar on a wide screen, and stacked inside the overlay on a narrow one. Core
 * marks the difference with is-menu-open, set at runtime - which is why none of
 * this can live in theme.json, and why every rule here says which of the two
 * cases it means.
 * ---------------------------------------------------------------------- */

/*
 * The dropdown on a wide screen. Core draws a border only while the
 * navigation has no background of its own, and this one has, so a white
 * panel arrived on a white page with no border, radius or shadow. Border
 * alone doesn't carry it either - neutral on white measures 1.13:1 against
 * the 3:1 WCAG ask, so the shadow does the separating work. :not(.is-menu-open)
 * keeps this off the overlay, where a panel border would fence off the menu.
 */
.wp-block-navigation__responsive-container:not(.is-menu-open) .has-child > .wp-block-navigation__submenu-container {
	border: 1px solid var(--wp--preset--color--neutral);
	border-radius: var(--wp--custom--border-radius--standard);
	box-shadow: var(--wp--preset--shadow--light);
	overflow: hidden;
	padding-block: var(--wp--preset--spacing--xx-small);
}

/*
 * In the overlay a submenu entry was indistinguishable from a top level one,
 * separated only by core's own indent - an accent bar in front now marks the
 * level. Accent is the marker, not the text colour: on the overlay's white
 * background it measures only 1.40:1, well under the 3:1 WCAG ask, so text
 * stays at full contrast and accent carries the meaning instead. Logical
 * properties (inline-start) put the bar on the other side in a
 * right-to-left language automatically.
 */
.wp-block-navigation__responsive-container.is-menu-open .has-child > .wp-block-navigation__submenu-container > .wp-block-navigation-item > .wp-block-navigation-item__content {
	border-inline-start: 3px solid var(--wp--preset--color--accent);
	padding-inline-start: var(--wp--preset--spacing--xx-small);
}

/*
 * The overlay has room, so the entries carry the heading scale instead of the
 * size they need in the bar: same size and weight as an h2. The font size is set
 * on the link itself, which beats the size the entries inherit from the
 * navigation block - no flag needed for that.
 */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item > .wp-block-navigation-item__content {
	font-size: var(--wp--preset--font-size--x-large);
	font-weight: var(--wp--custom--font-weight--semi-bold);
	line-height: var(--wp--custom--line-height--heading);
}

/*
 * Stacked entries turn the bar's horizontal justification into a vertical one,
 * so a menu aligned right in the bar would sit against the screen edge. Core
 * reads the alignment from this property, so redefining it for the overlay is
 * enough - centred is the sensible default on a narrow screen, whatever the bar
 * itself uses.
 */
.wp-block-navigation__responsive-container.is-menu-open,
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
	--navigation-layout-justification-setting: center;
}

/*
 * Centred stops working the moment one entry has a submenu: it stretches to
 * the container's full width while its centred siblings stay as wide as
 * their own text, reading as a mistake. :has() asks the actual question -
 * does this menu have a submenu at all - so a flat menu keeps the centred
 * layout above, and only a menu with real children flips to the left edge.
 */
.wp-block-navigation__responsive-container.is-menu-open:has(.has-child),
.wp-block-navigation__responsive-container.is-menu-open:has(.has-child) .wp-block-navigation__container {
	--navigation-layout-justification-setting: flex-start;
}

/* -------------------------------------------------------------------------
 * 12. Corner radius for blocks without the support
 *
 * Every surface in this theme reads --wp--custom--border-radius--standard, so
 * switching a style variation rounds all of them at once. The blocks below
 * cannot be reached through theme.json: their border support has no radius.
 * Before adding one here, check whether theme.json can do it - a rule in this
 * section is the exception.
 *
 * One rule runs through the whole section: alignfull squares the corner back
 * off. A rounded corner in the flow of the page reads as a shape; the same
 * corner on the window edge reads as a mistake, with no page margin left to
 * frame it. Wide and unaligned both keep the token.
 * ---------------------------------------------------------------------- */

/* Carries a background and an accent bar, so the corner is clearly visible. */
.wp-block-details {
	border-radius: var(--wp--custom--border-radius--standard);
}

.wp-block-details.alignfull {
	border-radius: 0;
}

/*
 * A cover gets its radius from theme.json, which is right for one sitting in
 * the flow of a page - but a full width cover reaches the window edge, and a
 * rounded corner there reads as a mistake rather than a shape. Same reasoning
 * that keeps core/group and core/columns out of the radius entirely: this
 * theme's full width bands are meant to run edge to edge. Cover is the one
 * block where both cases are common enough to be worth separating.
 */
.wp-block-cover.alignfull {
	border-radius: 0;
}

/*
 * The figure already scrolls sideways, and overflow clips as well - so the
 * radius needs nothing added. The table itself keeps square corners: with
 * border-collapse a table cannot round, and this theme removes its outer
 * borders anyway, leaving only the inner grid.
 */
.wp-block-table {
	border-radius: var(--wp--custom--border-radius--standard);
}

.wp-block-table.alignfull {
	border-radius: 0;
}

/*
 * A video and an embedded player are rectangles inside their figure, so the
 * figure has to clip them - a radius alone would sit behind the player.
 */
.wp-block-video,
.wp-block-embed__wrapper {
	border-radius: var(--wp--custom--border-radius--standard);
	overflow: hidden;
}

.wp-block-video.alignfull,
.wp-block-embed.alignfull .wp-block-embed__wrapper {
	border-radius: 0;
}

/*
 * core/media-text carries no surface of its own, so a radius on the block would
 * be invisible. What should round is the picture - that is what makes the block
 * match the text-with-image patterns, where the image is a core/image and
 * rounds through theme.json. The rule sits on the cell rather than the img so
 * that it also covers the variant where the picture is a background.
 */
.wp-block-media-text__media {
	border-radius: var(--wp--custom--border-radius--standard);
	overflow: hidden;
}

.wp-block-media-text.alignfull .wp-block-media-text__media {
	border-radius: 0;
}

/*
 * core/image, core/post-featured-image and core/avatar round through
 * theme.json, at the near-zero specificity every "css"-key and block-style
 * declaration gets there (see 4.3). A plain class selector already beats that
 * without !important - the same margin core/cover's own alignfull rule above
 * has.
 */
.wp-block-image.alignfull img,
.wp-block-post-featured-image.alignfull img,
.wp-block-avatar.alignfull img {
	border-radius: 0;
}

/*
 * Bound to the class alone, not a direct child of <main>: a coloured band can
 * sit several alignfull wrappers deep and still reach the window edge. Header
 * and footer bands are named on top of that, since a template part's band
 * carries no alignfull class in the editor canvas - the editor resolves that
 * alignment on the wrapper instead, so the class-bound selector alone missed
 * it there. Safe since this theme ships exactly one header and one footer.
 */
.wp-block-group.alignfull,
.wp-block-columns.alignfull,
header > .wp-block-group,
header > .wp-block-columns,
footer > .wp-block-group,
footer > .wp-block-columns {
	border-radius: 0;
}

/*
 * Radius alone is invisible on core/columns when the colour sits on its
 * individual column children instead of the row itself - numbered-columns'
 * tile row is exactly this case. overflow: hidden clips the flush children to
 * the row's own rounded box, same mechanism as .card (section 7). :has()
 * clips only a row where a column really carries a background, so a plain
 * text row - or the media-badge pattern's positioned label - is left alone.
 */
.wp-block-columns.alignwide:has(> .wp-block-column.has-background) {
	overflow: hidden;
}

/* -------------------------------------------------------------------------
 * 13. Video
 *
 * Wherever this theme places an image it places it cropped: core/image carries
 * aspectRatio and scale, and the patterns use both. core/video supports
 * neither, so a film dropped in where a picture used to be arrived at its own
 * proportions and broke the row it was standing in.
 *
 * These rules give a video the same frame an image would have had, so the two
 * are interchangeable in any pattern without the layout noticing.
 * ---------------------------------------------------------------------- */

.wp-block-video video,
.wp-block-media-text__media video {
	display: block;
	height: 100%;
	object-fit: cover;
	width: 100%;
}

.wp-block-video video {
	aspect-ratio: 16 / 9;
}

/* -------------------------------------------------------------------------
 * 14. Video stack
 *
 * patterns/video-stack.php: full height cover panels, each with its own film,
 * overlay, headline and button, that slide over one another while scrolling.
 *
 * position: sticky does all of it. Every panel but the last stops at the top
 * of the viewport and stays there while the next scrolls up over it - no
 * scroll listener, nothing to recalculate on resize, and it holds whether the
 * site owner keeps three panels or ends up with two or five.
 * ---------------------------------------------------------------------- */

.video-stack {
	/* A child theme with its own scroll container overrides this. The usual "declare vh, then dvh" fallback doesn't work on a custom property - its value isn't checked until used - so @supports asks the question instead. */
	--video-stack-panel-height: 100vh;

	/* A sticky panel is positioned and would otherwise paint over whatever follows the stack. */
	isolation: isolate;
}

body.admin-bar .video-stack {
	--video-stack-panel-height: calc(100vh - var(--wp-admin--admin-bar--height, 32px));
}

/* dvh follows a mobile toolbar sliding away; vh does not. */
@supports (height: 100dvh) {

	.video-stack {
		--video-stack-panel-height: 100dvh;
	}

	body.admin-bar .video-stack {
		--video-stack-panel-height: calc(100dvh - var(--wp-admin--admin-bar--height, 32px));
	}
}

/*
 * Two class names deep to outrank core's .wp-block-cover min-height of 430px
 * without !important; a site owner who sets a height in the editor still
 * overrides this. The background colour makes a panel opaque, since a
 * cover's overlay is half transparent and an unloaded film would otherwise
 * show the panel below through it. Square corners keep a sliding panel from
 * cutting notches out of its own top corners.
 */
.video-stack .video-stack__panel {
	background-color: var(--wp--preset--color--base);
	border-radius: 0;
	min-height: var(--video-stack-panel-height);
	position: sticky;
	top: 0;
}

/*
 * The last panel keeps scrolling instead of stopping at the top. A sticky one
 * would stand still while the next section scrolls up over it, arriving as
 * text drifting across a still-playing film.
 */
.video-stack .video-stack__panel:last-child {
	position: relative;
}

/* -------------------------------------------------------------------------
 * 15. Sticky quote
 *
 * patterns/sticky-quote.php: a quote stays in view while a line of steps
 * scrolls past beside it, filling as the visitor reaches each one.
 * ---------------------------------------------------------------------- */

.sticky-quote__mark {
	color: color-mix(in srgb, currentColor 30%, transparent);
	display: block;
	font-size: var(--wp--preset--font-size--xxxxx-large);
	line-height: 1;
}

/*
 * The track and its fill are both currentColor, so the line reads correctly
 * on whichever surface the band is switched to. --scroll-progress stays
 * unset without assets/js/scroll-progress.js, reading as an empty track.
 *
 * --gap-offset pulls the line out of the track's own column and into the
 * middle of the gap beside it - the pattern's two columns split an even
 * 50/50 for exactly this reason, so the gap sits at the row's true centre.
 */
.sticky-quote__track {
	--gap-offset: calc(var(--wp--preset--spacing--large) / -2);
	--scroll-progress: 0;

	position: relative;
}

.sticky-quote__track::before {
	background-color: color-mix(in srgb, currentColor 15%, transparent);
	content: "";
	inset-block: 0;
	inset-inline-start: var(--gap-offset);
	position: absolute;
	width: 2px;
}

.sticky-quote__track::after {
	background-color: currentColor;
	content: "";
	height: calc( var(--scroll-progress) * 100% );
	inset-block-start: 0;
	inset-inline-start: var(--gap-offset);
	position: absolute;
	width: 2px;
}

@media (prefers-reduced-motion: no-preference) {

	.sticky-quote__track::after {
		transition: height 0.1s linear;
	}
}

.sticky-quote__step {
	padding-inline-start: var(--wp--preset--spacing--medium);
	position: relative;
}

/*
 * The dot sits on the step, not the track: each one needs its own vertical
 * offset, which is where its step already is. Both align on the same
 * --gap-offset, inherited from .sticky-quote__track. inset-inline-start
 * centres the 10px dot on the 2px line, 4px further back than the line's
 * own centre.
 */
.sticky-quote__step::before {
	background-color: transparent;
	border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
	border-radius: var(--wp--custom--border-radius--standard);
	content: "";
	height: 10px;
	inset-inline-start: calc(var(--gap-offset) - 4px);
	inset-block-start: 0.3em;
	position: absolute;
	width: 10px;
}

.sticky-quote__step.is-passed::before {
	background-color: currentColor;
	border-color: currentColor;
}

/* -------------------------------------------------------------------------
 * 16. Text reveal
 *
 * patterns/text-reveal.php: a statement stays in view while scrolling lights
 * it up, then hands off to the rest of the page once it is fully lit. Same
 * --scroll-progress property as section 15, written by the same script.
 * ---------------------------------------------------------------------- */

.text-reveal {
	/* Taller reads as a slower reveal; 220vh keeps the pin on screen for a bit over one extra viewport of scrolling. */
	--scroll-progress: 1;

	/* Accent by default, the theme's emphasis colour; steps down to primary once the band underneath is accent itself, the same collision a badge or button hits. */
	--text-reveal-fill: var(--wp--preset--color--accent);

	min-height: 220vh;
	position: relative;
}

.text-reveal.is-style-on-accent,
.text-reveal.has-accent-background-color {
	--text-reveal-fill: var(--wp--preset--color--primary);
}

/*
 * justify-content is what actually centres the heading top-to-bottom - the
 * block's own "Justify items" control wires to align-items for a vertical
 * stack instead, leaving justify-content at its default and the heading
 * pinned to the top of the viewport.
 */
.text-reveal__pin {
	justify-content: center;
	position: sticky;
	top: 0;
}

/*
 * The default above is fully lit, not unlit like the quote line's empty
 * track: a heading has to read correctly before the script has run at all.
 * The unlit portion stays currentColor so it reads as "this text, dimmed" on
 * every surface. -webkit-text-fill-color is what needs to be transparent - a
 * browser that doesn't know the property is left with plain, visible text.
 */
.text-reveal__text {
	background-clip: text;
	-webkit-background-clip: text;
	background-image: linear-gradient(
		100deg,
		var(--text-reveal-fill) 0%,
		var(--text-reveal-fill) calc( var(--scroll-progress) * 100% ),
		color-mix(in srgb, currentColor 25%, transparent) calc( var(--scroll-progress) * 100% ),
		color-mix(in srgb, currentColor 25%, transparent) 100%
	);
	-webkit-text-fill-color: transparent;
}

.text-reveal__hint {
	align-items: center;
	display: flex;
	flex-direction: column;
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: var(--wp--custom--font-weight--semi-bold);
	gap: var(--wp--preset--spacing--xx-small);
	letter-spacing: 0.1em;
	opacity: calc( 1 - var(--scroll-progress) );
	text-transform: uppercase;
}

@media (prefers-reduced-motion: no-preference) {

	.text-reveal__hint {
		transition: opacity 0.2s linear;
	}
}

.text-reveal__hint-line {
	background-color: currentColor;
	display: block;
	height: var(--wp--preset--spacing--medium);
	width: 1px;
}

/* -------------------------------------------------------------------------
 * 17. Search prompt
 *
 * patterns/search-prompt.php: a headline over a search field.
 * ---------------------------------------------------------------------- */

/*
 * core/search's own flex layout lets the button shrink and wrap its label
 * onto two lines before it lets the input give up any width - fine for a
 * one-word buttonText, wrong the moment a site owner writes a longer one.
 * flex-shrink: 0 pins the button to its content's own width instead, so the
 * input is what gives way as the field narrows.
 */
.search-prompt .wp-block-search__button {
	flex-shrink: 0;
	white-space: nowrap;
}

/* -------------------------------------------------------------------------
 * 18. Header logo
 *
 * patterns/header.php: the site logo beside the main navigation.
 * ---------------------------------------------------------------------- */

/*
 * Capped by height, not the block's own width attribute: a fixed pixel width
 * is tuned to one image's aspect ratio and crushes a wide wordmark or a tall
 * mark the other way. Height is the one dimension this bar actually
 * constrains; width follows the logo's own ratio automatically. Matches
 * core's own .is-default-size img selector so its 120px width doesn't win
 * the cascade regardless of source order.
 */
.wp-block-site-logo.is-default-size img {
	height: 32px;
	width: auto;
}
