/**
 * Astrum — shared layer.
 *
 * Loaded on the front end (wp_enqueue_scripts) AND inside the editor canvas
 * (add_editor_style), so it must contain nothing that depends on the front-end
 * document shell: no `position: fixed`, no scroll padding, no admin-bar offsets.
 * Those live in _site.css.
 *
 * Hard rule (CLAUDE.md, plan §4.6): no hex colours and no px values taken from
 * the design kit. Every value comes from a theme.json token via var(). The only
 * literals allowed are values for which no token exists — grid ratios, media
 * query breakpoints, aspect ratios, blur radii, hairline widths.
 *
 * Layout ratios and breakpoints (980px / 620px) are carried over verbatim from
 * the approved static preview.
 *
 * SPECIFICITY NOTE: core's layout supports emit
 * `.wp-container-core-group-layout-N { grid-template-columns: … ; gap: … }`
 * with single-class specificity (0,1,0), printed AFTER the theme stylesheet.
 * Every rule of ours that has to beat them therefore repeats its own class
 * (`.astrum-x.astrum-x`, specificity (0,2,0)) or is scoped by a parent class.
 * Removing the doubled class silently breaks the layout — see plan §5.8.
 *
 * Contents:
 *   1.  Document basics
 *   2.  Accessibility (focus, skip link, touch targets, reduced motion)
 *   3.  Button variation states
 *   4.  Header
 *   5.  Navigation menus
 *   6.  Hero
 *   7.  Trust strip
 *   8.  Numbered list
 *   9.  Products
 *   10. Technology + specifications
 *   11. Story + testimonials
 *   12. Process
 *   13. Footer
 *   14. Riders (astrum_rider)
 *   15. Products (astrum_product)
 *   16. Responsive overrides
 */

/* ==========================================================================
   1. Document basics
   ========================================================================== */

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

/* 1.1 Long-word safety net (WCAG 2.2 SC 1.4.10 Reflow).

   Core ships `word-break: break-word` on `.wp-block-post-title` and
   `.wp-block-quote` only. Everything the client types into ordinary content —
   a heading, a paragraph, a list item, a bare URL — is left at
   `overflow-wrap: normal`, so a single unbroken 60-character string makes the
   whole document wider than the viewport and the entire site gains a
   horizontal scrollbar. Measured before this rule: a 70-character heading on a
   page took the document to 1691px inside a 375px viewport.

   `break-word` rather than `anywhere`: it breaks a word only when the word
   cannot fit on a line of its own, and — unlike `anywhere` — it does not shrink
   the element's min-content contribution, so it cannot make ordinary prose in a
   grid or flex track wrap earlier than it does today. Normal Czech words are
   never touched; only strings that would otherwise leave the page break. */
h1,
h2,
h3,
h4,
h5,
h6,
p,
li,
dt,
dd,
figcaption,
caption,
address,
.wp-block-button__link,
.wp-block-navigation-item__content {
	overflow-wrap: break-word;
}

/* 1.1b Let the safety net above actually reach inside a cover block
   (WCAG 2.2 SC 1.4.12 Text Spacing).

   `overflow-wrap: break-word` only breaks a word when the word cannot fit on a
   line of its own — so it does nothing while the LINE is allowed to be wider
   than the screen. core/cover is `display: flex` and its inner container is a
   flex item left at the default `min-width: auto`, which refuses to shrink
   below its min-content width. The hero headline therefore sets the floor: the
   single word "kompromisů." at hero size measures 355px of min-content, so
   inside a 320px viewport the inner container stays 355px wide and the cover —
   which is `overflow: hidden` — simply cuts the last 51px off. There is no
   scrollbar to recover it, so the text is gone.

   Measured at 320px with the SC 1.4.12 test declarations applied (line-height
   1.5, letter-spacing 0.12em, word-spacing 0.16em, paragraph spacing 2em):
   before, the headline rendered as "kompromis" with "ů." clipped away and
   every line of the lead lost its right edge; after, the inner container
   shrinks to 288px, the word breaks over two lines and nothing is lost.
   Document width stays 320px in both cases — the loss was silent.

   `min-width: 0` is the standard release valve for a flex item that has to be
   allowed to shrink; it changes nothing at widths where the content already
   fits. */
.wp-block-cover__inner-container {
	min-width: 0;
}

/* 1.2 Meta-bound paragraphs with nothing bound.

   Four templates print a paragraph whose text comes from a `core/post-meta`
   binding — `rider_sportline` on the rider hero and the rider card,
   `legal_summary` on the legal header, `product_tagline` on the wheelchair hero
   (that one keeps its own rule in section 15.5 next to the rest of the hero).
   All four fields are optional, and a binding that resolves to nothing still
   renders the paragraph. On the rider card the paragraph is the LAST child, so
   the block gap above it becomes dead space under the name; on the legal header
   it opens a gap under the title. Every one of these is genuinely `:empty` —
   unlike core/post-excerpt, which emits a space and is handled in PHP
   (inc/render-guards.php). */
.astrum-rider-hero .is-style-astrum-eyebrow:empty,
.astrum-rider-card__body p:empty,
.wp-block-post-title + .is-style-astrum-fine:empty {
	display: none;
}

/* 1.4 Full-width sections butt against each other.

   core/post-content is a constrained layout, so core hands every child after
   the first `margin-block-start: var(--wp--preset--spacing--40)`. Our page
   sections are full-width bands that carry their own vertical padding, so that
   24px is not spacing — it is a gap where the page background shows through
   between two coloured bands, read as a stray light stripe across the page.

   Only between two ADJACENT full-width blocks. An `alignfull` that follows
   normal-width prose still gets its gap, which is what the flow rule is for.

   Core's rule is `:root :where(.is-layout-constrained) > *`; `:where()` adds no
   specificity, so it weighs (0,1,0) and two classes are enough here. */
.entry-content > .alignfull + .alignfull {
	margin-block-start: 0;
}

/* 1.5 The site frame carries no flow spacing.

   Same defect as 1.4, one level up. `.wp-site-blocks` is the wrapper around the
   whole document, and core gives each of its children
   `margin-block-start: var(--wp--preset--spacing--40)` — 24px between the
   header, the main area and the footer.

   None of the three wants it. All three are full-bleed bands that bring their
   own padding, so the margin is not spacing but a stripe of page background
   between two coloured surfaces: on a product detail it showed as 24px of paper
   between the mist call-to-action and the ink footer.

   It also silently deepened the header offset. <main> reserves the fixed bar's
   height in `_site.css` §2; the extra margin pushed the content another 24px
   down, so every page without a hero sat lower than the design says.

   Core's selector is `:where(.wp-site-blocks) > *` — `:where()` contributes
   nothing and `*` contributes nothing, so it weighs (0,0,0) and a single class
   is enough to outrank it whatever order the sheets print in. `:first-child`
   and `:last-child` are already zeroed by core; this covers what is between
   them. */
.wp-site-blocks > * {
	margin-block-start: 0;
	margin-block-end: 0;
}

/* Numbers in spec/stat blocks must not jitter between values.
   theme.json drops fontVariantNumeric silently, so it has to live here. */
