/**
 * Animated Heading — .espt-block-heading
 *
 * Vars set by the block: --size --line-height --weight --base-color
 * --hl-color --hl-scale --hl-weight --hl-bg --hl-ink --deco-color --deco-size
 * --grad-a --grad-b --hl-pad-x --hl-pad-y --hl-radius --fx-dur
 * --cursor-color --max-width --box-bg --box-pad --box-bw --box-line
 * --box-radius --space-below. Root classes: is-template-*, is-effect-*,
 * is-text-align-*, is-hl-nowrap / no-hl-nowrap, is-limit-width, is-box-align-*,
 * is-box-shadow-*. JS adds .is-hn-ready and .is-in for the on-scroll entrances.
 * Mobile-first; nothing may overflow a 320px viewport.
 */

.espt-block-heading {
	margin-bottom: var(--space-below, 24px);
}

/* ------------------------------------------------------------- the box */

.espt-block-heading .espt-hn {
	background: var(--box-bg, transparent);
	border: var(--box-bw, 0) solid var(--box-line, var(--bd-line));
	border-radius: var(--box-radius, 0);
	display: block;
	max-width: 100%;
	min-width: 0;
	padding: var(--box-pad, 0);
}

.espt-block-heading.is-limit-width .espt-hn {
	max-width: min(100%, var(--max-width, 720px));
}

.espt-block-heading.is-box-align-left .espt-hn { margin-left: 0; margin-right: auto; }
.espt-block-heading.is-box-align-center .espt-hn { margin-left: auto; margin-right: auto; }
.espt-block-heading.is-box-align-right .espt-hn { margin-left: auto; margin-right: 0; }

.espt-block-heading.is-box-shadow-soft .espt-hn { box-shadow: 0 6px 24px -10px rgba(15, 23, 42, 0.18); }
.espt-block-heading.is-box-shadow-strong .espt-hn { box-shadow: 0 18px 40px -16px rgba(15, 23, 42, 0.35); }
.espt-block-heading.is-box-shadow-glow .espt-hn {
	box-shadow:
		0 0 0 1px color-mix(in srgb, var(--hl-color, var(--bd-primary)) 22%, transparent),
		0 14px 34px -14px color-mix(in srgb, var(--hl-color, var(--bd-primary)) 45%, transparent);
}

/* ------------------------------------------------------------ the line */

.espt-block-heading .espt-hn-line {
	color: var(--base-color, var(--bd-ink));
	font-size: var(--size, 26px);
	font-weight: var(--weight, 800);
	line-height: var(--line-height, 1.25);
	margin: 0;
	min-width: 0;
	overflow-wrap: anywhere;
	text-align: center;
	text-wrap: balance;
}

.espt-block-heading.is-text-align-left .espt-hn-line { text-align: left; }
.espt-block-heading.is-text-align-center .espt-hn-line { text-align: center; }
.espt-block-heading.is-text-align-right .espt-hn-line { text-align: right; }

.espt-block-heading .espt-hn-link {
	color: inherit;
	text-decoration: none;
	transition: filter 0.2s ease;
}

.espt-block-heading .espt-hn-link:hover,
.espt-block-heading .espt-hn-link:focus-visible {
	color: inherit;
	filter: brightness(1.07);
}

.espt-block-heading .espt-block-note {
	background: var(--bd-soft);
	border: 1px dashed var(--bd-soft-line);
	border-radius: 10px;
	color: var(--bd-muted);
	font-size: 14px;
	margin: 0;
	padding: 16px;
	text-align: center;
}

/* ------------------------------------------------------- the highlight */

.espt-block-heading .espt-hn-hl {
	background: var(--hl-bg, transparent);
	color: var(--hl-color, var(--bd-primary));
	font-size: calc(1em * var(--hl-scale, 100) / 100);
	font-weight: var(--hl-weight, 800);
	max-width: 100%;
	padding: var(--hl-pad-y, 0) var(--hl-pad-x, 0);
	position: relative;
}

/* Rotating words: a grid cell shared by the ghost list (which reserves the
   width of the longest word) and the live word, so nothing jumps. Baseline —
   not bottom — alignment keeps the rotating word on the same line as the
   static text even when the highlight is scaled up or the reserved box is two
   lines tall. Only the slide effect, which needs overflow:hidden and so loses
   its real baseline, falls back to bottom alignment further down. */
.espt-block-heading .espt-hn-stack {
	align-items: baseline;
	display: inline-grid;
	max-width: 100%;
	min-width: 0;
	vertical-align: baseline;
}

