/**
 * Finewerk Changing Heading
 *
 * Scoped, BEM-style styles for a heading whose last word or phrase rotates.
 * All custom properties are namespaced with --fw-ch- to avoid collisions.
 *
 * The seamless-sentence contract
 * ------------------------------
 * The changing part is an inline-block on the SAME line box as the static
 * text, separated from it by one ordinary word space in the markup. It has
 * no margin, no padding, no border, no background and no width of its own:
 * it is exactly as wide as the phrase currently showing, and it sits on the
 * text baseline like any other word. Every typographic value — family, size,
 * weight, line height, letter spacing, word spacing, transform — is
 * inherited, so at rest the sentence is indistinguishable from one written
 * as a single run of text.
 *
 * Why paint containment and not overflow: hidden
 * ----------------------------------------------
 * An inline-block whose overflow is anything other than `visible` stops
 * exposing its text baseline — CSS aligns it by its bottom margin edge
 * instead, which drops the changing word about a descender below the rest of
 * the sentence. Paint containment clips the same pixels while leaving layout
 * and the baseline completely untouched (only layout and size containment
 * affect baselines), so it is the clipping that keeps the promise above. It
 * also takes the parked phrases out of the page's scrollable area, which a
 * paint-only clip does not: a long phrase waiting above the line is still a
 * wide out-of-flow box, and on a narrow screen that alone is enough to give
 * the page a horizontal scrollbar. `clip-path` stays behind it as the
 * fallback for engines without containment.
 *
 * Containment clips at the padding box, so the clip region is shaped by the
 * box's own padding: --fw-ch-clip of headroom above and below for ascenders
 * and descenders at tight line heights, --fw-ch-clip-x at the sides for an
 * italic tail or a letter-spacing overhang. An equal negative margin gives
 * every pixel of that padding straight back, so the line box, the word space
 * either side and the sentence's total width are exactly what they would be
 * without it. (Inline-level boxes never collapse margins, so this cannot
 * disturb anything around the heading.)
 *
 * Because the clip reaches --fw-ch-clip past the line box, a phrase has to
 * travel its own height PLUS twice that headroom to be fully out of sight —
 * which is exactly --fw-ch-travel.
 */

/* ------------------------------------------------------------------
 * Typography defaults (see assets/css/fonts.css)
 *
 * Hubot Sans for headings. Declared at single-class specificity so an
 * explicit Elementor Typography control (which Elementor writes at
 * element-id specificity) always overrides it. No !important anywhere.
 * ---------------------------------------------------------------- */

.finewerk-changing-heading {
	/* Slide timing. The duration is also written inline on the element by
	   the widget, so PHP, CSS and JS always agree on one number. */
	--fw-ch-duration: 600ms;
	--fw-ch-easing: cubic-bezier(0.22, 0.61, 0.36, 1);

	/* Headroom kept outside the line box, above and below the clip. */
	--fw-ch-clip: 0.35em;

	/* Sideways slack: an italic tail's worth, never enough to let a phrase
	   escape the box while the sentence width is easing. */
	--fw-ch-clip-x: 0.12em;

	/* The width settles ahead of the slide, so the phrase is at its final
	   width well before it has finished coming down. */
	--fw-ch-width-duration: calc(var(--fw-ch-duration) * 0.7);

	/* Distance a phrase travels to leave (down) or to arrive from (up). */
	--fw-ch-travel: calc(100% + (var(--fw-ch-clip) * 2));
	--fw-ch-travel-up: calc((100% + (var(--fw-ch-clip) * 2)) * -1);

	display: block;
	margin: 0;
	color: #1E1E1E;
	font-family: var(--fw-font-title);
	font-size: clamp(28px, 3.6vw, 56px);
	font-weight: 700;
	line-height: 1.15;
	letter-spacing: -0.01em;
	overflow-wrap: break-word;
}

/* ---------- Static half ---------- */

/* Deliberately styleless: it is plain inline text in the heading, and the
   only thing between it and the changing half is the markup's word space. */
