/*
Theme Name: Halif Hatch
Theme URI: https://halifhatch.com
Author: Michal Matus
Author URI: https://halifhatch.com
Description: Custom block theme for Halif Hatch. Design-system driven, built from Figma. Blocks match the editor and the live site exactly.
Version: 0.1.20
Requires at least: 6.5
Tested up to: 6.8
Requires PHP: 8.1
License: Proprietary
Text Domain: halif
Tags: block-theme, full-site-editing, custom
*/

/* Runtime styles are compiled/enqueued separately.
   Design tokens live in /tokens/base.css. This file exists for the theme header. */

/* =============================================================
   Accessibility utility
   ============================================================= */

/* Text for assistive tech only — never rendered, so it can extend a
   terse visible label (e.g. "prev" → "prev slide") without touching
   the design. Standard WordPress recipe. */
.screen-reader-text {
	border: 0;
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	white-space: nowrap;
	width: 1px;
}

/* =============================================================
   Header & footer template parts (design: HALIF_HOME_PAGE)
   All values come from tokens (tokens/base.css).
   ============================================================= */

/* ---------- header ---------- */

/* fixed header — page content is offset below it and anchor
   jumps land under it */
body {
	padding-top: var(--header-height);
}

html {
	scroll-padding-top: var(--header-height);
}

/* department pages carry a second fixed bar (halif/subheader) flush under
   the header, so both offsets grow by its height. Halif pages have no
   subheader and stay on the plain --header-height above. */
body.halif-dept-hatch,
body.halif-dept-dock,
body.halif-dept-link {
	padding-top: calc(var(--header-height) + var(--subheader-height));
}

html:has(body.halif-dept-hatch),
html:has(body.halif-dept-dock),
html:has(body.halif-dept-link) {
	scroll-padding-top: calc(var(--header-height) + var(--subheader-height));
}

/* …except on phones, where the subheader is hidden (client, 2026-08-21):
   the bar reserves nothing, so the offset goes back to the header alone. */
@media (max-width: 767px) {

	body.halif-dept-hatch,
	body.halif-dept-dock,
	body.halif-dept-link {
		padding-top: var(--header-height);
	}

	html:has(body.halif-dept-hatch),
	html:has(body.halif-dept-dock),
	html:has(body.halif-dept-link) {
		scroll-padding-top: var(--header-height);
	}
}

/* The same stack as a single number, for anything inside the page that has to
   park itself clear of the fixed bars (the form's sticky step rail). The two
   offsets above cannot use it — `scroll-padding-top` sits on <html>, which is
   above where a body class can define anything — so this mirrors them rather
   than replacing them, phone rule included. Blocks read the variable and stay
   out of the "which pages have a subheader" business. */
body {
	--fixed-bars-height: var(--header-height);
}

body.halif-dept-hatch,
body.halif-dept-dock,
body.halif-dept-link {
	--fixed-bars-height: calc(
		var(--header-height) + var(--subheader-height)
	);
}

@media (max-width: 767px) {

	body.halif-dept-hatch,
	body.halif-dept-dock,
	body.halif-dept-link {
		--fixed-bars-height: var(--header-height);
	}
}

/* Editor: both bars are in the flow there (core forces every block into
   `position: relative`), so they take up the space they only reserve on the
   frontend — the offset above would show as a blank strip above the header.
   The doubled class outguns the department selectors above it. */
.editor-styles-wrapper.editor-styles-wrapper {
	padding-top: 0;
}

/* TT Hoves ships 400 and 600 only, so the UA's 700 bold is a faux-bold
   synthesis. One rule for the whole site — body copy, headings (already 600,
   so a <strong> inside one no longer jumps a weight) and anything an editor
   marks up in a rich-text field. Per-block rules that already pin 600 are
   redundant now but harmless. */
strong,
b {
	font-weight: 600;
}