.astrum-spec,
.astrum-trust__item,
.astrum-numbered__item,
.astrum-step {
	font-variant-numeric: tabular-nums;
}

/* Grid containers are core/group blocks, so core classes them is-layout-flow
   and hands every child after the first a `margin-block-start: 1.5rem`. Inside
   a CSS grid that shows up as a ragged first column. The grid owns the spacing
   here, so the flow margins are cleared.

   The doubled class is deliberate: core's layout rule is (0,1,0) and prints
   after the theme stylesheet. Collapsing these to a single class re-breaks the
   grids. */
.astrum-trust.astrum-trust > *,
.astrum-numbered.astrum-numbered > *,
.astrum-products.astrum-products > *,
.astrum-tech.astrum-tech > *,
.astrum-tech__list.astrum-tech__list > *,
.astrum-specs.astrum-specs > *,
.astrum-story.astrum-story > *,
.astrum-testimonials.astrum-testimonials > *,
.astrum-process.astrum-process > *,
.astrum-footer__grid.astrum-footer__grid > *,
.astrum-numbered__item.astrum-numbered__item > * {
	margin-block-start: 0;
	margin-block-end: 0;
}

/* The eyebrow is oxblood by default, which the preview also used inside dark
   sections — there it measures 2.03:1 on carbon at 12px/700, well under AA.
   Silver is the kit's documented muted-on-dark token (7.15:1 on carbon).
   Chartreuse would pass too but is reserved for the single call to action per
   view, so the label goes neutral instead. Two classes beat the variation's
   `:root :where(...)`.

   Only SOLID dark sections here — the hero is a photographic cover and is
   handled by the rule below. */
.is-style-astrum-section-dark .is-style-astrum-eyebrow {
	color: var(--wp--preset--color--silver);
}

/* 1.3 Muted text on top of a photograph (WCAG 2.2 SC 1.4.3).

   `silver` and the lead's `ash` are the kit's muted-on-dark tokens and both
   clear AA against the solid surfaces they were picked for — silver measures
   7.15:1 on carbon. A cover block is not a solid surface. The hero and the
   product cards dim a photo to 70%, so 30% of every background pixel is
   whatever the photograph happens to be, and a bright patch (a hall window, a
   light floor) lifts the composited background to about rgb(85,86,88).

   Measured on the real composited result — photo drawn to a canvas with the
   overlay colour and its gradient applied on top, worst pixel inside each text
   box:
     hero eyebrow, silver     3.15:1 worst / 3.61:1 median   (needs 4.5)
     product card excerpt     2.89:1 worst / 6.11:1 median   (needs 4.5)
   `mist` is the next token up the ramp and clears that same worst-case pixel
   at 6.5:1 / 6.0:1, while staying visibly quieter than the paper-white
   headline next to it. It does not change anything on solid surfaces, where
   mist only ever improves on silver.

   NOT fixed here: the hero lead paragraph measures 4.48:1 against the same
   worst-case pixel. Its colour is an INLINE style written into the block
   markup (`style="color:var(--wp--custom--color--ash)"` in
   patterns/home-hero.php and in the saved page content), so no stylesheet rule
   can reach it without `!important`. It has to be changed at the source. */
.astrum-hero .is-style-astrum-eyebrow {
	color: var(--wp--preset--color--mist);
}

/* ==========================================================================
   2. Accessibility
   The static preview had zero focus styles and no skip link (plan §9.4).
   Everything below is solved once, globally, instead of per component.
   ========================================================================== */

/* 2.1 Visible focus ring on every interactive element.

   Two-tone on purpose. This theme puts white cards inside carbon sections, so
   a focused control can sit on either surface and a single colour cannot pass
   WCAG 1.4.11 (3:1 against what is next to it) on both: astrum-blue measured
   2.54:1 on white. The ring is therefore an ink core inside a chartreuse halo —
   on a light surface the ink band carries the contrast (19.17:1 on white,
   17.68:1 on paper), on a dark one the chartreuse band does (14.79:1 on
   carbon), and the two bands contrast 15.83:1 with each other, so the ring is
   never invisible whichever way round it lands. Measured worst case across the
   site is 12.65:1 (carbon-raised).

   Mechanically: the box-shadow paints the halo 6px out from the border box and
   the 3px outline is drawn on top of it at a 2px offset, which reads
   chartreuse / ink / chartreuse outwards from the control.

   No `border-radius` here on purpose. The previous rule forced
   `corner--sm` onto every focused control, which visibly squared off the pill
   buttons the moment they took focus; outline and box-shadow both follow the
   element's own radius anyway. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible,
.wp-block-button__link:focus-visible,
.wp-block-navigation-item__content:focus-visible {
	outline: 3px solid var(--wp--preset--color--ink);
	outline-offset: 2px;
	box-shadow: 0 0 0 6px var(--wp--preset--color--chartreuse);
}

/* Browsers that do not support :focus-visible get the same ring on plain
   :focus. Guarded, because handing the full ring to `:focus` unconditionally
   would put it on every mouse click in the browsers that do support it. */
@supports not selector(:focus-visible) {

	a:focus,
	button:focus,
	input:focus,
	select:focus,
	textarea:focus,
	summary:focus {
		outline: 3px solid var(--wp--preset--color--ink);
		outline-offset: 2px;
		box-shadow: 0 0 0 6px var(--wp--preset--color--chartreuse);
	}
}

/* A card's featured image is a link that fills a figure with `overflow: hidden`,
   so the ring above — drawn outwards from the border box — is clipped away
   entirely and the image link looks unfocused. Drawing it inwards puts it back
   on screen without loosening the clipping that gives the cards their corners. */
.wp-block-post-featured-image a:focus-visible {
	outline-offset: -5px;
	box-shadow: inset 0 0 0 8px var(--wp--preset--color--chartreuse);
}

/* 2.2 Skip link — WordPress injects it into every block theme; only the
   styling is ours. Hidden until it takes focus. */
.skip-link.screen-reader-text {
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 700;
	text-decoration: none;
}

/* Colours belong on `:focus`, not on the rule above.

   Core prints its own skip-link CSS inline in the document, and its
   `.skip-link.screen-reader-text:focus` rule (0,3,0) sets
   `background-color:#eee; color:#444`. That outranks a plain
   `.skip-link.screen-reader-text` (0,2,0) no matter which stylesheet loads
   last, so the chartreuse declared there never survived: the focused skip link
   was measured rendering in WordPress's default grey. Declaring the colours at
   the same specificity as core's rule, from a stylesheet that loads after it,
   is what actually puts the theme's colours on screen. Chartreuse on ink is
   15.90:1. */
.skip-link.screen-reader-text:focus {
	background: var(--wp--preset--color--chartreuse);
	color: var(--wp--preset--color--ink);
	clip: auto !important;
	clip-path: none;
	display: block;
	height: auto;
	inset-block-start: var(--wp--preset--spacing--20);
	inset-inline-start: var(--wp--preset--spacing--20);
	line-height: normal;
	padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--40);
	position: absolute;
	width: auto;
	z-index: 100000;
	border-radius: var(--wp--custom--corner--md);
}

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

