/**
 * Librería de animaciones — Dr. Paul Terán
 *
 * CÓMO SE USA (sin tocar código):
 *   En Elementor, seleccionar la sección, columna o widget →
 *   pestaña "Avanzado" → campo "Clases CSS" → escribir el nombre de la clase.
 *
 * Se pueden combinar, por ejemplo:  pt-glass pt-reveal-up pt-hover-lift
 *
 * ÍNDICE
 *   1. Revelado al hacer scroll   pt-reveal-up / -down / -left / -right /
 *                                 -zoom / -fade / -mask / -blur
 *   2. Cascada                    pt-stagger  (anima los hijos uno tras otro)
 *   3. Parallax                   pt-parallax-slow / pt-parallax-fast
 *   4. Fondos animados            pt-aurora / pt-gradient-mesh / pt-grid-bg /
 *                                 pt-noise / pt-orbs / pt-beam
 *   5. Movimiento continuo        pt-float / pt-glow-pulse / pt-marquee
 *   6. Interacción                pt-hover-lift / pt-hover-glow / pt-hover-zoom /
 *                                 pt-spotlight / pt-border-glow
 *   7. Detalles                   pt-underline / pt-divider-glow /
 *                                 pt-gradient-text / pt-shimmer
 *
 * Todas las animaciones se desactivan solas si el visitante activó
 * "reducir movimiento" en su sistema operativo (ver bloque final).
 */

/* ==========================================================================
   1. Revelado al hacer scroll
   El script assets/js/site.js añade la clase .is-visible cuando el elemento
   entra en pantalla. Sin JavaScript, el contenido se ve igualmente.
   ========================================================================== */

/*
 * Duraciones cortas a propósito. La entrada tiene que haber terminado antes
 * de que la pieza llegue a la zona de lectura; si no, se percibe como que el
 * scroll se frena esperando al contenido. Un tercio de segundo basta para que
 * el movimiento se lea y no para que se espere.
 */
.pt-js [class*="pt-reveal-"] {
	opacity: 0;
	transition: opacity 0.32s var(--pt-ease-out),
		transform 0.32s var(--pt-ease-out),
		filter 0.32s var(--pt-ease-out),
		clip-path 0.36s var(--pt-ease-out);
	will-change: opacity, transform;
}

/*
 * Una vez revelado, el elemento deja de anunciar que va a cambiar. Sin esto
 * cada tarjeta de la página se quedaba para siempre en su propia capa de
 * composición y el navegador arrastraba ese peso durante todo el scroll.
 */
.pt-js [class*="pt-reveal-"].is-visible,
.pt-js .pt-stagger.is-visible > *,
.pt-js .pt-words.is-visible > span { will-change: auto; }

/* Recorridos cortos: el elemento llega antes a su sitio. */
.pt-js .pt-reveal-up    { transform: translateY(28px); }
.pt-js .pt-reveal-down  { transform: translateY(-28px); }
.pt-js .pt-reveal-left  { transform: translateX(-34px); }
.pt-js .pt-reveal-right { transform: translateX(34px); }
/* Mientras el contenido llega al borde de la ventana (una columna, o el
   contenedor de 1.352 px sin 34 px de margen a cada lado), entrando de lado se
   salía de la pantalla y la página se podía arrastrar hacia los lados
   mientras tanto. Ahí entra subiendo. */
@media (max-width: 1420px) {
	.pt-js .pt-reveal-left,
	.pt-js .pt-reveal-right { transform: translateY(28px); }
}
.pt-js .pt-reveal-zoom  { transform: scale(0.92); }
.pt-js .pt-reveal-fade  { transform: none; }

/* Desenfoque de entrada: el elemento se "enfoca" al aparecer. */
.pt-js .pt-reveal-blur {
	transform: translateY(20px);
	/* 8 px en lugar de 14: desenfocar mientras entra es de lo más caro que
	   puede hacer el navegador, y a este tamaño el efecto se lee igual. */
	filter: blur(8px);
}

/* Barrido: el contenido se descubre de abajo hacia arriba. */
.pt-js .pt-reveal-mask {
	clip-path: inset(0 0 100% 0);
	transform: translateY(14px);
}

.pt-js [class*="pt-reveal-"].is-visible {
	opacity: 1;
	transform: none;
	filter: none;
	clip-path: inset(0 0 0 0);
}

/*
 * Desplazamiento rápido: el script marca el documento y las entradas dejan de
 * animarse. Ver «soltarTodo()» en site.js.
 */