body * {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

/* header bar styles live in the halif/header block (blocks-src/header/style.css)
   so the editor renders it identically to the frontend */

/* uploadable brand logo (Appearance → Brand, rendered by halif/brand-logo) */
.halif-brand-logo {
	display: block;
}

.halif-brand-logo .halif-brand-logo__img {
	display: block;
	width: var(--logo-width);
	height: auto;
}

.halif-logo {
	color: #fff;
	line-height: 1;
	text-decoration: none;
}

/* Dock/Link lockups placed in content — follow the surrounding context color
   instead of the header's fixed white */
.halif-logo.is-static {
	display: block;
	color: inherit;
}

.halif-logo__halif {
	display: block;
	font-size: 2rem;
	font-weight: 600;
	letter-spacing: 0.14em;
}

.halif-logo__hatch {
	display: flex;
	align-items: center;
	gap: 0.5em;
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.3em;
	margin-top: 0.35em;
}

.halif-logo__hatch i {
	width: 3.2em;
	height: 1.5px;
	background: currentColor;
}

/* navigation — matches the static navbar links */
.halif-header__nav {
	gap: var(--nav-gap);
}

.halif-header__nav a {
	color: var(--color-text);
	font-weight: 600;
	font-size: var(--text-body-lg);
	line-height: var(--nav-line-height);
	letter-spacing: 0;
	text-decoration: none;
	border-bottom: 2.5px solid transparent;
}

.halif-header__nav a:hover,
.halif-header__nav .current-menu-item>a,
.halif-header__nav a[aria-current="page"] {
	border-bottom-color: var(--color-ink);
}

/* ---------- footer ---------- */

/* The footer is a halif/section (bg:dark) → halif/columns (3/6/3) →
   halif/footer-credits, so the container + dark ctx come from the section.
   These rules only style the footer-specific inner spacing and typography;
   they're scoped to the section that holds the credits line so they never
   touch any other dark section. */

/* footer surface is darker than the shared dark-section colour; the ctx
   token is re-pointed too so anything reading --ctx-surface inside stays
   in sync with the actual background */
.halif-section:has(.halif-footer__bottom) {
	--ctx-surface: var(--footer-bg);
	background: var(--footer-bg);
	/* every piece of footer copy is the same tone (client, 2026-08-21); the
	   per-element colour rules below are kept so they can't fall back to the
	   dark-ctx body colour */
	color: var(--footer-text);
}

/* the footer's own padding is fixed by the design, so it wins over whatever
   padTop/padBottom the section block was saved with (the DB copy of the part
   carries medium/small, which is how the footer tokens were silently unused
   until 2026-08-21) */
.halif-section.halif-section:has(.halif-footer__bottom) {
	padding-top: var(--footer-pad-top);
	padding-bottom: var(--footer-pad-bottom);
}

/* uploaded footer logo — size unchanged */
.halif-footer__logo .halif-brand-logo__img {
	width: var(--logo-width-footer);
}

/* top column row → bottom credits line: 180px apart */
.halif-section:has(.halif-footer__bottom) .halif-columns {
	margin-block: 0;
}

/* --- middle column: contact --- */
/* element+class specificity so the shared `.halif-footer__contact p { margin:0 }`
   below can't flatten the title's own spacing */
.halif-footer__contact p.halif-footer__label {
	color: var(--footer-text);
	margin: var(--footer-contact-title-space-top) 0 var(--footer-contact-title-space-bottom);
	font-size: var(--text-footer-meta);
	line-height: var(--leading-footer);
	font-weight: 400;
	letter-spacing: var(--tracking-body);
}

.halif-footer__contact p {
	margin: 0;
}

.halif-footer__contact p:not(.halif-footer__label) {
	color: var(--footer-text);
	font-size: var(--text-footer-contact);
	line-height: var(--leading-footer);
	font-weight: 400;
	letter-spacing: var(--tracking-body);
}

/* 10px between the phone and e-mail lines */
.halif-footer__contact p:not(.halif-footer__label) + p:not(.halif-footer__label) {
	margin-top: var(--footer-contact-gap);
}

/* the contact info itself (link) is demibold; the "T:"/"E:" label stays regular */
.halif-footer__contact strong {
	font-weight: 600;
}

/* --- right column: vertical nav menu --- */
/* colour sits on the container: core/navigation forces `color: inherit` on the
   links at (0,3,0), so the links pick it up from here rather than from a link rule */
.halif-footer__legal {
	align-items: flex-start;
	color: var(--footer-text);
}

/* 14px between stacked items (wins over core's is-layout-flex gap) */
.halif-footer__legal.wp-block-navigation {
	gap: var(--footer-menu-gap);
}

.halif-footer__legal a {
	color: var(--footer-text);
	font-size: var(--text-footer-meta);
	line-height: var(--leading-footer);
	font-weight: 400;
	letter-spacing: var(--tracking-body);
	text-decoration: none;
}

/* all footer links (contact phone/e-mail + language switcher) — #FAFAFA.
   Scoped to the footer section since there's no longer a `.halif-footer`
   wrapper; without this they fall back to the UA link colour. */
.halif-section:has(.halif-footer__bottom) a {
	color: var(--footer-text);
	text-decoration: none;
}

/* --- bottom credits line (halif/footer-credits) --- */
.halif-footer__bottom {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-s);
	margin-top: var(--footer-columns-to-bottom);
	/* 1px separator between the footer columns and this line */
	border-top: 1px solid var(--footer-rule-color);
	padding-top: var(--footer-rule-space);
	font-size: var(--text-base);
}