/* On a narrow phone the longest word may need two lines; the ghosts then
   reserve that height too, and baseline alignment keeps the static text on
   the first line instead of dropping it to the bottom of the reserved box. */
.espt-block-heading .espt-hn-ghosts {
	align-items: baseline;
	display: inline-grid;
	grid-area: 1 / 1;
	max-width: 100%;
	min-width: 0;
	visibility: hidden;
}

.espt-block-heading .espt-hn-ghosts > i {
	font-style: inherit;
	grid-area: 1 / 1;
	min-width: 0;
}

.espt-block-heading .espt-hn-live {
	grid-area: 1 / 1;
	min-height: 1em;
	min-width: 0;
	text-align: inherit;
}

.espt-block-heading .espt-hn-rot {
	display: inline-block;
	min-width: 0;
	will-change: transform, opacity;
}

.espt-block-heading .espt-hn-cursor {
	animation: espt-hn-blink 1.05s steps(1) infinite;
	border-right: 0.06em solid var(--cursor-color, currentColor);
	display: inline-block;
	height: 1em;
	margin-left: 0.05em;
	vertical-align: -0.12em;
	width: 0;
}

@keyframes espt-hn-blink { 50% { opacity: 0; } }

/* ---------------------------------------------------------- templates */

/* 02 — gradient text */
.espt-block-heading.is-template-gradient .espt-hn-hl {
	-webkit-background-clip: text;
	background-clip: text;
	background-image: linear-gradient(92deg, var(--grad-a, var(--bd-primary)), var(--grad-b, var(--bd-accent)));
	color: transparent;
	-webkit-text-fill-color: transparent;
}

/* 03 — underline bar */
.espt-block-heading.is-template-underline .espt-hn-hl {
	padding-bottom: max(var(--hl-pad-y, 0px), 0.14em);
}

.espt-block-heading.is-template-underline .espt-hn-hl::after {
	background: var(--deco-color, var(--bd-accent));
	border-radius: 999px;
	bottom: 0;
	content: "";
	height: var(--deco-size, 6px);
	left: 0;
	position: absolute;
	right: 0;
}

/* 04 — marker highlight */
.espt-block-heading.is-template-marker .espt-hn-hl {
	background-image: linear-gradient(
		transparent 12%,
		var(--deco-color, var(--bd-accent)) 12%,
		var(--deco-color, var(--bd-accent)) 88%,
		transparent 88%
	);
	-webkit-box-decoration-break: clone;
	box-decoration-break: clone;
	padding-left: max(var(--hl-pad-x, 0px), 0.14em);
	padding-right: max(var(--hl-pad-x, 0px), 0.14em);
}

