/*
Theme Name: DIGISTARTER
Theme URI: https://www.digistarter.de/
Author: DIGISTALTER GmbH
Author URI: https://www.digistalter.de/
Description: A base theme meant as a construction kit rather than a finished design. It ships the technical groundwork - design tokens, block styles, editor configuration - together with a preconfigured header, footer and blog layout, and several style variations in light and dark. Final colours and larger adjustments belong in a child theme and need no changes to any template.
Requires at least: 6.9
Tested up to: 7.1
Requires PHP: 7.4
Version: 1.0.1
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;
}

/*
 * 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;
}

/*
 * A theme pattern always states its own top and bottom spacing - every opener
 * pads itself, and index.html/archive.html add an explicit core/spacer where
 * a query loop cannot. Content this theme did not author has no such
 * guarantee: a plugin's own archive markup, rendered through its own
 * get_header()/get_footer() rather than through this theme's block
 * templates, can sit inside <main> with no spacing of its own at all -
 * measured on exactly such a page, its own top and bottom edge touched
 * <main>'s, which itself carries none either, and the content ran straight
 * into the header above and the footer below.
 *
 * The fallback below only fires when nothing already handles it: a first or
 * last child that is alignfull already carries its own padding by the
 * convention below, and a first or last child that is a core/spacer is
 * already an explicit request for exactly this space. Neither is doubled up
 * - only a plain wide or unaligned block, straight from a plugin or from
 * content this theme never designed for, falls through to it.
 *
 * The second test exists because templates/page.html opens with the page's
 * own title, which the front end then drops again (see inc/page-title.php).
 * While editing, that title is the first child, and it is neither alignfull
 * nor a spacer - so without this the guard above stopped recognising an
 * opener that pads itself, and every page gained a phantom band between the
 * header and the title. Measured in the site editor: 120px above the title
 * against 30px below the content. The block after the title is what decides
 * instead, which is the block that would have been first anyway.
 */
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);
}

/*
 * No two top level blocks in this theme touch by accident, and none are kept
 * apart by accident either. Core's block gap gives every block in flowing
 * content a margin-block-start equal to the theme's spacing scale - right for
 * a paragraph following another paragraph, wrong for a full width band with
 * its own colour: measured, two such bands stacked always left a 30px seam of
 * bare page between them, and there was no way to close it short of editing
 * each pattern by hand. The margin is invisible in the editor's block list,
 * so nothing there told a page author where it was even coming from.
 *
 * A band that carries its own surface already states its rhythm through its
 * own padding-top - a second, invisible margin on top of that is a default no
 * one asked for, once the band is alignfull. That is the alignment this
 * theme's own patterns ship with, and it reads as a deliberate choice to run
 * edge to edge - two full width bands in a row are meant to meet. Wide is a
 * different decision: switching a band to Wide already pulls its own edges in
 * from the page margin, so pulling its neighbours in towards it too would
 * fight the choice rather than follow it. A wide band keeps the ordinary block
 * gap for exactly that reason - it reads as a section apart, not a strip of a
 * continuous band, and the two alignments are meant to look different from
 * each other in this respect, not just in width.
 *
 * Only alignfull is targeted here, then - not every backgrounded block, and
 * not by is-layout-constrained the way 9.3b targets wide bands, since margin
 * and padding-inline are not the same question here. Wanting space between
 * two full width bands, or before the first one, now takes an explicit
 * core/spacer, exactly the block this theme already ships for the purpose -
 * not a margin no one can see or turn off.
 *
 * A block that sets its own margin - home-highlight's deliberate gap above
 * itself - is untouched regardless of alignment: an inline style always wins
 * over an external rule, with or without this one.
 */
main > .alignfull.has-background,
main .entry-content > .alignfull.has-background {
	margin-block-start: 0;
}