.halif-footer__bottom p {
	margin: 0;
}

/* left-hand brand + year line is set in caps (client, 2026-08-21) */
.halif-footer__bottom p:first-child {
	text-transform: uppercase;
}

/* --- social icons in the bottom bar (was the halif/footer-socials block
   under the logo; now rendered by halif/footer-credits) --- */
/* These rules used to live in blocks-src/footer-socials/style.css, which WP
   only enqueues when that block is on the page. The socials now render inside
   footer-credits, so the styling has to load with the footer itself. */
.halif-footer__socials {
	display: flex;
	align-items: center;
	gap: var(--footer-socials-gap);
	margin: 0;
	padding: 0;
	list-style: none;
}

.halif-footer__socials li {
	display: flex;
}

/* The footer section already paints all its links --footer-text; this makes
   the icon inherit it through currentColor and kills the inline gap. */
.halif-footer__socials a {
	display: block;
	color: var(--footer-text);
}

.halif-footer__socials svg {
	display: block;
	width: var(--footer-socials-size);
	height: var(--footer-socials-size);
}

/* Enhancement motion — gated on the Animations toggle (inc/animations.php);
   with the toggle off the hover state still applies, it just snaps. */
body.halif-anim .halif-footer__socials a {
	transition: opacity var(--anim-press-ms) var(--ease-out);
}

.halif-footer__socials a:hover,
.halif-footer__socials a:focus-visible {
	opacity: var(--footer-socials-hover-opacity);
}

/* --- footer, mobile (≤767px) --- */
/* All three columns are span-m-12, so the 12-track grid buys nothing here —
   and worse, its 11 × 32px gaps are wider than a 375px frame's container, so
   the tracks collapse to 0 and the row overflows to the right. Below 768px
   the footer row becomes a plain vertical stack with its own gap token.
   Sizes come from tokens/base.css (Footer — mobile scale). */
@media (max-width: 767px) {
	.halif-section:has(.halif-footer__bottom) .halif-columns {
		display: flex;
		flex-direction: column;
		gap: var(--footer-stack-gap);
	}

	/* brand line and social icons share one 315px row; keep a row-gap in
	   case the two wrap */
	.halif-footer__bottom {
		row-gap: var(--space-s);
	}
}

/* ---------- cookie bar ---------- */

/* global consent card (inc/cookie-bar.php), fixed bottom right.
   White (pure-white ctx) card — colors resolve through the ctx system
   like blocks. Paragraphs use --text-cookie, a size reserved for this
   card and never used in blocks. */
