/* Prices, dates and identifiers must not jitter between renders. */
.ss-tabular {
	font-variant-numeric: tabular-nums;
}

/*
 * The consent plugin's cookie-settings reopen control renders a bare
 * `<button>` with no styling of its own (see
 * `Soglasie152\Frontend\Shortcode\CookieBannerTriggerShortcode`) — it relies
 * entirely on the host theme. Left alone, the browser's default button
 * chrome measures well under the 44x44 CSS px touch target guideline
 * (WCAG 2.5.8 AA); this brings it in line with the footer's link styling and
 * gives it a real hit area.
 *
 * The underline is always on, at rest and on hover — same as the adjacent
 * `<a>` links in this footer column. Those links get their color from
 * `elements.link.color.text: foreground`, which is the same value the
 * surrounding paragraph text uses, so the underline is the *only* thing that
 * marks them as links at all (WCAG 1.4.1 — color cannot be the sole
 * distinguishing feature). This control sits in the same column as those
 * links and shares their color, so it needs the same permanent underline for
 * the same reason; a hover-only underline would make it the one item in the
 * column indistinguishable from plain text until pointed at.
 */
.soglasie-152-cookie-banner-trigger {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	margin-top: var(--wp--preset--spacing--20);
	padding: 0;
	font: inherit;
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--foreground);
	background: none;
	border: none;
	cursor: pointer;
	text-align: left;
	text-decoration: underline;
}

/*
 * WordPress core's mobile navigation open/close controls render as a bare
 * 24x24 icon with no padding — under the 44x44 CSS px touch target guideline
 * (WCAG 2.5.8 AA). Pad the hit area without resizing the icon itself.
 */
.wp-block-navigation__responsive-container-open,
.wp-block-navigation__responsive-container-close {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 44px;
	min-height: 44px;
}

/*
 * Space under the consent banner is NOT reserved here.
 *
 * It used to be, with three hand-measured fallbacks per breakpoint. The plugin
 * now measures its own bar and reserves the flow itself — it publishes
 * `--s152-banner-height` and marks `<html>` with `s152-banner-reserve`, and its
 * own stylesheet carries the padding rule (soglasie-152-fz#120). The numbers
 * here were taken from the English copy and were within 2px of failing on the
 * Russian one at 375px, so keeping them would mean keeping a prop that lies.
 *
 * To override the plugin's reserve you need STRICTLY HIGHER specificity than
 * `html.s152-banner-reserve body`. Restating that same selector here loses:
 * the plugin injects its stylesheet into `<head>` at mount time, i.e. after
 * this file, so it wins every tie — measured on the stand, an equal-specificity
 * override still resolved to the plugin's 191px. `html.s152-banner-reserve
 * body[class] { … }` wins and needs no PHP, since WordPress always emits a
 * `class` attribute on `<body>`.
 *
 * Do not reintroduce a fallback estimate: the theme cannot measure a bar it
 * does not own.
 */

/*
 * There is deliberately NO rule here undoing a text colour that core painted
 * onto bordered blocks. There used to be, and it was a patch over a name:
 * this palette's hairline token was once slugged `border`, so WordPress's
 * per-slug text-colour utility came out as `.has-border-color { color: …
 * !important }` — the same class block supports put on every block drawing a
 * border colour — and repainted their text at 1.55:1.
 *
 * The patch (`.has-border-color:not(.has-text-color)`) only covered blocks
 * that drew a border and nothing else. A block setting a border AND a text
 * colour carries `has-text-color`, skipped the patch, and was then decided by
 * utility order, which core prints in palette order: `background`,
 * `foreground`, `muted` and `surface` all sit before `border` and all lost.
 * That is both text tokens this theme actually writes with, on the theme's
 * single most common markup — `has-muted-color has-text-color` plus a border.
 *
 * Renaming the slug to `hairline` removed the collision instead of fighting
 * it, so there is nothing left to override. Do not reintroduce a palette slug
 * that matches a block-supports class name (`border`, `text`, `link`); the
 * failure is silent and looks like a design choice.
 *
 * `hairline` is still the mirror of `--border` on marmyshev.ru — the VALUE is
 * untouched, only the spelling changed. What the tokens have to match is the
 * main site's roles and numbers, not its identifiers, and this palette already
 * renames one: `muted` here (`oklch(47%)`) is the main site's
 * `--muted-foreground`, not its `--muted` (`oklch(94.8%)`).
 *
 * This paragraph lived in `theme.json` as a `description` key on the palette
 * entry and does not belong there: the official theme.json schema declares
 * `additionalProperties: false` for a palette item, and core gives
 * `settings.color.palette` a `null` schema, so nothing strips the key — it
 * survives into the block editor settings on every admin load. A file that
 * declares `$schema` has to satisfy it.
 */

/*
 * No `font-size` here, deliberately. This rule used to declare 0.6875rem and it
 * was dead everywhere it mattered: every eyebrow and every label but one is
 * written as a block with `fontSize: "small"`, and WordPress emits its preset
 * utilities as `.has-small-font-size{font-size:…!important}` — an `!important`
 * a bare class cannot outrank. So the theme's labels rendered at the preset's
 * 0.8125rem while this file claimed 0.6875rem, and the single label written as
 * raw HTML with no preset class (`patterns/pricing-page.php`, «Цена за сайт»)
 * was the only one that obeyed — at a size 2px off from every other label on
 * the site. The declaration is gone and that one label now carries the same
 * preset class as the rest; the size has one source, the preset (#146).
 */
.ss-eyebrow,
.ss-label {
	margin-block-end: 0.75rem;
	letter-spacing: 0.12em;
	text-transform: uppercase;
}

/*
 * A list of short statements set as separated rows — the treatment the hero
 * panel and the product page's "right after payment" panel share. It is one
 * class rather than two because they are one thing: before #146 the theme had
 * three lists inside cards with three different looks (mint dots here, browser
 * bullets there, no marker in the third), which is what "everything should come
 * from components" was already supposed to prevent.
 *
 * There is no marker. The hairline between rows is the theme's own separator
 * idiom and does the grouping work a bullet would, and the `<ul>`/`<li>`
 * semantics carry the meaning for anyone not looking at it. The markers that
 * were here were 8px circles filled with `accent`, which this theme spends on
 * exactly one meaning — "this is the selected/recommended one", always paired
 * with a second cue (the recommended plan's badge, the current tier column's
 * emphasised header). A decorative dot on every row of an ordinary checklist
 * spends that meaning on nothing, and at oklch 86% against a 99.4% surface it
 * measured about 1.1:1 anyway — a mark you cannot quite see, carrying a
 * significance it does not have.
 *
 * The top margin is stated here rather than left to the label's
 * `margin-block-end`: inside `.ss-card` the label is a `<p>`, so `.ss-card p`
 * (0,1,1) zeroes its margins, and core's own block gap is `:where()`-wrapped at
 * 0,0,0. Without this the caption and its first row would touch.
 */
.ss-rows {
	margin-block: 0.75rem 0;
	padding-inline-start: 0;
	list-style: none;
}