/*
 * Every full width band with its own surface in this theme's patterns is
 * built the way WordPress itself recommends: an alignfull outer group in
 * constrained layout, holding an alignwide child for the actual content. At
 * alignfull that already works without help - useRootPaddingAwareAlignments
 * in theme.json gives the outer group 30px of its own via the
 * has-global-padding class, and the wide child centres inside that at its own
 * max-width.
 *
 * Switching the outer group to Wide width in the editor breaks it. Measured:
 * core's own layout CSS gives a nested .alignwide only `max-width`, no margin
 * - " .is-layout-constrained > .alignwide { max-width: var(--wp--style--global--wide-size); }"
 * and nothing else. An outer band that is itself already wide computes to the
 * same max-width as its wide child, so the two collapse to one edge: the
 * child's content sits flush against the band's own side, with no inset left.
 * has-global-padding does not apply either - core reserves it for alignfull.
 *
 * The fix repeats the same 30px this theme already uses for the root padding,
 * explicitly, so it no longer depends on which alignment produced it. It only
 * targets an alignwide block that is itself in constrained layout and carries
 * a background - the outer band, not the wide content sitting inside it,
 * which stays flex or grid and so never matches. alignfull is untouched; its
 * padding was never the problem.
 *
 * has-background is the deciding line, not just is-layout-constrained: a
 * pattern like numbered-columns has no surface of its own on its outer
 * group - the colour sits on the four tiles nested inside it, which are
 * alignfull on purpose so they run edge to edge within whatever contains
 * them. Padding on the bare outer group at Wide width pushed those tiles in
 * from both sides regardless, measured directly - a parent's padding narrows
 * a child's available width whether or not the parent itself is visible.
 * There is no colour edge to protect on a transparent group, so no inset is
 * added to one.
 */
.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);
}

/* -------------------------------------------------------------------------
 * 2. Typography
 * ---------------------------------------------------------------------- */

/*
 * 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, a 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 by writing a new rule.
 *
 * A pill sets the property itself rather than inheriting it from whatever it
 * sits in. Otherwise the same category label would be underlined in a term list
 * on the blog page and plain inside a card, purely because the card happens to
 * carry "Subtle links" - and a component is not allowed to depend on where it
 * was placed. The rule that draws the pill is in section 7; this is the one
 * that decides how its text is marked.
 *
 * 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 - measured on the footer band: LinkedIn blue,
 * Instagram pink, Facebook blue, YouTube red, four saturated colours on a dark
 * surface and 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. Scoped to :not(.has-icon-color) on purpose: that class only
 * appears once someone picks an icon colour in the editor, and their choice
 * has to keep beating the theme's default. Choosing a colour is therefore
 * still the way to get brand colours back.
 */