.halif-cookie-bar {
	position: fixed;
	right: var(--space-m);
	bottom: var(--space-m);
	z-index: 20; /* above the fixed header (10) */
	width: min(var(--cookie-bar-width), calc(100vw - 2 * var(--space-m)));
	padding: var(--space-m);
	border-radius: var(--radius-m);
	background: var(--ctx-surface);
	color: var(--ctx-text);
	font-family: var(--font-body);
}

.halif-cookie-bar__title {
	margin: 0 0 var(--space-xs);
	font-size: var(--text-cookie);
	font-weight: 400;
	line-height: var(--leading-snug);
}

/* TT Hoves ships 400/600 only — pin <strong> to 600, no faux bold */
.halif-cookie-bar__title strong {
	font-weight: 600;
}

.halif-cookie-bar__text {
	margin: 0 0 var(--space-s);
	font-size: var(--text-cookie);
	font-weight: 400;
	line-height: var(--leading-normal);
	letter-spacing: var(--tracking-body);
	color: var(--ctx-body);
}

/* small pill — temporary size (--cookie-btn-* tokens) until Figma detail
   lands; colors resolve through the ctx system like halif/button */
.halif-cookie-bar__accept {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	height: var(--cookie-btn-height);
	padding: 2px var(--cookie-btn-pad-x) 0; /* top nudge matches .halif-btn */
	border: 0;
	border-radius: var(--btn-radius);
	background: var(--ctx-btn-bg);
	color: var(--ctx-btn-text);
	font-family: var(--font-body);
	font-size: var(--cookie-btn-text);
	font-weight: 600;
	line-height: 1;
	white-space: nowrap;
	cursor: pointer;
}

/* three actions share a wrapping row; Accept keeps the filled pill, the
   other two are lighter so the card doesn't read as three loud buttons */
.halif-cookie-bar__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-xs);
}

.halif-cookie-bar__decline,
.halif-cookie-bar__settings {
	height: var(--cookie-btn-height);
	padding: 2px var(--cookie-btn-pad-x) 0;
	border: 0;
	background: none;
	color: var(--ctx-body);
	font-family: var(--font-body);
	font-size: var(--cookie-btn-text);
	font-weight: 600;
	line-height: 1;
	white-space: nowrap;
	cursor: pointer;
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* =============================================================
   Cookie settings modal (inc/cookie-bar.php) — pure-white ctx card
   centered over a scrim. Provisional styling (--cookie-* tokens),
   no Figma delivered; the switch mirrors the form switch.
   ============================================================= */
.halif-cookie-modal[hidden] {
	display: none;
}

.halif-cookie-modal {
	position: fixed;
	inset: 0;
	z-index: 30; /* above the bar (20) and header (10) */
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--space-m);
	font-family: var(--font-body);
}

body.halif-cookie-modal-open {
	overflow: hidden;
}

.halif-cookie-modal__overlay {
	position: absolute;
	inset: 0;
	background: var(--cookie-overlay);
}

.halif-cookie-modal__dialog {
	position: relative;
	width: min(var(--cookie-modal-max-w), 100%);
	max-height: calc(100vh - 2 * var(--space-m));
	overflow-y: auto;
	padding: var(--space-m);
	border-radius: var(--cookie-modal-radius);
	background: var(--ctx-surface);
	color: var(--ctx-text);
}

.halif-cookie-modal__close {
	position: absolute;
	top: var(--space-xs);
	right: var(--space-xs);
	width: 2rem;
	height: 2rem;
	border: 0;
	background: none;
	color: var(--ctx-text);
	font-size: 1.75rem;
	line-height: 1;
	cursor: pointer;
}

.halif-cookie-modal__title {
	margin: 0 var(--space-l) var(--space-xs) 0;
	font-size: var(--text-lg);
	font-weight: 600;
	line-height: var(--leading-snug);
}

.halif-cookie-modal__intro {
	margin: 0 0 var(--space-m);
	font-size: var(--text-cookie);
	line-height: var(--leading-normal);
	color: var(--ctx-body);
}