.ss-rows li {
	padding-block: 0.5rem;
	border-block-start: 1px solid var(--wp--preset--color--hairline);
	font-size: 0.875rem;
	line-height: 1.45;
}

.ss-rows li:first-child {
	border-block-start: 0;
}

/*
 * `<caption class="screen-reader-text">` in the comparison and journal
 * tables (`patterns/vs-free.php`, `patterns/consent-journal.php`) does NOT
 * need a rule here. Checked on the running shop stand (view-source of `/`):
 * WordPress core's block-library stylesheet already ships `.screen-reader-text`
 * (and `.screen-reader-text:focus`) unconditionally once a theme declares
 * `add_theme_support( 'wp-block-styles' )` — which `inc/Setup.php` already
 * does — so the caption renders hidden with no theme-level CSS at all. Do
 * not add a duplicate rule here: this is WordPress core's own block-library
 * stylesheet, a separate enqueue from the parent theme's `style.css` that
 * `Setup::drop_parent_stylesheet()` dequeues (README «Наблюдение 1») — core's
 * block styles ship regardless of what theme, or which of its parent, is
 * active.
 */

/* Wide tables scroll inside their own box; the page never scrolls sideways. */
.ss-compare-wrap {
	overflow-x: auto;
	margin-block-start: 1.5rem;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 0.5rem;
	background: var(--wp--preset--color--surface);
}

.ss-compare {
	inline-size: 100%;
	border-collapse: collapse;
	font-size: 0.875rem;
}

.ss-compare th,
.ss-compare td {
	padding: 0.7rem 0.9rem;
	text-align: start;
	border-block-start: 1px solid var(--wp--preset--color--hairline);

	/*
	 * Words break at word boundaries, not inside words. Something above this
	 * file — core's block library, not the theme — computes
	 * `overflow-wrap: break-word` here, and a table squeezed into 341px on a
	 * 375px screen then obeys it literally: the stand rendered «Возможност/ь»,
	 * «Perso/nal» and «Busin/ess», one header per line broken mid-word.
	 *
	 * `.ss-compare-wrap` already carries `overflow-x: auto` precisely so the
	 * table can be wider than the screen and scroll inside its own box, which
	 * is what the design spec asks for («Таблицы сравнения прокручиваются
	 * горизонтально внутри своего контейнера»). Breaking words was the browser
	 * working around a constraint that does not exist.
	 */
	overflow-wrap: normal;
	word-break: normal;
}

.ss-compare thead th {
	border-block-start: 0;
	font-weight: 600;
	color: var(--wp--preset--color--muted);
}

.ss-compare tbody th {
	font-weight: 400;
}

/* Meaning is carried by the cell text; the colour only reinforces it. */
.ss-compare td[data-has="yes"] {
	color: var(--wp--preset--color--accent-foreground);
	font-weight: 600;
}

.ss-compare td[data-has="no"] {
	color: var(--wp--preset--color--muted);
}

/*
 * The tier the visitor is looking at, marked down the whole column so the row
 * they care about is findable without counting headers across. Only used on
 * the product card — the landing page's comparison tables have no "current".
 *
 * The tint is mixed from the accent token rather than set to `background`,
 * which is what it was first written as: `background` (oklch 98.4%) against
 * the wrapper's `surface` (99.4%) measured 1.02:1 — a difference no one can
 * see, under a comment claiming the column was findable. Either the highlight
 * is visible or the comment is a lie; this makes it visible.
 *
 * `color-mix` keeps the value derived from a token, so it still passes the
 * theme's ban on raw colour literals and still moves if the accent moves.
 * `data-current` is set server-side by `\SoglasieShop\Woo::renderTierTable()`;
 * the tint alone would be a colour-only cue, so the column header is also
 * emphasised and the pill above the table already says which tier this is.
 */
.ss-compare [data-current] {
	background: color-mix(in oklch, var(--wp--preset--color--accent) 22%, var(--wp--preset--color--surface));

	/* Two accent rules down the column edges. A tint between two near-white
	   surfaces measures ~1.1:1 however it is mixed — that is what a tint IS —
	   so the column also gets a literal drawn boundary, which does not depend
	   on telling two pale hues apart. */
	box-shadow:
		inset 1px 0 0 0 var(--wp--preset--color--accent),
		inset -1px 0 0 0 var(--wp--preset--color--accent);
}

.ss-compare thead th[data-current] {
	color: var(--wp--preset--color--foreground);
	font-weight: 680;
	box-shadow:
		inset 1px 0 0 0 var(--wp--preset--color--accent),
		inset -1px 0 0 0 var(--wp--preset--color--accent),
		inset 0 -2px 0 0 var(--wp--preset--color--accent);
}

/*
 * `.ss-grid` / `.ss-card` are shared, not section-specific: `.ss-features`
 * (Task 5) uses a 3-column card grid, `.ss-grid--numbered` layers a numbered
 * sequence onto the same grid for "How it works"'s `<ol>`, and Task 6's
 * pricing cards and Task 7's product card are documented
 * (`docs/plans/2026-08-02-storefront-design-PLAN.md`) to reuse both classes
 * unchanged — keep additions here generic, never scoped to one section only.
 */
.ss-grid {
	display: grid;
	gap: 1rem;
	margin-block-start: 1.5rem;
	padding-inline-start: 0;
	grid-template-columns: 1fr;
	list-style: none;
}

@media (width >= 48rem) {

	.ss-grid {
		grid-template-columns: repeat(3, 1fr);
	}
}

/* Named for what it does (renders a numbered sequence), not for the column
   count `.ss-grid` already controls responsively — a "3" in the name read as
   "three columns" when it actually meant "numbered". */
.ss-grid--numbered {
	counter-reset: ss-step;
}

.ss-grid--numbered > li.ss-card {
	counter-increment: ss-step;
}

/* Turns the three "How it works" cards into an explicit numbered sequence. */
.ss-grid--numbered > li.ss-card h3::before {
	content: counter(ss-step) ". ";
}

.ss-card {
	padding: 1rem 1.1rem;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 0.5rem;
	background: var(--wp--preset--color--surface);
}

.ss-card h3 {
	margin: 0 0 0.4rem;
	font-size: 0.9375rem;
	font-weight: 620;
}

.ss-card p {
	margin: 0;
	color: var(--wp--preset--color--muted);
	font-size: 0.875rem;
	line-height: 1.5;
}

.ss-faq__list {
	display: grid;
	gap: 0.6rem;
	margin-block-start: 1.5rem;
}

/*
 * `.ss-card`'s own padding (1rem 1.1rem) wraps the whole `<details>` box, but
 * the element that actually receives click/keyboard activation for a native
 * disclosure widget is `<summary>` alone — the parent's padding above and
 * below it is not part of that hit area. Moving the padding onto `<summary>`
 * itself keeps the same visual card look while making the real interactive
 * target at least 44x44 CSS px, the same touch-target convention already
 * used for `.soglasie-152-cookie-banner-trigger` and the core nav toggles
 * above (WCAG 2.5.8 AA floor is 24x24; this theme holds itself to 44x44).
 * No animation is added on open/close, so there is nothing here that needs a
 * `prefers-reduced-motion` guard — the browser's native disclosure transition
 * already respects it.
 */
