/* ==========================================================================
 * QynTro AI - Hero heading
 *
 * Line one rises word by word out of a blur, with a light passing across it.
 * Line two cycles the full model roster, each name arriving on a WIPE: a blade
 * of light crosses the line, takes the old name with it and leaves the new one
 * behind.
 *
 * The Animated Headlines plugin keeps running and keeps its timing. This file
 * and its JS only change how the switch LOOKS - the plugin still decides when.
 * If the JS never runs, the headline is exactly what it is today.
 *
 * ADDITIVE. Everything is .qhh- prefixed except where it has to reach the
 * theme's own `.ah-` classes, and those are only ever decorated.
 *
 * DEPLOY ORDER MATTERS: upload FIRST, then bump ?v=. 7-day cache.
 * ========================================================================== */

/* ==========================================================================
 * LINE ONE
 * ========================================================================== */

/* AOS already fades the whole h1 in. Its animation is neutralised here so the
   per-word rise below is the only thing moving - running both at once reads as
   mush, and AOS wins on the parent while the words move inside it.

   Deliberately NOT scoped to #main: this page has two elements sharing that id,
   and while a CSS id selector happens to match both, depending on that is a
   trap waiting for whoever reads this next. The .qhh- class is only ever added
   to the real headline, so it is scope enough. */
.frontend-body h1.qhh-h1 {
	opacity: 1 !important;
	transform: none !important;
	position: relative;
}

/* qy-speed-3 (2026-10-02): .01, not 0 - a word that starts fully transparent and rises on the compositor is never
   recorded as painted until something else repaints it, so PageSpeed read the headline (desktop's LCP) seconds after
   it was on screen.
   qy-speed-8 (2026-10-02): .6 and a 4px blur - since qy-speed-7 the headline is on screen before the scripts, and a word
   at .01 was counted only once it had faded in (desktop LCP 2.8 or 4.3 s, depending on how busy the machine was). The
   rise is the same. Uddhab: "Headline fix + lazy tracking (Recommended)". */
.qhh-h1 .qhh-w {
	display: inline-block;
	opacity: .6;
	filter: blur(4px);
	transform: translateY(26px) scale(.97);
	animation: qhh-word .8s cubic-bezier(.2, .9, .3, 1) forwards;
	animation-delay: var(--d, 0ms);
}
@keyframes qhh-word {
	to { opacity: 1; filter: blur(0); transform: none; }
}

/* A light crossing the line every 7s. Sits above the type but takes no clicks. */
.qhh-h1::after {
	content: "";
	position: absolute;
	top: -6%;
	bottom: -6%;
	left: -25%;
	width: 22%;
	background: linear-gradient(100deg, transparent, rgba(209, 254, 23, .20), transparent);
	filter: blur(14px);
	pointer-events: none;
	animation: qhh-shine 7s ease-in-out 2.2s infinite;
}
@keyframes qhh-shine {
	0%        { left: -25%; opacity: 0; }
	8%        { opacity: 1; }
	38%, 100% { left: 110%; opacity: 0; }
}

/* ==========================================================================
 * LINE TWO - the roster
 * ========================================================================== */

/* `width: auto` with !important, and it has to be !important.
 *
 * The plugin sizes this wrapper to fit the category words that ship in the
 * markup - 299px, the width of "AVATAR" - and writes that as an INLINE style it
 * re-animates on every switch. The line now shows model names instead, up to
 * "Luma Dream Machine" at 759px, so that inline width would crop most of them.
 * A stylesheet !important is the one thing that outranks an inline style, and
 * it lets the wrapper size itself to whatever is actually in it. */
.qhh-rot .ah-words-wrapper {
	position: relative;
	width: auto !important;
	white-space: nowrap;
}

/* Brackets are pseudo-elements on the plugin's own wrapper, so no element is
   inserted into markup the plugin walks with .next() to pick the next word. */