.pt-al-instante [class*="pt-reveal-"],
.pt-al-instante .pt-stagger > *,
.pt-al-instante .pt-words > span {
	transition: none !important;
	transition-delay: 0s !important;
}

/*
 * Dentro del lienzo del editor de Elementor el contenido se muestra siempre.
 * Allí no se ejecuta el IntersectionObserver, así que sin esta regla quien
 * edita la página vería las secciones en blanco.
 */
.elementor-editor-active [class*="pt-reveal-"],
.elementor-editor-active .pt-stagger > *,
.elementor-editor-active .pt-words > span {
	opacity: 1 !important;
	transform: none !important;
	filter: none !important;
	clip-path: none !important;
}

/* Retrasos opcionales: añadir junto a la clase de revelado. */
.pt-delay-1 { transition-delay: 0.04s; }
.pt-delay-2 { transition-delay: 0.08s; }
.pt-delay-3 { transition-delay: 0.12s; }
.pt-delay-4 { transition-delay: 0.16s; }
.pt-delay-5 { transition-delay: 0.2s; }

/* ==========================================================================
   1b. Titular palabra por palabra
   El script envuelve cada palabra en un <span>. Aplicar pt-words al widget
   de encabezado. Sin JavaScript el titular se ve normal.
   ========================================================================== */

.pt-js .pt-words > span {
	display: inline-block;
	opacity: 0;
	transform: translateY(0.5em) rotate(1.5deg);
	transition: opacity 0.4s var(--pt-ease-out), transform 0.45s var(--pt-ease-spring);
	/* El titular termina de entrar aunque tenga muchas palabras: a partir de
	   la undécima todas arrancan a la vez. */
	transition-delay: calc(min(var(--pt-word, 0), 10) * 20ms);
	will-change: opacity, transform;
}

.pt-js .pt-words.is-visible > span {
	opacity: 1;
	transform: none;
}

/* ==========================================================================
   2. Cascada: los hijos directos aparecen uno tras otro
   Aplicar pt-stagger al contenedor (sección, columna o contenedor Elementor).
   ========================================================================== */

.pt-js .pt-stagger > * {
	opacity: 0;
	transform: translateY(22px);
	transition: opacity 0.32s var(--pt-ease-out), transform 0.32s var(--pt-ease-out);
	will-change: opacity, transform;
}

.pt-js .pt-stagger.is-visible > * {
	opacity: 1;
	transform: none;
}

/*
 * El retraso se calcula con --pt-i, que el script asigna a cada hijo. Así la
 * cascada funciona con cualquier número de elementos, no sólo con los ocho
 * primeros.
 */
/*
 * El retraso se limita a los cinco primeros. En la biblioteca de vídeos hay
 * dieciséis tarjetas: sin tope, la última tardaba más de un segundo en
 * aparecer y la rejilla se comía el scroll.
 */
.pt-js .pt-stagger.is-visible > * { transition-delay: calc(min(var(--pt-i, 0), 5) * 22ms); }

/* ==========================================================================
   3. Parallax — el script actualiza --pt-scroll con el avance del elemento
   ========================================================================== */

.pt-parallax-slow,
.pt-parallax-fast {
	will-change: transform;
}

.pt-parallax-slow { transform: translate3d(0, calc(var(--pt-scroll, 0) * -50px), 0); }
.pt-parallax-fast { transform: translate3d(0, calc(var(--pt-scroll, 0) * -110px), 0); }

/* ==========================================================================
   4. Fondos animados
   Aplicar a una sección. El fondo se dibuja detrás del contenido.
   ========================================================================== */

/*
 * «overflow: clip» recorta las capas de luz, que se dibujan más grandes que la
 * sección a propósito. Se usa «clip» y no «hidden» porque «hidden» crearía un
 * contenedor de desplazamiento y rompería la cabecera fija.
 */
.pt-aurora,
.pt-gradient-mesh,
.pt-grid-bg,
.pt-noise,
.pt-orbs,
.pt-beam {
	position: relative;
	isolation: isolate;
}

/* La clase se repite para ganar a la regla de contenedor de Elementor. */
.pt-aurora.pt-aurora,
.pt-gradient-mesh.pt-gradient-mesh,
.pt-grid-bg.pt-grid-bg,
.pt-noise.pt-noise,
.pt-orbs.pt-orbs,
.pt-beam.pt-beam { overflow: clip; }