.ss-faq details.ss-card {
	padding: 0;
}

/*
 * `display: flex` (needed to vertically centre the row and to make room for
 * the +/− indicator below) takes `<summary>` out of `display: list-item`,
 * which is also where its native disclosure triangle came from — without
 * this rule the six FAQ cards read as inert text, not something a sighted
 * visitor would think to click. `list-style: none` plus the `::after`
 * indicator below replace it with an explicit, deliberate affordance instead
 * of an accidental one; the open/closed *state* itself is still carried by
 * the native `<details>`/`<summary>` semantics, not by this glyph — a
 * screen reader announces "expanded"/"collapsed" regardless of what CSS
 * does here.
 */
.ss-faq summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
	min-height: 44px;
	padding: 1rem 1.1rem;
	cursor: pointer;
	font-weight: 600;
	font-size: 0.9375rem;
	list-style: none;
}

/* Belt-and-braces: some WebKit builds render this pseudo-element's marker
   even after `list-style: none` on the `<summary>` itself. */
.ss-faq summary::-webkit-details-marker {
	display: none;
}

.ss-faq summary::after {
	content: "+";
	flex: none;
	color: var(--wp--preset--color--muted);
	font-weight: 400;
}

.ss-faq details[open] summary::after {
	content: "−";
}

/* Scoped to the answer paragraph specifically (inside a `<details>`), not
   every `<p>` in the section — a future intro paragraph directly in
   `.ss-faq` (the pattern every other prose section on this page uses)
   must not pick up padding meant for the accordion body. */
.ss-faq details p {
	margin: 0.6rem 0 0;
	padding: 0 1.1rem 1rem;
}

/*
 * A pricing grid is read across, not down: the eye compares the three prices
 * to each other and then the three buttons to each other. That only works if
 * they sit on the same line, which they did not — the "Рекомендуем" badge is
 * one card's extra block of flow, so Business's price, its audience line and
 * its buy button all rode ~30px lower than its neighbours', and the scan had
 * to start over on every row.
 *
 * Two rules fix it without touching any markup: the badge leaves the flow
 * entirely (positioned into the card's own corner, which is also where a
 * "recommended" mark is conventionally looked for), and the card becomes a
 * flex column so `margin-block-end: auto` on the site count below can absorb
 * whatever slack a longer audience line leaves, pinning the calls to action to
 * a common baseline.
 */
.ss-plan {
	position: relative;
	display: flex;
	flex-direction: column;
}

.ss-plan--recommended {
	border-color: var(--wp--preset--color--accent);
	border-width: 2px;
}

/*
 * Room for the badge in the corner — and only on the card that has one.
 *
 * The reserve is measured from the BADGE, not from the tier name: "Рекомендуем"
 * at 0.6875rem uppercase with 0.05em tracking and 0.5rem of padding each side
 * renders about 98px wide, and it is inset 1.1rem from the card edge, i.e.
 * flush with the heading's own right edge. 7.5rem (120px) leaves ~22px of air
 * — enough for the label to grow a couple of characters before it touches the
 * tier name. Written as `.ss-plan h3` this reserve was also taken from the two
 * cards with no badge at all, where it is 120px of nothing.
 */
.ss-plan--recommended h3 {
	padding-inline-end: 7.5rem;
}

/*
 * `.ss-plan__badge` and `.ss-plan__price` below are both plain `<p>`
 * elements inside `.ss-card`, so they compete with `.ss-card p` (0,1,1:
 * one class plus the `p` type) over `color`, `font-size` and `margin`. A
 * bare `.ss-plan__badge` / `.ss-plan__price` class selector is only 0,1,0
 * and loses that fight regardless of source order — checked live on the
 * stand: the badge rendered in `.ss-card p`'s muted grey instead of
 * `accent-foreground`, at 14px instead of 11px, and the price rendered
 * muted and un-enlarged. Adding the `p` type selector brings both back to
 * 0,1,1, and their later position in this file then decides the tie in
 * their favour.
 */
p.ss-plan__badge {
	position: absolute;
	inset-block-start: 0.9rem;
	inset-inline-end: 1.1rem;
	display: inline-block;
	margin: 0;
	padding: 0.15rem 0.5rem;
	border-radius: 0.25rem;
	background: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--accent-foreground);
	font-size: 0.6875rem;
	font-weight: 650;
	letter-spacing: 0.05em;
	text-transform: uppercase;
}

p.ss-plan__price {
	margin: 0.3rem 0 0.2rem;
	color: var(--wp--preset--color--foreground);
	font-size: 1.5rem;
	font-weight: 660;
	letter-spacing: -0.02em;
}

.ss-plan__price span {
	font-size: 0.8125rem;
	font-weight: 400;
	color: var(--wp--preset--color--muted);
}

.ss-plan__scenario {
	font-weight: 600;
}

/*
 * Stands where the button would be when a tier has no published product. Same
 * specificity fight as `p.ss-plan__badge` above — `.ss-card p` is 0,1,1, so the
 * `p` type selector is needed for this to win at all.
 */
p.ss-plan__unavailable {
	margin: 0;
	color: var(--wp--preset--color--muted);
	font-size: 0.8125rem;
	font-style: italic;
}

/*
 * The catalog link in each pricing card carries the bare `.wp-element-button`
 * class rather than `wp:buttons`/`.wp-block-button__link` (only the block
 * variant gets core's `display: inline-block`, which is what turns padding
 * into an actual box). Left as a plain inline element, its vertical padding
 * doesn't expand its hit box — measured live at ~40px tall, under the 44x44
 * CSS px touch target guideline (WCAG 2.5.8 AA) already used elsewhere in
 * this file (`.soglasie-152-cookie-banner-trigger`, the nav toggles).
 */
.ss-plan a.wp-element-button {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
}

/* --- Pricing page (#87) --------------------------------------------------- */

/*
 * The site count is the one thing that actually tells the three tiers apart
 * (`TierBaselineLimits::$max_activations`, 1 / 3 / unlimited — every other
 * limit key is identical across the paid tiers), so it gets weight on the card
 * rather than sitting in the same muted grey as the rest of `.ss-card p`.
 * The `p` type selector is the same specificity fight `p.ss-plan__badge`
 * documents above: `.ss-card p` is 0,1,1 and a bare class would lose it.
 */
p.ss-plan__sites {

	/*
	 * `margin-block-end: auto` is what lines the three buy buttons up: it eats
	 * whatever vertical slack a shorter audience line leaves above it, so the
	 * call to action lands at the foot of every card. The gap above the button
	 * is padding rather than margin for that reason — a margin here would be
	 * the thing being stretched.
	 */
	margin: 0 0 auto;
	padding-block-end: 0.75rem;
	color: var(--wp--preset--color--foreground);
	font-size: 0.9375rem;
}

/* The secondary link under each buy button; the button is the primary action
   and this must not compete with it. */
p.ss-plan__more {
	margin: 0.5rem 0 0;
	font-size: 0.8125rem;
}

/*
 * The two blocks between the comparison table and the FAQ: the per-site
 * figures and the note about what an activation binds to. Both are aside
 * material — read after the table, not instead of it.
 */