/* 05 — solid box */
.espt-block-heading.is-template-box .espt-hn-hl {
	background: var(--hl-bg, var(--hl-color, var(--bd-primary)));
	border-radius: max(var(--hl-radius, 0px), 0.14em);
	color: var(--hl-ink, #fff);
	padding: max(var(--hl-pad-y, 0px), 0.06em) max(var(--hl-pad-x, 0px), 0.3em);
}

/* 06 — outline text */
.espt-block-heading.is-template-outline .espt-hn-hl {
	color: transparent;
	-webkit-text-fill-color: transparent;
	-webkit-text-stroke: max(1px, 0.022em) var(--hl-color, var(--bd-primary));
}

/* 07 — soft pill */
.espt-block-heading.is-template-pill .espt-hn-hl {
	background: var(--hl-bg, color-mix(in srgb, var(--hl-color, var(--bd-primary)) 14%, transparent));
	border-radius: 999px;
	padding: max(var(--hl-pad-y, 0px), 0.06em) max(var(--hl-pad-x, 0px), 0.44em);
}

/* 08 — shadow pop */
.espt-block-heading.is-template-shadow .espt-hn-hl {
	text-shadow: 0.03em 0.06em 0 rgba(0, 0, 0, 0.16), 0 0.16em 0.3em rgba(0, 0, 0, 0.18);
}

/* 09 — wavy underline */
.espt-block-heading.is-template-wave-line .espt-hn-hl {
	text-decoration-color: var(--deco-color, var(--bd-accent));
	text-decoration-line: underline;
	text-decoration-style: wavy;
	text-decoration-thickness: max(1px, 0.035em);
	text-underline-offset: 0.2em;
}

/* 10 — circled words */
.espt-block-heading.is-template-circle .espt-hn-hl {
	padding: max(var(--hl-pad-y, 0px), 0.1em) max(var(--hl-pad-x, 0px), 0.42em);
}

.espt-block-heading.is-template-circle .espt-hn-hl::after {
	border: max(2px, calc(var(--deco-size, 6px) * 0.5)) solid var(--deco-color, var(--bd-accent));
	border-radius: 46% 54% 50% 50% / 60% 55% 45% 40%;
	content: "";
	inset: -0.04em -0.06em;
	opacity: 0.9;
	pointer-events: none;
	position: absolute;
	transform: rotate(-1.5deg);
}

/* 11 — gradient box */
.espt-block-heading.is-template-grad-box .espt-hn-hl {
	background-image: linear-gradient(92deg, var(--grad-a, var(--bd-primary)), var(--grad-b, var(--bd-primary-dark)));
	border-radius: max(var(--hl-radius, 0px), 0.14em);
	color: var(--hl-ink, #fff);
	padding: max(var(--hl-pad-y, 0px), 0.06em) max(var(--hl-pad-x, 0px), 0.3em);
}

/* Filled templates need a block box so padding + radius behave. */
.espt-block-heading.is-template-box .espt-hn-hl,
.espt-block-heading.is-template-pill .espt-hn-hl,
.espt-block-heading.is-template-grad-box .espt-hn-hl {
	display: inline-block;
}

/* -------------------------------- effects: cycling word transitions */

/* The perspective must sit on the rotating word's *direct* parent, otherwise
   rotateX() flattens into a vertical squash instead of a 3D flip. */
.espt-block-heading.is-effect-flip .espt-hn-live { perspective: 600px; }

.espt-block-heading.is-effect-flip .espt-hn-rot.is-out { animation: espt-hn-flip-out var(--hn-trans, 500ms) forwards; }
.espt-block-heading.is-effect-flip .espt-hn-rot.is-in { animation: espt-hn-flip-in var(--hn-trans, 500ms); }
@keyframes espt-hn-flip-out { to { opacity: 0; transform: rotateX(-90deg); } }
@keyframes espt-hn-flip-in { from { opacity: 0; transform: rotateX(90deg); } to { opacity: 1; transform: rotateX(0); } }

.espt-block-heading.is-effect-slide .espt-hn-stack {
	margin-bottom: -0.1em;
	overflow: hidden;
	padding-bottom: 0.1em;
	vertical-align: bottom;
}

.espt-block-heading.is-effect-slide .espt-hn-rot.is-out { animation: espt-hn-slide-out var(--hn-trans, 500ms) forwards; }
.espt-block-heading.is-effect-slide .espt-hn-rot.is-in { animation: espt-hn-slide-in var(--hn-trans, 500ms); }
@keyframes espt-hn-slide-out { to { opacity: 0; transform: translateY(-115%); } }
@keyframes espt-hn-slide-in { from { opacity: 0; transform: translateY(115%); } to { opacity: 1; transform: none; } }

.espt-block-heading.is-effect-fade .espt-hn-rot.is-out { animation: espt-hn-fade-out var(--hn-trans, 500ms) forwards; }
.espt-block-heading.is-effect-fade .espt-hn-rot.is-in { animation: espt-hn-fade-in var(--hn-trans, 500ms); }
@keyframes espt-hn-fade-out { to { opacity: 0; } }
@keyframes espt-hn-fade-in { from { opacity: 0; } to { opacity: 1; } }

.espt-block-heading.is-effect-swap .espt-hn-rot.is-out { animation: espt-hn-swap-out var(--hn-trans, 500ms) forwards; }
.espt-block-heading.is-effect-swap .espt-hn-rot.is-in { animation: espt-hn-swap-in var(--hn-trans, 500ms); }
@keyframes espt-hn-swap-out { to { opacity: 0; transform: scale(0.4); } }
@keyframes espt-hn-swap-in { from { opacity: 0; transform: scale(1.6); } to { opacity: 1; transform: none; } }

/* The typewriter grows letter by letter, so the word stays inline. */
.espt-block-heading.is-effect-typing .espt-hn-rot { display: inline; }

/* ------------------------------------ effects: looping CSS animations */

.espt-block-heading.is-effect-gradient .espt-hn-hl {
	animation: espt-hn-grad var(--fx-dur, 4s) linear infinite;
	-webkit-background-clip: text;
	background-clip: text;
	background-image: linear-gradient(
		90deg,
		var(--grad-a, var(--hl-color, var(--bd-primary))),
		var(--grad-b, var(--bd-accent)),
		var(--grad-a, var(--hl-color, var(--bd-primary)))
	);
	background-size: 200% auto;
	color: transparent;
	-webkit-text-fill-color: transparent;
	-webkit-text-stroke: 0;
}

@keyframes espt-hn-grad { to { background-position: 200% center; } }

.espt-block-heading.is-effect-shine .espt-hn-hl {
	animation: espt-hn-shine var(--fx-dur, 4s) linear infinite;
	-webkit-background-clip: text;
	background-clip: text;
	background-image: linear-gradient(
		110deg,
		var(--hl-color, var(--bd-primary)) 0 42%,
		#fff 50%,
		var(--hl-color, var(--bd-primary)) 58% 100%
	);
	background-size: 220% auto;
	color: transparent;
	-webkit-text-fill-color: transparent;
	-webkit-text-stroke: 0;
}

@keyframes espt-hn-shine { to { background-position: -220% center; } }

.espt-block-heading.is-effect-wave .espt-hn-w {
	animation: espt-hn-wave var(--fx-dur, 4s) ease-in-out infinite;
	display: inline-block;
}

@keyframes espt-hn-wave {
	0%, 60%, 100% { transform: none; }
	30% { transform: translateY(-0.22em); }
}

/* ------------------------------- effects: one-shot on-scroll entrance */

.espt-block-heading.is-hn-ready .espt-hn-anim { will-change: opacity, transform, filter, clip-path; }

.espt-block-heading.is-hn-ready.is-effect-reveal .espt-hn-anim {
	clip-path: inset(0 100% 0 0);
	transition: clip-path 0.8s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.espt-block-heading.is-hn-ready.is-effect-reveal.is-in .espt-hn-anim { clip-path: inset(0 0 0 0); }

.espt-block-heading.is-hn-ready.is-effect-fade-up .espt-hn-anim {
	opacity: 0;
	transform: translateY(28px);
	transition: opacity 0.7s ease, transform 0.7s ease;
}
.espt-block-heading.is-hn-ready.is-effect-fade-up.is-in .espt-hn-anim { opacity: 1; transform: none; }

.espt-block-heading.is-hn-ready.is-effect-blur-in .espt-hn-anim {
	filter: blur(14px);
	opacity: 0;
	transition: opacity 0.7s ease, filter 0.7s ease;
}
.espt-block-heading.is-hn-ready.is-effect-blur-in.is-in .espt-hn-anim { filter: blur(0); opacity: 1; }

.espt-block-heading.is-hn-ready.is-effect-focus-in .espt-hn-anim {
	filter: blur(12px);
	letter-spacing: 0.28em;
	opacity: 0;
	transition: opacity 0.8s ease, filter 0.8s ease, letter-spacing 0.8s ease;
}
.espt-block-heading.is-hn-ready.is-effect-focus-in.is-in .espt-hn-anim {
	filter: blur(0);
	letter-spacing: normal;
	opacity: 1;
}

.espt-block-heading.is-hn-ready.is-effect-zoom-in .espt-hn-anim {
	opacity: 0;
	transform: scale(0.6);
	transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.espt-block-heading.is-hn-ready.is-effect-zoom-in.is-in .espt-hn-anim { opacity: 1; transform: none; }

/* ------------------------------------------------------- breakpoints */

@media (min-width: 768px) {
	/* "Keep on one line" only from tablets up — a phone must never side-scroll
	   because a long highlighted phrase refused to wrap. */
	.espt-block-heading.is-hl-nowrap .espt-hn-hl,
	.espt-block-heading.is-hl-nowrap .espt-hn-ghosts > i {
		white-space: nowrap;
	}

	.espt-block-heading.is-template-circle .espt-hn-hl::after { inset: -0.06em -0.1em; }
}

@media (prefers-reduced-motion: reduce) {
	.espt-block-heading .espt-hn-cursor,
	.espt-block-heading .espt-hn-rot,
	.espt-block-heading .espt-hn-w,
	.espt-block-heading.is-effect-gradient .espt-hn-hl,
	.espt-block-heading.is-effect-shine .espt-hn-hl,
	.espt-block-heading.is-effect-wave .espt-hn-w,
	.espt-block-heading:is(.is-effect-flip, .is-effect-slide, .is-effect-fade, .is-effect-swap) .espt-hn-rot:is(.is-in, .is-out) {
		animation: none;
		opacity: 1;
		transform: none;
	}

	.espt-block-heading .espt-hn-anim,
	.espt-block-heading .espt-hn-link {
		transition: none;
	}
}
