/* The page's own ground is white: every page frame in the .fig that carries a
   fill carries #FFFFFF — «Сінгл вистави» 3106:1887, «Сінгл вистави» (390)
   4234:26099, «Events Content» 3214:4621, «Репертуар» 3498:11890 / 3219:10508
   and 4379:24549, «V9. 13» 1159:9399 (home) — and none carries #FAFAFA. That
   token (--color-neutral-50) is a *surface* colour in this design (the sticky
   buy bar 4277:17386 is #FAFAFA and its spec-diff measures it), which is why it
   was reached for here by mistake (plan-end batch item 10). */
body {
	margin: 0;
	background: var(--color-base-white);
	color: var(--color-neutral-900);
	font-family: var(--font-body);
	font-size: var(--fs-m-text);
	line-height: var(--lh-m-text);
}

.skip-link {
	position: absolute;
	left: -9999px;
}

/* Standard WordPress visually-hidden pattern: content read by
   assistive tech, not shown on screen. */
.screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
	padding: 0;
}

.skip-link:focus {
	left: 16px;
	top: 16px;
	z-index: 1000;
	padding: 8px 16px;
	background: var(--color-neutral-900);
	color: var(--color-neutral-50);
}

:focus-visible {
	outline: 2px solid var(--color-primary-700);
	outline-offset: 2px;
}

/* Background scroll lock for any open modal dialog: dialog.js (initDialog,
   shared infra) toggles `has-dialog` on <html>, so the rule lives with the
   other site-wide rules, not in one consumer's stylesheet.
   `overflow: hidden` on :root alone is not enough: measured (Playwright,
   Chromium) that `window.scrollTo()` still moves
   `document.scrollingElement` — overflow:hidden blocks user-driven
   scrolling (wheel/touch/keys) but does not remove the scrollable range a
   script can still scroll into. Pinning `body`'s height to the viewport
   closed that gap but reset the scroll position to the top on open. The
   body-fixed technique instead — `position: fixed` on `body` at its
   current (negative) scroll offset, set by dialog.js on open and cleared
   on close together with a matching `scrollTo` — removes the same
   scrollable range without moving the page. */
:root.has-dialog {
	overflow: hidden;
}

:root.has-dialog body {
	position: fixed;
	left: 0;
	right: 0;
	overflow: hidden;
}

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

	*,
	*::before,
	*::after {
		/* stylelint-disable-next-line declaration-no-important -- reduced motion */
		animation-duration: 0.01ms !important;
		/* stylelint-disable-next-line declaration-no-important -- reduced motion */
		transition-duration: 0.01ms !important;
	}
}

/* Windows High Contrast / `forced-colors: active`: Chromium drops author
   `box-shadow` entirely, and the project's own convention for a Figma
   "inside stroke" is exactly that — `box-shadow: inset` — used as the
   ONLY edge on several controls and several structural dividers
   (a11y-panel.css, ticket-button.css, site-footer.css, site-header.css,
   subscribe.css, mobile-menu.css). Without this block the a11y panel's
   outline, its size steppers, the ticket button, footer social links,
   the header's mobile-menu toggle and the footer/mobile-menu row
   dividers all render with no visible boundary at all under
   forced-colors.
   `outline` (not `border`) draws the replacement edge: outline never
   participates in the box model, so it is unaffected by each control's
   own `box-sizing` (mixed border-box/content-box across these files) —
   zero risk of the box growing/shrinking or content reflowing, which a
   `border` would require compensated padding to avoid. A negative
   `outline-offset` pulls it to the same "inside" position the box-shadow
   held. Colours are the forced-colors system keywords (`CanvasText`,
   `ButtonText`, `GrayText`) rather than design tokens, since Chromium
   strips author colour from most properties here anyway — `outline-color`
   is one of the few it still honours, so it is pinned explicitly instead
   of left to the browser's own remap. */
@media (forced-colors: active) {

	.a11y-panel,
	.a11y-panel__step,
	.a11y-panel__value,
	.c-ticket,
	.socials__link,
	.site-header__menu-toggle,
	.subscribe__field {
		outline: 1px solid CanvasText;
		outline-offset: -1px;
	}

	/* Disabled ticket / disabled stepper keep an edge but a dimmer one,
	   so the disabled state stays visually distinct from the enabled
	   one — GrayText is forced-colors' own system colour for disabled
	   UI, same intent as the neutral-300 token these replace.
	   `:not(:focus-visible)`: `outline` is a shorthand, and the cascade
	   resolves each of its longhands (width/style/color) independently
	   — a higher-specificity `outline-color` here (0,2,0 from the class
	   + attribute selector) would keep winning over `:focus-visible`'s
	   `outline: 2px solid Highlight` below (0,1,0) even while focused,
	   since only `outline-color` collides; width/style still came from
	   `:focus-visible` uncontested, so a Tab lands a 2px GrayText ring —
	   present, but not the Highlight colour the focus ring promises
	   everywhere else. Excluding `:focus-visible` here means this rule
	   simply doesn't match while focused, so `outline-color` has only
	   one declaration left (Highlight) and wins outright — cascade order
	   no longer matters. */
	.c-ticket--disabled,
	.a11y-panel__step[aria-disabled="true"]:not(:focus-visible) {
		outline-color: GrayText;
	}

	/* Switch track/knob (a11y-panel.css): drawn with `::after`/`::before`
	   pseudo-elements carrying only a `background`, no edge of their
	   own — invisible once forced-colors flattens `background` to a
	   system colour, losing the on/off distinction entirely. */
	.a11y-panel__switch::after {
		outline: 1px solid ButtonText;
		outline-offset: -1px;
	}

	.a11y-panel__switch::before {
		outline: 1px solid CanvasText;
		outline-offset: -1px;
	}

	/* Structural dividers: also `box-shadow: inset`, per the same
	   project convention, but on non-interactive rows rather than a
	   control's own edge — a real `border` (not `outline`, which would
	   draw on every side) on just the side the shadow drew keeps the
	   division visible without adding edges the design never had.
	   These never had `box-sizing: border-box`, but a 1px border on a
	   dividing line between flex/block siblings doesn't grow any
	   dimension a layout test asserts on — no compensation needed. */
	.site-footer__main {
		border-bottom: 1px solid CanvasText;
	}

	.site-footer__sitemap,
	.site-footer__col--partner,
	.mobile-menu__item,
	.mobile-menu__stage {
		border-top: 1px solid CanvasText;
	}

	/* `button.mobile-menu__a11y`, not the bare class: mobile-menu.css's
	   own `.mobile-menu__a11y { border: 0; … }` (a button reset) loads
	   AFTER base.css (wp_enqueue_style dependency order), so at equal
	   (0,1,0) specificity it would win this cascade tie and zero the
	   border back out — the element+class compound is (0,1,1), which
	   wins regardless of source order. */
	button.mobile-menu__a11y {
		border-top: 1px solid CanvasText;
	}

	/* ≥1440 (site-footer.css): the sitemap/partner divider itself turns
	   from a top edge to a left edge (row layout replaces the stacked
	   column), so the forced-colors border has to follow it there. */
	@media (min-width: 1440px) {

		.site-footer__sitemap,
		.site-footer__col--partner {
			border-top: 0;
			border-left: 1px solid CanvasText;
		}
	}

	/* Placed last so it wins (equal specificity, later in source) over
	   the resting-state outlines above on whichever control is also
	   focused — the ring must never be the thing forced-colors hides. */
	:focus-visible {
		outline: 2px solid Highlight;
		outline-offset: 2px;
	}
}