.ss-note {
	margin-block-start: 1rem;
	color: var(--wp--preset--color--muted);
	font-size: 0.8125rem;
	line-height: 1.5;
}

.ss-per-site {
	display: grid;
	gap: 0.35rem;
	margin: 0 0 1.5rem;
	padding-inline-start: 0;
	list-style: none;
	font-size: 0.875rem;
}

@media (width >= 48rem) {

	.ss-per-site {
		grid-template-columns: repeat(3, 1fr);
		gap: 1rem;
	}
}

/*
 * Free, deliberately NOT a fourth card in `.ss-grid`.
 *
 * A four-column grid dilutes a three-tier comparison and reads as an
 * invitation to treat the free tier as a fourth product of the same kind
 * (issue #87). A full-width band under the table carries the same facts
 * without competing for the same glance — which is why this is a horizontal
 * strip with an inline list rather than anything card-shaped, and why it uses
 * the page background instead of `surface`.
 */
.ss-free-note {
	margin-block: 1.5rem;
	padding: 1rem 1.1rem;
	border-block: 1px solid var(--wp--preset--color--hairline);
}

.ss-free-note__lead {
	margin: 0 0 0.5rem;
	font-weight: 600;
	font-size: 0.9375rem;
}

.ss-free-note__list {
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem 1rem;
	margin: 0;
	padding-inline-start: 0;
	list-style: none;
	color: var(--wp--preset--color--muted);
	font-size: 0.875rem;
}

/* --- Product card (Task 7) ------------------------------------------------ */

.ss-tiers {
	display: flex;
	gap: 0.35rem;
	flex-wrap: wrap;
	margin-block-end: 1rem;
}

/*
 * `display: inline-flex` + the 44px floors are not in the original design
 * sketch, which had padding alone: measured on the stand that produced a
 * 30x84px pill, comfortably under the 44x44 CSS px touch target this theme
 * holds itself to everywhere else. `min-inline-size` matters as much as the
 * block one — "Personal" is wide enough on its own, but a future short tier
 * name would not be.
 */
.ss-tiers a,
.ss-tiers span[aria-current="page"] {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-inline-size: 44px;
	min-block-size: 44px;
	padding: 0.3rem 0.9rem;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 1.5rem;
	font-size: 0.8125rem;
	text-decoration: none;
	color: var(--wp--preset--color--muted);
}

/*
 * The current tier is marked three ways, only one of which is colour: the pill
 * is filled rather than outlined (shape), its label is bold (weight), and it
 * carries `aria-current="page"` and is not a link at all (semantics). WCAG
 * 1.4.1 is satisfied without the colour — which is the point, since the pills
 * sit in a row where colour is the first thing to go on a bad monitor.
 */
.ss-tiers span[aria-current="page"] {
	background: var(--wp--preset--color--primary);
	border-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--background);
	font-weight: 640;
}

/* Block themes inherit the UA focus ring, which is invisible against the
   filled pill. State it explicitly instead of hoping. */
.ss-tiers a:focus-visible {
	outline: 2px solid var(--wp--preset--color--foreground);
	outline-offset: 2px;
}

/*
 * The buy panel rides along on desktop; on mobile it stays in the flow so it
 * never competes with the fixed consent banner for the bottom edge. The design
 * spec rules out a sticky bottom purchase bar outright — two fixed bars would
 * take a third of a 375px screen.
 *
 * `align-self: flex-start` is load-bearing: `wp:columns` is a flex container
 * whose items stretch to the row height by default, and a stretched item is
 * exactly as tall as its scroll container, so `position: sticky` has nothing
 * to travel through and silently does nothing.
 */
@media (width >= 64rem) {

	.ss-buybox {
		position: sticky;
		inset-block-start: 1.5rem;
		align-self: flex-start;
	}
}

/*
 * Below 782px core stacks `wp:columns` in source order, which would bury the
 * price and the buy button under the whole description-plus-comparison-table
 * column. `order: -1` lifts the panel to the top of the stack, right under the
 * H1 — which is why the title sits ABOVE the columns in the template rather
 * than inside the left one, as it does in WooCommerce's own layout: otherwise
 * the price would print before the name of the thing being priced.
 *
 * 782px is not a number of ours — it is core's own stacking breakpoint
 * (`@media (min-width: 782px) { .wp-block-columns { flex-wrap: nowrap } }`).
 * Reordering while the columns are still side by side would swap left and
 * right, so the two have to agree exactly.
 */
@media (width < 782px) {

	.wp-block-columns .ss-buybox {
		order: -1;
	}
}

.ss-buybox .wp-block-woocommerce-product-price {
	margin-block: 0.25rem 0.75rem;
	font-weight: 660;
	letter-spacing: -0.02em;
}

/* WooCommerce's own button chrome is replaced with the theme's, and the CTA
   spans the panel — it is the one action this column exists for. */
.ss-buybox .single_add_to_cart_button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 100%;
	min-block-size: 44px;
	padding: 0.7rem 1.1rem;
	border: 0;
	border-radius: 0.5rem;
	background: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--background);
	font-size: 1rem;
	font-weight: 560;
	cursor: pointer;
}

.ss-buybox__guarantees {
	padding-inline-start: 0;
	list-style: none;
}

/*
 * Scoped through `.ss-buybox` rather than written bare. Not decoration:
 * `.ss-rows li:first-child` earlier in this file is 0,2,1, so a bare
 * `.ss-buybox__guarantees li` (0,1,1) would be a descending-specificity rule
 * for the same `li` key selector — which stylelint rejects, and rightly, since
 * it is the shape that makes later rules stop winning for reasons no one can
 * see. Scoping brings it to 0,2,1, and this list genuinely only ever exists
 * inside the buy panel.
 *
 * It stays its own thing rather than becoming `.ss-rows`: the guarantees are
 * fine print under the price — muted, tight, no separators — not a card's
 * checklist. The list this rule used to sit beside, `.ss-after-pay ul`, IS a
 * card checklist and is now `.ss-rows` in the template, which is why the two
 * rules that used to style it (a 1.1rem indent for browser bullets, and a
 * second size/leading for its rows) are gone rather than moved.
 */
.ss-buybox .ss-buybox__guarantees li {
	padding-block: 0.25rem;
	color: var(--wp--preset--color--muted);
	font-size: 0.8125rem;
}

.ss-after-pay {
	margin-block-start: 1rem;
}

/* --- Checkout (Task 9) ---------------------------------------------------- */

/*
 * Two facts the buyer is owed next to the total: why the price carries no VAT
 * line, and that nothing will be charged again next year.
 *
 * They are static markup in `templates/checkout.html`, not PHP. The obvious
 * hook — `woocommerce_review_order_after_submit` — belongs to the CLASSIC
 * checkout and never fires on the block one, which is what this shop ships;
 * an earlier draft of `inc/Woo.php` printed them there and they simply never
 * appeared. There is no block-checkout equivalent that would put them under
 * the summary either: the summary is assembled in the browser from the Store
 * API, so a server-side hook has nowhere to write. Static markup below the
 * checkout block is the whole of what a template can do, and it is enough —
 * neither sentence depends on the cart.
 */