.halif-cookie-modal__policy {
	color: var(--ctx-text);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.halif-cookie-modal__cats {
	display: flex;
	flex-direction: column;
	gap: var(--space-s);
}

.halif-cookie-cat {
	padding-bottom: var(--space-s);
	border-bottom: 1px solid var(--cookie-rule);
}

.halif-cookie-cat__row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-s);
}

.halif-cookie-cat__label {
	font-size: var(--text-lg);
	font-weight: 600;
	cursor: pointer;
}

.halif-cookie-cat__desc {
	margin: var(--space-xs) 0 0;
	font-size: var(--text-cookie);
	line-height: var(--leading-normal);
	color: var(--ctx-body);
}

/* switch: the native checkbox IS the track (appearance:none) with the knob
   laid over it, so focus, keyboard and state stay native */
.halif-cookie-switch {
	position: relative;
	flex: 0 0 auto;
	display: block;
	width: var(--cookie-switch-w);
	height: var(--cookie-switch-h);
}

.halif-cookie-switch__input {
	appearance: none;
	-webkit-appearance: none;
	width: 100%;
	height: 100%;
	margin: 0;
	border: 0;
	border-radius: calc(var(--cookie-switch-h) / 2);
	background: var(--cookie-switch-track);
	cursor: pointer;
}

.halif-cookie-switch__input:checked {
	background: var(--color-brand);
}

.halif-cookie-switch.is-locked .halif-cookie-switch__input {
	cursor: default;
	opacity: 0.6;
}

.halif-cookie-switch__knob {
	position: absolute;
	top: var(--cookie-switch-pad);
	left: var(--cookie-switch-pad);
	width: var(--cookie-switch-knob);
	height: var(--cookie-switch-knob);
	border-radius: 50%;
	background: var(--cookie-switch-knob-bg);
	box-shadow: 0 1px 3px rgb(47 48 53 / 0.24);
	pointer-events: none;
}

.halif-cookie-switch__input:checked ~ .halif-cookie-switch__knob {
	transform: translateX(var(--cookie-switch-travel));
}

.halif-cookie-cat__list {
	margin-top: var(--space-xs);
}

.halif-cookie-cat__list summary {
	display: inline-block;
	font-size: var(--text-cookie);
	color: var(--ctx-body);
	cursor: pointer;
	text-decoration: underline;
	text-underline-offset: 3px;
}

.halif-cookie-table {
	width: 100%;
	margin-top: var(--space-xs);
	border-collapse: collapse;
	font-size: var(--text-cookie);
	line-height: var(--leading-normal);
}

.halif-cookie-table th,
.halif-cookie-table td {
	padding: var(--space-xs) var(--space-xs) var(--space-xs) 0;
	text-align: left;
	vertical-align: top;
	border-bottom: 1px solid var(--cookie-rule);
	color: var(--ctx-body);
}

.halif-cookie-table th {
	font-weight: 600;
	color: var(--ctx-text);
}

.halif-cookie-table code {
	font-size: 0.95em;
	word-break: break-word;
}

.halif-cookie-cat__empty {
	margin: var(--space-xs) 0 0;
	font-size: var(--text-cookie);
	color: var(--ctx-body);
}

.halif-cookie-modal__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-s);
	margin-top: var(--space-m);
}

/* Save keeps the filled pill (mirrors the bar's accept button); Accept
   all / Decline all are the light text actions */
.halif-cookie-modal__save {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	height: var(--cookie-btn-height);
	padding: 2px var(--cookie-btn-pad-x) 0;
	border: 0;
	border-radius: var(--btn-radius);
	background: var(--ctx-btn-bg);
	color: var(--ctx-btn-text);
	font-family: var(--font-body);
	font-size: var(--cookie-btn-text);
	font-weight: 600;
	line-height: 1;
	white-space: nowrap;
	cursor: pointer;
}