/* 2.3 Minimum 44x44 px pointer targets (WCAG 2.2 SC 2.5.8 asks for 24; the
   UI kit commits to 44 and the audience makes that the right bar).
   Inline elements ignore min-height, hence the inline-flex. */
.wp-block-button__link,
.wp-block-navigation-item__content,
.wp-block-navigation__responsive-container-open,
.wp-block-navigation__responsive-container-close {
	box-sizing: border-box;
	min-height: 44px;
	display: inline-flex;
	align-items: center;
}

.wp-block-navigation__responsive-container-open,
.wp-block-navigation__responsive-container-close {
	min-width: 44px;
	justify-content: center;
}

/* 2.4 Reduced motion. The preview had smooth scrolling and hover transforms
   with no opt-out; for vestibular disorders that is an active trigger. */
@media (prefers-reduced-motion: reduce) {

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

/* ==========================================================================
   3. Button variation states
   `:hover` written at the ROOT of a block style variation is silently dropped
   by WordPress (plan §4.5), so the hover/active states of the button
   variations have to live here. Two classes beat the variation selector
   `:root :where(.wp-block-button.is-style-X .wp-block-button__link)` (0,1,0).
   ========================================================================== */

.wp-block-button .wp-block-button__link {
	justify-content: center;
	text-align: center;
	transition: background-color var(--wp--custom--duration--base) var(--wp--custom--easing--standard),
		color var(--wp--custom--duration--base) var(--wp--custom--easing--standard),
		border-color var(--wp--custom--duration--base) var(--wp--custom--easing--standard),
		transform var(--wp--custom--duration--fast) var(--wp--custom--easing--out);
}

.wp-block-button.is-style-astrum-primary .wp-block-button__link:hover {
	background-color: var(--wp--custom--color--chartreuse-600);
	color: var(--wp--preset--color--ink);
	transform: translateY(-1px);
}

.wp-block-button.is-style-astrum-primary .wp-block-button__link:active {
	background-color: var(--wp--custom--color--chartreuse-700);
	transform: none;
}

/* Both colours are named outright. `background-color: currentColor` was the
   clever version and it was wrong on light sections: there the label is already
   ink, so filling with currentColor painted a dark button under dark text and
   the label vanished. The fill has to be the OPPOSITE of the resting label, and
   only the section knows which that is. */
.wp-block-button.is-style-astrum-ghost .wp-block-button__link:hover {
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--custom--color--text-inverse);
	border-color: var(--wp--preset--color--ink);
}

.is-style-astrum-section-dark .wp-block-button.is-style-astrum-ghost .wp-block-button__link:hover {
	background-color: var(--wp--custom--color--text-inverse);
	color: var(--wp--preset--color--carbon);
	border-color: var(--wp--custom--color--text-inverse);
}

.wp-block-button.is-style-astrum-heat .wp-block-button__link:hover {
	background-color: var(--wp--custom--color--oxblood-600);
	transform: translateY(-1px);
}

.wp-block-button.is-style-astrum-heat .wp-block-button__link:active {
	transform: none;
}

/* ==========================================================================
   4. Header
   Only the paint lives here so the editor canvas shows the real bar; the
   `position: fixed` layer and the backdrop-filter are in _site.css.
   ========================================================================== */

.astrum-header {
	background: var(--wp--custom--header--bg);
	border-block-end: 1px solid var(--wp--custom--color--border-inv);
	color: var(--wp--custom--color--text-inverse);
}

.astrum-header__inner {
	min-height: var(--wp--custom--header--height);
	gap: var(--wp--preset--spacing--40);
}

/* The supplied logo is a stacked lockup (689x783), so constraining it by width
   alone makes the bar 170px tall and pushes the hero underneath. Height is the
   binding dimension: the bar keeps its token height and the mark scales to fit.
   A horizontal lockup would sit far better here — flagged for the client. */
.astrum-header .wp-block-site-logo img {
	width: auto;
	max-width: 150px;
	max-height: calc(var(--wp--custom--header--height) - var(--wp--preset--spacing--40));
	height: auto;
	object-fit: contain;
}

/* ==========================================================================
   5. Navigation menus
   All four menus are core/navigation blocks tagged with an astrum-menu--*
   class; astrum-core resolves the class to the matching wp_navigation post.
   ========================================================================== */

/* The 44px touch target in §2.3 sets `display: inline-flex` so the label can be
   centred inside it, but core's navigation stylesheet ships
   `.wp-block-navigation .wp-block-navigation-item__content { display: block }`
   at (0,2,0) and prints after the theme, so the theme's (0,1,0) rule loses.
   The box was the right height and the label sat against its top edge.

   Matching core's own specificity is enough — this rule comes later in the
   cascade. Kept here rather than by doubling the class in §2.3 so the reason is
   next to the block it belongs to. */
.wp-block-navigation .wp-block-navigation-item__content {
	display: flex;
	align-items: center;
}

.astrum-menu--header-main .wp-block-navigation-item__content {
	color: var(--wp--preset--color--mist);
	font-size: var(--wp--preset--font-size--small);
	/* The underline is declared here and only revealed on hover, so the label
	   never reflows: `text-decoration-line` is set from the start and just its
	   colour changes. Transitioning the line itself is not possible. */
	text-decoration: underline;
	text-decoration-color: transparent;
	text-decoration-thickness: 2px;
	text-underline-offset: 6px;
	transition:
		color var(--wp--custom--duration--base) var(--wp--custom--easing--standard),
		text-decoration-color var(--wp--custom--duration--base) var(--wp--custom--easing--standard);
}

/* Same underline the footer menus and card titles already use, so the hover
   reads as one idiom across the site. Also on :focus-visible — a keyboard user
   should get the affordance the mouse gets, not only the focus ring. */
.astrum-menu--header-main .wp-block-navigation-item__content:hover,
.astrum-menu--header-main .wp-block-navigation-item__content:focus-visible {
	color: var(--wp--custom--color--text-inverse);
	text-decoration-color: var(--wp--preset--color--chartreuse);
}

/* Footer menus: vertical lists, one link per row. */
.astrum-menu--footer-products .wp-block-navigation__container,
.astrum-menu--footer-company .wp-block-navigation__container,
.astrum-menu--footer-legal .wp-block-navigation__container {
	gap: 0;
}

.astrum-menu--footer-products .wp-block-navigation-item__content,
.astrum-menu--footer-company .wp-block-navigation-item__content,
.astrum-menu--footer-legal .wp-block-navigation-item__content {
	color: var(--wp--custom--color--text-inverse);
	font-size: var(--wp--preset--font-size--small);
	text-decoration: none;
}

.astrum-menu--footer-products .wp-block-navigation-item__content:hover,
.astrum-menu--footer-company .wp-block-navigation-item__content:hover,
.astrum-menu--footer-legal .wp-block-navigation-item__content:hover {
	text-decoration: underline;
	text-decoration-color: var(--wp--preset--color--chartreuse);
	text-decoration-thickness: 2px;
	text-underline-offset: 4px;
}

/* The mobile overlay is white-on-black by default, which reads as a bug on a
   dark brand; the pattern sets ink/mist, this only sizes the typography. */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content {
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--heading-s);
	font-weight: 700;
}