.ss-checkout__notes {
	margin-block-start: 0.75rem;
	padding: 0.9rem 1rem;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 0.5rem;
	background: var(--wp--preset--color--surface);
}

/*
 * Scoped through `.ss-checkout` rather than written bare: `.ss-faq details p`
 * earlier in this file is 0,2,1, so a bare `.ss-checkout__notes p` (0,1,1)
 * would be a descending-specificity rule for the same `p` key — the shape that
 * makes later rules quietly stop winning. The notes only ever exist inside the
 * checkout main element, so the scope costs nothing.
 */
.ss-checkout .ss-checkout__notes p {
	margin: 0;
	padding-block: 0.15rem;
	color: var(--wp--preset--color--muted);
	font-size: 0.8125rem;
	line-height: 1.5;
}

/*
 * The consent dialog.
 *
 * It is the plugin's, not the theme's: on the block checkout there is no
 * server-rendered form to host inline checkboxes, so `BlockIntegrationAsset`
 * promotes the group's strategy to a modal and `assets/form-gate/src/modal.js`
 * builds it in the browser. The theme's job is the two things a host theme may
 * do to a component it does not own — dress it in the shop's tokens, and give
 * its controls a real hit area. Nothing below changes the markup or touches the
 * `data-soglasie152-*` attributes the script finds its own elements by.
 *
 * **Every selector here is scoped through `.soglasie-152-modal-backdrop` on
 * purpose, and it is load-bearing.** The plugin's `form-gate.css` is enqueued
 * AFTER this stylesheet (measured on the stand: `soglasie-shop-storefront-css`
 * then `soglasie-152-form-gate-css`), so an equal-specificity rule loses every
 * tie. The extra ancestor class puts each rule exactly one class above its
 * counterpart, which is the smallest margin that wins and the least likely to
 * fight a future plugin rule.
 */
body .soglasie-152-modal-backdrop {

	/*
	 * The plugin's own scrim is a raw `rgba(15, 23, 42, .55)` — near enough to
	 * this palette's foreground to look deliberate, but it would not move if
	 * the palette did. Mixing it from the token keeps one source of truth and
	 * satisfies the theme's ban on colour literals (`.stylelintrc.json`).
	 */
	background: color-mix(in oklch, var(--wp--preset--color--foreground) 55%, transparent);
	font-family: var(--wp--preset--font-family--geist);
}

.soglasie-152-modal-backdrop .soglasie152-modal {
	border-radius: 0.5rem;
	background: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--foreground);
}

.soglasie-152-modal-backdrop .soglasie152-modal > h2 {
	letter-spacing: -0.025em;
	font-size: 1.25rem;
	font-weight: 640;
}

.soglasie-152-modal-backdrop .soglasie152-modal__error {
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 0.5rem;

	/*
	 * Error styling stays on the plugin's red, deliberately. This is the one
	 * element in the dialog whose colour is semantic rather than decorative,
	 * and the palette has no danger token to say it with — repainting it in
	 * `surface`/`muted` would make the alert look like another note. What the
	 * theme contributes is the radius and the border, so it sits in the same
	 * family as the cards around it. The message is announced through
	 * `role="alert"` regardless of colour.
	 */
}

.soglasie-152-modal-backdrop .soglasie152-modal__list label {

	/*
	 * A checkbox renders 13x13 in every browser, and no amount of styling
	 * makes that a 44x44 target. What the buyer actually clicks is this
	 * label — the input sits inside it — so the floor belongs here. The rows
	 * already run about 67px tall with the current wording; the minimum is
	 * what keeps that true after an edit shortens a label to one line.
	 */
	min-block-size: 44px;
	align-items: center;
	padding: 0.55rem;
	border-radius: 0.5rem;
	font-size: 0.9375rem;
	line-height: 1.45;
}

.soglasie-152-modal-backdrop .soglasie152-modal__list label:hover {
	background: color-mix(in oklch, var(--wp--preset--color--accent) 16%, var(--wp--preset--color--surface));
}

.soglasie-152-modal-backdrop .soglasie152-modal__list input[type="checkbox"] {

	/*
	 * Enlarged, never replaced. `appearance: none` is what would let a tick
	 * become a coloured square — state carried by hue alone, which WCAG 1.4.1
	 * forbids and no theme token can fix. The native control draws a glyph and
	 * a border change on top of the colour, so it survives a monochrome screen;
	 * `accent-color` recolours that glyph without giving up any of it.
	 * `tests/e2e/storefront/checkout.spec.ts` asserts the appearance is still
	 * not `none`.
	 */
	inline-size: 1.15rem;
	block-size: 1.15rem;
	margin-block-start: 0;
	accent-color: var(--wp--preset--color--primary);
}

.soglasie-152-modal-backdrop .soglasie152-modal__actions button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-inline-size: 44px;
	min-block-size: 44px;
	padding: 0.55rem 1.1rem;
	border-radius: 0.5rem;
	font: inherit;
	font-size: 0.9375rem;
	font-weight: 560;
}

.soglasie-152-modal-backdrop .soglasie152-modal__actions button[data-soglasie152-confirm] {
	border-color: var(--wp--preset--color--primary);
	background: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--background);
}

.soglasie-152-modal-backdrop .soglasie152-modal__actions button[data-soglasie152-cancel] {

	/*
	 * Refusing has to be as reachable as accepting — same size, same weight,
	 * same row. Only the fill differs, which is what tells them apart without
	 * making one of them hard to find.
	 */
	border-color: var(--wp--preset--color--hairline);
	background: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--foreground);
}

.soglasie-152-modal-backdrop .soglasie152-modal > button[data-soglasie152-cancel] {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inset-block-start: 0.4rem;
	inset-inline-end: 0.4rem;
	min-inline-size: 44px;
	min-block-size: 44px;
	border-radius: 0.5rem;
	color: var(--wp--preset--color--muted);
}

/*
 * Block themes inherit the UA focus ring, which disappears against the filled
 * confirm button — the same reason `.ss-tiers a:focus-visible` exists above.
 */
.soglasie-152-modal-backdrop .soglasie152-modal :focus-visible {
	outline: 2px solid var(--wp--preset--color--foreground);
	outline-offset: 2px;
}

/*
 * Links inside the dialog — one "Ознакомиться с документом" per checkbox, and
 * nothing else; the dialog carries no headline link. They take the same
 * permanent underline as every other link on this storefront, and for the same
 * reason: link colour equals body colour here, so the underline is the only
 * thing marking them as links (WCAG 1.4.1).
 */
.soglasie-152-modal-backdrop .soglasie152-modal a {
	color: var(--wp--preset--color--foreground);
	text-decoration: underline;
}

/* --- Post-purchase (Task 10) --------------------------------------------- */

/*
 * The slot this template reserves for the store plugin's own markup — the
 * licence key (#81) and the activations portal (#83), neither of which is this
 * theme's job to render or fake. On `order-confirmation.html` the slot always
 * carries its two placeholder paragraphs, so it is never empty there. On
 * `page-my-account.html` it starts truly empty (`<div ... />`, no children)
 * until one of those two tasks fills it in, so `:empty` keeps the account page
 * from showing a bare bordered box with nothing inside while that logic ships.
 */