.halif-cookie-modal__link {
	border: 0;
	background: none;
	color: var(--ctx-body);
	font-family: var(--font-body);
	font-size: var(--cookie-btn-text);
	font-weight: 600;
	cursor: pointer;
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* =============================================================
   Core blocks used inside content
   ============================================================= */

/* core/image: the figure never carries bottom spacing of its own — whatever
   sits below it owns the gap, the same rule halif/columns follows. The
   editor-scoped copy re-asserts it against the canvas' default margins. */
figure.wp-block-image,
.editor-styles-wrapper figure.wp-block-image {
	margin-bottom: 0;
}

/* root template stack: no gaps between header / content / footer */
.wp-site-blocks>*+* {
	margin-block-start: 0 !important;
}

main.wp-block-group {
	margin: 0;
}
/* =============================================================
   Animations master toggle (Appearance → Customize → Animations)
   inc/animations.php puts `halif-anim` on <body> when enabled.
   Convention: ALL enhancement motion (reveals, hovers, micro-
   interactions from the animation-opportunities work) is scoped
   under body.halif-anim — with the toggle off those rules simply
   don't exist. Functional motion (slider, tabs underline, menu)
   stays ungated. See inc/animations.php header for the full rules.
   ============================================================= */

/* ---------- scroll/load reveal (assets/js/halif-anim.js) ----------
   .halif-reveal is added BY JS only — base CSS never hides content,
   so no JS / toggle off / old browser all mean "just visible".

   The transition lives on the destination (.is-in) state ONLY — never on
   the hidden state — so if a paint sneaks in before the deferred script
   runs (common on a warm-cache reload of a long page), the hidden state
   snaps on instantly instead of animating the content OUT and back in.
   That reverse fade was the "loading-bug" wobble on reload. The pre-paint
   arming guard (inc/animations.php) closes the last one-frame gap. */

body.halif-anim .halif-reveal {
	opacity: 0;
	transform: translateY(12px);
}

/* load cascade (hero entrance): a touch more travel than a scroll reveal,
   same ease-out family (via .is-in) so the first screen and everything
   below it read as one system — just a slightly longer beat */
body.halif-anim .halif-reveal.is-load {
	transform: translateY(16px);
}

/* photo panels: the panel fades while its image settles from a gentle
   zoom — panel's own overflow:hidden clips the oversize frame */
body.halif-anim .halif-reveal.is-photo img {
	transform: scale(1.05);
}

body.halif-anim .halif-reveal.is-in {
	opacity: 1;
	transform: none;
	transition:
		opacity var(--anim-reveal-ms) var(--ease-out),
		transform var(--anim-reveal-ms) var(--ease-out);
	/* stagger: halif-anim.js sets --reveal-delay per element; children
	   (lead lines below) offset from the same var so they never animate
	   inside a still-transparent parent */
	transition-delay: var(--reveal-delay, 0ms);
}

body.halif-anim .halif-reveal.is-load.is-in {
	transition-duration: var(--anim-reveal-load-ms);
}

body.halif-anim .halif-reveal.is-photo.is-in img {
	transform: scale(1);
	transition: transform var(--anim-photo-ms) var(--ease-out);
	transition-delay: var(--reveal-delay, 0ms);
}

/* lead lines (heading rule, text dash) draw in from the left
   while their block fades up — starts 80ms in, settles 40ms before the
   parent does, so the block reads as one motion finished by its line.
   Transition on the destination state only, same reason as above. */
body.halif-anim .halif-reveal .halif-heading__rule,
body.halif-anim .halif-reveal .halif-text__dash {
	transform: scaleX(0);
	transform-origin: left center;
}

body.halif-anim .halif-reveal.is-in .halif-heading__rule,
body.halif-anim .halif-reveal.is-in .halif-text__dash {
	transform: scaleX(1);
	transition: transform 380ms var(--ease-out);
	transition-delay: calc(var(--reveal-delay, 0ms) + 80ms);
}

/* checked list: a group, not a single unit. The container stays put and its
   rows carry the reveal, so the ticked grid builds top-down off one trigger
   instead of arriving as a slab — same override shape as the separator below.
   --check-row is the VISUAL row, measured in halif-anim.js: the list is
   `columns: 2`, so the sixth item sits at the top of the second column and a
   source-order stagger would throw the eye back up. 8px of travel, not the
   unit's 12: a row is 46px tall, and repeated five times over the 12px reads
   as a ripple rather than a settle. */
body.halif-anim .halif-list.is-style-checks.halif-reveal {
	opacity: 1;
	transform: none;
}

body.halif-anim .halif-list.is-style-checks.halif-reveal li {
	opacity: 0;
	transform: translateY(8px);
}

body.halif-anim .halif-list.is-style-checks.halif-reveal.is-in li {
	opacity: 1;
	transform: none;
	transition:
		opacity var(--anim-reveal-ms) var(--ease-out),
		transform var(--anim-reveal-ms) var(--ease-out);
	/* offsets from the container's own delay, so a row never starts before
	   the unit it belongs to — same inheritance the lead lines above use */
	transition-delay: calc(
		var(--reveal-delay, 0ms) + var(--check-row, 0) * var(--anim-check-stagger)
	);
}

/* brand separator draws in from the left instead of fading up;
   the full-width hairline variant is structural and keeps the fade */
body.halif-anim .halif-separator.halif-reveal:not(.is-style-line) {
	opacity: 1;
	transform: scaleX(0);
	transform-origin: left center;
}

body.halif-anim .halif-separator.halif-reveal.is-in:not(.is-style-line) {
	transform: scaleX(1);
}

/* decoration stroke draw (assets/js/halif-anim.js): inlined lined SVGs
   (inc/decoration.php) trace their outline once when scrolled into view.
   .halif-deco-draw + --deco-len (measured path length) are set BY JS only —
   no JS means the artwork is simply visible. The --deco-beat delay lets the
   section arrive first; the eye lands on content, then catches the line. */
body.halif-anim .halif-deco {
	--deco-beat: 0ms;
}

/* above the fold at load (.is-load): the decoration rides the load cascade,
   so its beat is one cascade step — it arrives with the body text, a step
   behind the headline, instead of ahead of it or 200ms after the page. */
body.halif-anim .halif-deco.is-load {
	--deco-beat: 80ms;
}

body.halif-anim .halif-deco .halif-deco-draw {
	stroke-dasharray: var(--deco-len);
	stroke-dashoffset: var(--deco-len);
}

/* Transition lives on the destination state only — the hidden state must
   apply instantly even if a paint sneaks in before the deferred script.
   --ease-in-out, not --ease-out: a stroke drawing itself is movement across
   the screen, not an element entering. The strong ease-out (quint) put ~76%
   of the path down in the first fifth of the duration and then crawled the
   rest — a flash followed by a second of dead time. */
body.halif-anim .halif-deco.is-in .halif-deco-draw {
	stroke-dashoffset: 0;
	transition: stroke-dashoffset var(--anim-deco-line-ms) var(--ease-in-out);
	transition-delay: var(--deco-beat);
}

/* filled decorations draw too: the shape borrows a stroke in its own fill
   color (--deco-fill, set by JS) — the outline traces itself like the lined
   glyphs, then the fill blooms in under it while the line finishes. Cleanup
   drops the borrowed stroke; fill-opacity's resting value is 1, so the end
   state is exactly the plain filled artwork.
   The borrowed stroke is the fill's own color, so this only reads on artwork
   whose fill already contrasts with its section; halif-anim.js measures that
   and routes the watermarks to the backdrop instead. */
body.halif-anim .halif-deco .halif-deco-trace {
	fill-opacity: 0;
	stroke: var(--deco-fill);
	stroke-width: 2;
	stroke-dasharray: var(--deco-len);
	stroke-dashoffset: var(--deco-len);
}

body.halif-anim .halif-deco.is-in .halif-deco-trace {
	stroke-dashoffset: 0;
	fill-opacity: 1;
	transition:
		stroke-dashoffset var(--anim-deco-ms) var(--ease-in-out) var(--deco-beat),
		fill-opacity 700ms var(--ease-out) calc(var(--deco-beat) + 800ms);
}

/* Decorations with no outline to trace settle in as the backdrop instead —
   photo-panel convention: fade + 1.04 → 1, on the same trigger and beat.
   Two kinds land here: img fallbacks that couldn't inline, and filled
   decorations too low-contrast to trace (halif-anim.js). The hidden transform
   repeats the anchor translate from shared/decoration.css so the scale
   composes with it instead of replacing it; .is-in restates the base values
   so both properties transition, then cleanup returns it to the base rules. */
body.halif-anim .halif-deco.halif-deco-backdrop {
	opacity: 0;
	transform: translate(
			calc(var(--deco-ax-e) * -100%),
			calc(var(--deco-ay-e) * -100%)
		)
		scale(1.04);
}

body.halif-anim .halif-deco.halif-deco-backdrop.is-in {
	opacity: var(--deco-o-e);
	transform: translate(
		calc(var(--deco-ax-e) * -100%),
		calc(var(--deco-ay-e) * -100%)
	);
	transition:
		opacity var(--anim-photo-ms) var(--ease-out),
		transform var(--anim-photo-ms) var(--ease-out);
	transition-delay: var(--deco-beat);
}

/* on the load cascade it settles at the cascade's own pace, so the first
   screen finishes as one gesture instead of the backdrop trailing it by
   half a second */
body.halif-anim .halif-deco.halif-deco-backdrop.is-load.is-in {
	transition-duration: var(--anim-reveal-load-ms);
}

/* ---------- button feedback (halif/button + cookie accept) ---------- */

body.halif-anim .halif-btn,
body.halif-anim .halif-cookie-bar__accept {
	transition:
		transform var(--anim-press-ms) var(--ease-out),
		opacity var(--anim-press-ms) var(--ease-out);
}

body.halif-anim .halif-btn:active,
body.halif-anim .halif-cookie-bar__accept:active {
	transform: scale(0.97);
}

@media (hover: hover) and (pointer: fine) {
	body.halif-anim .halif-btn.is-style-primary:hover,
	body.halif-anim .halif-cookie-bar__accept:hover {
		opacity: 0.88;
	}
}

/* ---------- cookie bar enter/exit (assets/js/halif-bar.js) ----------
   symmetric path: rises in from the bottom edge, leaves the same way */

body.halif-anim .halif-cookie-bar {
	transition:
		opacity var(--anim-bar-ms) var(--ease-out),
		transform var(--anim-bar-ms) var(--ease-out);
}

body.halif-anim .halif-cookie-bar.is-out {
	opacity: 0;
	transform: translateY(8px);
	/* exit is snappier than enter — the user already acted */
	transition-duration: 200ms;
}

/* Reduced motion always wins, toggle on or off: collapse every
   animation/transition to imperceptible instead of `none` so JS
   waiting on transitionend/animationend still completes. */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		transition-delay: 0s !important; /* staggers off too — no delayed pops */
		scroll-behavior: auto !important;
	}

	/* Reduced motion means fewer and gentler animations, not none. The blanket
	   above snaps the entire first screen into existence at once, which is its
	   own jolt. Reveal units keep the fade and lose the travel: the movement is
	   the part that provokes motion sickness, the opacity is the part that says
	   "this arrived". Everything else — the decoration draw, the photo zoom,
	   the slider push — stays snapped, because those ARE movement. Both rules
	   outrank the `*` above on specificity, so source order is not load-bearing. */
	body.halif-anim .halif-reveal {
		transform: none !important;
	}

	body.halif-anim .halif-reveal.is-in {
		transition-duration: var(--anim-reduced-ms) !important;
	}
}

/* =============================================================
   Archive pages (archive-news / archive-event / news taxonomies)
   The page title is core query-title — the one core block those
   templates still need — styled to the H2 heading scale so the
   archives read like any other section lead.
   ============================================================= */

.halif-section .wp-block-query-title {
	margin: 0 0 var(--heading-space-md);
	color: var(--ctx-text);
	font-family: var(--font-heading);
	font-weight: 600;
	font-size: var(--text-display-h2);
	line-height: var(--leading-display-h2);
	letter-spacing: 0;
}