.qhh-rot .ah-words-wrapper::before,
.qhh-rot .ah-words-wrapper::after {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	color: var(--qp-lime, #d1fe17);
	font-weight: 400;
	opacity: .42;
	pointer-events: none;
}
.qhh-rot .ah-words-wrapper::before { content: "["; left: -.42em;  animation: qhh-brl 2.8s ease-in-out infinite; }
.qhh-rot .ah-words-wrapper::after  { content: "]"; right: -.42em; animation: qhh-brr 2.8s ease-in-out infinite; }
@keyframes qhh-brl {
	0%, 100% { transform: translateY(-50%) translateX(0);    opacity: .42; }
	50%      { transform: translateY(-50%) translateX(-7px); opacity: .85; }
}
@keyframes qhh-brr {
	0%, 100% { transform: translateY(-50%) translateX(0);    opacity: .42; }
	50%      { transform: translateY(-50%) translateX(7px);  opacity: .85; }
}

/* The plugin's own `slide` transition is neutralised. It exists to move a word
   in from the side, and with the wipe doing that job the two read as one
   fighting the other - and worse, the outgoing word stays on screen through its
   slide, so both names are briefly visible at once. The plugin keeps switching
   these classes; they just no longer animate anything. */
.qhh-rot b.is-hidden  { opacity: 0 !important; transform: none !important; }
.qhh-rot b.is-visible {
	opacity: 1 !important;
	transform: none !important;
	position: relative !important;
	display: inline-block;
	text-align: center;
	text-shadow: 0 0 38px rgba(209, 254, 23, .5);
}

/* ---- the wipe ----
 *
 * The name is clipped rather than faded, so it is revealed by an edge travelling
 * across it. clip-path is the right tool: it costs nothing to animate and it
 * leaves the type completely crisp, where a mask or a gradient would soften it.
 */
.qhh-txt {
	display: inline-block;
	clip-path: inset(0 0 0 0);
}
.qhh-txt.qhh-out { animation: qhh-wipe-out .3s cubic-bezier(.5, 0, .7, .5) forwards; }
.qhh-txt.qhh-in  { animation: qhh-wipe-in .55s cubic-bezier(.3, .9, .3, 1) forwards; }
@keyframes qhh-wipe-out {
	from { clip-path: inset(0 0 0 0); }
	to   { clip-path: inset(0 0 0 100%); }
}
@keyframes qhh-wipe-in {
	from { clip-path: inset(0 100% 0 0); }
	to   { clip-path: inset(0 0 0 0); }
}

/* The blade itself. Runs out and back: out on the clear, back on the reveal,
   so one pass of light accounts for both halves of the change. */
.qhh-edge {
	position: absolute;
	top: -8%;
	bottom: -8%;
	left: 0;
	width: 4px;
	border-radius: 3px;
	opacity: 0;
	pointer-events: none;
	background: var(--qp-lime, #d1fe17);
	box-shadow: 0 0 22px 5px rgba(209, 254, 23, .8);
}
.qhh-edge.qhh-run { animation: qhh-blade .85s cubic-bezier(.3, .9, .3, 1) forwards; }
@keyframes qhh-blade {
	0%   { left: 0;    opacity: 0; }
	8%   { opacity: 1; }
	35%  { left: 100%; }
	65%  { left: 0;    opacity: 1; }
	100% { left: 0;    opacity: 0; }
}

/* Category line under the name. "Veo 3.1" on its own tells a visitor nothing
   about what it does; this is what keeps the rotation legible. */
.qhh-cat {
	margin: 11px auto 0;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--qp-text-muted, #9b9ea6);
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 9px;
	min-height: 1.2em;   /* reserved, so the line below never shifts */
}
.qhh-new {
	padding: 2px 7px;
	border-radius: 5px;
	font-size: 9px;
	letter-spacing: .1em;
	background: rgba(255, 61, 154, .16);
	border: 1px solid rgba(255, 61, 154, .45);
	color: #ff7ab8;
}

/* Underline, sized to each name by the JS. */
.qhh-bar {
	height: 3px;
	width: 0;
	margin: 14px auto 0;
	border-radius: 3px;
	background: linear-gradient(90deg, transparent, var(--qp-lime, #d1fe17), transparent);
	box-shadow: 0 0 16px rgba(209, 254, 23, .75);
	transition: width .55s cubic-bezier(.2, .9, .3, 1);
	pointer-events: none;
}

/* ==========================================================================
 * CHIP - same pulse as the section heads, so the page has one language
 * ========================================================================== */
.frontend-body h6.qhh-chip {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 7px 16px 7px 13px;
	border-radius: 999px;
	background: rgba(255, 255, 255, .04);
	border: 1px solid rgba(255, 255, 255, .12);
}
.qhh-chip i {
	width: 6px;
	height: 6px;
	border-radius: 50%;
	flex: none;
	background: var(--qp-lime, #d1fe17);
	box-shadow: 0 0 10px var(--qp-lime, #d1fe17);
	animation: qhh-pulse 1.7s ease-in-out infinite;
}
@keyframes qhh-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }

@media (prefers-reduced-motion: reduce) {
	.qhh-h1 .qhh-w { opacity: 1; filter: none; transform: none; animation: none; }
	.qhh-h1::after,
	.qhh-rot .ah-words-wrapper::before,
	.qhh-rot .ah-words-wrapper::after,
	.qhh-chip i { animation: none; }
	.qhh-txt.qhh-out, .qhh-txt.qhh-in { animation: none; clip-path: none; }
	.qhh-edge { display: none; }
	.qhh-bar { transition: none; }
}

/* qy-speed-9 (2026-10-02): the rise the visitor actually sees is qyntro-premium.css's "Liquid Spectrum" -
   `.frontend-body #main .text-container h1.qhh-h1 .qhh-w` outranks the word rule above and animates each word with
   qyh-spectrum-rise, from opacity 0. A word whose first paint is at opacity 0 is not recorded as painted, and the
   rise runs on the compositor, so PageSpeed counted the headline (desktop's largest paint) only when the whole page
   repainted, seconds after it was on screen. The same rise, from 60%: this file loads after qyntro-premium.css, so
   this @keyframes replaces premium's. Uddhab: "Headline fix + lazy tracking (Recommended)". */
@keyframes qyh-spectrum-rise {
	from { opacity: .6; transform: translateY(.3em); }
	to   { opacity: 1; transform: none; }
}