.ss-license-slot {
	margin-block: 1.5rem;
}

.ss-license-slot:empty {
	display: none;
}

/* --- Licence block on the confirmation page (#81) ------------------------- */

/*
 * The markup belongs to «Согласие 152-ФЗ Store»
 * (`templates/order-confirmation/licenses.php`), printed on
 * `woocommerce_thankyou`; the theme only dresses it as one of its cards. Same
 * division as the consent bar below: what a buyer paid for must not depend on
 * the theme, so the theme styles it and never reimplements it.
 */
.s152-order-license {
	margin-block: 1.5rem;
	padding: 1rem 1.1rem;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 0.5rem;
	background: var(--wp--preset--color--surface);
}

.s152-order-license__title {
	margin: 0 0 0.6rem;
	font-size: 1rem;
	font-weight: 620;
}

.s152-order-license__label {
	margin: 0 0 0.25rem;
	color: var(--wp--preset--color--muted);
	font-size: 0.8125rem;
}

.s152-order-license__row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	margin: 0 0 0.6rem;
}

/*
 * `user-select: all` so a tap selects the whole key: the copy button needs
 * `navigator.clipboard`, which a browser may withhold, and selecting 29
 * characters by hand on a phone is the fallback people actually hit.
 */
.s152-order-license__key {
	font-family: var(--wp--preset--font-family--geist-mono);
	font-size: 1.0625rem;
	letter-spacing: 0.02em;
	user-select: all;
	word-break: break-all;
}

.s152-order-license__copy {
	padding: 0.3rem 0.6rem;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 0.35rem;
	background: transparent;
	color: inherit;
	cursor: pointer;
	font-size: 0.8125rem;
}

.s152-order-license__meta,
.s152-order-license__pending,
.s152-order-license__support {
	color: var(--wp--preset--color--muted);
	font-size: 0.875rem;
}

.s152-order-license__steps {
	margin-block: 0.9rem;
	padding-inline-start: 1.1rem;
	font-size: 0.875rem;
	line-height: 1.6;
}

/* --- Consent bar (Task 11) ------------------------------------------------ */

/*
 * The bar itself is the plugin's, markup and behaviour both — the design spec
 * is explicit that the theme must not reimplement it. What the spec does put on
 * the theme is the other half: "стилизовать баннер токенами магазина через
 * theme.json и точечный CSS, не подменяя разметку". Until this block existed
 * that half was simply missing, and the storefront — a near-monochrome
 * editorial layout — carried a bright `#1d4ed8` bar in the system font across
 * the bottom of every page. The plugin's palette is a sensible default for a
 * theme that never styles it; it is not this shop's.
 *
 * Selector weight: the plugin injects its stylesheet into `<head>` when the bar
 * mounts, i.e. AFTER this file, so an equal-specificity rule loses. Every rule
 * below therefore beats its counterpart on weight — the descendant rules by
 * being anchored on `.s152-banner` (0,2,0 against the plugin's bare `.s152-btn`
 * at 0,1,0), and the block right here by the `[class]` attribute trick, the
 * same lesson `html.s152-banner-reserve body[class]` records above.
 *
 * That trick is not decoration. This block was first written as a plain
 * `.s152-banner { … }` — (0,1,0), exactly the plugin's own — and every one of
 * its six declarations lost the tie and did nothing at all, under a comment
 * three lines up claiming otherwise. Measured on the stand at the time:
 * `background rgb(255,255,255)`, `border-top-color rgb(208,212,220)`,
 * `font-family -apple-system`, the plugin's shadow still drawn. The storefront
 * carried a white bar with a foreign grey-blue hairline and a system font
 * across the bottom of every page — on a theme whose whole identity is its
 * typeface. `acceptance.spec.ts` → «полоса согласия одета в токены витрины»
 * now measures the rendered surface, so weight cannot silently lose again.
 */
.s152-banner[class] {
	background: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--foreground);
	border-top-color: var(--wp--preset--color--hairline);
	box-shadow: none;
	font-family: var(--wp--preset--font-family--geist);
	font-size: var(--wp--preset--font-size--small);
}

.s152-banner .s152-banner__title {
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 640;
	letter-spacing: -0.02em;
}

/*
 * All three actions keep one appearance, because they must: the plugin gives
 * accept, reject and customise the same `s152-btn--primary` class on purpose,
 * and a theme that filled one and outlined the others would reintroduce exactly
 * the dark pattern the regulation forbids. So the token swap is applied to the
 * shared class only, and nothing here distinguishes refusing from accepting.
 */
.s152-banner .s152-btn {
	background: var(--wp--preset--color--primary);
	border-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--background);
	border-radius: 0.5rem;
	font-weight: 560;
}

.s152-banner .s152-btn:focus-visible {
	outline: 2px solid var(--wp--preset--color--foreground);
	outline-offset: 2px;
}

.s152-banner .s152-banner__policy-link {
	color: var(--wp--preset--color--foreground);
	text-decoration: underline;
}

.s152-banner .s152-banner__cat-desc {
	color: var(--wp--preset--color--muted);
}

/*
 * Below 480px the three Russian labels no longer fit one row — they wrap onto
 * two, and the bar grows to 257px, better than a third of a 375x700 screen.
 * The cost is not abstract: the landing page's own acceptance criterion is that
 * the buy button stays visible above the bar at 375, and the wrap alone pushed
 * the bar's top edge 18px above that button. That is also why this rule exists
 * here and not in the plugin — the height budget being blown belongs to the
 * page, and only the theme knows what else is on it.
 *
 * `nowrap` plus a smaller label is what buys the row back; the touch target is
 * untouched, since the height floor is `min-height` on the plugin's side and
 * `padding` is all that shrinks.
 */
@media (width < 30rem) {

	.s152-banner .s152-banner__actions {
		flex-wrap: nowrap;
	}

	.s152-banner .s152-btn {
		padding-inline: 0.5rem;
		font-size: 0.8125rem;
	}
}

/* --- Site chrome: header and footer ------------------------------------- */

/*
 * These four declarations used to live in the block attributes of
 * `parts/header.html` and `parts/footer.html` — and never reached a browser.
 *
 * `core/group` is a **static** block: what ships to the front end is the saved
 * HTML between the block comments, and that HTML carried no `style` attribute.
 * The JSON in the comment is what the editor re-serializes from, nothing more.
 * So `spacing.padding` and `border.top/bottom` were declared, reviewed in
 * diffs, and silently absent from every page — the header's real vertical
 * padding was core's own
 * `:where(.wp-block-group.has-background){padding:1.25em 2.375em}`, and
 * neither hairline existed at all. An audit of the whole theme found the same
 * shape in 13 places (11 patterns, both parts); the table in `README.md` →
 * "Declared-but-dead block styles" records what was done with each.
 *
 * They are restated here, at the values the markup declared, because CSS is
 * also the only place that can express what this theme needs next to them: a
 * breakpoint. The narrow-screen override below is what keeps the buy button
 * above the consent bar on a phone, and a block attribute cannot say "unless
 * the screen is under 30rem".
 *
 * Why the separators matter rather than being decoration: `surface`
 * (oklch 99.4%) and `background` (oklch 98.4%) differ by one percent of
 * lightness. Without a hairline the header does not read as a bar at all, and
 * the footer — which has no background of its own — simply runs on from the
 * last section. That is what the theme author declared and what the page has
 * been missing.
 */