/*
 * Los selectores van con la clase repetida (.pt-aurora.pt-aurora) porque
 * Elementor declara «.e-con::before { content: var(--background-overlay) }»
 * en una hoja que carga DESPUÉS de esta. Con la misma especificidad ganaba
 * Elementor y el ::before de estas capas no llegaba a dibujarse: las
 * secciones quedaban planas. Al repetir la clase, la regla propia gana.
 */
.pt-aurora.pt-aurora::before,
.pt-aurora.pt-aurora::after,
.pt-gradient-mesh.pt-gradient-mesh::before,
.pt-grid-bg.pt-grid-bg::before,
.pt-noise.pt-noise::before,
.pt-orbs.pt-orbs::before,
.pt-orbs.pt-orbs::after,
.pt-beam.pt-beam::before {
	content: "";
	display: block;
	mix-blend-mode: normal;
	opacity: 1;
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
}

/* --------------------------------------------------------------------------
   Aurora: dos capas de luz que se desplazan a distinto ritmo. La segunda gira
   en sentido contrario, de modo que el patrón nunca se repite igual.
   -------------------------------------------------------------------------- */

/*
 * La suavidad va escrita en los propios degradados, no en «filter: blur()».
 * Desenfocar una capa del tamaño de la sección obligaba al navegador a
 * rehacerla en cada fotograma del scroll; con la caída de opacidad repartida
 * en varias paradas se ve igual y se compone sin coste. Vale para todas las
 * capas de luz de este archivo.
 */
.pt-aurora.pt-aurora::before {
	background:
		radial-gradient(52% 62% at 16% 18%,
			rgba(119, 236, 255, 0.3) 0%, rgba(119, 236, 255, 0.17) 34%,
			rgba(119, 236, 255, 0.06) 60%, transparent 80%),
		radial-gradient(48% 58% at 86% 26%,
			rgba(88, 166, 255, 0.26) 0%, rgba(88, 166, 255, 0.15) 36%,
			rgba(88, 166, 255, 0.05) 62%, transparent 82%),
		radial-gradient(60% 68% at 52% 92%,
			rgba(0, 92, 226, 0.28) 0%, rgba(0, 92, 226, 0.16) 36%,
			rgba(0, 92, 226, 0.05) 64%, transparent 84%);
	animation: pt-aurora-drift 26s ease-in-out infinite alternate;
}

.pt-aurora.pt-aurora::after {
	background:
		radial-gradient(44% 50% at 72% 8%,
			rgba(123, 140, 255, 0.22) 0%, rgba(123, 140, 255, 0.12) 38%,
			rgba(123, 140, 255, 0.04) 64%, transparent 84%),
		radial-gradient(46% 52% at 12% 78%,
			rgba(119, 236, 255, 0.17) 0%, rgba(119, 236, 255, 0.09) 38%,
			rgba(119, 236, 255, 0.03) 64%, transparent 84%);
	animation: pt-aurora-drift-2 34s ease-in-out infinite alternate;
}

@keyframes pt-aurora-drift {
	0%   { transform: translate3d(0, 0, 0) scale(1) rotate(0deg); }
	50%  { transform: translate3d(3%, -3%, 0) scale(1.12) rotate(4deg); }
	100% { transform: translate3d(-3%, 3%, 0) scale(1.05) rotate(-3deg); }
}

@keyframes pt-aurora-drift-2 {
	0%   { transform: translate3d(0, 0, 0) scale(1.1) rotate(0deg); }
	50%  { transform: translate3d(-4%, 3%, 0) scale(1) rotate(-6deg); }
	100% { transform: translate3d(4%, -2%, 0) scale(1.14) rotate(5deg); }
}

/* --------------------------------------------------------------------------
   Orbes: dos esferas de luz nítidas, para secciones que necesitan un punto
   de color sin la difusión de la aurora.
   -------------------------------------------------------------------------- */

.pt-orbs.pt-orbs::before {
	/* «closest-side»: el degradado llega a transparente justo en el borde de
	   la caja, sin dejar el filo recto que se vería al quitar el blur. */
	background: radial-gradient(closest-side circle at 50% 50%,
		rgba(119, 236, 255, 0.34) 0%, rgba(119, 236, 255, 0.24) 34%,
		rgba(119, 236, 255, 0.12) 60%, rgba(119, 236, 255, 0.04) 82%,
		transparent 100%);
	inset: auto auto -20% -10%;
	width: min(52vw, 620px);
	height: min(52vw, 620px);
	animation: pt-orb-a 24s ease-in-out infinite alternate;
}