.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--preset--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. */
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--preset--border-radius--standard);
	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 that has a direction is
 * written with logical properties - inset-inline-start instead of left,
 * padding-inline-start instead of padding-left - so the browser mirrors it on
 * its own. Symmetric values (a button's left and right padding) stay physical;
 * there is nothing to mirror.
 *
 * The two rules below exist because their source cannot express the logical
 * form:
 *
 * - theme.json only knows "left" and "right", never "inline-start". The accent
 *   bar of the details block and the indent of a list therefore live here.
 * - background-position has no logical counterpart in CSS at all, so the select
 *   arrow needs an explicit mirror image.
 * ---------------------------------------------------------------------- */

.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.
 * ---------------------------------------------------------------------- */

/*
 * 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--preset--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;
}

/*
 * 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--preset--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);
	padding: 0.2em 0.7em;
	white-space: nowrap;
	width: fit-content;
}

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

/* On a primary surface the fill would disappear, so the label steps down a shade. */
.has-primary-background-color .badge:not(.badge-accent) {
	background-color: var(--wp--preset--color--secondary);
	color: var(--wp--custom--on-secondary);
}

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

/* 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;
}

/*
 * 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);
}

/*
 * 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--preset--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. margin: 0 because the flow layout
 * block gap applies to absolutely positioned elements as well.
 */
.media-frame {
	position: relative;
}

/*
 * .wp-block-group.media-frame rather than plain .media-frame: measured in the
 * block editor, the pill stayed in the flow instead of floating over the image,
 * leaving a 34px gap at the bottom of the tile. The block editor's own
 * ".block-editor-block-list__layout .block-editor-block-list__block" rule
 * carries two classes and ties this selector's specificity - which one wins is
 * then a matter of load order, and in the editor that was core's rule, not
 * ours. The extra class is true to the markup - the frame is always a
 * wp-block-group - and 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 - right on most
 * surfaces, wrong on the two where accent itself is too close in luminance to
 * read against: measured 1:1 on the accent surface (same value on both sides)
 * and 1.21:1 on neutral (both light). Same reasoning as the badge's surface
 * flip above: a fixed accent value cannot sit on a surface this close to it,
 * so those two get a rule of their own, stepping the mark to the surface's own
 * on-* role instead of a second fixed colour.
 *
 * Specificity (0,2,1) beats the "css" key's (0,1,0) without !important - see
 * section 4.3 in ARCHITECTURE.md for why that key is capped there.
 */
.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. This is deliberately plain CSS rather than the
 * post-template block's own "layout":{"type":"grid"} attribute.
 *
 * Measured: the moment a page or template holding one of these patterns is
 * opened in the editor, core/post-template's grid layout attribute is silently
 * rewritten to {"type":"default"} - visible directly in getEditedPostContent(),
 * not just on screen. Saving at that point would permanently flatten the grid
 * to one column. core/group's own grid layout survives the same pattern-flatten
 * step intact, so the fault is specific to core/post-template, not to "layout"
 * attributes in general - measured on that block too before reaching for a
 * workaround.
 *
 * A className is a plain string and was never affected, so the class carries
 * the columns instead. Also gives the grid the responsive step-down core's
 * fixed columnCount never had on its own: three columns is too narrow to
 * survive to a phone screen unaided.
 */
.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" now (see above), which core reads
 * as its flow layout and spaces children with a margin on every one but the
 * first - the same margin that stacks blockGap in a document. That collides
 * with this grid's own gap, so the first tile came out with no top margin and
 * every other tile 20px shorter. Two class names match the specificity of
 * core's own generated container rule, so this wins without a flag; the grid's
 * gap is the only spacing left once the margin is zeroed.
 */
.wp-block-post-template.post-grid > * {
	margin-block: 0;
}

/*
 * Same fix as .post-grid above, for a query loop presented as a divided list
 * instead of a grid: the rule below carries the row's own padding as the only
 * space around it, so the flow layout's margin would add to that unevenly -
 * still worth zeroing so the rule alone controls the rhythm.
 *
 * Not the theme's "Divided rows" block style: that one compiles to
 * ".wp-block-group.is-style-rows-divided", tying the rule to core/group
 * specifically - measured zero matching CSS after applying the class here, on
 * core/post-template. A block-style partial bakes its owning block's own class
 * into the selector, so it never reaches a different block, however the class
 * is applied. Same visual formula, written directly instead.
 */
.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 three slides inside .carousel__
 * viewport are genuine wp:image blocks, styled by core; nothing here
 * targets them beyond the display rule below and zeroing the margin a
 * standalone image carries. assets/js/carousel.js does the actual
 * switching, setting each slide's own inline display style and toggling
 * each dot's is-active class.
 *
 * The controls sit over the image rather than beside it, so .carousel
 * carries the positioning context they are placed against.
 */
.carousel {
	position: relative;
}

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

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

/*
 * The pattern's markup cannot mark this one hidden or active itself - see
 * patterns/gallery-carousel.php - so the first slide has to be picked out
 * structurally instead. Correct only until the script runs and gives an
 * explicit display value to whichever slide is actually current.
 */
.carousel__viewport .wp-block-image:first-child {
	display: block;
}

.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--preset--border-radius--standard);
	color: var(--wp--preset--color--contrast);
	cursor: pointer;
	display: flex;
	height: 3rem;
	justify-content: center;
	padding: 0;
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: 3rem;
}

.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: flex;
	gap: var(--wp--preset--spacing--xx-small);
	justify-content: center;
	margin-top: var(--wp--preset--spacing--small);
}