.ss-header {
	padding-block: var(--wp--preset--spacing--30);
	border-block-end: 1px solid var(--wp--preset--color--hairline);
}

.ss-footer {
	padding-block: var(--wp--preset--spacing--50);
	border-block-start: 1px solid var(--wp--preset--color--hairline);
}

/*
 * The header's buy button, set to match the navigation beside it rather than
 * the theme's base 1rem.
 *
 * It reads as a duplicate of what `parts/header.html` already said — the button
 * block carried `style.typography.fontSize: "0.8125rem"` — and that is the
 * point: `core/button` is a static block, so a font size in its attributes is
 * serialised by the EDITOR into the saved `<a>`, never applied server-side.
 * Hand-written markup that omits it from the `style` attribute, as this part
 * did, ships nothing at all: production served
 * `<a class="wp-block-button__link wp-element-button" href="/shop/">` and the
 * button sat at 16px next to 13px nav links. The block attribute is gone; this
 * is now the only statement of the size. Same defect, same page, as the hero
 * card's missing padding — see #146.
 *
 * Targets the inner `<a>` because a block `className` lands on the outer
 * `.wp-block-button` wrapper. 0,2,0 clears theme.json's own button rule, which
 * core emits `:where()`-wrapped at 0,1,0.
 */
.ss-header__cta .wp-block-button__link {
	font-size: 0.8125rem;
}

/*
 * The other half of the same budget: the first screen itself.
 *
 * Shrinking the bar bought back one row and stopped there — the buy button
 * still ended 15px BELOW the bar's top edge on 375x700, i.e. the one element
 * the landing page exists for was covered until the visitor dismissed a
 * dialog. Measured before this block: CTA 462.45–507.42, bar top 492.13.
 *
 * `padding-bottom` on the page cannot fix that, and it is worth being explicit
 * about why, because it is the obvious first idea: reserving space adds room at
 * the END of the flow, below the fold, while the button's problem is its
 * distance from the TOP. Only removing height above the button moves it.
 *
 * The bar's 207.9px of a 700px screen is a product constant, not a styling
 * choice: it is what three Russian labels, the title and the policy link
 * occupy. So the page has ~492px to say what it sells and offer the button,
 * and this block spends that budget deliberately:
 *
 *  - the site header loses its wrapped-row gap and some vertical padding
 *    (126.9 → 103.6px). On a phone the title and the nav wrap onto two rows,
 *    and 24px between them plus 17.7px top and bottom is desktop rhythm
 *    applied to a screen that has no room for it;
 *  - the hero's own vertical rhythm drops 24 → 16px between elements, three
 *    of which sit above the button;
 *  - the H1 comes down with the `display` clamp in `theme.json`
 *    (2rem → 1.75rem floor), which at 375px also lets the headline set in
 *    three lines instead of four.
 *
 * Result on 375x700: CTA 364.22–409.19, bar top 492.13 — 82.9px of clearance,
 * enough to survive an extra line in the H1 (~33px) AND one in the lede
 * (~23px) before the button touches the bar again.
 * `landing.spec.ts` → «кнопка покупки видна без прокрутки и выше полосы
 * согласия» measures both boxes live and fails the moment it stops being true.
 *
 * The header padding below is a real value, not a tweak of an inherited one:
 * see the `.ss-header` / `.ss-footer` block above for why the site chrome is
 * styled here rather than in the block attributes of `parts/header.html`.
 */
@media (width < 30rem) {

	/*
	 * 0.625rem rather than the 0.75rem this block shipped with, and the
	 * difference is not cosmetic: the hairline this file now draws under the
	 * header adds a pixel to its height, and the clearance measured above is a
	 * number the landing spec defends. Trading 2px of padding for 1px of rule
	 * keeps it moving the right way — 82.94 → 85.94px — and the separator does
	 * the work the extra padding was doing anyway.
	 */
	.ss-header {
		padding-block: 0.625rem;
	}

	/*
	 * Specificity 0,2,0 on purpose: core's flex gap ships as
	 * `:root :where(.is-layout-flex){gap:24px}`, which is 0,1,0 and would
	 * otherwise win or lose depending on stylesheet order.
	 */
	.ss-header .ss-header__bar {
		gap: 0.75rem;
	}

	.ss-hero .wp-block-column > * + * {
		margin-block-start: 1rem;
	}
}

/* --- Subpage vertical rhythm (Task 3, #125) ------------------------------ */

/*
 * One source of truth for the gap under the header and the clearance above
 * the footer on every subpage that is not the landing page — applied via the
 * `ss-main` class on `<main>` in all SEVEN of `templates/{page,page-docs,
 * page-my-account,order-confirmation,single-product,checkout,
 * page-pricing}.html`, and restated for the parent's 404 in the rule right
 * below this one.
 *
 * Before this rule the five templates the defect named disagreed with each
 * other rather than with a design: `single-product.html` was the only one of
 * the five that declared a top padding at all (`spacing--40` in the block's
 * own `style` attribute), the other four declared none, and the jump was
 * visible on the one link a visitor is guaranteed to click right after paying
 * — the order-confirmation page's own link to `/docs/`. A single class
 * removes the four-vs-one split instead of copying `single-product.html`'s
 * value into four more `style` attributes, which would still be five places
 * to keep in sync by hand.
 *
 * The bottom half is the footer clearance. `page-my-account.html` used to
 * end its content with whatever margin the last paragraph in
 * `wp:post-content` happened to carry (~24px) rather than a page rhythm, so
 * the footer read as stuck to the cabinet's last line. `single-product.html`
 * already reserved a full `spacing--60` (4rem) before its own footer; this
 * gives every page that same reserve instead of an accident of its last
 * block.
 *
 * `checkout.html` and the then-existing `index.html` were not in the defect's
 * five-template list but carried the same problem for the same reason:
 * `checkout.html` set its own `spacing--50` top padding inline — a SIXTH
 * value, still a source of truth other than this class, on the one page in
 * the money path a buyer cannot avoid (card → checkout → thank-you).
 * `index.html` (the 404/search/archive fallback) set no padding at all, i.e.
 * the original zero-gap defect on a page nobody designs for but anybody can
 * land on after a typo. Both were folded in rather than left as a second and
 * third source of truth; `index.html` has since been deleted (#131 — the
 * parent theme answers all five of the routes it used to catch), and its half
 * of the story now lives in the 404 rule below.
 *
 * `page-pricing.html` arrived with #87, after this comment first listed
 * seven, and the list was not updated with it. That is the failure mode this
 * rule exists against, so the count is spelled out in words. Re-derive it,
 * never recall it:
 * `grep -rl ss-main themes/soglasie-shop/templates/`.
 *
 * `front-page.html` alone stays outside `.ss-main`: the hero fills to the
 * header's own hairline by design (Task 74, `landing.spec.ts`), which is a
 * deliberate zero, not a missing rule.
 */