.pt-orbs.pt-orbs::after {
	background: radial-gradient(closest-side circle at 50% 50%,
		rgba(0, 92, 226, 0.36) 0%, rgba(0, 92, 226, 0.26) 34%,
		rgba(0, 92, 226, 0.13) 60%, rgba(0, 92, 226, 0.045) 82%,
		transparent 100%);
	inset: -25% -12% auto auto;
	width: min(46vw, 560px);
	height: min(46vw, 560px);
	animation: pt-orb-b 30s ease-in-out infinite alternate;
}

@keyframes pt-orb-a {
	0%   { transform: translate3d(0, 0, 0) scale(1); }
	100% { transform: translate3d(14%, -12%, 0) scale(1.24); }
}

@keyframes pt-orb-b {
	0%   { transform: translate3d(0, 0, 0) scale(1.12); }
	100% { transform: translate3d(-12%, 14%, 0) scale(1); }
}

/* Malla de degradados, suave y estática */
.pt-gradient-mesh.pt-gradient-mesh::before {
	background:
		radial-gradient(60% 70% at 12% 8%, rgba(119, 236, 255, 0.2), transparent 60%),
		radial-gradient(50% 60% at 92% 18%, rgba(88, 166, 255, 0.18), transparent 62%),
		radial-gradient(70% 60% at 50% 110%, rgba(0, 92, 226, 0.22), transparent 64%),
		linear-gradient(180deg, var(--pt-bg-1), var(--pt-bg-0));
}

/* Retícula técnica, con deriva muy lenta */
.pt-grid-bg.pt-grid-bg::before {
	background-image:
		linear-gradient(to right, var(--pt-line) 1px, transparent 1px),
		linear-gradient(to bottom, var(--pt-line) 1px, transparent 1px);
	background-size: 62px 62px;
	mask-image: radial-gradient(76% 74% at 50% 40%, #000 30%, transparent 100%);
	-webkit-mask-image: radial-gradient(76% 74% at 50% 40%, #000 30%, transparent 100%);
	opacity: 0.75;
	animation: pt-grid-drift 40s linear infinite;
}

@keyframes pt-grid-drift {
	0%   { background-position: 0 0, 0 0; }
	100% { background-position: 62px 62px, 62px 62px; }
}

/* Haz de luz diagonal que recorre la sección */
.pt-beam.pt-beam::before {
	background: linear-gradient(104deg, transparent 38%, rgba(160, 235, 255, 0.11) 48%, rgba(119, 236, 255, 0.05) 54%, transparent 64%);
	background-size: 260% 100%;
	animation: pt-beam-sweep 9s ease-in-out infinite;
}

@keyframes pt-beam-sweep {
	0%, 100% { background-position: 160% 0; }
	50%      { background-position: -60% 0; }
}

/* Grano sutil sobre imágenes o vídeo */
.pt-noise.pt-noise::before {
	background-image: radial-gradient(rgba(255, 255, 255, 0.055) 1px, transparent 1px);
	background-size: 3px 3px;
	opacity: 0.55;
	mix-blend-mode: overlay;
}

/* ==========================================================================
   5. Movimiento continuo
   ========================================================================== */

.pt-float { animation: pt-float 7s ease-in-out infinite; }

@keyframes pt-float {
	0%, 100% { transform: translateY(0); }
	50%      { transform: translateY(-14px); }
}

.pt-glow-pulse { animation: pt-glow-pulse 4.5s ease-in-out infinite; }

@keyframes pt-glow-pulse {
	0%, 100% { box-shadow: var(--pt-glow); }
	50%      { box-shadow: var(--pt-glow-strong); }
}

/* Cinta de texto en movimiento continuo. Aplicar al contenedor. */
.pt-marquee {
	overflow: hidden;
	mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

.pt-marquee > * {
	display: flex;
	gap: 48px;
	width: max-content;
	animation: pt-marquee 38s linear infinite;
}

.pt-marquee:hover > * { animation-play-state: paused; }

@keyframes pt-marquee {
	0%   { transform: translateX(0); }
	100% { transform: translateX(-50%); }
}

/* ==========================================================================
   6. Interacción
   ========================================================================== */

.pt-hover-lift { transition: transform var(--pt-transition-smooth), box-shadow var(--pt-transition-smooth); }

.pt-hover-lift:hover {
	transform: translateY(-6px);
	box-shadow: var(--pt-shadow-3);
}

.pt-hover-glow { transition: border-color var(--pt-transition-base), box-shadow var(--pt-transition-smooth); }

.pt-hover-glow:hover {
	border-color: var(--pt-line-strong);
	box-shadow: var(--pt-glow-strong);
}

.pt-hover-zoom { overflow: hidden; }
.pt-hover-zoom img { transition: transform 0.8s var(--pt-ease-out); }
.pt-hover-zoom:hover img { transform: scale(1.07); }

/* --------------------------------------------------------------------------
   Foco que sigue al puntero. El script actualiza --pt-mx y --pt-my con la
   posición del ratón dentro del elemento.
   -------------------------------------------------------------------------- */

.pt-spotlight { position: relative; isolation: isolate; }

.pt-spotlight::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	border-radius: inherit;
	pointer-events: none;
	opacity: 0;
	transition: opacity var(--pt-transition-base);
	background: radial-gradient(
		320px circle at var(--pt-mx, 50%) var(--pt-my, 50%),
		rgba(119, 236, 255, 0.14),
		transparent 68%
	);
}

.pt-spotlight:hover::after { opacity: 1; }

/* Borde que se ilumina siguiendo al puntero. */
.pt-border-glow { position: relative; }

.pt-border-glow::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 2;
	border-radius: inherit;
	padding: 1px;
	pointer-events: none;
	opacity: 0;
	transition: opacity var(--pt-transition-base);
	background: radial-gradient(
		220px circle at var(--pt-mx, 50%) var(--pt-my, 50%),
		var(--pt-cyan),
		transparent 70%
	);
	-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	-webkit-mask-composite: xor;
	mask-composite: exclude;
}

.pt-border-glow:hover::before { opacity: 1; }

/* ==========================================================================
   7. Detalles
   ========================================================================== */

/* Texto con degradado de marca. Aplicar al widget de encabezado. */
.pt-gradient-text,
.pt-gradient-text h1,
.pt-gradient-text h2,
.pt-gradient-text h3 {
	background: var(--pt-grad-text);
	background-size: 220% 100%;
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	animation: pt-gradient-shift 12s ease-in-out infinite alternate;
}

@keyframes pt-gradient-shift {
	0%   { background-position: 0% 50%; }
	100% { background-position: 100% 50%; }
}

/* Brillo que recorre un elemento, útil sobre botones o insignias. */
.pt-shimmer { position: relative; overflow: hidden; }

.pt-shimmer::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: linear-gradient(105deg, transparent 40%, rgba(255, 255, 255, 0.22) 50%, transparent 60%);
	background-size: 250% 100%;
	animation: pt-shimmer 5s ease-in-out infinite;
}