/* The switcher block (astrum/language-switcher) brings its own stylesheet from
   the plugin — `block.json` loads it only on views that render the block, and a
   plugin block styling itself keeps the two shipping together. Nothing about it
   belongs here. */

/* ==========================================================================
   6. Hero
   The section is a core/cover; the cover block paints its own scrim and
   min-height. Everything here is the layer on top of it.
   ========================================================================== */

.astrum-hero {
	position: relative;
	overflow: hidden;
	color: var(--wp--custom--color--text-inverse);
	/* Base surface under the cover overlay. Without it an image-less hero mixes
	   the overlay with the page background and lands on mid-grey, which drops
	   the hero note below AA. */
	background-color: var(--wp--preset--color--carbon);
	/* Clears the fixed header — the bar is translucent, the hero flows under it. */
	padding-block-start: calc(var(--wp--custom--header--height) + var(--wp--preset--spacing--60));
	padding-block-end: var(--wp--preset--spacing--60);
}

/* The hero is a core/cover, so the scrim is painted onto the block's own
   overlay span rather than an extra ::before — two stacked layers would darken
   twice. opacity:1 takes the gradient off dimRatio's control; the ramp itself
   ends at 0.55, not the preview's 0.18, where the hero note measured 2.4:1
   against the photo — a hard AA failure (plan §9.4). */
.astrum-hero .wp-block-cover__background {
	opacity: 1;
	background: linear-gradient(90deg,
		color-mix(in srgb, var(--wp--preset--color--ink) 96%, transparent) 0%,
		color-mix(in srgb, var(--wp--preset--color--ink) 86%, transparent) 45%,
		color-mix(in srgb, var(--wp--preset--color--ink) 55%, transparent) 100%);
}

/* Decorative ring echoing the logo swoosh. Purely ornamental, never announced. */
.astrum-hero::after {
	content: "";
	position: absolute;
	inset-inline-end: -21vw;
	inset-block-start: 11vh;
	width: 52vw;
	height: 52vw;
	border: 1px solid color-mix(in srgb, var(--wp--preset--color--chartreuse) 24%, transparent);
	border-radius: var(--wp--custom--corner--pill);
	pointer-events: none;
}

.astrum-hero__content {
	position: relative;
	z-index: 2;
	max-width: 850px;
}

.astrum-hero__content .wp-block-buttons {
	margin-block-start: var(--wp--preset--spacing--50);
}

/* Hero note: short line of reassurance under the CTAs, prefixed by a rule. */
.astrum-hero__note {
	position: relative;
	z-index: 2;
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--40);
	margin-block-start: var(--wp--preset--spacing--60);
	color: var(--wp--custom--color--ash);
	font-size: var(--wp--preset--font-size--small);
}

.astrum-hero__note::before {
	content: "";
	flex: 0 0 auto;
	width: var(--wp--preset--spacing--60);
	height: 2px;
	background: var(--wp--preset--color--chartreuse);
}

/* ==========================================================================
   7. Trust strip
   Four figures on a white band. The 1px gap over a tinted background is the
   hairline-grid trick: the "borders" are the container showing through.
   ========================================================================== */

.astrum-trust.astrum-trust {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 1px;
	background: var(--wp--custom--color--border);
	border-block-end: 1px solid var(--wp--custom--color--border);
}

.astrum-trust__item {
	background: var(--wp--preset--color--white);
	padding: var(--wp--preset--spacing--40);
}

.astrum-trust__item strong,
.astrum-trust__item b {
	display: block;
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--x-large);
	font-weight: 700;
	line-height: var(--wp--custom--leading--tight);
}

/* ==========================================================================
   8. Numbered list
   Editorial "01 / 02 / 03" rows. The numbers are real, editable paragraphs,
   not a CSS counter — the client has to be able to add a fourth one.
   ========================================================================== */

.astrum-numbered {
	margin-block-start: var(--wp--preset--spacing--50);
	border-block-start: 1px solid var(--wp--custom--color--border);
}

.astrum-numbered__item.astrum-numbered__item {
	display: grid;
	grid-template-columns: 80px 1fr;
	gap: var(--wp--preset--spacing--40);
	padding-block: var(--wp--preset--spacing--40);
	border-block-end: 1px solid var(--wp--custom--color--border);
}

.astrum-numbered__item > :first-child {
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--heading-s);
	font-weight: 700;
	line-height: var(--wp--custom--leading--tight);
	color: var(--wp--preset--color--oxblood);
	margin: 0;
}

/* ==========================================================================
   9. Products
   Three cards, the first one twice as wide. Each card is a core/cover with a
   featured image, so the photo is an ordinary block attribute the client can
   swap — that was the main editability blocker in the preview.
   ========================================================================== */

.astrum-products.astrum-products {
	display: grid;
	grid-template-columns: 1.3fr 0.85fr 0.85fr;
	gap: var(--wp--preset--spacing--40);
	margin-block-start: var(--wp--preset--spacing--50);
}

.astrum-product {
	position: relative;
	display: flex;
	align-items: flex-end;
	min-height: 540px;
	overflow: hidden;
	border-radius: var(--wp--custom--corner--xl);
	background-color: var(--wp--custom--color--carbon-raised);
	color: var(--wp--custom--color--text-inverse);
}

/* Same reasoning as the hero: the cards are covers, so the scrim goes onto the
   block's own overlay span instead of a second stacked layer. */
.astrum-product .wp-block-cover__background {
	opacity: 0.7;
	background: linear-gradient(to top,
		color-mix(in srgb, var(--wp--preset--color--ink) 94%, transparent) 0%,
		color-mix(in srgb, var(--wp--preset--color--ink) 6%, transparent) 68%);
}

.astrum-product > * {
	position: relative;
	z-index: 2;
}

/* mist, not silver: the card text sits on a dimmed photograph, where silver
   bottoms out at 2.89:1. See the note in section 1.3. */
.astrum-product p {
	color: var(--wp--preset--color--mist);
}

/* Text link with the accent underline from the preview. The :not() keeps
   buttons out of it — .wp-block-button__link is an <a> too. */
.astrum-product a:not(.wp-block-button__link) {
	font-weight: 700;
	text-decoration: underline;
	text-decoration-color: var(--wp--preset--color--chartreuse);
	text-decoration-thickness: 2px;
	text-underline-offset: 5px;
}

/* ==========================================================================
   10. Technology + specifications
   ========================================================================== */

.astrum-tech.astrum-tech {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--wp--preset--spacing--60);
	align-items: center;
}

.astrum-tech__list.astrum-tech__list {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--wp--preset--spacing--30);
	margin-block-start: var(--wp--preset--spacing--50);
}

.astrum-tech__list > * {
	background: var(--wp--preset--color--white);
	border: 1px solid var(--wp--custom--color--border);
	border-radius: var(--wp--custom--corner--lg);
	padding: var(--wp--preset--spacing--40);
}

.astrum-tech__list h3 {
	font-size: var(--wp--preset--font-size--large);
	margin-block: 0 var(--wp--preset--spacing--20);
}

.astrum-tech__list p {
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--iron);
	margin: 0;
}