.carousel__dot {
	background-color: color-mix(in srgb, currentColor 25%, transparent);
	border: 0;
	border-radius: 9999px;
	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-bars pattern's track and fill. Accent is the fill on
 * purpose rather than a colour per entry: the rest of the theme already
 * reserves accent for exactly this role - a marker showing where things
 * stand, never running text - so every bar reads as "the same kind of
 * measurement" instead of an arbitrary palette.
 */
.progress-bar {
	background-color: var(--wp--preset--color--neutral);
	border-radius: var(--wp--preset--border-radius--standard);
	height: 8px;
	overflow: hidden;
}

.progress-bar__fill {
	background-color: var(--wp--preset--color--accent);
	border-radius: var(--wp--preset--border-radius--standard);
	height: 100%;
}

/*
 * 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;
}

/*
 * The three logo-strip patterns size their logos by height alone, so each
 * one keeps its own proportions (see logo-strip-primary.php). That works
 * on the front end, where the markup carries nothing but style="height:
 * 40px" - but the editor resolves the real file and adds width="1200"
 * height="900" to the same img. A width attribute is a presentational
 * hint, so with no width in the CSS to beat it the logo came out 1200px
 * wide and 40px tall, and the row of five became a stack of five.
 * Measured in the editor canvas; the front end was correct throughout.
 *
 * Any declared width wins over the attribute, and auto is the value the
 * front end was already using, so this makes the two agree rather than
 * introducing a third size.
 */
.logo-strip .wp-block-image img {
	width: auto;
}

/*
 * An outline button sitting on a coloured surface. Seven patterns clear
 * its resting fill to transparent, because the "Outlined" variation fills
 * itself with base (theme.json, core/button variations) and a white slab
 * on a dark band is not an outline button.
 *
 * That left the hover state saying the wrong thing. The variation's own
 * :hover fills with secondary and sets the label to on-secondary - but the
 * cleared fill is an inline style, so it outweighs the fill and the label
 * went from white to white. The border alone reacted, flipping from light
 * to near-black. Measured on the primary hero: the only visible change was
 * the frame disappearing into the band behind it.
 *
 * Border and label now dim together. border-color goes back to
 * currentColor so it follows the label rather than the fill that never
 * arrives, and the label takes the muted role - the text colour at 70
 * percent, which resolves against whatever the surface already carries.
 * A fixed light grey would read on the light palettes and vanish on the
 * dark ones, where neutral is #1e1e1e against a #383838 band.
 */
.button-on-surface > .wp-block-button__link:hover {
	border-color: currentColor;
	color: var(--wp--custom--muted);
}

/*
 * The timeline pattern's rule and dots. Both are pseudo-elements: neither
 * is content anybody would edit, and keeping them out of the markup leaves
 * every block in that pattern a real, valid one.
 *
 * Positioned with inset-inline-start alone rather than a 50% offset pulled
 * back by translateX: a transform moves the same way in both writing
 * directions, so the dot - unlike the one-pixel rule - would land visibly
 * off-centre in a right-to-left language. Subtracting half the width from
 * the offset instead is correct either way round.
 */
.timeline {
	position: relative;
}

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

.timeline__item {
	position: relative;
}

/*
 * Sits level with the first line of the date opposite, which is why the
 * offset is in em: it follows that paragraph's own font size rather than
 * drifting away from it whenever the type scale is changed.
 */
.timeline__item::before {
	background-color: var(--wp--preset--color--base);
	border: 2px solid var(--wp--preset--color--accent);
	border-radius: 9999px;
	content: "";
	height: 0.85rem;
	inset-inline-start: calc(50% - 0.425rem);
	position: absolute;
	top: 0.4em;
	width: 0.85rem;
}

/*
 * 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, each entry is
 * indented past it, and the dots line up along it. The indent has to clear
 * the dot itself - spacing--small is 30px at this width against a 13.6px
 * dot, which leaves the entry a visible gap rather than butting it
 * against the mark.
 *
 * The date moves above its card here rather than staying below it. A dot
 * on a timeline marks a moment, so it has to sit level with the date, not
 * with whatever image the card happens to open with - and at this width
 * the date is the only thing that can be up there. Only the visual order
 * changes; both readings say the same thing, and a screen reader still
 * meets the entry before its date.
 */
@media (max-width: 781px) {

	.timeline::before {
		inset-inline-start: calc(0.425rem - 0.5px);
	}

	.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, so a closed
 * item had 37px above its label and 67px below it. Core marks the panel with
 * hidden="until-found" - that hides the content through content-visibility so
 * the browser can still find it on the page, but the element's own box, padding
 * included, stays in the flow. The padding from theme.json therefore applied
 * whether the panel was open or not.
 *
 * Zeroed while the attribute is there. Core removes it on opening, so the panel
 * gets its padding back exactly when it has something to pad. The attribute
 * selector settles the specificity against theme.json's own (0,1,0) rule
 * without a flag.
 */
.wp-block-accordion-panel[hidden] {
	padding-block-end: 0;
}

/*
 * The row's breathing room sits on the toggle, not on 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. Moving the
 * padding onto the button makes the whole row the target and leaves the
 * spacing identical, because the heading has no padding of its own any more.
 *
 * Two class names to outweigh core's own single-class rule on the toggle,
 * whose padding this replaces.
 */
.wp-block-accordion-heading .wp-block-accordion-heading__toggle {
	padding-block: var(--wp--custom--spacing--gap);
}

/*
 * The grid stays inside; the outer edges are carried by the surrounding layout.
 *
 * Anchored on the section rather than written as "tr:first-child", which counts
 * per thead, tbody and tfoot and would also strip the first body row - taking
 * the seam below the table head with it.
 *
 * Only the width is zeroed; the shorthand would reset the colour set above back
 * to currentColor.
 */
.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: the colour block support renders every "has-*-background-color" preset
 * class with !important - measured on this button's own markup, ".has-primary-
 * background-color{background-color: var(--wp--preset--color--primary)
 * !important;}". theme.json's styles.elements.button:hover compiles to
 * ":root :where(.wp-element-button:hover, .wp-block-button__link:hover){...}",
 * and :where() carries no specificity of its own, so that rule never had a
 * chance against an !important class - a primary button stayed primary on
 * hover no matter what theme.json said. Matching the !important here, on a
 * selector already ahead at (0,3,0) against the class's (0,1,0), settles it
 * for good; the same fight repeats for the text colour, which arrives inline
 * from the pattern for the same reason a plain rule could never touch it.
 *
 * Scoped to buttons on purpose: a primary background elsewhere (a card, a
 * section) has no hover state to correct, and the fix belongs with the thing
 * that measured broken.
 */
.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/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--preset--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 around it only while the
 * navigation has no background of its own - and this one has (see the note in
 * patterns/header.php on why). So a white panel arrived on a white page with no
 * border, no radius and no shadow: measured background #ffffff, border none,
 * border-radius 0, box-shadow none. Nothing said "submenu".
 *
 * The border repeats the rule under the header, same colour and same width, so
 * the panel reads as part of the same bar. The radius is the shared token, so
 * the dropdown rounds along with every other surface when a style variation is
 * chosen - and overflow clips the entries, whose hover background would
 * otherwise square off the corner again.
 *
 * The border alone does not carry it: neutral on white measures 1.13:1, well
 * under the 3:1 WCAG asks of a component edge, so at one pixel it is barely
 * there. The shadow preset the theme already defines does the separating work -
 * the panel lifts off the page instead of being outlined on it - and the border
 * stays as the crisp edge underneath. No new value is introduced for either.
 *
 * The selector carries :not(.is-menu-open) because the overlay needs none of
 * it: there the submenu sits in the flow, where a panel border would fence off
 * part of the menu. That also lifts specificity to (0,4,0), enough to beat
 * core's (0,3,0).
 */
.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--preset--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 -
 * measured identical colour #111111, 24.4px and weight 600, separated only by
 * the indent core adds. An accent bar in front of the entry marks the level.
 *
 * Accent is the marker, not the text colour: on the overlay's white background
 * #7cf461 measures 1.40:1, where WCAG AA asks 3:1 at this size. The text stays
 * contrast at 18.88:1 and accent still carries the meaning.
 *
 * The inline padding core gives the entry is 24px, which left the bar floating
 * a good 30px away from the word instead of belonging to it. Narrowing it on
 * the marked side alone pulls the bar up to the text and leaves the entry's
 * own spacing intact.
 *
 * border-inline-start and padding-inline-start rather than the left variants,
 * so a right-to-left language puts the bar on the other side without a second
 * stylesheet.
 */
.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. Measured in the
 * open overlay: an entry with children stretches to the full 315px of the
 * container, while its siblings stay as wide as their own text and sit centred
 * - so the parent reads as left aligned and everything else as centred, which
 * looks like a mistake rather than a choice.
 *
 * :has() asks the actual question - does this menu have a submenu at all -
 * instead of tying the rule to a particular menu or a body class. A flat menu
 * keeps the centred layout above; only a menu that really has children flips
 * to the left edge, where a parent and its children line up on the same axis.
 *
 * align-items is what the custom property feeds in the overlay (the container
 * is a column there), so redefining the property is enough - no second rule
 * and no override of core's own layout declarations.
 */
.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--preset--border-radius--standard, so
 * that switching a style variation rounds all of them at once. The blocks below
 * cannot be reached through theme.json: their border support does not include
 * radius. Checked against the block registry, not assumed.
 *
 * Whenever a block is added here, check first whether theme.json can do it -
 * a rule in this section is the exception, not the pattern.
 *
 * A second rule runs through this whole section: alignfull always squares the
 * corner back off, whatever the style variation says. A rounded corner in the
 * flow of the page reads as a shape; the same corner sitting exactly on the
 * window edge reads as a mistake, because there is no page margin left to
 * frame it. align:wide and no alignment at all both stay on the token - only
 * full width is the exception, and only because it is the one alignment with
 * no edge of its own to round away from.
 *
 * Applies to every block whose supports.align reaches "full" and which also
 * carries a radius, in either theme.json or this file - checked against the
 * block registry, not assumed. core/code (align: ["wide"] only) and
 * core/button (align: false) cannot reach alignfull at all, so neither needs
 * an exception here.
 * ---------------------------------------------------------------------- */

/* Carries a background and an accent bar, so the corner is clearly visible. */
.wp-block-details {
	border-radius: var(--wp--preset--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--preset--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--preset--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--preset--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;
}

/*
 * core/group and core/columns round through theme.json too, added once this
 * rule existed to catch them at alignfull - so a band that is switched to
 * Wide or has no alignment at all rounds like every other surface when a
 * style variation asks for it, and only Full is squared off.
 *
 * Unconditional on the class alone, the same as every other rule in this
 * section - not restricted to a direct child of <main>, which an earlier
 * version of this rule was. A coloured band can sit several alignfull
 * wrappers deep, built by combining one pattern inside another in the site
 * editor rather than placing each as its own top-level section, and every one
 * of those wrappers still reaches the real window edge precisely because
 * every ancestor up to <main> is Full too. Restricting the rule to direct
 * children missed exactly that case: measured with hero-centered-colour
 * nested inside hero-image-below inside a page's own wrapping group, the
 * inner coloured band kept a 10px corner three levels down, on a page where
 * the outer two carry no background of their own and the corner sits on the
 * real edge regardless of how deep it is.
 *
 * header.php and footer.php's own bands are named on top rather than left to
 * the class alone. Measured in the editor canvas: a template part's own band
 * renders there as "block-editor-block-list__block wp-block ...
 * wp-block-group" - full canvas width, but with no alignfull class at all,
 * because the editor resolves that alignment through the template part
 * wrapper instead of on the band. The class-bound selector therefore matched
 * on the front end and silently missed in the editor, showing the footer
 * rounded there while square on the published page. Safe to drop the class
 * requirement precisely here: this theme ships exactly one header and one
 * footer, and every direct group or columns child of either is one of their
 * full-width bands by construction.
 */
.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 core/column children instead of the row itself, which has no
 * fill of its own to show a corner on - numbered-columns' tile row is
 * exactly this case, aligned Wide so it keeps the token radius instead of
 * being squared off by the rule above. overflow: hidden clips the flush
 * children to the row's own rounded box, the same mechanism .card already
 * uses for the same reason (section 7).
 *
 * :has() states the actual condition rather than a position in the tree:
 * clip a columns row only when a column inside it really carries a
 * background. A row of plain text columns is left alone, so nothing that
 * legitimately spills past a column's edge - the media-badge pattern
 * positions a label this way - is caught by accident. A Full columns row
 * needs no equivalent rule here: the rule above already squares it off
 * unconditionally, and a square corner clips nothing.
 */
.wp-block-columns.alignwide:has(> .wp-block-column.has-background) {
	overflow: hidden;
}