@keyframes pt-shimmer {
	0%, 100% { background-position: 150% 0; }
	55%      { background-position: -50% 0; }
}

/* Subrayado que crece al pasar el ratón */
.pt-underline a,
a.pt-underline {
	position: relative;
	text-decoration: none;
}

.pt-underline a::after,
a.pt-underline::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: -3px;
	height: 1px;
	background: var(--pt-cyan);
	transform: scaleX(0);
	transform-origin: left;
	transition: transform var(--pt-transition-smooth);
}

.pt-underline a:hover::after,
a.pt-underline:hover::after { transform: scaleX(1); }

/* Línea divisoria con degradado */
.pt-divider-glow {
	height: 1px;
	border: 0;
	background: var(--pt-grad-line);
}

/* ==========================================================================
   8. Accesibilidad — respetar "reducir movimiento"
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	.pt-js [class*="pt-reveal-"],
	.pt-js .pt-stagger > *,
	.pt-js .pt-words > span {
		opacity: 1 !important;
		transform: none !important;
		filter: none !important;
		clip-path: none !important;
		transition: none !important;
	}

	.pt-parallax-slow,
	.pt-parallax-fast {
		transform: none !important;
	}

	.pt-aurora::before,
	.pt-aurora::after,
	.pt-orbs::before,
	.pt-orbs::after,
	.pt-grid-bg::before,
	.pt-beam::before,
	.pt-float,
	.pt-glow-pulse,
	.pt-gradient-text,
	.pt-shimmer::after,
	.pt-marquee > * {
		animation: none !important;
	}

	.pt-hover-lift:hover,
	.pt-hover-zoom:hover img {
		transform: none !important;
	}
}