/* Specification strip: left rule carries the product accent.
   --wp--custom--product-accent defaults to graphite; section 15 overrides it
   per product from the `has-accent-*` class.

   `auto-fit` rather than a fixed `repeat(4, 1fr)`: on a product the number of
   parameters is chosen by the client (see section 15), so a hard four-column
   track would leave two dead columns on a wheelchair with two parameters.
   Empty tracks collapse and the remaining ones absorb the space through 1fr,
   so the homepage technology strip with its four figures still renders as four
   equal columns exactly as before. */
.astrum-specs.astrum-specs {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
	gap: var(--wp--preset--spacing--30);
	margin-block-start: var(--wp--preset--spacing--50);
}

.astrum-spec {
	background: var(--wp--preset--color--white);
	border-radius: var(--wp--custom--corner--lg);
	border-inline-start: 4px solid var(--wp--custom--product-accent);
	padding: var(--wp--preset--spacing--40);
}

/* The one chartreuse element allowed in this view (kit: one accent per view). */
.astrum-spec:first-child {
	border-inline-start-color: var(--wp--preset--color--chartreuse);
}

.astrum-spec strong {
	display: block;
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--heading-l);
	font-weight: 700;
	line-height: var(--wp--custom--leading--tight);
}

.astrum-spec span {
	display: block;
	font-size: var(--wp--preset--font-size--x-small);
	color: var(--wp--preset--color--iron);
	text-transform: uppercase;
	letter-spacing: var(--wp--custom--tracking--wide);
}

/* ==========================================================================
   11. Story + testimonials
   ========================================================================== */

.astrum-story.astrum-story {
	display: grid;
	grid-template-columns: 1.05fr 0.95fr;
	gap: var(--wp--preset--spacing--60);
	align-items: center;
}

.astrum-story .wp-block-image img {
	border-radius: var(--wp--custom--corner--xl);
}

.astrum-testimonials.astrum-testimonials {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--preset--spacing--30);
	margin-block-start: var(--wp--preset--spacing--50);
}

.astrum-testimonials > * {
	overflow: hidden;
	border-radius: var(--wp--custom--corner--xl);
}

.astrum-testimonials img {
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
}

/* ==========================================================================
   12. Process
   Four steps sharing a top rule. On carbon the neutral rule shifts from
   graphite to steel, otherwise it is invisible (preview bug, plan §2 row 7).
   ========================================================================== */

.astrum-process.astrum-process {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 0;
	margin-block-start: var(--wp--preset--spacing--50);
}

.astrum-step {
	position: relative;
	padding: var(--wp--preset--spacing--40);
	border-block-start: 3px solid var(--wp--preset--color--steel);
}

.astrum-step:first-child {
	border-block-start-color: var(--wp--preset--color--chartreuse);
}

.astrum-step > :first-child {
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--heading-m);
	font-weight: 700;
	line-height: var(--wp--custom--leading--tight);
	color: var(--wp--preset--color--silver);
	margin: 0;
}

/* ==========================================================================
   13. Footer
   ========================================================================== */

.astrum-footer {
	background: var(--wp--preset--color--ink);
	color: var(--wp--custom--color--text-inverse);
}

.astrum-footer__grid.astrum-footer__grid {
	display: grid;
	grid-template-columns: 1.4fr repeat(3, 1fr);
	gap: var(--wp--preset--spacing--50);
}

.astrum-footer h2,
.astrum-footer h3,
.astrum-footer h4 {
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: var(--wp--custom--tracking--wider);
	color: var(--wp--preset--color--silver);
}

.astrum-footer a:not(.wp-block-button__link) {
	color: var(--wp--custom--color--text-inverse);
	text-decoration: none;
}

.astrum-footer a:not(.wp-block-button__link):hover {
	text-decoration: underline;
	text-decoration-color: var(--wp--preset--color--chartreuse);
	text-decoration-thickness: 2px;
	text-underline-offset: 4px;
}

.astrum-footer .wp-block-site-logo img {
	max-width: 180px;
	height: auto;
}

.astrum-footer__bottom {
	display: flex;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--40);
	margin-block-start: var(--wp--preset--spacing--50);
	padding-block-start: var(--wp--preset--spacing--40);
	border-block-start: 1px solid var(--wp--custom--color--border-inv);
	color: var(--wp--preset--color--silver);
	font-size: var(--wp--preset--font-size--x-small);
}

/* 13.1 Partner logos

   Both marks are supplied in colour with black lettering, which is invisible on
   ink. Recolouring someone else's logo is the one thing every brand manual
   forbids, so each one sits on a white plate instead — a clear light field is
   what those manuals ask for anyway. If a partner ever supplies an official
   negative version, drop the plate for that logo rather than inverting theirs.

   Each logo carries its own --astrum-partner-height (set in inc/partners.php,
   unitless so the narrow screens below can scale all of them at once), while the
   plates themselves stay one height — see the row rule. */
.astrum-footer__partners {
	margin-block-start: var(--wp--preset--spacing--50);
	padding-block-start: var(--wp--preset--spacing--40);
	border-block-start: 1px solid var(--wp--custom--color--border-inv);
}

.astrum-footer__partners h2 {
	margin: 0;
}

/* Doubled to beat core's flex-layout container rule, which centres the items:
   stretching them instead makes every plate as tall as the tallest one on its
   line, so the plates stay a tidy row while the logos inside keep their own
   heights. */
.astrum-footer__partner-logos.astrum-footer__partner-logos {
	display: flex;
	flex-wrap: wrap;
	align-items: stretch;
	gap: var(--wp--preset--spacing--30);
	margin-block-start: var(--wp--preset--spacing--30);
}

/* Doubled for specificity: core's .wp-block-image carries its own margin. */
.astrum-footer__partner.astrum-footer__partner {
	display: flex;
	align-items: center;
	justify-content: center;
	margin: 0;
	background: var(--wp--preset--color--white);
	border-radius: var(--wp--custom--corner--md);
	padding: var(--wp--preset--spacing--30);
}

.astrum-footer__partner img {
	display: block;
	height: calc(var(--astrum-partner-height, 36) * 1px);
	width: auto;
	max-width: 100%;
}

/* 13.2 Social profiles

   core/social-links in the logos-only style, which paints every service in its
   own brand colour — `:where(.wp-block-social-links.is-style-logos-only)
   .wp-social-link-facebook` and one such rule per network. On ink those read as
   three unrelated accents, and chartreuse is reserved for the single call to
   action per view, so the row goes monochrome like the rest of the footer.

   The colour has to be set on the LIST ITEM, because that is the element core
   colours; the anchor and its SVG below it are pinned to currentColor. */
.astrum-footer__social.astrum-footer__social {
	gap: var(--wp--preset--spacing--30);
}

.astrum-footer__social .wp-social-link {
	color: var(--wp--preset--color--silver);
	transition: color var(--wp--custom--duration--base) var(--wp--custom--easing--out);
}

.astrum-footer__social .wp-social-link:hover,
.astrum-footer__social .wp-social-link:focus-within {
	color: var(--wp--custom--color--text-inverse);
}

/* The footer underlines links on hover, which under a bare icon reads as a
   stray dash. Core says text-decoration: none at exactly the same specificity,
   so this only takes the outcome away from source order. */
.astrum-footer .astrum-footer__social a:hover {
	text-decoration: none;
}