.ss-main {
	padding-block: var(--wp--preset--spacing--40) var(--wp--preset--spacing--60);
}

/*
 * The one page whose `<main>` this theme does not write: the 404.
 *
 * Since #131 an unresolved URL is answered by the PARENT's `templates/404.html`
 * — deliberately, because it renders a "not found" pattern with a search box
 * where the storefront's own fallback rendered an empty post loop, and
 * `theme.spec.ts` pins that. What comes with it is Twenty Twenty-Five's
 * spacing: `var:preset|spacing|50` on all four sides, serialised into the
 * template's own `style` attribute. Note what is hard-coded and what is not —
 * the parent fixes the TOKEN, and the 40px a browser computes are ours,
 * because `--wp--preset--spacing--50` is 2.5rem in this theme's scale. Change
 * that scale and the 404's padding follows it, which is one more reason to
 * state the rhythm here rather than leave it to a number nobody owns.
 *
 * That is a page rhythm a visitor sees between our header and our footer, so
 * it is ours to state, and `acceptance.spec.ts` measures it next to the other
 * `.ss-main` carriers.
 *
 * `!important` is not a shortcut here, it is the only tool: the value being
 * overridden is an INLINE style on the parent's `<main>` (block templates
 * serialise `style` into the markup), and inline beats every selector an
 * author stylesheet can write. Author `!important` is the one thing that
 * outranks it.
 *
 * The horizontal half restates the root padding rather than a number, because
 * the parent's template uses `layout: default` and so never gets the
 * `has-global-padding` class every storefront page is inset by.
 */
body.error404 main.wp-block-group {
	padding-block: var(--wp--preset--spacing--40) var(--wp--preset--spacing--60) !important;
	padding-inline: var(--wp--style--root--padding-right) !important;
}

/* --- Documentation (Task 11) --------------------------------------------- */

/*
 * The contents column rides along on desktop only. `align-self: flex-start` is
 * load-bearing for the same reason it is on `.ss-buybox`: `wp:columns` is a
 * flex container whose items stretch to the row height, and an item exactly as
 * tall as its scroll container has nothing for `position: sticky` to travel
 * through — the rule would apply and do nothing at all.
 *
 * 64rem is above core's own 782px column-stacking breakpoint, so the sidebar is
 * only ever sticky while it actually sits beside the text. Below it the list
 * stacks under the page title and above the body, which is the reading order a
 * contents list wants anyway.
 */
@media (width >= 64rem) {

	.ss-docs__aside {
		position: sticky;
		inset-block-start: 1.5rem;
		align-self: flex-start;
		max-block-size: calc(100vh - 3rem);
		overflow-y: auto;
	}
}

.ss-docs__toc-title {
	margin-block-end: 0.5rem;
	font-size: var(--wp--preset--font-size--small);
	font-weight: 640;
	letter-spacing: 0;
	text-transform: uppercase;
}

.ss-docs__toc-list {
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.45;
}

.ss-docs__toc-item {
	margin-block: 0.35rem;
}

.ss-docs__toc-item--level-3 {
	padding-inline-start: 0.75rem;
}

/*
 * No underline at rest here, unlike body copy links — and the exception is
 * deliberate rather than an oversight of the rule stated above
 * `.soglasie-152-cookie-banner-trigger`. WCAG 1.4.1 is about a link being
 * distinguishable from the text AROUND it; every item in this list is a link,
 * so there is no surrounding prose to be confused with. The same reasoning
 * already governs `.ss-tiers a`, the other pure navigation control in this
 * theme. Hover and keyboard focus still bring the underline back, so the
 * affordance is never absent when a pointer or a Tab key asks for it.
 */
.ss-docs .ss-docs__toc-list a {
	display: inline-block;
	min-block-size: 1.5rem;
	color: var(--wp--preset--color--muted);
	text-decoration: none;
}

.ss-docs .ss-docs__toc-list a:hover,
.ss-docs .ss-docs__toc-list a:focus-visible {
	color: var(--wp--preset--color--foreground);
	text-decoration: underline;
	outline-offset: 2px;
}

.ss-docs .ss-docs__toc-list a:focus-visible {
	outline: 2px solid var(--wp--preset--color--foreground);
}

/*
 * A heading the sidebar links to must not land under the sticky header space
 * once it is scrolled to; without this the browser puts it flush against the
 * viewport edge and the section title reads as if it belonged to the previous
 * block.
 */
.ss-docs h2[id],
.ss-docs h3[id] {
	scroll-margin-block-start: 1.5rem;
}

.ss-docs .wp-block-post-content h2 {
	margin-block-start: 2rem;
}

.ss-docs .wp-block-post-content h3 {
	margin-block-start: 1.5rem;
}

/* --- My Account authentication --- */

.woocommerce-account .woocommerce-form-login,
.woocommerce-account .otp-verify-conditional {
	inline-size: 100%;
	max-inline-size: 34rem;
}

.woocommerce-account .woocommerce-form-login {
	box-sizing: border-box;
	display: grid;
	gap: var(--wp--preset--spacing--30);
	padding: var(--wp--preset--spacing--40);
	text-align: start;
}

.woocommerce-account .woocommerce-form-login > p {
	float: none;
	clear: both;
	inline-size: auto;
	margin: 0;
}

.woocommerce-account .woocommerce-form-login .otp-verify-code-wrap,
.woocommerce-account .woocommerce-form-login .otp-verify-resend,
.woocommerce-account .woocommerce-form-login .otp-verify-restart,
.woocommerce-account .woocommerce-form-login .woocommerce-form-login__rememberme {
	text-align: start;
}

.woocommerce-account .woocommerce-form-login:has(#otp-verify-code) .lost_password {
	display: none;
}

.woocommerce-account .otp-verify-conditional {
	padding: var(--wp--preset--spacing--30);
	border: 1px solid var(--wp--preset--color--hairline);
	background: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--foreground);
}

.woocommerce-account .woocommerce-form-login button,
.woocommerce-account .otp-verify-secondary-action {
	min-block-size: 44px;
}

.woocommerce-account .otp-verify-secondary-action {
	color: var(--wp--preset--color--foreground);
	background: transparent;
	border-color: var(--wp--preset--color--hairline);
}

.woocommerce-account .woocommerce-form-login .lost_password a,
.woocommerce-account .woocommerce-form-login .otp-verify-restart a {
	display: inline-flex;
	align-items: center;
	min-block-size: 44px;
}

.woocommerce-account .woocommerce-form-login input:focus-visible {
	outline: 2px solid var(--wp--preset--color--foreground);
	outline-offset: 2px;
}

.woocommerce-account .ss-account-license-note {
	inline-size: 100%;
	max-inline-size: 34rem;
	margin: var(--wp--preset--spacing--60) 0 0;
}

@media (width < 30rem) {

	.woocommerce-account .woocommerce-form-login {
		padding: var(--wp--preset--spacing--30);
	}
}