.finewerk-changing-heading__static {
	color: inherit;
}

/* ---------- Changing half ---------- */

/* inline-block so its children can be transformed and clipped, `baseline`
   so it sits on the sentence's baseline like a word, and shrink-to-fit so
   it is only ever as wide as the phrase on screen — the longest phrase
   reserves nothing. During a change JavaScript locks `width` in pixels and
   transitions it to the next phrase's width, so the sentence grows or
   shrinks smoothly instead of snapping. */
.finewerk-changing-heading__changing {
	position: relative;
	display: inline-block;
	vertical-align: baseline;

	/* Clip headroom, handed straight back to the line — see the note above.
	   content-box is pinned so a theme-wide `border-box` cannot make the
	   width JavaScript sets mean something else. */
	box-sizing: content-box;
	padding: var(--fw-ch-clip) var(--fw-ch-clip-x);
	margin: calc(var(--fw-ch-clip) * -1) calc(var(--fw-ch-clip-x) * -1);
	border: 0;
	background: none;
	color: inherit;
	font: inherit;
	letter-spacing: inherit;
	word-spacing: inherit;
	text-transform: inherit;
	clip-path: inset(0);
	contain: paint;
}

.finewerk-changing-heading__changing.is-animating {
	transition: width var(--fw-ch-width-duration) var(--fw-ch-easing);
}

/* ---------- Phrases ---------- */

/* Every phrase but the current one is out of flow, hidden, and parked one
   travel above the line, ready to come down. `width: max-content` keeps a
   phrase hugging its own text even while the wrapper is a different width
   mid-transition, so the text never drifts sideways under a centered or
   right alignment. `nowrap` keeps a phrase on one line, which is what holds
   the heading's height steady from one phrase to the next. */
.finewerk-changing-heading__phrase {
	position: absolute;
	/* The padding box is what absolute offsets resolve against, so the
	   headroom is added back here to land on the in-flow phrase exactly. */
	inset-inline-start: var(--fw-ch-clip-x);
	top: var(--fw-ch-clip);
	display: block;
	width: max-content;
	max-width: none;
	margin: 0;
	padding: 0;
	color: inherit;
	font: inherit;
	letter-spacing: inherit;
	word-spacing: inherit;
	text-transform: inherit;
	white-space: nowrap;
	visibility: hidden;
	pointer-events: none;
	transform: translateY(var(--fw-ch-travel-up));
}

/* The one visible phrase is the only in-flow child: it gives the wrapper its
   width, its height and — through it — the sentence's baseline. */
.finewerk-changing-heading__phrase.is-current {
	position: relative;
	top: auto;
	inset-inline-start: auto;
	visibility: visible;
	pointer-events: auto;
	transform: none;
}

/* Staged for the change: visible, still parked above the line. */
.finewerk-changing-heading__phrase.is-incoming {
	visibility: visible;
}

/* Ordered after .is-current and .is-incoming on purpose: these two carry the
   same specificity, so source order is what decides the transform. */
.finewerk-changing-heading__phrase.is-entering {
	transform: none;
}

.finewerk-changing-heading__phrase.is-leaving {
	transform: translateY(var(--fw-ch-travel));
}

/* Transitions exist only while a change is running, so the class swap that
   ends one can never animate. will-change is scoped to the same window
   rather than left on every phrase for the life of the page. */
.finewerk-changing-heading__changing.is-animating .finewerk-changing-heading__phrase {
	transition: transform var(--fw-ch-duration) var(--fw-ch-easing);
	will-change: transform;
}

/* ---------- Reduced motion ---------- */

/* changing-heading.js never starts the rotation for these visitors, so the
   heading simply stands on its first phrase. This is the belt-and-braces
   half: should a change already be in flight when the preference changes,
   it lands instantly instead of sliding. */
@media (prefers-reduced-motion: reduce) {
	.finewerk-changing-heading__changing.is-animating,
	.finewerk-changing-heading__changing.is-animating .finewerk-changing-heading__phrase {
		transition: none;
	}
}