/* ==========================================================================
   14. Riders (astrum_rider)

   One card, three views: the astrum/riders-featured section on the homepage,
   the rider archive and the rider detail. Everything below is generated by
   core blocks (post-template, post-featured-image, post-excerpt, post-title),
   so only the parts core cannot express live here.

   The photo is a portrait 3/4 crop rather than the landscape 4/3 of
   .astrum-testimonials: the supplied rider photos are 1365x2048, and the
   landscape crop cut the riders off around the chest.
   ========================================================================== */

/* core/post-template is a <ul>; with the default (flow) layout core hands every
   item after the first a block-gap margin, which inside a grid shows up as a
   ragged first column. Same doubled-class reasoning as section 1: core's layout
   rule is (0,1,0) and prints after the theme stylesheet. */
.astrum-riders.astrum-riders {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--preset--spacing--30);
}

.astrum-riders.astrum-riders > * {
	margin-block-start: 0;
	margin-block-end: 0;
}

/* Grid items stretch, so the card has to fill its row or the white surfaces of
   one row end at three different heights. */
.astrum-rider-card.astrum-rider-card {
	display: flex;
	flex-direction: column;
	height: 100%;
	overflow: hidden;
	border-radius: var(--wp--custom--corner--xl);
}

/* `min-height: 0` is load-bearing. As a flex item the figure keeps the default
   `min-height: auto`, whose automatic minimum size is the intrinsic height of
   the photo — for the 1365x2048 rider portraits that is taller than the 3/4 box
   the block asks for, so the crop was silently ignored and the photo pushed the
   card body out of alignment with its neighbours. */
.astrum-rider-card .wp-block-post-featured-image {
	margin-block: 0;
	min-height: 0;
	overflow: hidden;
}

.astrum-rider-card .wp-block-post-featured-image img {
	display: block;
	width: 100%;
}

/* Lets the body absorb the leftover height so cards of unequal quote length
   still share one bottom edge. */
.astrum-rider-card__body.astrum-rider-card__body {
	flex: 1 1 auto;
}

.astrum-rider-card__quote .wp-block-post-excerpt__excerpt {
	margin: 0;
	color: var(--wp--preset--color--iron);
}

/* The global link element style underlines every link; on the card the name is
   the link, and a permanent underline competes with the name itself. */
.astrum-rider-card__name a {
	text-decoration: none;
}

.astrum-rider-card__name a:hover {
	text-decoration: underline;
	text-decoration-color: var(--wp--preset--color--chartreuse);
	text-decoration-thickness: 2px;
	text-underline-offset: 4px;
}

/* 14.1 Detail hero.

   The dark band runs UNDER the translucent bar, like the homepage hero: the
   template cancels the header offset of _site.css §2 with an inline
   `padding-top: 0` on <main> and the section clears the bar with its own
   spacing|80 top padding. Leaving the offset in place instead would expose a
   paper-coloured strip between the bar and the dark band — core gives <main>
   a block-gap margin as the second child of .wp-site-blocks, so the reserved
   height and the bar height no longer line up. */

.astrum-rider-hero {
	position: relative;
	overflow: hidden;
}

/* Decorative ring echoing the logo swoosh, same device as the homepage hero.
   Purely ornamental, never announced. */
.astrum-rider-hero::after {
	content: "";
	position: absolute;
	inset-inline-end: -18vw;
	inset-block-start: -14vw;
	width: 46vw;
	height: 46vw;
	border: 1px solid color-mix(in srgb, var(--wp--preset--color--chartreuse) 20%, transparent);
	border-radius: var(--wp--custom--corner--pill);
	pointer-events: none;
}

.astrum-rider-hero > * {
	position: relative;
	z-index: 2;
}

.astrum-rider-hero .wp-block-post-featured-image img {
	display: block;
	width: 100%;
}

/* The `astrum-display-quote` variation is registered for core/quote only, and
   the rider quote lives in the excerpt (core/post-excerpt), so the variation's
   token set is repeated here rather than duplicated as a second variation. */
.astrum-rider-hero__quote .wp-block-post-excerpt__excerpt {
	margin: 0;
	max-width: var(--wp--custom--measure);
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--heading-l);
	font-weight: 700;
	line-height: var(--wp--custom--leading--snug);
	letter-spacing: var(--wp--custom--tracking--tight);
}

/* Accent rule above the quote, mirroring .astrum-hero__note::before. */
.astrum-rider-hero__quote .wp-block-post-excerpt__excerpt::before {
	content: "";
	display: block;
	width: var(--wp--preset--spacing--60);
	height: 2px;
	margin-block-end: var(--wp--preset--spacing--40);
	background: var(--wp--preset--color--chartreuse);
}

/* A rider without a written profile would leave a band of nothing but the
   section's full padding between the hero and the CTA. core/post-content still
   renders its wrapper when the body is empty, so emptiness is detected by that
   wrapper having no children rather than by the section. */
.astrum-rider-profile:not(:has(.wp-block-post-content > *)) {
	display: none;
}

/* A card whose rider has no photo must not stretch to the height of its
   neighbours and leave a few hundred pixels of blank space under the text.

   The alignment has to be set on the GRID ITEM, and the grid item is the <li>
   core/post-template emits — not the card, which is a group nested one level
   inside it. Carrying `align-self` on the card did nothing: the <li> still
   stretched to the row and the card's own `height: 100%` filled it. Measured
   with a real photo-less rider, the card came out 875px tall against 130px of
   content — a 700px white hole in the middle of the archive. */
.astrum-riders.astrum-riders > *:not(:has(.wp-block-post-featured-image)) {
	align-self: start;
}

/* A rider with no photo also leaves the hero's 42% image column standing empty,
   which pushes the name and the quote into the right-hand half of a dark band
   with a void beside them. Dropping the columns block to block flow lets the
   empty column collapse and the text column run the full width; the inline
   `flex-basis` core prints stops applying with it, so no !important is needed.
   Same detection as the card above — core/post-featured-image renders nothing
   at all when there is no thumbnail. */
.astrum-rider-hero .wp-block-columns:not(:has(.wp-block-post-featured-image)) {
	display: block;
}

/* ==========================================================================
   15. Products (astrum_product)

   Three views again: the astrum/products-featured section on the homepage, the
   product archive and the product detail. Everything is core blocks
   (post-template, cover with useFeaturedImage, post-terms, post-title,
   post-excerpt, read-more) plus the free-form parameter rows the client edits
   inside the post content, so only what core cannot express lives here.

   ACCENT MECHANICS (plan §7). The accent is not a colour the client picks; it
   is a closed enum stored in the `product_accent` meta, which astrum-core turns
   into a `has-accent-*` class — on the <li> of the query loop, and on <body> on
   the detail (a block theme renders no post_class wrapper on a single). This
   file is the only place that maps the class to a colour, and it maps it onto
   --wp--custom--product-accent, so exactly three things can read it: the rule
   above the card, the availability tag and the left rule of a parameter row.
   Nothing else refers to the token, which is what keeps the accent from ever
   becoming a surface or a button colour.
   ========================================================================== */

/* 15.1 Accent token.

   Doubled class for the same reason as everywhere else in this file: the
   token's default is printed by global styles on `:root` (0,1,0), so a single
   class would be a coin flip on stylesheet order. `neutral` is spelled out
   rather than left to the default, so the class always fully describes the
   product — including when a card sits inside a section that set the token. */
.has-accent-neutral.has-accent-neutral {
	--wp--custom--product-accent: var(--wp--preset--color--graphite);
}

.has-accent-chartreuse.has-accent-chartreuse {
	--wp--custom--product-accent: var(--wp--preset--color--chartreuse);
}

.has-accent-oxblood.has-accent-oxblood {
	--wp--custom--product-accent: var(--wp--preset--color--oxblood);
}

.has-accent-blue.has-accent-blue {
	--wp--custom--product-accent: var(--wp--preset--color--astrum-blue);
}

/* A fourth parameter narrows every track, and the display face is wide enough
   that a word-length value ("LiFePO₄", "CAN-bus") must not split mid-word — the
   very edit the free-form parameters exist to make safe.

   `anywhere` rather than the `normal` this rule used to carry. `normal` keeps a
   word whole by making it the row's min-content width, and `1fr` tracks size to
   min-content, so ONE unbreakable value dragged the whole grid — and with it
   the document — past the viewport: a 58-character certification value took the
   product page to 2130px inside a 1440px viewport and to 1101px inside 375px,
   putting a horizontal scrollbar on every section of the page. The old comment
   promised such a string would "scroll its own row"; nothing implemented that.

   `anywhere` breaks a word only when it cannot fit on a line of its own, so
   "LiFePO4" and "CAN-bus" still never split (they need ~150px at heading-m and
   the narrowest track this grid produces is 150px), while a genuinely
   unbreakable string wraps inside its own row and the set keeps its shape.
   `min-width: 0` on the row backs it up for the label, which is not free to
   break the same way. */
.astrum-spec__value {
	overflow-wrap: anywhere;
	word-break: normal;
	hyphens: none;
}

.astrum-specs.astrum-specs > * {
	min-width: 0;
}

/* Fewer than three parameters: `auto-fit` collapses the unused tracks and the
   surviving rows stretch across the full column — a single parameter rendered
   as a 795px white band with one number in its left corner and 700px of
   nothing. Capping the row (not the track) leaves the four-up and twelve-up
   sets untouched and keeps the mobile single column full width, because the
   column there is narrower than the cap. */
.astrum-specs:has(.astrum-spec__value):not(:has(> :nth-child(3))) .astrum-spec {
	max-width: 24rem;
}

/* 15.2 Card grid.

   `.astrum-products` (homepage, exactly three cards) keeps the asymmetric
   1.3 / .85 / .85 track from section 9; the archive uses equal columns because
   it can hold any number of products.

   core/post-template is a <ul> and its <li> items are the grid items, so the
   card — a cover nested one level deeper — has to be told to fill its item, or
   a short excerpt leaves the photo ending above its neighbours. */
.astrum-products-grid.astrum-products-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--preset--spacing--40);
}

.astrum-products.astrum-products > .wp-block-post,
.astrum-products-grid.astrum-products-grid > .wp-block-post {
	display: flex;
	margin-block: 0;
}

.wp-block-post > .astrum-product-card.astrum-product-card {
	flex: 1 1 auto;
}

/* 15.3 Accent rule above the card.

   The one place the accent paints a full-width shape — 3 px of it. It sits on
   top of the cover's scrim (z-index 2 in section 9), hence 3. */
.astrum-product-card {
	position: relative;
}

.astrum-product-card::before {
	content: "";
	position: absolute;
	inset-block-start: 0;
	inset-inline: 0;
	z-index: 3;
	height: 3px;
	background: var(--wp--custom--product-accent);
}

/* The card title is the link; the global link element style underlines every
   link, and a permanent underline competes with the name. Same treatment as
   .astrum-rider-card__name.

   Doubled class: the card is also an `.astrum-product`, and section 9 gives
   `.astrum-product a:not(.wp-block-button__link)` the accent underline at
   (0,2,1). A single class here would lose and the name would stay underlined.
   Matching that specificity is enough, because section 15 comes later. */
.astrum-product-card__name.astrum-product-card__name a {
	text-decoration: none;
}

.astrum-product-card__name.astrum-product-card__name a:hover {
	text-decoration: underline;
	text-decoration-color: var(--wp--preset--color--chartreuse);
	text-decoration-thickness: 2px;
	text-underline-offset: 4px;
}

.astrum-product-card__text .wp-block-post-excerpt__excerpt {
	margin: 0;
}

/* core/post-excerpt always renders its wrapper; a product without a short
   description would otherwise contribute a block gap and push the card text
   off the baseline it shares with its neighbours. */
.astrum-product-card__text:not(:has(.wp-block-post-excerpt__excerpt)) {
	display: none;
}

/* 15.4 Availability tag.

   The second accent reader. Border only — never a fill: oxblood and graphite
   as a surface would need a light label, chartreuse a dark one, and the tag
   would have to know which. A 1 px outline is legible over the scrim in all
   four cases and needs no contrast bookkeeping.

   The pill is the term link itself, not the post-terms wrapper: core's
   constrained layout hands every flow child `margin-left/right: auto` with
   `!important`, so a `width: fit-content` wrapper would sit centred on the
   detail page and no amount of specificity would pull it back to the left.
   Styling the links also gives a product with two availability terms two pills
   instead of one box around both. */
.astrum-product-tag.astrum-product-tag {
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--20);
	margin-block: 0;
}

.astrum-product-tag.astrum-product-tag a {
	display: inline-flex;
	padding: var(--wp--preset--spacing--20) var(--wp--preset--spacing--30);
	border: 1px solid var(--wp--custom--product-accent);
	border-radius: var(--wp--custom--corner--pill);
	/* The pill sits over a photograph, so an accent outline alone is not a
	   boundary: oxblood measured 1.00:1 against the scrim. A solid ground makes
	   the shape and its label legible whatever the photo does, and leaves the
	   accent as an accent rather than the only thing holding the pill together. */
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--custom--color--text-inverse);
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 700;
	letter-spacing: var(--wp--custom--tracking--wide);
	line-height: var(--wp--custom--leading--normal);
	text-decoration: none;
	text-transform: uppercase;
}

.astrum-product-tag.astrum-product-tag a:hover {
	border-color: var(--wp--custom--color--text-inverse);
}

/* The separator only makes sense between inline terms; between pills it is a
   stray comma. */
.astrum-product-tag .wp-block-post-terms__separator {
	display: none;
}

/* A product with no availability term must not leave an empty pill behind. */
.astrum-product-tag:not(:has(a)) {
	display: none;
}

/* 15.5 Detail hero.

   Same construction as .astrum-rider-hero: the dark band runs under the
   translucent bar because the template cancels the header offset with an inline
   `padding-top: 0` on <main>. */
.astrum-product-hero {
	position: relative;
	overflow: hidden;
}

/* Decorative ring echoing the logo swoosh, as on the homepage and rider heroes.
   Purely ornamental, never announced. */
.astrum-product-hero::after {
	content: "";
	position: absolute;
	inset-inline-end: -20vw;
	inset-block-start: -16vw;
	width: 48vw;
	height: 48vw;
	border: 1px solid color-mix(in srgb, var(--wp--preset--color--chartreuse) 20%, transparent);
	border-radius: var(--wp--custom--corner--pill);
	pointer-events: none;
}

.astrum-product-hero > * {
	position: relative;
	z-index: 2;
}

/* `product_tagline` is optional and empty on most wheelchairs. A binding that
   resolves to an empty string still renders the paragraph, so without this the
   hero opens with a blank line the client cannot see or delete. */
.astrum-product-hero__tagline:empty {
	display: none;
}

.astrum-product-hero__lead .wp-block-post-excerpt__excerpt {
	margin: 0;
	max-width: var(--wp--custom--measure);
	color: var(--wp--preset--color--silver);
	font-size: var(--wp--preset--font-size--x-large);
	line-height: var(--wp--custom--leading--relaxed);
}

.astrum-product-hero .wp-block-post-featured-image img {
	display: block;
	width: 100%;
}

/* 15.6 Parameter rows.

   The client's requirement: the parameters differ from wheelchair to
   wheelchair, so they are not meta fields but ordinary blocks inside the post
   content — a row can be duplicated and deleted like any other block. The
   astrum/product-specs pattern and the astrum-core seeder both emit this exact
   markup; `.astrum-spec__value` is what tells a product row apart from the
   four fixed figures of the homepage technology strip (section 10), which uses
   the same wrapper class but plain <p> children.

   The rows are laid out as a column with the value first, whatever layout the
   block carries — the seeder wrote them as flex rows, the pattern writes them
   with the default layout, and both must read the same. Core's layout support
   prints `.wp-container-core-group-layout-N { display: flex }` at (0,1,0)
   after this file, hence the doubled class. */
.astrum-spec.astrum-spec:has(.astrum-spec__value) {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	justify-content: flex-start;
	gap: var(--wp--preset--spacing--20);
}

/* Section 10 hands the first figure of the strip the chartreuse rule. On a
   product the rule is the product's own accent instead — a chartreuse row on an
   oxblood wheelchair would be a second accent in one view. Equal specificity to
   `.astrum-spec:first-child`, so this wins on source order. */
.astrum-spec:has(.astrum-spec__value) {
	border-inline-start-color: var(--wp--custom--product-accent);
}

/* `order` rather than markup order: the label is the first paragraph in the
   editor, which is how the client reads and writes the row, but the value is
   what the eye should land on first. */
.astrum-spec__value {
	order: -1;
	margin: 0;
	font-family: var(--wp--preset--font-family--display);
	font-size: var(--wp--preset--font-size--heading-m);
	font-weight: 700;
	line-height: var(--wp--custom--leading--tight);
}

.astrum-spec__label {
	margin: 0;
	color: var(--wp--preset--color--iron);
	font-size: var(--wp--preset--font-size--x-small);
	letter-spacing: var(--wp--custom--tracking--wide);
	text-transform: uppercase;
}

/* The "one highlighted number per view" rule of the kit, as an opt-in block
   style variation (styles/astrum-spec-lead.json) instead of a positional
   `:first-child` — which row deserves it differs per wheelchair. The variation
   JSON sets the same 6 px border, so the two agree whichever stylesheet the
   browser applies last. */
.astrum-spec.is-style-astrum-spec-lead {
	border-inline-start-width: 6px;
}

.is-style-astrum-spec-lead .astrum-spec__value {
	font-size: var(--wp--preset--font-size--heading-l);
}

/* 15.7 Detail body.
   A wheelchair with no description would leave the section's full padding
   between the hero and the CTA. Same detection as .astrum-rider-profile. */
.astrum-product-body:not(:has(.wp-block-post-content > *)) {
	display: none;
}

/* 15.8 Reading measure in both detail bodies.

   The description used to sit in a 70% column with an eyebrow label beside it
   in a 30% column ("O vozíku" on a wheelchair, "O jezdci" on a rider). The
   label is gone, so core/post-content now runs the section's full 1200px — and
   Inter 16px across 1200px is about 200 characters a line, roughly three times
   the 65–75 the eye tracks without losing its place (WCAG 2.2 SC 1.4.8 asks for
   80 or fewer).

   Capped with `max-width` on the children rather than by handing post-content a
   constrained layout: core's constrained layout prints
   `margin-left: auto !important`, which would centre the text under a hero
   whose title starts at the container's left edge, and undoing that would mean
   answering !important with !important. 720px is theme.json's own contentSize,
   so the detail body reads at the same measure as the legal pages.

   The parameter grid is let out to the full width on purpose — it is a strip of
   figures, not prose, and it is the same four-up as the homepage technology
   strip. `auto-fit` then gives the four seeded parameters 288px cards instead of
   the 187px they had inside the 70% column, which is what a value needs to stay
   on one line: "2× 400 W" used to break after the multiplication sign. The
   longest of them, "LiFePO4, 2× 20 Ah", still takes two lines either way. */
.astrum-product-body .wp-block-post-content > *,
.astrum-rider-profile .wp-block-post-content > * {
	max-width: 720px;
}

.astrum-product-body .wp-block-post-content > .astrum-specs,
.astrum-rider-profile .wp-block-post-content > .astrum-specs {
	max-width: none;
}

/* ==========================================================================
   16. Responsive overrides
   Breakpoints taken verbatim from the approved preview. They cannot read
   var(--wp--custom--breakpoint--*) — @media does not resolve custom
   properties — so the numbers are duplicated here on purpose.
   ========================================================================== */

@media (max-width: 980px) {

	.astrum-products.astrum-products,
	.astrum-testimonials.astrum-testimonials {
		grid-template-columns: 1fr;
	}

	.astrum-product {
		min-height: 500px;
	}

	.astrum-tech.astrum-tech,
	.astrum-story.astrum-story {
		grid-template-columns: 1fr;
		gap: var(--wp--preset--spacing--50);
	}

	.astrum-specs.astrum-specs,
	.astrum-process.astrum-process,
	.astrum-trust.astrum-trust,
	.astrum-footer__grid.astrum-footer__grid,
	.astrum-riders.astrum-riders,
	.astrum-products-grid.astrum-products-grid {
		grid-template-columns: 1fr 1fr;
	}
}

@media (max-width: 620px) {

	.astrum-specs.astrum-specs,
	.astrum-process.astrum-process,
	.astrum-footer__grid.astrum-footer__grid,
	.astrum-tech__list.astrum-tech__list,
	.astrum-riders.astrum-riders,
	.astrum-products-grid.astrum-products-grid {
		grid-template-columns: 1fr;
	}

	.astrum-trust.astrum-trust {
		grid-template-columns: 1fr 1fr;
	}

	.astrum-trust__item {
		padding: var(--wp--preset--spacing--30);
	}

	.astrum-numbered__item.astrum-numbered__item {
		grid-template-columns: 1fr;
		gap: var(--wp--preset--spacing--20);
	}

	.astrum-footer__bottom {
		flex-direction: column;
	}

	.astrum-footer__partner img {
		height: calc(var(--astrum-partner-height, 36) * 0.8px);
	}

	.astrum-header .wp-block-site-logo img {
		max-width: 140px;
	}
}
