/**
 * Onda Motion — Estilos base de los widgets de Elementor.
 *
 * Variables de control (las setea el motor JS o los selectores de Elementor):
 *   --onda-dur               duración del efecto
 *   --onda-ease              curva CSS
 *   --onda-hover-color       color del relleno/overlay en hover
 *   --onda-hover-text-color  color del texto en hover
 *   --onda-x / --onda-y      posición del cursor (hover-iris)
 */

.onda-fx {
	--onda-dur: 0.5s;
	--onda-ease: cubic-bezier(0.16, 1, 0.3, 1);
	--onda-hover-color: #ff3d00;
	--onda-hover-text-color: #ffffff;
	display: flex;
	width: 100%;
}

.onda-fx__inner {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--onda-icon-gap, 8px);
	overflow: hidden;
	text-decoration: none;
	cursor: pointer;
	line-height: 1.2;
	isolation: isolate;
	will-change: transform;
}

/* Posición del icono (antes/después = fila; arriba/abajo = columna). */
.onda-fx__inner--icon-top,
.onda-fx__inner--icon-bottom {
	flex-direction: column;
}

.onda-fx__icon {
	position: relative;
	z-index: 2;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	line-height: 0;
	transition: color var(--onda-dur) var(--onda-ease);
}

.onda-fx__icon svg {
	width: 1em;
	height: 1em;
	fill: currentColor;
}

/* El icono acompaña el cambio de color de texto en hover. */
.onda-fx__inner:hover .onda-fx__icon,
.onda-fx.is-hover .onda-fx__icon {
	color: var(--onda-hover-text-color);
}

.onda-fx__label {
	position: relative;
	z-index: 2;
	display: inline-block;
	transition: color var(--onda-dur) var(--onda-ease);
}

/* Cambio de color de texto al hover (CSS y JS-driven con .is-hover). */
.onda-fx__inner:hover .onda-fx__label,
.onda-fx.is-hover .onda-fx__label {
	color: var(--onda-hover-text-color);
}

/* Overlays genéricos detrás del label. */
.onda-fx__overlay {
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
}

/* ---------------------------------------------------------------------
 * Hover Slide — relleno que entra según dirección (píldora por defecto).
 * ------------------------------------------------------------------- */
/* El radio lo controla el botón (Elementor); el efecto NO lo fuerza. overflow:hidden
   recorta el relleno a la forma real del botón (rectangular o redondeado, lo que ponga el autor). */
.onda-fx--hover-slide .onda-fx__inner { overflow: hidden; }
.onda-fx--hover-slide .onda-fx__inner::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	/* fallback para que el relleno SIEMPRE sea visible aunque el control de color
	   no emita su default (p.ej. botón inyectado por JSON sin la var seteada). */
	background: var(--onda-hover-color, #ff3d00);
	transition: transform var(--onda-dur) var(--onda-ease);
}

.onda-fx--hover-slide[data-dir="up"] .onda-fx__inner::before { transform: translateY(101%); }
.onda-fx--hover-slide[data-dir="down"] .onda-fx__inner::before { transform: translateY(-101%); }
.onda-fx--hover-slide[data-dir="left"] .onda-fx__inner::before { transform: translateX(101%); }
.onda-fx--hover-slide[data-dir="right"] .onda-fx__inner::before { transform: translateX(-101%); }
.onda-fx--hover-slide .onda-fx__inner:hover::before { transform: translate(0, 0); }

/* ---------------------------------------------------------------------
 * Hover Split — la píldora se parte al medio y revela el centro.
 * (inner = centro que se revela; ::before/::after = mitades del botón)
 * ------------------------------------------------------------------- */
.onda-fx--hover-split .onda-fx__inner {
	overflow: hidden;
}
.onda-fx--hover-split .onda-fx__inner::before,
.onda-fx--hover-split .onda-fx__inner::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	height: 50.5%;
	z-index: 1;
	background: var(--onda-split-color, #1e88d4);
	transition: transform var(--onda-dur) var(--onda-ease);
}
.onda-fx--hover-split .onda-fx__inner::before { top: 0; transform: translateY(-101%); }
.onda-fx--hover-split .onda-fx__inner::after { bottom: 0; transform: translateY(101%); }
.onda-fx--hover-split .onda-fx__inner:hover::before,
.onda-fx--hover-split .onda-fx__inner:hover::after { transform: translateY(0); }
.onda-fx--hover-split .onda-fx__label,
.onda-fx--hover-split .onda-fx__icon { position: relative; z-index: 2; }

/* ---------------------------------------------------------------------
 * Hover Column Wipe — columnas que suben en stagger (GSAP).
 * ------------------------------------------------------------------- */
.onda-fx--hover-column-wipe .onda-fx__inner { overflow: hidden; }
.onda-fx__overlay--cols .onda-fx__col {
	position: absolute;
	top: 0;
	bottom: 0;
	background: var(--onda-wipe-color, #a52121);
	transform: scaleY(0);
}

/* ---------------------------------------------------------------------
 * Hover Iris — círculo que se expande desde el cursor.
 * ------------------------------------------------------------------- */
.onda-fx--hover-iris .onda-fx__inner { overflow: hidden; }
.onda-fx__overlay--iris {
	background: var(--onda-iris-color, #6a0dad);
	clip-path: circle(0% at var(--onda-x, 50%) var(--onda-y, 50%));
	transition: clip-path var(--onda-dur) var(--onda-ease);
}
.onda-fx--hover-iris .onda-fx__inner:hover .onda-fx__overlay--iris {
	clip-path: circle(150% at var(--onda-x, 50%) var(--onda-y, 50%));
}

/* ---------------------------------------------------------------------
 * Hover Pixel — grilla de celdas (GSAP controla la opacidad).
 * ------------------------------------------------------------------- */
.onda-fx--hover-pixel .onda-fx__inner { overflow: hidden; }
.onda-fx__overlay--pixel {
	display: grid;
}
.onda-fx__overlay--pixel .onda-fx__cell {
	background: var(--onda-pixel-color, #e08a2e);
	transform: scale(0);
}

/* ---------------------------------------------------------------------
 * Magnetic Hover — sin overlay; el inner se mueve por GSAP.
 * ------------------------------------------------------------------- */
.onda-fx--magnetic-hover .onda-fx__inner {
	transition: background-color var(--onda-dur) var(--onda-ease);
}

/* =====================================================================
 * FASE 2 — BUTTON EFFECTS
 * ===================================================================*/

/* --- Space Button: píldora oscura con borde de gradiente neón + sparkles. --- */
.onda-fx--space-button .onda-fx__inner {
	overflow: hidden;
	position: relative;
	isolation: auto;
	overflow: visible;
	transition: transform var(--onda-dur) var(--onda-ease);
	--onda-sp-grad: linear-gradient(120deg, var(--onda-sp-c1, #ff2d9b), var(--onda-sp-c2, #7b2ff7), var(--onda-sp-c3, #2b7cff), var(--onda-sp-c1, #ff2d9b));
}
.onda-fx--space-button .onda-fx__inner:hover { transform: scale(1.08); }

/* Borde con gradiente (técnica de máscara: solo se ve el anillo). */
.onda-fx--space-button .onda-fx__inner::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	padding: 2px;
	border-radius: inherit;
	background: var(--onda-sp-grad);
	background-size: 250% 250%;
	-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
	-webkit-mask-composite: xor;
	mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
	mask-composite: exclude;
	animation: onda-space-flow var(--onda-sp-speed, 4s) linear infinite;
	pointer-events: none;
}

/* Glow exterior (copia borrosa del gradiente). */
.onda-fx--space-button .onda-fx__inner::after {
	content: "";
	position: absolute;
	inset: -3px;
	z-index: -1;
	border-radius: inherit;
	background: var(--onda-sp-grad);
	background-size: 250% 250%;
	filter: blur(11px);
	opacity: 0.5;
	animation: onda-space-flow var(--onda-sp-speed, 4s) linear infinite;
	transition: opacity var(--onda-dur) var(--onda-ease);
	pointer-events: none;
}
.onda-fx--space-button .onda-fx__inner:hover::after { opacity: 0.85; }

@keyframes onda-space-flow {
	0% { background-position: 0% 50%; }
	100% { background-position: 250% 50%; }
}

/* Sparkles internos. */
.onda-fx__space-stars {
	position: absolute;
	inset: 0;
	z-index: 0;
	overflow: hidden;
	border-radius: inherit;
	pointer-events: none;
}
.onda-fx__star {
	position: absolute;
	width: 3px;
	height: 3px;
	border-radius: 50%;
	background: #ffffff;
	opacity: 0.4;
}

/* --- Sparkle Button: reposo sólido; al hover se invierte y estallan estrellas. --- */
.onda-fx--sparkle-button .onda-fx__inner {
	overflow: hidden;
	overflow: visible;
	border: 2px solid transparent;
	transition: background-color var(--onda-dur) var(--onda-ease), border-color var(--onda-dur) var(--onda-ease), box-shadow var(--onda-dur) var(--onda-ease);
}
.onda-fx--sparkle-button .onda-fx__inner:hover {
	background: #0e0e14 !important;
	border-color: var(--onda-spark-accent, #f5a623);
	box-shadow: 0 0 22px var(--onda-spark-accent, #f5a623);
}
.onda-fx--sparkle-button .onda-fx__inner:hover .onda-fx__label,
.onda-fx--sparkle-button .onda-fx__inner:hover .onda-fx__icon {
	color: var(--onda-spark-accent, #f5a623);
}
.onda-fx__sparkles {
	position: absolute;
	inset: 0;
	z-index: 3;
	pointer-events: none;
}
.onda-fx__spark {
	position: absolute;
	width: 20px;
	height: 20px;
	transform: scale(0);
	background: var(--onda-spark-color, #ffffff);
	clip-path: polygon(50% 0, 58% 42%, 100% 50%, 58% 58%, 50% 100%, 42% 58%, 0 50%, 42% 42%);
	filter: drop-shadow(0 0 6px var(--onda-spark-accent, #f5a623));
}

/* --- Double Text Button: dos textos apilados que se deslizan. --- */
.onda-fx--double-text-button .onda-fx__inner { overflow: hidden; }
/* El texto no cambia de color en hover (mantiene el de reposo). */
.onda-fx--double-text-button .onda-fx__inner:hover .onda-fx__label { color: inherit; }
.onda-fx__label--double {
	position: relative;
	display: inline-grid;
	overflow: hidden;
	line-height: 1.2;
}
.onda-fx__label--double .onda-fx__t {
	grid-area: 1 / 1;
	transition: transform var(--onda-dur) var(--onda-ease);
}
.onda-fx__label--double[data-dir="up"] .onda-fx__t2 { transform: translateY(110%); }
.onda-fx__label--double[data-dir="down"] .onda-fx__t2 { transform: translateY(-110%); }
.onda-fx__inner:hover .onda-fx__label--double[data-dir="up"] .onda-fx__t1 { transform: translateY(-110%); }
.onda-fx__inner:hover .onda-fx__label--double[data-dir="down"] .onda-fx__t1 { transform: translateY(110%); }
.onda-fx__inner:hover .onda-fx__label--double .onda-fx__t2 { transform: translateY(0); }

/* --- Dynamic Button: texto grande de fondo + cápsula glass que sigue el cursor. --- */
.onda-fx--dynamic-button .onda-fx__inner {
	position: relative;
	display: block;
	width: 100%;
	overflow: hidden;
	text-decoration: none;
	cursor: pointer;
}
.onda-fx--dynamic-button .onda-fx__bigtext {
	display: block;
	text-align: center;
	font-weight: 800;
	line-height: 1.05;
}
.onda-fx--dynamic-button .onda-fx__glass {
	position: absolute;
	top: 0;
	left: 0;
	display: inline-flex;
	align-items: center;
	gap: 0.6em;
	padding: 0.7em 1.4em;
	border-radius: 999px;
	white-space: nowrap;
	background: var(--onda-glass-bg, rgba(20, 20, 20, 0.55));
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
	border: 1px solid rgba(255, 255, 255, 0.18);
	pointer-events: none;
	will-change: transform;
}
.onda-fx--dynamic-button .onda-fx__glass .onda-fx__icon { line-height: 0; }
/* Editor: cápsula centrada (estado representativo; el JS no la mueve en el editor). */
.onda-fx--dynamic-button.onda-fx--editor .onda-fx__glass {
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
}

/* --- Icon Background Pulse: el badge se separa del texto y se transparenta en hover. --- */
.onda-fx--icon-bg-pulse .onda-fx__inner {
	overflow: hidden;
	overflow: visible;
	gap: 1em;
}
/* el texto y el badge centrados verticalmente (el badge es más alto que la línea de texto). */
.onda-fx--icon-bg-pulse .onda-fx__inner,
.onda-fx--icon-bg-pulse .elementor-button-content-wrapper { align-items: center; }
.onda-fx--icon-bg-pulse .onda-fx__icon {
	position: relative;
	flex: 0 0 auto;
	width: 2.1em;
	height: 2.1em;
	border-radius: 50%;
	color: #6c5ce7; /* chevron en reposo; lo pisa el control "Color del icono" */
	transition: margin-left var(--onda-dur) var(--onda-ease), color var(--onda-dur) var(--onda-ease);
}
/* El fondo del badge va en ::before. En hover CAMBIA de color (no se desvanece),
   como la ref de ProEffects: el badge se desplaza y vira a un tono claro. */
.onda-fx--icon-bg-pulse .onda-fx__icon::before {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: 50%;
	background: var(--onda-badge-color, #ffffff);
	transition: background-color var(--onda-dur) var(--onda-ease);
}
.onda-fx--icon-bg-pulse .onda-fx__icon i,
.onda-fx--icon-bg-pulse .onda-fx__icon svg {
	position: relative;
	z-index: 1;
}
.onda-fx--icon-bg-pulse .onda-fx__icon svg { fill: currentColor; }
/* Hover: el badge se desplaza y su fondo vira al color de hover; el chevron queda visible. */
.onda-fx--icon-bg-pulse .onda-fx__inner:hover .onda-fx__icon {
	margin-left: 1.3em;
	color: var(--onda-badge-icon-hover, #6c5ce7);
}
.onda-fx--icon-bg-pulse .onda-fx__inner:hover .onda-fx__icon::before {
	background: var(--onda-badge-hover, #d3c7fd);
}

/* --- Icon Capsule: cápsula (texto) y círculo (icono) montados cada uno en un
 * CUBO 3D. La misma cara está repetida; al hover la cápsula gira 90° (su
 * contenido sube) y el círculo gira 90° al revés (su contenido baja), ambos
 * aterrizando en una cara idéntica. El motor (JS) arma las caras y rota. --- */
/* el botón NO aporta fondo: el efecto ES la cápsula (pill) + el círculo. Así no queda un
   rectángulo de fondo detrás. La cápsula y el círculo llevan su propio color. */
.onda-fx--icon-capsule .onda-fx__inner { overflow: visible; background: transparent !important; border: 0 !important; }
.onda-fx--icon-capsule .onda-fx__label {
	background: var(--onda-capsule-color, #e0922f);
	padding: 0.75em 1.5em;
	border-radius: 999px;
	line-height: 1.3;
}
.onda-fx--icon-capsule .onda-fx__icon {
	flex: 0 0 auto;
	width: 2.9em;
	height: 2.9em;
	border-radius: 50%;
	background: var(--onda-capsule-color, #e0922f);
}
.onda-fx--icon-capsule .onda-fx__icon svg { fill: currentColor; }

/* Estructura del cubo 3D (la arma el motor). */
.onda-fx--icon-capsule .onda-ic-flip {
	position: relative;
	display: inline-block;
	flex: 0 0 auto;
}
.onda-fx--icon-capsule .onda-ic-cube {
	position: relative;
	display: inline-block;
	transform-style: preserve-3d;
	will-change: transform;
}

/* --- Icon Fly: el icono vuela (GSAP); overflow visible para verlo + píldora. --- */
.onda-fx--icon-fly .onda-fx__inner {
	overflow: hidden;
	overflow: visible;
}

/* --- Icon Slide: el icono (visible) se desliza al hacer hover + fondo oscurece. --- */
.onda-fx--icon-slide .onda-fx__inner {
	overflow: hidden;
	transition: background-color var(--onda-dur) var(--onda-ease);
}
/* separación icono↔texto: van anidados en .elementor-button-content-wrapper (flex),
   así que el gap real va ahí (no en __inner) para que el icono no se pegue al texto. */
.onda-fx--icon-slide .elementor-button-content-wrapper { gap: 0.75em; }
.onda-fx--icon-slide .onda-fx__inner:hover {
	background: var(--onda-is-hover-bg, #2e7d16) !important;
}
.onda-fx--icon-slide .onda-fx__icon {
	transition: transform var(--onda-dur) var(--onda-ease);
}
.onda-fx--icon-slide[data-from="right"] .onda-fx__inner:hover .onda-fx__icon { transform: translateX(8px); }
.onda-fx--icon-slide[data-from="left"] .onda-fx__inner:hover .onda-fx__icon { transform: translateX(-8px); }

/* --- Center Reveal: el texto se toma por el centro; cada letra sube y entra
 * una copia desde abajo (JS). Cada letra es una cajita con 2 copias. --- */
.onda-fx--center-reveal .onda-fx__inner {
	overflow: hidden;
	overflow: visible;
}
.onda-fx--center-reveal .onda-fx__inner:hover .onda-fx__label,
.onda-fx--center-reveal .onda-fx__inner:hover .onda-fx__icon { color: inherit; }
.onda-fx--center-reveal .onda-cr-ch {
	position: relative;
	display: inline-block;
	vertical-align: top;
}
.onda-fx--center-reveal .onda-cr-face {
	display: inline-block;
	will-change: transform, opacity;
}
.onda-fx--center-reveal .onda-cr-face--in {
	position: absolute;
	left: 0;
	top: 0;
}

/* --- Particles: burbujas que suben dentro del botón; cambia color en hover. --- */
.onda-fx--particles .onda-fx__inner {
	overflow: hidden;
	transition: background-color var(--onda-dur) var(--onda-ease);
}
.onda-fx--particles .onda-fx__inner:hover {
	background: var(--onda-ph-bg, #1e4d8c) !important;
}
.onda-fx__particles {
	position: absolute;
	inset: 0;
	z-index: 0;
	overflow: hidden;
	border-radius: inherit;
	pointer-events: none;
}
.onda-fx__particle {
	position: absolute;
	top: 0;
	border-radius: 50%;
	background: var(--onda-particle-color, #ffffff);
}

/* =====================================================================
 * FASE 3 — TEXT EFFECTS
 * ===================================================================*/

/* Preset "text": el inner se comporta como texto, no como botón. */
.onda-fx--preset-text .onda-fx__inner { overflow: visible; }
.onda-fx--preset-text span.onda-fx__inner { cursor: default; }

/* Piezas del splitting (caracteres/palabras). */
.onda-char,
.onda-word { display: inline-block; }

/* --- Glitch Text: dos capas RGB desfasadas. --- */
.onda-fx--glitch-text .onda-fx__inner {
	overflow: hidden; }
.onda-fx--glitch-text .onda-fx__label {
	position: relative;
	display: inline-block;
}
.onda-fx--glitch-text .onda-fx__label::before,
.onda-fx--glitch-text .onda-fx__label::after {
	content: attr(data-text);
	position: absolute;
	left: 0;
	top: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
}
.onda-fx--glitch-text .onda-fx__label::before { color: var(--onda-glitch-c1, #00e5ff); }
.onda-fx--glitch-text .onda-fx__label::after { color: var(--onda-glitch-c2, #ff00c1); }
.onda-fx--glitch-text .onda-fx__label:not(.onda-glitch--hover)::before { animation: onda-glitch-1 2.6s steps(2) infinite; }
.onda-fx--glitch-text .onda-fx__label:not(.onda-glitch--hover)::after { animation: onda-glitch-2 2.6s steps(2) infinite; }
.onda-fx--glitch-text .onda-glitch--hover:hover::before { animation: onda-glitch-1 0.55s steps(2) infinite; }
.onda-fx--glitch-text .onda-glitch--hover:hover::after { animation: onda-glitch-2 0.55s steps(2) infinite; }
@keyframes onda-glitch-1 {
	0%, 100% { transform: translate(0, 0); clip-path: inset(0 0 62% 0); }
	20% { transform: translate(-2px, -2px); clip-path: inset(22% 0 40% 0); }
	40% { transform: translate(-2px, 2px); clip-path: inset(52% 0 20% 0); }
	60% { transform: translate(2px, -1px); clip-path: inset(10% 0 70% 0); }
	80% { transform: translate(2px, 2px); clip-path: inset(40% 0 33% 0); }
}
@keyframes onda-glitch-2 {
	0%, 100% { transform: translate(0, 0); clip-path: inset(60% 0 0 0); }
	20% { transform: translate(2px, 1px); clip-path: inset(35% 0 30% 0); }
	40% { transform: translate(2px, -2px); clip-path: inset(15% 0 55% 0); }
	60% { transform: translate(-2px, 2px); clip-path: inset(65% 0 8% 0); }
	80% { transform: translate(-2px, -1px); clip-path: inset(30% 0 45% 0); }
}

/* --- Text Shine: una banda de brillo recorre el texto (background-clip:text);
 * lo anima el motor. Este widget no tiene icono. --- */
.onda-fx--text-shine .onda-fx__label {
	display: inline-block;
	background: linear-gradient(110deg, var(--onda-shine-base, #555) 38%, var(--onda-shine, #fff) 50%, var(--onda-shine-base, #555) 62%);
	background-size: 250% 100%;
	background-position: 150% 0;
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: transparent;
	color: transparent;
}

/* --- Stroke Wipe: contorno + relleno neón que va y viene en loop (JS setea --onda-fill). --- */
.onda-fx--stroke-wipe .onda-fx__label {
	position: relative;
	display: inline-block;
	-webkit-text-stroke: 1.2px var(--onda-stroke-color, #cccccc);
	/* Relleno interior: transparente por defecto (texto hueco). Si la fuente deja
	 * "líneas de construcción" dentro de las letras, poné el color del fondo en el
	 * control "Color interior": con paint-order el relleno tapa esas costuras. */
	-webkit-text-fill-color: var(--onda-sw-inner, transparent);
	paint-order: stroke fill;
}
.onda-fx--stroke-wipe .onda-fx__label::before {
	content: attr(data-text);
	position: absolute;
	left: 0;
	top: 0;
	width: var(--onda-fill, 0%);
	overflow: hidden;
	white-space: nowrap;
	color: var(--onda-fill-color, #3effa0);
	-webkit-text-fill-color: var(--onda-fill-color, #3effa0);
	/* Contorno en el color de relleno para que la letra rellena se vea sólida y
	 * limpia. Glow controlable (suave por defecto, para no formar un rectángulo). */
	-webkit-text-stroke: 1.2px var(--onda-fill-color, #3effa0);
	text-shadow: 0 0 var(--onda-sw-glow, 4px) var(--onda-fill-color, #3effa0);
}
.onda-fx--stroke-wipe .onda-fx__label::after {
	content: "";
	position: absolute;
	top: 6%;
	bottom: 6%;
	left: var(--onda-cur, -10px);
	width: 4px;
	/* Cuadrado (sin border-radius) y SIN glow. El motor mueve --onda-cur: en reposo
	 * queda a la izquierda separado del texto, y al 100% llega al borde derecho. */
	transform: translateX(-50%);
	background: var(--onda-fill-color, #3effa0);
	opacity: 1;
}
/* El cursor verde está siempre visible: en reposo queda a la izquierda
 * (--onda-fill: 0%) y al hover viaja con el relleno hasta la derecha. */

/* =====================================================================
 * R2 ola 3 — "efecto sobre widget": cuando text-shine / glitch-text /
 * stroke-wipe se aplican a un Heading/Button/Text-Editor, el texto nativo
 * NO lleva las clases .onda-fx--<slug> .onda-fx__label que arman su CSS. El
 * handler JS le agrega una clase marca (.onda-shine-node / .onda-glitch-node
 * / .onda-stroke-node); estas reglas espejan el CSS del widget standalone
 * sobre ese nodo, scopeadas por el atributo data-onda-fx del wrapper.
 * ===================================================================== */
[data-onda-fx="text-shine"] .onda-shine-node {
	display: inline-block;
	background: linear-gradient(110deg, var(--onda-shine-base, #555) 38%, var(--onda-shine, #fff) 50%, var(--onda-shine-base, #555) 62%);
	background-size: 250% 100%;
	background-position: 150% 0;
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: transparent;
	color: transparent;
}
[data-onda-fx="glitch-text"] .onda-glitch-node {
	position: relative;
	display: inline-block;
}
[data-onda-fx="glitch-text"] .onda-glitch-node::before,
[data-onda-fx="glitch-text"] .onda-glitch-node::after {
	content: attr(data-text);
	position: absolute;
	left: 0;
	top: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
}
[data-onda-fx="glitch-text"] .onda-glitch-node::before { color: var(--onda-glitch-c1, #00e5ff); }
[data-onda-fx="glitch-text"] .onda-glitch-node::after { color: var(--onda-glitch-c2, #ff00c1); }
[data-onda-fx="glitch-text"] .onda-glitch-node:not(.onda-glitch--hover)::before { animation: onda-glitch-1 2.6s steps(2) infinite; }
[data-onda-fx="glitch-text"] .onda-glitch-node:not(.onda-glitch--hover)::after { animation: onda-glitch-2 2.6s steps(2) infinite; }
[data-onda-fx="glitch-text"] .onda-glitch-node.onda-glitch--hover:hover::before { animation: onda-glitch-1 0.55s steps(2) infinite; }
[data-onda-fx="glitch-text"] .onda-glitch-node.onda-glitch--hover:hover::after { animation: onda-glitch-2 0.55s steps(2) infinite; }
[data-onda-fx="stroke-wipe"] .onda-stroke-node {
	position: relative;
	display: inline-block;
	-webkit-text-stroke: 1.2px var(--onda-stroke-color, #cccccc);
	-webkit-text-fill-color: var(--onda-sw-inner, transparent);
	paint-order: stroke fill;
}
[data-onda-fx="stroke-wipe"] .onda-stroke-node::before {
	content: attr(data-text);
	position: absolute;
	left: 0;
	top: 0;
	width: var(--onda-fill, 0%);
	overflow: hidden;
	white-space: nowrap;
	color: var(--onda-fill-color, #3effa0);
	-webkit-text-fill-color: var(--onda-fill-color, #3effa0);
	-webkit-text-stroke: 1.2px var(--onda-fill-color, #3effa0);
	text-shadow: 0 0 var(--onda-sw-glow, 4px) var(--onda-fill-color, #3effa0);
}
[data-onda-fx="stroke-wipe"] .onda-stroke-node::after {
	content: "";
	position: absolute;
	top: 6%;
	bottom: 6%;
	left: var(--onda-cur, -10px);
	width: 4px;
	transform: translateX(-50%);
	background: var(--onda-fill-color, #3effa0);
	opacity: 1;
}

/* --- Marker Wipe: trazo SVG en zigzag que se "dibuja" como un marcador (JS). --- */
.onda-fx--marker-wipe .onda-fx__inner {
	overflow: hidden;
}
.onda-fx__marker {
	position: absolute;
	inset: 0;
	z-index: 1;
	width: 100%;
	height: 100%;
	pointer-events: none;
}
/* La cinta del garabato (ancho variable) se pinta con FILL. */
.onda-fx__marker-ink {
	fill: var(--onda-marker-color, #6a0dad);
}
/* Relleno sólido final (sobre los trazos), aparece cuando terminan de dibujarse. */
.onda-fx--marker-wipe .onda-fx__marker-fill {
	position: absolute;
	inset: 0;
	z-index: 2;
	background: var(--onda-marker-color, #6a0dad);
	border-radius: inherit;
	pointer-events: none;
}

/* --- Arc Text: píldora; el texto sale por la derecha y reentra por la
 * izquierda mientras el chevron parpadea (loop horizontal, JS). El inner
 * recorta el deslizamiento; el texto conserva su color (sin fill en hover). --- */
.onda-fx--arc-text .onda-fx__inner {
	overflow: hidden;
}
.onda-fx--arc-text .onda-fx__inner:hover .onda-fx__label,
.onda-fx--arc-text.is-hover .onda-fx__label,
.onda-fx--arc-text .onda-fx__inner:hover .onda-fx__icon,
.onda-fx--arc-text.is-hover .onda-fx__icon { color: inherit; }

/* --- Letter Bounce: botón píldora; las letras rebotan en hover (JS). --- */
.onda-fx--letter-bounce .onda-fx__inner {
	overflow: hidden;
	overflow: visible;
}

/* --- Circle Expand: círculo + texto; al hover crece a píldora completa. --- */
.onda-fx--circle-expand .onda-fx__inner {
	overflow: hidden;
	position: relative;
	background: transparent;
	height: var(--onda-ce-size, 3.2em);
	gap: 0.6em;
	overflow: visible;
	/* texto centrado en el botón; el icono se saca del flujo (absolute) al círculo.
	   padding horizontal = tamaño del círculo → reserva su espacio a la izquierda y
	   mantiene el texto centrado sin solaparse (funciona en cualquier ancho). */
	justify-content: center;
	padding-left: calc(var(--onda-ce-size, 3.2em) + 0.5em);
	padding-right: calc(var(--onda-ce-size, 3.2em) + 0.5em);
}
.onda-fx--circle-expand .onda-fx__inner::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0;
	bottom: 0;
	z-index: 0;
	width: var(--onda-ce-size, 3.2em);
	background: var(--onda-ce-color, #111111);
	border-radius: 999px; /* círculo al reposo */
	transition: width var(--onda-dur) var(--onda-ease), border-radius var(--onda-dur) var(--onda-ease);
}
/* al crecer toma el radius del BOTÓN (pastilla → pastilla; radius 30 → 30; 0 → recto). */
.onda-fx--circle-expand .onda-fx__inner:hover::before { width: 100%; border-radius: inherit; }
.onda-fx--circle-expand .onda-fx__icon {
	position: absolute;
	left: 0;
	top: 50%;
	transform: translateY(-50%);
	z-index: 1;
	flex: 0 0 auto;
	width: var(--onda-ce-size, 3.2em);
	height: var(--onda-ce-size, 3.2em);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	color: #ffffff;
}
.onda-fx--circle-expand .onda-fx__label {
	position: relative;
	z-index: 1;
	text-align: center;
}

/* --- Letter Reveal: cada letra hace un roll vertical en su lugar (2 copias),
 * escalonado izq→der (JS). Reversa al salir. --- */
.onda-fx--letter-reveal .onda-fx__label {
	display: inline-block;
}
.onda-fx--letter-reveal .onda-lr-ch {
	position: relative;
	display: inline-block;
	vertical-align: top;
}
.onda-fx--letter-reveal .onda-lr-face {
	display: inline-block;
	will-change: transform, opacity;
}
.onda-fx--letter-reveal .onda-lr-face--in {
	position: absolute;
	left: 0;
	top: 0;
}

/* Accesibilidad: respetar reduced motion. */
@media (prefers-reduced-motion: reduce) {
	.onda-fx *,
	.onda-fx *::before,
	.onda-fx *::after {
		transition-duration: 0.01ms !important;
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
	}
}

/* =====================================================================
 * FASE 2 — EFFECTS (extensiones de Container/Section, scroll-driven)
 * Estos efectos viven en el _wrapper de un Container/Section nativo
 * ([data-onda-fx]) y los anima el motor JS.
 * ===================================================================*/

/* Scroll Zoom: el motor envuelve el contenedor en un .onda-zoom-spacer,
 * lo deja position:sticky y anima un clip-path inset() inline (cápsula que
 * se abre). Acá solo aseguramos el compositing fluido del recorte. */
[data-onda-fx="scroll-zoom"] {
	will-change: clip-path;
}

.onda-zoom-spacer {
	/* La altura la setea el motor (viewport + distancia). El contenedor
	 * adentro queda sticky; este wrapper aporta la distancia de scroll. */
	pointer-events: none;
}

.onda-zoom-spacer > * {
	pointer-events: auto;
}

/* =====================================================================
 * PREVIEW EN EL EDITOR (CSS puro, sin JS — la edición queda intacta).
 * El atributo data-onda-fx-preview SOLO se emite dentro del editor de
 * Elementor; en el front no existe. Imita el layout final de los efectos
 * HORIZONTALES: los ítems se ven en una fila desplazable (como en el
 * front) en vez de apilados en vertical. La animación no se previsualiza.
 * ===================================================================*/

/* El contenedor del efecto se vuelve una banda de una pantalla de alto,
 * desplazable en horizontal dentro del editor. */
[data-onda-fx-preview="horizontal-slider"],
[data-onda-fx-preview="gallery-3d"] {
	max-height: 100vh;
	overflow-x: auto !important;
	overflow-y: hidden !important;
}

/* La fila: los hijos (ítems y widgets sueltos) se acomodan en horizontal,
 * centrados verticalmente, sin envolver. Cubre containers full (hijos
 * directos) y boxed (hijos dentro de .e-con-inner). */
[data-onda-fx-preview="horizontal-slider"]:not(.e-con-boxed),
[data-onda-fx-preview="gallery-3d"]:not(.e-con-boxed),
[data-onda-fx-preview="horizontal-slider"] > .e-con-inner,
[data-onda-fx-preview="gallery-3d"] > .e-con-inner {
	display: flex !important;
	flex-direction: row !important;
	flex-wrap: nowrap !important;
	align-items: center !important;
	gap: 24px;
	min-height: 100vh;
	width: max-content;
	min-width: 100%;
}

/* Los ítems conservan su tamaño y no se encogen. */
[data-onda-fx-preview="horizontal-slider"]:not(.e-con-boxed) > .e-con,
[data-onda-fx-preview="gallery-3d"]:not(.e-con-boxed) > .e-con,
[data-onda-fx-preview="horizontal-slider"] > .e-con-inner > .e-con,
[data-onda-fx-preview="gallery-3d"] > .e-con-inner > .e-con {
	flex: 0 0 auto !important;
}

/* =====================================================================
 * PREVIEW EN EL EDITOR — FAMILIA SECCIONES (2.92.0).
 * Cada efecto de CONTENEDOR aplica su animación sobre los hijos del autor.
 * En el editor el handler JS hace early-return (isEditor), así que NO hay
 * pin/scroll/rAF; estas reglas SOLO dibujan una composición REPRESENTATIVA
 * estática vía CSS para que el autor entienda el layout final y pueda
 * ordenar su contenido. Sin movimiento.
 *
 * Modelo DOM: en containers "full" los ítems son hijos directos `> .e-con`;
 * en "boxed" viven dentro de `> .e-con-inner > .e-con`. Cubrimos ambos.
 * ===================================================================*/

/* --- STACK (Cards & Sections) + SCROLL STACK: pila con offset (un "peek"
 * del mazo). Los hijos se solapan ligeramente desplazados para insinuar que
 * en el front se apilan en el mismo lugar y se navegan con el scroll. --- */
[data-onda-fx-preview="stacked-cards"]:not(.e-con-boxed),
[data-onda-fx-preview="scroll-stack"]:not(.e-con-boxed),
[data-onda-fx-preview="stacked-cards"] > .e-con-inner,
[data-onda-fx-preview="scroll-stack"] > .e-con-inner {
	display: grid !important;
	grid-template-columns: 1fr;
	min-height: 70vh;
	align-content: start;
	padding-top: 14px;
}
/* Cada card cae en la MISMA celda (apiladas) y se desplaza por orden con un
 * nth-child escalonado → se ven los bordes del mazo (peek). */
[data-onda-fx-preview="stacked-cards"]:not(.e-con-boxed) > .e-con,
[data-onda-fx-preview="scroll-stack"]:not(.e-con-boxed) > .e-con,
[data-onda-fx-preview="stacked-cards"] > .e-con-inner > .e-con,
[data-onda-fx-preview="scroll-stack"] > .e-con-inner > .e-con {
	grid-area: 1 / 1 !important;
	transform-origin: top center;
	box-shadow: 0 8px 28px rgba(0, 0, 0, 0.12);
	border-radius: 8px;
}
[data-onda-fx-preview="stacked-cards"] .e-con:nth-child(1),
[data-onda-fx-preview="scroll-stack"] .e-con:nth-child(1) { transform: translate3d(0, 0, 0) scale(1); }
[data-onda-fx-preview="stacked-cards"] .e-con:nth-child(2),
[data-onda-fx-preview="scroll-stack"] .e-con:nth-child(2) { transform: translate3d(20px, 22px, 0) scale(0.97); }
[data-onda-fx-preview="stacked-cards"] .e-con:nth-child(3),
[data-onda-fx-preview="scroll-stack"] .e-con:nth-child(3) { transform: translate3d(40px, 44px, 0) scale(0.94); }
[data-onda-fx-preview="stacked-cards"] .e-con:nth-child(n+4),
[data-onda-fx-preview="scroll-stack"] .e-con:nth-child(n+4) { transform: translate3d(60px, 66px, 0) scale(0.91); opacity: 0.9; }

/* --- SWITCHER SCROLL: los paneles comparten EL MISMO lugar en el front
 * (cross-fade). En el editor los apilamos en la misma celda con un pequeño
 * offset + opacidad decreciente para que se distingan sin perder la idea de
 * que ocupan el mismo punto. --- */
[data-onda-fx-preview="switcher-scroll"]:not(.e-con-boxed),
[data-onda-fx-preview="switcher-scroll"] > .e-con-inner {
	display: grid !important;
	grid-template-columns: 1fr;
	min-height: 60vh;
	align-content: center;
	justify-items: stretch;
}
[data-onda-fx-preview="switcher-scroll"]:not(.e-con-boxed) > .e-con,
[data-onda-fx-preview="switcher-scroll"] > .e-con-inner > .e-con {
	grid-area: 1 / 1 !important;
}
[data-onda-fx-preview="switcher-scroll"] .e-con:nth-child(1) { transform: translate3d(0, 0, 0); }
[data-onda-fx-preview="switcher-scroll"] .e-con:nth-child(2) { transform: translate3d(14px, 14px, 0); opacity: 0.7; }
[data-onda-fx-preview="switcher-scroll"] .e-con:nth-child(n+3) { transform: translate3d(28px, 28px, 0); opacity: 0.45; }

/* --- VERTICAL SCROLL STEPPER: pasos en SECUENCIA (uno bajo otro) con un
 * número de paso, para que el autor vea el orden. En el front se pinea y los
 * pasos se cruzan; acá solo los listamos en orden. --- */
[data-onda-fx-preview="vertical-scroll-stepper"]:not(.e-con-boxed),
[data-onda-fx-preview="vertical-scroll-stepper"] > .e-con-inner {
	display: flex !important;
	flex-direction: column !important;
	gap: 18px;
	padding-top: 14px;
}
[data-onda-fx-preview="vertical-scroll-stepper"]:not(.e-con-boxed) > .e-con,
[data-onda-fx-preview="vertical-scroll-stepper"] > .e-con-inner > .e-con {
	position: relative;
	counter-increment: onda-step;
	border-left: 2px solid rgba(120, 122, 130, 0.35);
	padding-left: 14px;
}
[data-onda-fx-preview="vertical-scroll-stepper"] > .e-con-inner,
[data-onda-fx-preview="vertical-scroll-stepper"]:not(.e-con-boxed) { counter-reset: onda-step; }
[data-onda-fx-preview="vertical-scroll-stepper"] > .e-con-inner > .e-con::before,
[data-onda-fx-preview="vertical-scroll-stepper"]:not(.e-con-boxed) > .e-con::before {
	content: counter(onda-step);
	position: absolute;
	left: -13px;
	top: 0;
	width: 22px;
	height: 22px;
	border-radius: 50%;
	background: rgba(120, 122, 130, 0.9);
	color: #fff;
	font: 700 11px/22px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
	text-align: center;
}

/* --- STICKY SCROLL: dos columnas (lado fijo + lado que scrollea) puestas una
 * al lado de la otra para que el autor arme las dos columnas. En el front una
 * queda sticky y la otra fluye. --- */
[data-onda-fx-preview="sticky-scroll"]:not(.e-con-boxed),
[data-onda-fx-preview="sticky-scroll"] > .e-con-inner {
	display: flex !important;
	flex-direction: row !important;
	flex-wrap: nowrap !important;
	align-items: flex-start !important;
	gap: 32px;
}
[data-onda-fx-preview="sticky-scroll"]:not(.e-con-boxed) > .e-con,
[data-onda-fx-preview="sticky-scroll"] > .e-con-inner > .e-con {
	flex: 1 1 0 !important;
	min-width: 0;
}

/* --- CURTAIN REVEAL: capas reveladas (apiladas, separadas) — el hero +
 * cada capa visible para que el autor las edite. En el front se descubren con
 * una cortina al scrollear. --- */
[data-onda-fx-preview="curtain-reveal"]:not(.e-con-boxed),
[data-onda-fx-preview="curtain-reveal"] > .e-con-inner {
	display: flex !important;
	flex-direction: column !important;
	gap: 12px;
}
[data-onda-fx-preview="curtain-reveal"]:not(.e-con-boxed) > .e-con,
[data-onda-fx-preview="curtain-reveal"] > .e-con-inner > .e-con {
	outline: 1px dashed rgba(120, 122, 130, 0.4);
	outline-offset: -1px;
}

/* --- ZOOM THROUGH: el contenido a una escala representativa (insinúa que el
 * texto crece y atraviesa la pantalla). Solo escala el texto, como en el
 * front; un valor estático moderado para que siga editable. --- */
[data-onda-fx-preview="zoom-through"] .elementor-widget-heading .elementor-heading-title,
[data-onda-fx-preview="zoom-through"] .elementor-widget-heading h1,
[data-onda-fx-preview="zoom-through"] .elementor-widget-heading h2,
[data-onda-fx-preview="zoom-through"] .elementor-widget-heading h3 {
	transform: scale(1.18);
	transform-origin: center center;
	display: inline-block;
}

/* --- FULLSCREEN GROW: el elemento a un estado "crecido" representativo (un
 * pelín recortado por los lados con esquinas redondeadas → insinúa el clip que
 * se abre). --- */
[data-onda-fx-preview="fullscreen-grow"]:not(.e-con-boxed),
[data-onda-fx-preview="fullscreen-grow"] > .e-con-inner {
	border-radius: 14px;
	overflow: hidden;
	box-shadow: inset 0 0 0 6px rgba(120, 122, 130, 0.18);
}

/* --- SCROLL ZOOM: el contenido a un zoom representativo dentro de una
 * "cápsula" redondeada (como arranca el efecto antes de abrirse a pantalla
 * completa). Estado estático, editable. --- */
[data-onda-fx-preview="scroll-zoom"]:not(.e-con-boxed),
[data-onda-fx-preview="scroll-zoom"] > .e-con-inner {
	border-radius: 20px;
	overflow: hidden;
	box-shadow: 0 12px 40px rgba(0, 0, 0, 0.16);
}

/* =====================================================================
 * PREVIEW EN EL EDITOR — COMPORTAMIENTOS (2.92.0).
 * No reshapean ítems; muestran un ESTADO representativo o un marcador.
 * ===================================================================*/

/* --- CONTENT SCALE: los hijos a su estado ENTRADO (full, nítidos). El front
 * los revela al entrar en viewport; en el editor ya se ven completos para que
 * no queden ocultos al editar. (El handler hace early-return; esto solo
 * neutraliza cualquier estado inicial heredado.) --- */
[data-onda-fx-preview="content-scale"] > .e-con-inner > *,
[data-onda-fx-preview="content-scale"]:not(.e-con-boxed) > * {
	opacity: 1 !important;
	filter: none !important;
	transform: none !important;
}

/* --- COLOR SCRUB: el fondo de la sección a un color medio de sus stops. El
 * color real lo aplica el script del editor leyendo los stops del modelo (vive
 * en background-color inline), así el autor ve el tinte y lo edita en vivo.
 * Esta regla solo asegura que el tinte ocupe el alto mínimo visible. --- */
[data-onda-fx-preview="color-scrub"] {
	min-height: 120px;
}

/* --- THEME ZONES: estado representativo "invertido" (el script del editor
 * aplica el bg/texto invertido del primer par del modelo). Marcador de zona. --- */
[data-onda-fx-preview="theme-zones"] {
	outline: 1px dashed rgba(120, 122, 130, 0.45);
	outline-offset: -1px;
	min-height: 80px;
}

/* --- ADAPTIVE HEADER: marcador de estado de contraste (no hay composición
 * estática real; el swap ocurre por scroll en el front). --- */
[data-onda-fx-preview="adaptive-header"] {
	outline: 1px dashed rgba(120, 122, 130, 0.45);
	outline-offset: -1px;
}

/* --- ADAPTIVE HEADER — modo BLEND (mix-blend-difference, CSS puro, sin JS) ---
 * TODO el header se auto-invierte contra lo que scrollea DETRÁS: sobre secciones
 * claras se ve oscuro, sobre oscuras claro, sin detectar nada ni setear colores.
 * Se aplica igual en editor y front.
 *
 * POR QUÉ va en el CONTENEDOR y no en los hijos: un header real es fixed/sticky, y
 * un elemento posicionado crea un contexto de apilado que AÍSLA el grupo de blend
 * → si el mix-blend va en los HIJOS, mezclan contra el fondo (transparente) del
 * propio header, NO contra la página (verificado: fixed/sticky/absolute lo atrapan;
 * solo static deja pasar). Puesto en el HEADER mismo, el header entero mezcla contra
 * la página y sí invierte estando fijo. Consecuencia inevitable: es un grupo único,
 * un hijo NO puede quedarse afuera de la mezcla (un `.no-blend` NO evita la
 * inversión — mix-blend-mode del padre no se puede cancelar desde el hijo). Para un
 * elemento que deba conservarse tal cual, usá el modo Auto-contraste/Manual.
 *
 * Gotcha #1: el header NO puede tener fondo opaco (mezclaría contra su propio fondo)
 * → forzamos transparente. Gotcha #3: `isolation:isolate` en un ancestro MÁS ARRIBA
 * atrapa el blend (hay que quitarlo a mano allí). El tipo (difference|exclusion)
 * llega por la var --onda-ah-blend. */
.onda-ah--blend {
	background-color: transparent !important;
	background-image: none !important;
	isolation: auto !important;
	mix-blend-mode: var(--onda-ah-blend, difference);
}
/* Legibilidad: bajo difference el BLANCO es el único color que invierte bien en
 * ambos lados (el negro se volvería invisible sobre fondos oscuros). Forzamos el
 * primer plano a blanco; `.no-blend` (y su contenido) conserva el color del autor
 * —sigue invirtiendo con el header, pero parte de su propio color, útil para un
 * logo/píldora de marca. */
.onda-ah--blend .elementor-heading-title:not(.no-blend):not(.no-blend *),
.onda-ah--blend .elementor-item:not(.no-blend):not(.no-blend *),
.onda-ah--blend a:not(.no-blend):not(.no-blend *):not(.elementor-button):not(.elementor-button *),
.onda-ah--blend .elementor-icon-list-text:not(.no-blend):not(.no-blend *) {
	color: #ffffff !important;
}
.onda-ah--blend .elementor-icon:not(.no-blend):not(.no-blend *) svg {
	fill: #ffffff !important;
}

/* --- EFECTOS DE CURSOR (Image Trail, Cursor Follower, Float Image) ---
 * Son puramente dirigidos por el puntero: NO tienen una composición estática
 * (sería engañoso fingir una). Mostramos solo un indicador claro de que el
 * efecto está aplicado + una etiqueta que aclara que SE VE EN EL FRONT. El
 * marcador ⚡ genérico de abajo sigue presente; acá agregamos el aviso. */
[data-onda-fx-preview="image-trail"],
[data-onda-fx-preview="cursor-follower"],
[data-onda-fx-preview="float-cursor"] {
	position: relative;
	min-height: 90px;
}
[data-onda-fx-preview="image-trail"]::after,
[data-onda-fx-preview="cursor-follower"]::after,
[data-onda-fx-preview="float-cursor"]::after {
	content: '🖱 efecto de cursor — se ve en el front';
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	z-index: 98;
	font: 600 11px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
	color: rgba(60, 62, 68, 0.92);
	background: rgba(120, 122, 130, 0.18);
	border: 1px dashed rgba(120, 122, 130, 0.5);
	border-radius: 6px;
	padding: 6px 12px;
	white-space: nowrap;
	pointer-events: none;
}

/* =====================================================================
 * SCROLL HEADER — Header Effect: MEJORAS de scroll sobre un header que el autor
 * hace sticky con ELEMENTOR (ya NO duplicamos el sticky). El JS togglea las
 * clases de estado; el CSS hace las transiciones (vars inline: --onda-sh-dur /
 * -shrink / -solid-bg / -solid-blur).
 * ===================================================================*/
.onda-sh {
	transition:
		transform var(--onda-sh-dur, 0.35s) cubic-bezier(0.4, 0, 0.2, 1),
		background-color var(--onda-sh-dur, 0.35s) ease,
		padding-top var(--onda-sh-dur, 0.35s) ease,
		padding-bottom var(--onda-sh-dur, 0.35s) ease,
		box-shadow var(--onda-sh-dur, 0.35s) ease,
		-webkit-backdrop-filter var(--onda-sh-dur, 0.35s) ease,
		backdrop-filter var(--onda-sh-dur, 0.35s) ease;
	will-change: transform;
}
/* Transparente → sólido con blur (solo al pasar el umbral). */
.onda-sh--solid {
	background-color: transparent;
}
.onda-sh--solid.is-solid {
	background-color: var(--onda-sh-solid-bg, rgba(255, 255, 255, 0.85));
	-webkit-backdrop-filter: blur(var(--onda-sh-solid-blur, 12px));
	backdrop-filter: blur(var(--onda-sh-solid-blur, 12px));
}
/* Sombra sutil al estar fijado tras el umbral. */
.onda-sh--can-shadow.is-stuck {
	box-shadow: 0 6px 24px rgba(0, 0, 0, 0.1);
}
/* Ocultar al bajar / mostrar al subir. */
.onda-sh.is-hidden {
	transform: translateY(-100%);
}
/* Encoger: el relleno lo ajusta el JS (respeta el del autor); acá achicamos el
 * logo (primera imagen del header) con el mismo factor. */
.onda-sh--can-shrink.is-shrunk img {
	transform: scale(var(--onda-sh-shrink, 0.7));
	transform-origin: left center;
	transition: transform var(--onda-sh-dur, 0.35s) cubic-bezier(0.4, 0, 0.2, 1);
}
/* prefers-reduced-motion: sin animación (el JS ya pone --onda-sh-dur:0s, pero
 * reforzamos por si el control de duración quedó alto). */
@media (prefers-reduced-motion: reduce) {
	.onda-sh,
	.onda-sh--can-shrink.is-shrunk img {
		transition: none !important;
	}
}
/* Editor: hint honesto (en el editor no hay scroll → no hay nada que animar). */
[data-onda-fx-preview="scroll-header"] {
	position: relative;
	outline: 1px dashed rgba(120, 122, 130, 0.4);
	outline-offset: -1px;
}
[data-onda-fx-preview="scroll-header"]::after {
	content: '⚓ Scroll Header — se ve al hacer scroll en el front';
	position: absolute;
	top: 6px;
	right: 8px;
	z-index: 98;
	font: 600 11px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
	color: rgba(60, 62, 68, 0.92);
	background: rgba(120, 122, 130, 0.18);
	border: 1px dashed rgba(120, 122, 130, 0.5);
	border-radius: 6px;
	padding: 4px 10px;
	white-space: nowrap;
	pointer-events: none;
}

/* Footer Reveal: el footer se fija detrás del contenido (estilos inline los pone el
   runtime). La sombra de profundidad es una línea suave hacia arriba en el borde del
   footer, que aparece al revelarse (da sensación de que el contenido se levanta). */
.onda-footer-reveal--shadow {
	box-shadow: 0 -24px 48px -12px rgba(0, 0, 0, 0.35);
}
.onda-footer-reveal-spacer {
	pointer-events: none;
}
/* Editor: hint honesto (en el editor no hay scroll). */
[data-onda-fx-preview="footer-reveal"] {
	position: relative;
	outline: 1px dashed rgba(120, 122, 130, 0.4);
	outline-offset: -1px;
}
[data-onda-fx-preview="footer-reveal"]::after {
	content: '⚓ Footer Reveal — se revela al hacer scroll al final, en el front';
	position: absolute;
	top: 6px;
	right: 8px;
	z-index: 98;
	font: 600 11px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
	color: rgba(60, 62, 68, 0.92);
	background: rgba(120, 122, 130, 0.18);
	border: 1px dashed rgba(120, 122, 130, 0.5);
	border-radius: 6px;
	padding: 4px 10px;
	white-space: nowrap;
	pointer-events: none;
}

/* Elastic Title: en el editor mostramos el heading levemente inclinado (skew
 * estático) para insinuar el efecto sin mover nada con JS. */
[data-onda-fx-preview="elastic-title"] .elementor-heading-title,
[data-onda-fx-preview="elastic-title"] h1,
[data-onda-fx-preview="elastic-title"] h2,
[data-onda-fx-preview="elastic-title"] h3 {
	display: inline-block;
	transform: skewX(-8deg);
	white-space: nowrap;
}

/* Scramble Text: cada carácter del texto descifrado vive en su propio span con
 * el ancho fijado al del carácter real (lo hace el JS) → al ciclar glifos
 * aleatorios la caja no cambia de ancho y el texto NO reflowa a ningún viewport.
 * Esta regla solo asegura el inline-block aunque algún reset lo pise. */
.onda-stx-char {
	display: inline-block;
}

/* Editor: el texto se muestra normal (sin descifrar); la etiqueta ⚡ genérica
 * de abajo ya indica qué efecto tiene el contenedor. Sin reglas de movimiento. */

/* =====================================================================
 * PREVIEW EN EL EDITOR — FAMILIA EFECTOS sobre WIDGET (2.93.0).
 *
 * Estos efectos se aplican a un widget nativo (Heading / Button / Image) que
 * Elementor YA renderiza en el editor: el texto/botón/imagen ya se ven (con su
 * tipografía/color/tamaño en vivo). En el editor, el motor JS NO corre sobre el
 * widget (before_render emite data-onda-fx-PREVIEW, no data-onda-fx → initElement
 * nunca lo toca). Así que acá:
 *
 *   - Los REVEALS de texto (blur-text/block-reveal/slide-reveal/line-reveal/
 *     letter-reveal/letter-bounce/word-assembly/scramble-text): su estado FINAL
 *     es el texto normal, que ya se ve → no se toca nada (solo la etiqueta ⚡).
 *   - Los DECORATIVOS visibles (text-shine/glitch/stroke-wipe/scroll-fill +
 *     marker-wipe/particles/sparkle-button/space-button sobre Botón): se pinta un
 *     ESTADO REPRESENTATIVO ESTÁTICO con las MISMAS vars de color que el control
 *     fija en {{WRAPPER}} (= el wrapper con data-onda-fx-preview) → el autor ve la
 *     decoración y la estila en vivo. Sin data-text (lo setea el JS en el front),
 *     sin rAF, sin loops de puntero. El visual completo corre en el front.
 *
 * Honesto: para los hover (hover-*/icon-*/center-reveal/double-text/arc/circle)
 * el botón se ve en reposo decorado por su propio CSS; no congelamos un mid-hover
 * engañoso → la etiqueta ⚡ basta para que el efecto sea descubrible.
 * ===================================================================*/

/* --- TEXT SHINE: el degradado de brillo aplicado ESTÁTICO sobre el texto nativo
 * (band de luz centrada). Usa las vars vivas --onda-shine-base / --onda-shine que
 * el control fija en el wrapper → editable en vivo. Sin animación. --- */
[data-onda-fx-preview="text-shine"] .elementor-heading-title,
[data-onda-fx-preview="text-shine"] .elementor-button-text,
[data-onda-fx-preview="text-shine"] .elementor-widget-container > p {
	display: inline-block;
	background: linear-gradient(110deg, var(--onda-shine-base, #555) 30%, var(--onda-shine, #fff) 50%, var(--onda-shine-base, #555) 70%);
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: transparent;
	color: transparent;
}

/* --- GLITCH TEXT: frame representativo de desfase RGB sobre el texto nativo. No
 * usamos las capas ::before/::after (necesitan data-text, que solo setea el JS):
 * en su lugar un doble text-shadow con los dos colores neón desplazados → la
 * "firma" glitch en reposo, editable por --onda-glitch-c1 / -c2. Sin animación. --- */
[data-onda-fx-preview="glitch-text"] .elementor-heading-title,
[data-onda-fx-preview="glitch-text"] .elementor-button-text,
[data-onda-fx-preview="glitch-text"] .elementor-widget-container > p {
	text-shadow:
		-2px 0 0 var(--onda-glitch-c1, #00e5ff),
		2px 0 0 var(--onda-glitch-c2, #ff00c1);
}

/* --- STROKE WIPE: estado LLENO representativo (el final del barrido): el texto
 * nativo se dibuja con contorno --onda-stroke-color y relleno --onda-fill-color
 * + glow, como queda cuando el relleno completó. Editable en vivo. Sin animación. --- */
[data-onda-fx-preview="stroke-wipe"] .elementor-heading-title,
[data-onda-fx-preview="stroke-wipe"] .elementor-button-text,
[data-onda-fx-preview="stroke-wipe"] .elementor-widget-container > p {
	-webkit-text-stroke: 1.2px var(--onda-stroke-color, #cccccc);
	-webkit-text-fill-color: var(--onda-fill-color, #3effa0);
	paint-order: stroke fill;
	text-shadow: 0 0 var(--onda-sw-glow, 4px) var(--onda-fill-color, #3effa0);
}

/* --- SCROLL FILL: relleno PARCIAL representativo (~60%): el texto nativo recibe
 * un degradado de color recortado al texto que va del color final (relleno) en el
 * primer 60% al gris apagado en el resto → insinúa "se rellena con el scroll".
 * Estático, sin JS. (El muted real lo aplica el front por char; acá un gris
 * neutro honesto.) --- */
[data-onda-fx-preview="scroll-fill"] .elementor-heading-title,
[data-onda-fx-preview="scroll-fill"] .elementor-button-text,
[data-onda-fx-preview="scroll-fill"] .elementor-widget-container > p {
	display: inline-block;
	background: linear-gradient(90deg, currentColor 0%, currentColor 58%, rgba(160, 160, 160, 0.85) 64%, rgba(160, 160, 160, 0.85) 100%);
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: transparent;
	color: transparent;
}

/* --- MARKER WIPE (botón): marcador "ya dibujado" detrás del label → una banda
 * de --onda-marker-color a lo ancho del botón. Insinúa el subrayado/resaltado
 * final sin construir el SVG (que arma el JS en el front). --- */
[data-onda-fx-preview="marker-wipe"] .elementor-button {
	position: relative;
	z-index: 0;
}
[data-onda-fx-preview="marker-wipe"] .elementor-button::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background: var(--onda-marker-color, #6a0dad);
	border-radius: inherit;
	opacity: 0.9;
	pointer-events: none;
}

/* --- PARTICLES (botón): unas pocas partículas ESTÁTICAS (puntos) en reposo, con
 * --onda-particle-color, para que la decoración se vea y se estile. El JS arma el
 * sistema vivo en el front; acá solo un frame representativo. --- */
[data-onda-fx-preview="particles"] .elementor-button {
	position: relative;
	overflow: hidden;
}
[data-onda-fx-preview="particles"] .elementor-button::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	background-image:
		radial-gradient(circle, var(--onda-particle-color, #ffffff) 1.5px, transparent 2px),
		radial-gradient(circle, var(--onda-particle-color, #ffffff) 1.5px, transparent 2px),
		radial-gradient(circle, var(--onda-particle-color, #ffffff) 1px, transparent 1.5px),
		radial-gradient(circle, var(--onda-particle-color, #ffffff) 1px, transparent 1.5px);
	background-size: 100% 100%;
	background-repeat: no-repeat;
	background-position: 22% 70%, 55% 38%, 78% 80%, 40% 20%;
	opacity: 0.65;
}

/* --- SPARKLE BUTTON (botón): unas pocas estrellas/destellos ESTÁTICOS alrededor
 * del label, con --onda-spark-color / --onda-spark-accent. En el front estallan al
 * hover; acá un frame representativo en reposo (sin loop). --- */
[data-onda-fx-preview="sparkle-button"] .elementor-button {
	position: relative;
	overflow: visible;
}
[data-onda-fx-preview="sparkle-button"] .elementor-button::after {
	content: "";
	position: absolute;
	inset: -6px;
	z-index: 2;
	pointer-events: none;
	background-image:
		radial-gradient(circle, var(--onda-spark-color, #ffffff) 2px, transparent 3px),
		radial-gradient(circle, var(--onda-spark-accent, #f5a623) 1.5px, transparent 2.5px),
		radial-gradient(circle, var(--onda-spark-color, #ffffff) 1.5px, transparent 2.5px);
	background-size: 100% 100%;
	background-repeat: no-repeat;
	background-position: 6% 24%, 92% 14%, 80% 90%;
	opacity: 0.85;
}

/* --- SPACE BUTTON (botón): borde de gradiente neón ESTÁTICO + un par de estrellas
 * → la "firma" del botón espacial en reposo, con --onda-sp-c1/-c2/-c3. El flujo del
 * gradiente y los sparkles vivos corren en el front; acá un frame representativo. --- */
[data-onda-fx-preview="space-button"] .elementor-button {
	position: relative;
	border: 2px solid transparent;
	background-image:
		linear-gradient(#0e0e14, #0e0e14),
		linear-gradient(120deg, var(--onda-sp-c1, #ff2d9b), var(--onda-sp-c2, #7b2ff7), var(--onda-sp-c3, #2b7cff), var(--onda-sp-c1, #ff2d9b));
	background-origin: border-box;
	background-clip: padding-box, border-box;
}
[data-onda-fx-preview="space-button"] .elementor-button::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	border-radius: inherit;
	background-image:
		radial-gradient(circle, #ffffff 1px, transparent 1.5px),
		radial-gradient(circle, #ffffff 1px, transparent 1.5px);
	background-size: 100% 100%;
	background-repeat: no-repeat;
	background-position: 28% 36%, 70% 66%;
	opacity: 0.5;
}

/* Los reveals de texto (blur-text/block-reveal/slide-reveal/line-reveal/
 * letter-reveal/letter-bounce/word-assembly/scramble-text) NO necesitan regla:
 * su estado final ES el texto normal del heading, que ya se ve en el editor (los
 * handlers JS hacen early-return en isEditor ANTES de ocultar/dispersar, y el motor
 * ni siquiera corre sobre el widget en el editor). image-reveal → la imagen se ve
 * revelada (final). Los hover/icon de botón se ven en reposo decorados por su CSS.
 * Para todos, la etiqueta ⚡ genérica de abajo los hace descubribles. */

/* Etiqueta para TODOS los contenedores con efecto (cualquier slug): el script
 * del preview los marca a todos; el layout especial de arriba solo aplica a
 * los efectos horizontales. Así siempre se ve QUÉ contenedor tiene QUÉ efecto. */
[data-onda-fx-preview] {
	position: relative;
}
[data-onda-fx-preview]::before {
	content: '⚡ ' attr(data-onda-fx-preview);
	position: absolute;
	top: 6px;
	left: 6px;
	z-index: 99;
	font: 600 10px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
	letter-spacing: 0.2px;
	color: rgba(60, 62, 68, 0.92);
	background: rgba(120, 122, 130, 0.25);
	border: 1px solid rgba(120, 122, 130, 0.4);
	border-radius: 4px;
	padding: 1px 6px;
	pointer-events: none;
}

/* =====================================================================
 * BLUR LIST (widget, R5 2.84.0) — antes el efecto de contenedor `listado-blur`,
 * ahora un WIDGET propio que renderiza su markup con clases .onda-lb-*. Estados
 * pintados por CSS (la transición acá es deseable: es filter/opacity, no
 * posición). El JS solo togglea la clase activa + sticky de etiquetas/laterales.
 * ===================================================================*/

/* --- Layout propio del widget --- */
/* overflow-x:clip contiene el desplazamiento del ítem activo (translateX + scale del
 * efecto de foco): si la lista ocupa el ancho completo (tablet/desktop), ese shift
 * empujaba el borde derecho fuera del viewport y generaba SCROLL HORIZONTAL real.
 * `clip` (no `hidden`) recorta sin crear contenedor de scroll → no rompe el sticky
 * de las etiquetas/laterales del modo Menú. El contenido del ítem va a la izquierda,
 * así que lo recortado a la derecha es espacio vacío. */
.onda-lb { width: 100%; overflow-x: clip; }
.onda-lb-list {
	display: flex;
	flex-direction: column;
	gap: var(--onda-lb-gap, 24px);
}
.onda-lb-item {
	display: flex;
	align-items: baseline;
	gap: 0.6em;
}
.onda-lb-item-num   { opacity: 0.55; font-variant-numeric: tabular-nums; }
.onda-lb-item-body  { display: flex; flex-direction: column; }
.onda-lb-item-title { display: block; }
.onda-lb-item-name  { display: block; opacity: 0.6; font-size: 0.4em; letter-spacing: 0.04em; }
.onda-lb-item a     { color: inherit; text-decoration: none; }
/* Menú: 3 columnas [nº | contenido | nombre]. */
.onda-lb-menu .onda-lb-cols {
	display: flex;
	flex-direction: row;
	align-items: flex-start;
	gap: var(--onda-lb-col-gap, 32px);
	width: 100%;
}
.onda-lb-menu .onda-lb-num   { flex: 0 0 auto; min-width: 2ch; }
.onda-lb-menu .onda-lb-name  { flex: 0 0 auto; text-align: right; margin-left: auto; }
.onda-lb-menu .onda-lb-content {
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
	gap: var(--onda-lb-group-gap, 96px);
	min-width: 0;
}
.onda-lb-menu .onda-lb-group  { display: flex; flex-direction: row; align-items: flex-start; gap: var(--onda-lb-col-gap, 32px); }
.onda-lb-menu .onda-lb-cat    { flex: 0 0 22%; }
.onda-lb-menu .onda-lb-items  { flex: 1 1 auto; display: flex; flex-direction: column; gap: var(--onda-lb-gap, 24px); min-width: 0; }

/* --- Estado base: BLUR (desenfoque clásico) --- */
[data-onda-fx="blur-list"] .onda-lb-item {
	filter: blur(var(--onda-lb-blur, 8px));
	opacity: var(--onda-lb-op, 0.5);
	transform: translateX(0) scale(1);
	/* El activo se corre a la derecha "esquivando" la etiqueta y se enfoca. */
	transition:
		filter 0.32s cubic-bezier(0.77, 0, 0.18, 1),
		opacity 0.22s ease,
		transform 0.37s cubic-bezier(0.77, 0, 0.18, 1),
		text-shadow 0.32s ease;
	pointer-events: none;
	z-index: 0;
}
[data-onda-fx="blur-list"] .onda-lb-item.onda-lb-active {
	filter: none;
	opacity: 1;
	transform: translateX(var(--onda-lb-shift, 48px)) scale(var(--onda-lb-scale, 1.04));
	pointer-events: auto;
	z-index: 1;
}

/* =====================================================================
 * BLUR LIST — modo de presentación SPOTLIGHT (referencia fromanother.love
 * "What We Do"): los ítems se ILUMINAN al cruzar el centro del viewport. El
 * enfocado brilla (glow + brillo pleno + leve crecida) y el resto se APAGAN
 * (brillo Y opacidad) EN VEZ del desenfoque. Reutiliza la MISMA máquina de foco
 * que el blur. Se activa solo con [data-lb-focus="spotlight"|"both"]; sin ese
 * atributo (o ="blur") no aplica NADA → el modo blur queda intacto.
 *
 * R5 — Fix "Spotlight no aparecía": el contrato JS→CSS era correcto, pero el
 * apagado de los inactivos era SOLO un brightness() — sobre tipografía clara
 * sobre fondo oscuro (el caso de uso) eso casi no se nota → "no aparece". Ahora
 * los inactivos combinan brightness + opacidad (var --onda-lb-dim manda en
 * AMBAS), dando un apagado inequívoco, y el activo recupera brillo + opacidad
 * plenos + un glow en capas (filter drop-shadow + text-shadow) más sólido.
 * Las reglas spotlight tienen +1 selector de atributo → ganan al base por
 * especificidad. Sin calc(var()) redundante.
 * ===================================================================*/

/* SPOTLIGHT: inactivos apagados (brillo + opacidad), sin blur. */
[data-onda-fx="blur-list"][data-lb-focus="spotlight"] .onda-lb-item {
	filter: brightness(var(--onda-lb-dim, 0.45));
	opacity: var(--onda-lb-dim, 0.45);
}
[data-onda-fx="blur-list"][data-lb-focus="spotlight"] .onda-lb-item.onda-lb-active {
	filter: brightness(1)
		drop-shadow(0 0 var(--onda-lb-glow, 18px) var(--onda-lb-glow-color, rgba(255, 244, 214, 0.85)));
	text-shadow:
		0 0 var(--onda-lb-glow, 18px) var(--onda-lb-glow-color, rgba(255, 244, 214, 0.85)),
		0 0 calc(var(--onda-lb-glow, 18px) * 2) var(--onda-lb-glow-color, rgba(255, 244, 214, 0.85));
	opacity: 1;
	transform: translateX(var(--onda-lb-shift, 48px)) scale(var(--onda-lb-fscale, 1.04));
}

/* BOTH: el desenfoque del modo blur SE MANTIENE (heredado de la regla base) y
 * al activo se le suma el glow (drop-shadow + text-shadow en capas) y la crecida
 * de foco. Solo aumentamos el activo; los inactivos siguen como en blur puro. */
[data-onda-fx="blur-list"][data-lb-focus="both"] .onda-lb-item.onda-lb-active {
	filter: drop-shadow(0 0 var(--onda-lb-glow, 18px) var(--onda-lb-glow-color, rgba(255, 244, 214, 0.85)));
	text-shadow:
		0 0 var(--onda-lb-glow, 18px) var(--onda-lb-glow-color, rgba(255, 244, 214, 0.85)),
		0 0 calc(var(--onda-lb-glow, 18px) * 2) var(--onda-lb-glow-color, rgba(255, 244, 214, 0.85));
	transform: translateX(var(--onda-lb-shift, 48px)) scale(var(--onda-lb-fscale, 1.04));
}

/* Blur List — modo MENÚ en MÓVIL: layout legible (nº arriba-izq, nombre arriba-der,
   contenido abajo) y categorías como SUBTÍTULO estático de sus ítems (no sticky → no
   se montan unas sobre otras). El JS suelta los sticky en móvil. */
@media (max-width: 767px) {
	[data-onda-fx="blur-list"].onda-lb-menu .onda-lb-cols {
		display: grid;
		grid-template-columns: 1fr 1fr;
		grid-template-areas: "num name" "content content";
		align-items: start;
		gap: 4px 12px;
	}
	[data-onda-fx="blur-list"].onda-lb-menu .onda-lb-num     { grid-area: num;  justify-self: start; }
	[data-onda-fx="blur-list"].onda-lb-menu .onda-lb-name    { grid-area: name; justify-self: end; text-align: right; margin-left: 0; }
	[data-onda-fx="blur-list"].onda-lb-menu .onda-lb-content { grid-area: content; width: 100%; gap: 40px; }
	/* Cada categoría en columna: nombre (subtítulo) arriba de sus ítems. */
	[data-onda-fx="blur-list"].onda-lb-menu .onda-lb-group {
		flex-direction: column;
		align-items: stretch;
	}
	[data-onda-fx="blur-list"].onda-lb-menu .onda-lb-cat {
		position: static;
		margin: 0 0 6px;
		align-self: stretch;
		flex-basis: auto;
	}
	/* Centrado opcional del texto en móvil (control "Centrar texto en móvil"). */
	[data-onda-fx="blur-list"][data-lb-malign="center"] .onda-lb-content,
	[data-onda-fx="blur-list"][data-lb-malign="center"] .onda-lb-cat,
	[data-onda-fx="blur-list"][data-lb-malign="center"] .onda-lb-item {
		text-align: center;
	}
	[data-onda-fx="blur-list"][data-lb-malign="center"] .onda-lb-item {
		justify-content: center;
	}
}

/* Vertical Scroll Stepper (efecto de sección): CSS retirado — convertido en WIDGET (v3.51.0). */

/* =====================================================================
 * VERTICAL SCROLL STEPPER — WIDGET (.onda-vss-w*). Estructura fija
 * (barra | texto | media). Port de la referencia ProEffects: grid de
 * 3 columnas en desktop; texto en máscaras apiladas (cortina por línea);
 * media = par editorial frente/fondo (capa A sobre B, offset/tamaños por
 * CSS-vars). El JS pinea (sticky) y anima. Mobile = apilado y CENTRADO,
 * con media más baja (no choca con las barras del navegador).
 * ===================================================================*/
.onda-vss-w {
	--onda-vssw-left: 42%;
	--onda-vssw-gap: 48px;
	--onda-vssw-pad-x: 80px;
	--onda-vssw-bar-active: #111;
	--onda-vssw-bar-inactive: rgba(17, 17, 17, 0.2);
	--onda-vssw-bar-w: 2px;
	--onda-vssw-bar-h: 40vh;
	--onda-vssw-a-w: 48%;
	--onda-vssw-a-h: 45vh;
	--onda-vssw-a-ox: -1px;
	--onda-vssw-a-oy: 100px;
	--onda-vssw-b-w: 63%;
	--onda-vssw-b-h: 63vh;
	--onda-vssw-img-radius: 0px;
	--onda-vssw-progress: 0;

	position: relative;
	width: 100%;
	min-height: 100vh;
	display: grid;
	grid-template-columns: auto var(--onda-vssw-left) 1fr;
	align-items: center;
	gap: var(--onda-vssw-gap);
	padding: 0 var(--onda-vssw-pad-x);
	box-sizing: border-box;
	isolation: isolate;
	color: inherit;
}

/* ─── Barra ─────────────────────────────────────────────────────────── */
.onda-vss-w__bar {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 8px;
	height: var(--onda-vssw-bar-h);
	width: var(--onda-vssw-bar-w);
}
.onda-vss-w__seg {
	width: var(--onda-vssw-bar-w);
	flex: 1 1 0;
	background: var(--onda-vssw-bar-inactive);
	border-radius: calc(var(--onda-vssw-bar-w) / 2);
	transition: background 0.35s ease;
}
.onda-vss-w__seg.is-active {
	background: var(--onda-vssw-bar-active);
}
.onda-vss-w__bar--progress {
	position: relative;
	display: block;
	height: var(--onda-vssw-bar-h);
	width: var(--onda-vssw-bar-w);
}
.onda-vss-w__bar--progress .onda-vss-w__bar-track {
	position: absolute;
	inset: 0;
	background: var(--onda-vssw-bar-inactive);
	border-radius: calc(var(--onda-vssw-bar-w) / 2);
}
.onda-vss-w__bar--progress .onda-vss-w__bar-fill {
	position: absolute;
	inset: 0;
	background: var(--onda-vssw-bar-active);
	border-radius: calc(var(--onda-vssw-bar-w) / 2);
	transform-origin: top;
	transform: scaleY(var(--onda-vssw-progress, 0));
	will-change: transform;
}

/* ─── Texto: máscaras apiladas ──────────────────────────────────────── */
.onda-vss-w__text {
	display: flex;
	flex-direction: column;
	gap: 24px;
	justify-content: center;
	max-width: 100%;
	min-width: 0;
}
.onda-vss-w__eyebrow-mask,
.onda-vss-w__title-mask,
.onda-vss-w__desc-mask,
.onda-vss-w__btn-mask {
	position: relative;
	width: 100%;
	display: grid;
	grid-template-columns: 1fr;
	grid-template-rows: auto;
}
.onda-vss-w__eyebrow-mask > *,
.onda-vss-w__title-mask > *,
.onda-vss-w__desc-mask > *,
.onda-vss-w__btn-mask > * {
	grid-column: 1;
	grid-row: 1;
	min-width: 0;
	margin: 0;
}
.onda-vss-w__title { font-size: clamp(32px, 4vw, 56px); line-height: 1.05; }
.onda-vss-w__desc { max-width: 46ch; }
.onda-vss-w__eyebrow { letter-spacing: 0.04em; }
/* Máscara de línea (la genera el JS con splitLines): recorta el desborde. */
.onda-vss-w .onda-line {
	display: block;
	line-height: inherit;
	/* Sangrado horizontal para itálicas/swashes (Safari no soporta
	 * overflow-clip-margin, así que el padding es el fallback real). */
	padding: 0.1em 0.35em;
	margin: -0.1em -0.35em;
	overflow: hidden;
	overflow: clip;
	overflow-clip-margin: 0.15em;
}
.onda-vss-w .onda-line-inner {
	display: block;
	line-height: inherit;
	will-change: transform;
	overflow-wrap: break-word;
}
/* Botón: bloque enmascarado (overflow hidden lo pone el JS en el mask del botón). */
.onda-vss-w__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	text-decoration: none;
	cursor: pointer;
	color: #fff;
	background: #111;
	border-radius: 4px;
	padding: 14px 28px;
	transition: background 0.25s ease, color 0.25s ease;
	will-change: transform;
}
.onda-vss-w__btn-mask { overflow: hidden; justify-items: start; }
.onda-vss-w__btn--empty { visibility: hidden; pointer-events: none; }
/* Con efecto de botón Onda Motion: la caja real es .onda-fx__inner → neutralizamos
 * el estilo base del <a> para no duplicar fondo/padding/borde. */
.onda-vss-w__btn.onda-fx {
	background: none;
	padding: 0;
	border: 0;
	border-radius: 0;
}

/* ─── Media: slots a/b ──────────────────────────────────────────────── */
.onda-vss-w__media {
	position: relative;
	width: 100%;
	height: 60vh;
	max-height: 760px;
}
.onda-vss-w__slot {
	position: absolute;
	inset: 0;
}
.onda-vss-w__layer {
	position: absolute;
	overflow: hidden;
	border-radius: var(--onda-vssw-img-radius);
	will-change: clip-path;
	display: block;
}
.onda-vss-w__layer--b { /* fondo: abajo-derecha, grande */
	bottom: 0;
	right: 0;
	width: var(--onda-vssw-b-w);
	height: var(--onda-vssw-b-h);
	transform: translate( var(--onda-vssw-b-ox, 0px), var(--onda-vssw-b-oy, 0px) );
}
.onda-vss-w__layer--a { /* frente: arriba-izquierda, chica, con offset */
	top: 0;
	left: 0;
	width: var(--onda-vssw-a-w);
	height: var(--onda-vssw-a-h);
	transform: translate( var(--onda-vssw-a-ox, 0px), var(--onda-vssw-a-oy, 0px) );
	z-index: 1;
}
.onda-vss-w__media {
	color: inherit;
}
.onda-vss-w__layer .onda-vss-w__el { /* el <img>/<video> llena EXACTAMENTE su capa */
	position: absolute;   /* la capa es position:absolute → contiene a este media */
	inset: 0;             /* fija el media al alto/ancho REAL de la caja (no del slot) */
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center; /* la ALINEACIÓN reencuadra la imagen acá (no mueve la caja) */
	display: block;
}
.onda-vss-w__empty,
.onda-vss-w--editor .onda-vss-w__media { min-height: 200px; }

/* ─── Mobile/tablet: apilado y CENTRADO, media más baja ─────────────── */
@media (max-width: 1024px) {
	.onda-vss-w {
		grid-template-columns: 1fr;
		justify-items: stretch;
		align-content: center;
		gap: 24px;
		padding: 8vh var(--onda-vssw-pad-x);
	}
	.onda-vss-w__bar { display: none; } /* la barra no aplica en columna única */
	.onda-vss-w__media { height: 42vh; max-height: 460px; }
}

/* Editor: el JS no corre (pin/coreografía off). Mostramos SOLO el paso 1 limpio
 * (sin apilar los demás) para que el autor vea el armado real. */
.onda-vss-w--editor { min-height: auto; }
.onda-vss-w--editor .onda-vss-w__slot.is-pending { display: none; }
.onda-vss-w--editor .onda-vss-w__title:not(.is-current),
.onda-vss-w--editor .onda-vss-w__desc:not(.is-current),
.onda-vss-w--editor .onda-vss-w__eyebrow:not(.is-current),
.onda-vss-w--editor .onda-vss-w__btn:not(.is-current) { display: none; }

/* =====================================================================
 * 3D GALLERY — WIDGET (.onda-g3d-w*). Renderiza los ítems como sub-containers
 * .e-con + data-onda-fx="gallery-3d"; el motor (OndaFX.register('gallery-3d'))
 * los mueve en una tira horizontal con la máscara curva. Este CSS solo dimensiona
 * los ítems antes de que el JS tome el control (y el preview del editor).
 * ===================================================================*/
/* El widget es una composición a PANTALLA COMPLETA. Forzamos ancho completo en su
 * wrapper de Elementor: al mover los ítems a la tira absoluta, el .onda-g3d-w se
 * queda sin contenido en flujo y, en un contenedor padre FLEX-ROW, el ítem flex
 * colapsaría a 0 (la galería desaparecería). width:100% lo evita en cualquier
 * dirección de flex. */
.elementor-widget-onda-gallery-3d {
	width: 100%;
}
.onda-g3d-w {
	--onda-g3dw-item-h: 60vh;
	--onda-g3dw-radius: 0px;
	position: relative;
	display: flex;
	flex-wrap: nowrap;
	align-items: center;
	gap: 24px;
	width: 100%;
	min-height: 100vh;
	overflow: hidden;
	box-sizing: border-box;
}
.onda-g3d-w__item {
	flex: 0 0 auto;
	height: var(--onda-g3dw-item-h, 60vh);
	border-radius: var(--onda-g3dw-radius, 0);
	overflow: hidden;
	position: relative;
}
.onda-g3d-w__img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
.onda-g3d-w__link { display: block; width: 100%; height: 100%; }
.onda-g3d-w__tpl { width: 100%; height: 100%; }
.onda-g3d-w__ph {
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	background: rgba(128, 128, 128, 0.12);
	color: rgba(128, 128, 128, 0.7);
	font-style: italic;
}
.onda-g3d-w__empty { padding: 24px; text-align: center; opacity: 0.6; font-style: italic; }
/* Editor: alto acotado para que el preview no ocupe toda la pantalla. */
.onda-g3d-w--editor { min-height: 0; }
.onda-g3d-w--editor .onda-g3d-w__item { height: 240px; }
/* En mobile/tablet (<1024) el efecto AHORA corre (variante sticky, ondaG3dMobile):
 * la tira viaja igual que en desktop. Los anchos de ítem los pone el autor por
 * control responsive; no forzamos apilado. */

/* =====================================================================
 * SCROLL WORD REVEAL — hueco inline de la palabra rotativa dentro del
 * título + estados de las palabras (transiciones inline desde el JS, con
 * la duración del control). El color de la palabra es configurable desde
 * Elementor vía variable en el wrapper (default: hereda del título).
 * ===================================================================*/
.onda-swr-wrap {
	position: relative;
	display: inline-block;
	vertical-align: bottom;
	white-space: nowrap;
}
.onda-swr-word {
	position: absolute;
	left: 0;
	bottom: 0;
	white-space: nowrap;
	opacity: 0;
	transform: translateY(-20px);
	color: var(--onda-swr-word-color, inherit);
	will-change: opacity, transform;
	pointer-events: none;
}
.onda-swr-word.is-active {
	pointer-events: auto;
}

/* ── Widgets activadores (pilar 2): badge SOLO en el editor ─────────────── */
.onda-activator-badge {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 4px;
	padding: 18px 24px;
	border: 2px dashed #93003c;
	border-radius: 8px;
	background: rgba(147, 0, 60, 0.06);
	color: #93003c;
	font-size: 15px;
	font-weight: 600;
	text-align: center;
	line-height: 1.3;
}
.onda-activator-badge small {
	font-size: 11px;
	font-weight: 400;
	opacity: 0.75;
}

/* ── Fase 3: Image Effects ──────────────────────────────────────────────── */

/* Floating Loop: vaivén suave en loop (variables las setea el JS). */
@keyframes onda-float {
	0%, 100% { transform: translateY(calc(var(--onda-fi-amp, 14px) * -1)) rotate(calc(var(--onda-fi-rot, 2deg) * -1)); }
	50%      { transform: translateY(var(--onda-fi-amp, 14px)) rotate(var(--onda-fi-rot, 2deg)); }
}
.onda-float-img {
	animation: onda-float var(--onda-fi-dur, 4s) ease-in-out infinite;
	will-change: transform;
}
.onda-float-img.onda-float-hover {
	transition: filter .3s ease;
}
.onda-float-img.onda-float-hover:hover {
	animation-play-state: paused;
	filter: brightness(1.06) drop-shadow(0 14px 34px rgba(0, 0, 0, 0.28));
}

/* Float Image (hover-reveal): capa flotante única que sigue al cursor. */
.onda-float-reveal {
	position: fixed;
	top: 0;
	left: 0;
	z-index: 9990;
	pointer-events: none;
	will-change: transform;
}
.onda-float-reveal img {
	display: block;
	width: auto;
	max-width: none;
	border-radius: 6px;
	transform: translate(-50%, -50%) scale(0.6) rotate(calc(var(--onda-fr-rot, 4deg) * -1));
	opacity: 0;
	transition: opacity .35s ease, transform .5s cubic-bezier(0.22, 0.61, 0.36, 1);
	will-change: transform, opacity;
}
.onda-float-reveal.is-visible img {
	opacity: 1;
	transform: translate(-50%, -50%) scale(1) rotate(var(--onda-fr-rot, 4deg));
}

/* Image Trail: imágenes efímeras que siguen el cursor. */
.onda-trail-img {
	position: absolute;
	pointer-events: none;
	width: auto;
	max-width: none;
	will-change: transform, opacity;
}

/* ── Components: Logo Marquee ────────────────────────────────────────────── */
/* El widget corre full-bleed (ancho de la ventana) aunque esté en una caja. */
.onda-cmarquee--full {
	position: relative;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	max-width: 100vw;
}
.onda-cmarquee__viewport {
	overflow: hidden;
	/* Fade de los bordes: los logos aparecen/desaparecen fundidos. */
	-webkit-mask-image: linear-gradient(to right, transparent, #000 var(--onda-cm-fade, 12%), #000 calc(100% - var(--onda-cm-fade, 12%)), transparent);
	mask-image: linear-gradient(to right, transparent, #000 var(--onda-cm-fade, 12%), #000 calc(100% - var(--onda-cm-fade, 12%)), transparent);
}
.onda-cmarquee__track {
	display: flex;
	align-items: center;
	width: max-content;
	animation: onda-cmarquee var(--onda-cm-speed, 25s) linear infinite;
	will-change: transform;
}
.onda-cmarquee--reverse .onda-cmarquee__track {
	animation-direction: reverse;
}
.onda-cmarquee--pause:hover .onda-cmarquee__track {
	animation-play-state: paused;
}
.onda-cmarquee__group {
	display: flex;
	align-items: center;
	flex-wrap: nowrap;
	flex-shrink: 0;
	/* Separación entre logos por CSS var (responsive: el control "gap" la setea por
	   dispositivo). Antes iba inline con el valor base → ignoraba tablet/móvil. */
	gap: var(--onda-cm-gap, 64px);
	padding-right: var(--onda-cm-gap, 64px);
}
.onda-cmarquee__item {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
}
.onda-cmarquee__item img {
	display: block;
	width: auto;
	max-width: none;
	height: var(--onda-cm-logo, 48px);
	-o-object-fit: contain;
	object-fit: contain;
	-o-object-position: center;
	object-position: center; /* logos siempre centrados (centro-centro) */
}
/* Proporción FIJA: cada logo va en una CAJA con el aspecto elegido (--onda-cm-ratio),
   alto = --onda-cm-logo; la imagen llena la caja con el ajuste elegido (--onda-cm-fit,
   contain por defecto) y queda centrada. Así todos los logos quedan parejos. */
.onda-cmarquee--ratio .onda-cmarquee__item {
	height: var(--onda-cm-logo, 48px);
	aspect-ratio: var(--onda-cm-ratio, 1 / 1);
}
.onda-cmarquee--ratio .onda-cmarquee__item img {
	width: 100%;
	height: 100%;
	max-width: 100%;
	-o-object-fit: var(--onda-cm-fit, contain);
	object-fit: var(--onda-cm-fit, contain);
}
@keyframes onda-cmarquee {
	from { transform: translateX(0); }
	to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
	.onda-cmarquee__track { animation: none; }
}
/* Editor: tira estática (sin animación) para mostrar un frame representativo. */
.onda-cmarquee--editor .onda-cmarquee__track { animation: none; }


/* ── Header Effects: Contextual Nav ──────────────────────────────────────── */
/* Píldora flotante cuyo set de ítems muta según la zona de scroll. Los sets se
   apilan (position:absolute); solo el activo es visible. El motor JS morphea el
   ancho de la píldora y hace el crossfade. */
.onda-cnav {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	justify-content: center;
	width: 100%;
}
.onda-cnav__pill {
	--onda-cnav-pad-x: 16px;
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 12px;
	min-height: 72px;
	padding: 6px var(--onda-cnav-pad-x);
	border-radius: 55px;
	background-color: #fff;
	border-width: 0;
	border-color: transparent;
	box-sizing: border-box;
	max-width: 100%;
	will-change: width;
}
/* SEGURO contra el "CTA/logo se sale del fondo" SOLO durante el morph: si en algún
   transitorio (scroll muy rápido, fuente cargando, set saliente) el contenido se
   adelanta al ancho del fondo, queda RECORTADO por la cápsula en vez de asomar afuera.
   Solo mientras morfa → en reposo overflow:visible para no recortar los efectos de
   hover del CTA (magnetic, space-button, etc.). El box-shadow no se ve afectado. */
.onda-cnav.is-morphing .onda-cnav__pill {
	overflow: hidden;
}
.onda-cnav--shadow .onda-cnav__pill {
	box-shadow: 0 10px 40px -12px rgba(0, 0, 0, 0.25), 0 2px 8px -4px rgba(0, 0, 0, 0.12);
}
.onda-cnav__brand {
	display: inline-flex;
	align-items: center;
	flex-shrink: 0;
}
.onda-cnav__brand img {
	display: block;
	height: 28px;
	width: auto;
	/* La píldora es una cápsula (border-radius grande): un logo muy alto choca con la
	   curvatura de la esquina y se ve cortado. Lo acotamos al alto de la píldora menos
	   un margen → siempre queda dentro del área recta/segura, nunca cortado por la curva.
	   (No degrada logos normales; solo limita los que excederían la cápsula.) */
	max-height: calc(var(--onda-cnav-pill-h, 72px) - 16px);
}

/* Pila de sets: el contenedor toma el alto del set activo; los inactivos se
   superponen sin ocupar espacio. */
.onda-cnav__sets {
	position: relative;
	display: inline-flex;
	align-items: center;
	flex: 1 1 auto;
	min-width: 0;
}
.onda-cnav__set {
	display: inline-flex;
	align-items: center;
	gap: 16px; /* separación ENTRE piezas (logo · menú · dots · CTA). */
	white-space: nowrap;
}
/* Envoltura de los ítems del menú (una pieza); su gap = separación entre ítems. */
.onda-cnav__items {
	display: inline-flex;
	align-items: center;
	gap: var(--onda-cnav-item-gap, 8px);
}
/* Los sets NO activos quedan absolutos y ocultos (apilados sobre el activo). */
.onda-cnav__set:not(.is-active) {
	position: absolute;
	top: 50%;
	left: 0;
	transform: translateY(-50%);
	visibility: hidden;
	opacity: 0;
	pointer-events: none;
}
.onda-cnav__set.is-active {
	position: relative;
	visibility: visible;
	opacity: 1;
}
/* Set SALIENTE durante el crossfade: overlay ABSOLUTO centrado y VISIBLE (lo funde
   el JS por opacidad). Al no ser relativo, NO afecta la altura de la fila → el set
   entrante define la altura solo y el contenido no se re-centra al quitarse el
   saliente (sin saltos verticales arriba/abajo). Va después de :not(.is-active)
   para ganarle el empate de especificidad por orden. */
.onda-cnav__set.is-leaving {
	position: absolute;
	top: 50%;
	left: 0;
	transform: translateY(-50%);
	visibility: visible;
	opacity: 1;
	pointer-events: none;
}
.onda-cnav__set--condensed { gap: 0; }

/* Indicador deslizante sobre el ítem activo (modos Menú WP / Adoptar). Píldora suave
   que se mueve y redimensiona hacia el ítem de la sección activa (estilo umano). El JS
   posiciona width + translateX; arranca oculto y aparece cuando hay sección activa. */
.onda-cnav__indicator {
	position: absolute;
	top: 6px;
	bottom: 6px;
	left: 0;
	width: 0;
	background-color: var(--onda-cnav-indicator, rgba(17, 17, 17, 0.07));
	border-radius: 999px;
	opacity: 0;
	pointer-events: none;
	z-index: 0;
	will-change: transform, width;
	transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), width 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s ease;
}
.onda-cnav__set .onda-cnav__item,
.onda-cnav__set .onda-cnav__cta { z-index: 1; }
@media (prefers-reduced-motion: reduce) {
	.onda-cnav__indicator { transition: opacity 0.2s ease; }
}

/* Indicador horizontal de secciones (dots estilo umano). */
.onda-cnav__nav {
	display: inline-flex;
	align-items: center;
	gap: var(--onda-cnav-dot-gap, 10px);
	flex: 0 0 auto;
	padding: 0 4px;
}
/* Modo SLIDER: el runtime puebla los dots SOLO si hay un slider funcionando (deslizando en
   horizontal). Si no hay slider activo (ej: el Horizontal Slider que en tablet/móvil apila los
   paneles en vertical, o un slider que no inicializó), el nav queda VACÍO → se oculta para no
   mostrar un indicador huérfano que confunde al usuario. */
.onda-cnav__nav--slider:empty { display: none; }
.onda-cnav__dot {
	width: var(--onda-cnav-dot-size, 9px);
	height: var(--onda-cnav-dot-size, 9px);
	padding: 0;
	border: 0;
	border-radius: 50%;
	background-color: var(--onda-cnav-dot, rgba(17, 17, 17, 0.25));
	cursor: pointer;
	transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), background-color 0.25s ease;
}
.onda-cnav__dot:hover { transform: scale(1.3); }
.onda-cnav__dot.is-active {
	background-color: var(--onda-cnav-dot-active, #111);
	transform: scale(1.35);
}
/* Modo slider: los puntos SOLO INDICAN el slide/panel activo (no navegables). Su valor es
   para sliders scroll-driven sin dots propios (Horizontal Slider); los sliders click/swipe
   (Image Carousel, Distort) ya traen sus propios controles, así que no se duplican. */
.onda-cnav__dot--slide { cursor: default; }
.onda-cnav__dot--slide:hover { transform: none; }
.onda-cnav__dot--slide.is-active { transform: scale(1.35); }
/* ── Animación de los dots: FUSIÓN líquida (gooey) / PÍLDORA deslizante ── */
.onda-cnav__nav--fuse,
.onda-cnav__nav--pill { position: relative; }
/* Indicador que viaja al dot activo (lo posiciona el runtime con translateX). */
.onda-cnav__dot-travel {
	position: absolute;
	left: 0;
	top: 0;
	width: var(--onda-cnav-dot-size, 9px);
	height: var(--onda-cnav-dot-size, 9px);
	border-radius: 99px;
	background-color: var(--onda-cnav-dot-active, #111);
	pointer-events: none;
	transform-origin: center;
	will-change: transform, width, height;
	transition: transform 0.6s cubic-bezier(0.34, 1.4, 0.5, 1), width 0.3s ease, height 0.3s ease;
}
/* FUSIÓN (estilo umano): el contenedor lleva el filtro gooey → el indicador se estira y se
   fusiona con los puntos al viajar (metaball). En fuse los puntos NO se escalan: el blob es el
   que marca el activo, y se ve como líquido conectándose entre puntos. */
.onda-cnav__nav--fuse { filter: url(#onda-goo); }
/* En fuse los puntos van OPACOS (el filtro gooey "borra" lo translúcido), pero RESPETAN tus
   colores: inactivo = "Color del punto", activo = "Color del punto activo" (igual que en los
   otros modos). El indicador (blob) viaja en el color activo → marca el activo como una gota
   que se fusiona. NOTA: para que el gooey luzca, usá colores OPACOS (un inactivo translúcido
   puede desvanecerse bajo el metaball). */
.onda-cnav__nav--fuse .onda-cnav__dot--slide {
	background-color: var(--onda-cnav-dot, rgba(17, 17, 17, 0.25));
	opacity: 1;
	transform: none;
}
.onda-cnav__nav--fuse .onda-cnav__dot--slide.is-active { background-color: var(--onda-cnav-dot-active, #111); transform: none; }
.onda-cnav__nav--fuse .onda-cnav__dot-travel { background-color: var(--onda-cnav-dot-active, #111); }
/* PÍLDORA: una cápsula TENUE (el color activo, translúcida) se desliza por DETRÁS del punto
   activo, como un highlight tipo "tab". Los puntos conservan TUS colores (control de color /
   activo): no se fuerza ningún color → no rompe al combinar con otros modos. */
.onda-cnav__nav--pill .onda-cnav__dot-travel {
	z-index: 0;
	border-radius: 99px;
	background-color: var(--onda-cnav-dot-active, #111);
	opacity: 0.2;
}
.onda-cnav__nav--pill .onda-cnav__dot--slide {
	position: relative;
	z-index: 1;
	transition: background-color 0.3s ease, transform 0.3s ease;
}
.onda-cnav__nav--pill .onda-cnav__dot--slide.is-active { transform: none; }
@media (prefers-reduced-motion: reduce) {
	.onda-cnav__dot-travel { transition: none; }
}

/* En móvil la píldora colapsa a burger: los dots de SECCIÓN (navegables) se ocultan (el
   panel los cubre). Los dots de SLIDER (indicador del panel activo) SÍ se mantienen — son
   un indicador, no parte del menú colapsable; se ven junto al burger (ver regla más abajo
   que los reexpone en el set activo). Si quedan vacíos (sin contrato de slider) el `:empty`
   de arriba los oculta igual, así que nunca aparece un indicador huérfano. */
.onda-cnav.is-mobile .onda-cnav__nav:not(.onda-cnav__nav--slider) { display: none; }
/* NOTA (v3.54.1): se RETIRÓ el `@media (max-width:1024px){ .onda-cnav__nav--slider{display:none} }`.
   Era de cuando el Horizontal Slider era DESKTOP-ONLY (en tablet/móvil apilaba → sin slide
   activo). Ahora los sliders horizontales (widget Horizontal Panels, Card Slider) CORREN en
   tablet/móvil y emiten el contrato data-onda-slide-total/active, así que el indicador SÍ
   tiene sentido ahí. Cuando no hay contrato, el `:empty` los oculta. */

.onda-cnav__item {
	position: relative;
	display: inline-flex;
	align-items: center;
	padding: 0 12px;
	color: #111;
	text-decoration: none;
	white-space: nowrap;
	line-height: 1.2;
	transition: color 0.25s ease;
}
/* Subrayado sutil al hover (premium, no fade plano). */
.onda-cnav__item::after {
	content: "";
	position: absolute;
	left: 12px;
	right: 12px;
	bottom: 2px;
	height: 1.5px;
	background-color: currentColor;
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.onda-cnav__item:hover::after { transform: scaleX(1); }
/* El CTA no lleva subrayado. */
.onda-cnav__cta::after { display: none; }

/* ── HOVER DE LOS ÍTEMS configurable (clase en el nav: onda-cnav--ihover-<modo>) ──
   El subrayado desde la izquierda es el DEFAULT (reglas de arriba); estas variantes lo
   ajustan o lo reemplazan. Los modos que NO usan subrayado ocultan el ::after. */
.onda-cnav--ihover-highlight .onda-cnav__item::after,
.onda-cnav--ihover-color .onda-cnav__item::after,
.onda-cnav--ihover-dim .onda-cnav__item::after,
.onda-cnav--ihover-none .onda-cnav__item::after { display: none; }

/* Subrayado que crece desde el CENTRO. */
.onda-cnav--ihover-underline-center .onda-cnav__item::after { transform-origin: center center; }

/* Resaltado: píldora de fondo detrás del ítem al hover. */
.onda-cnav--ihover-highlight .onda-cnav__item {
	border-radius: 999px;
	padding-top: 6px;
	padding-bottom: 6px;
	transition: color 0.25s ease, background-color 0.25s ease;
}
.onda-cnav--ihover-highlight .onda-cnav__item:hover {
	background-color: var(--onda-cnav-ihover-bg, rgba(17, 17, 17, 0.08));
}

/* Atenuar los DEMÁS ítems: al pasar por el menú, todos bajan opacidad menos el que tiene
   el cursor (resalta por contraste). Solo aplica a los ítems del menú (no al CTA/logo). */
.onda-cnav--ihover-dim .onda-cnav__items:hover .onda-cnav__item { opacity: 0.4; }
.onda-cnav--ihover-dim .onda-cnav__items .onda-cnav__item:hover { opacity: 1; }
.onda-cnav--ihover-dim .onda-cnav__item { transition: color 0.25s ease, opacity 0.25s ease; }

/* Sin efecto: el ítem no cambia de color ni muestra subrayado. */
.onda-cnav--ihover-none .onda-cnav__item:hover { color: inherit; }

.onda-cnav__cta {
	display: inline-flex;
	align-items: center;
	flex-shrink: 0;
	padding: 0 20px;
	min-height: 48px;
	margin-left: 6px;
	border-radius: 40px;
	background-color: #111;
	color: #fff;
	text-decoration: none;
	white-space: nowrap;
	line-height: 1;
	transition: background-color 0.25s ease, color 0.25s ease, transform 0.25s ease;
}
.onda-cnav__cta:hover { transform: translateY(-1px); }
/* CTA CON efecto de botón (.onda-fx): el motor de efectos pone `width:100%` en
   `.onda-fx` → el <a> exterior se estiraba a todo el ancho de la píldora. Acá el
   <a> exterior pasa a ser un wrapper inline SIN caja; la caja visible (fondo,
   relleno, redondeo) la lleva el `.onda-fx__inner` (que es donde el efecto clipea
   su relleno de hover). El fondo/redondeo se redirigen al inner desde los controles
   (selectores con :not(.onda-fx)). */
.onda-cnav__cta.onda-fx {
	width: auto;
	min-height: 0;
	padding: 0;
	background: transparent;
}
.onda-cnav__cta.onda-fx .onda-fx__inner {
	min-height: 48px;
	padding: 0 20px;
}

/* --- Burger (oculto en desktop) ------------------------------------------- */
.onda-cnav__burger {
	display: none;
	flex-direction: column;
	justify-content: center;
	gap: 5px;
	width: 44px;
	height: 44px;
	padding: 0;
	border: 0;
	background: transparent;
	cursor: pointer;
	flex-shrink: 0;
}
/* Neutraliza el fondo que algunos temas (Hello/Astra) ponen a button:hover/:focus →
   el burger NUNCA debe pintar un recuadro de color al pasar el mouse o al enfocar. */
.onda-cnav__burger:hover,
.onda-cnav__burger:focus,
.onda-cnav__burger:active {
	background: transparent;
	outline: none;
	box-shadow: none;
}
.onda-cnav__burger span {
	display: block;
	width: 22px;
	height: 2px;
	margin: 0 auto;
	/* Color PROPIO del burger (no el del panel): debe contrastar con la barra clara. */
	background-color: var(--onda-cnav-burger, #111);
	transition: transform 0.3s ease, opacity 0.2s ease;
}
/* Ícono propio del burger (control "Icono del burger"): usa el color del burger. El elemento
   .onda-cnav__burger-icon ES el <i> (Font Awesome) o el <svg> (SVG inline), no un wrapper. */
.onda-cnav__burger--icon { color: var(--onda-cnav-burger, #111); }
.onda-cnav__burger--icon .onda-cnav__burger-icon {
	font-size: 24px;
	line-height: 1;
	color: currentColor;
	display: inline-flex;
	align-items: center;
	justify-content: center;
}
.onda-cnav__burger--icon svg { width: 24px; height: 24px; fill: currentColor; display: block; }
.onda-cnav__burger--icon i { font-size: 24px; color: currentColor; line-height: 1; }

/* --- Móvil: BARRA casi full-width + MENÚ a pantalla completa (estilo umano) ---
   La píldora se vuelve una barra ancha (logo a la izquierda, burger a la derecha);
   al abrir, el menú ocupa toda la pantalla con los ÍTEMS grandes y el CTA abajo.
   En móvil NO se muestran los dots (buildMobilePanel solo lleva ítems + CTA). */
/* FULL-BLEED en móvil: la barra puede ir de lado a lado de la PANTALLA aunque el header
   esté en un contenedor boxed/con padding. 100vw centrado por márgenes (sin transform, para
   no romper el position:fixed del overlay). El ancho real de la cápsula lo da el control
   "Ancho de la barra" (% relativo a este 100vw → 100% = edge-to-edge, 92% = con aire). */
.onda-cnav.is-mobile {
	justify-content: center;
	width: 100vw;
	max-width: 100vw;
	margin-left: calc(50% - 50vw);
	margin-right: calc(50% - 50vw);
}
.onda-cnav.is-mobile .onda-cnav__set:not(.is-active) { display: none; }
.onda-cnav.is-mobile .onda-cnav__set.is-active { position: static; transform: none; opacity: 1; visibility: visible; }
/* En la barra solo vive el LOGO del set activo; el menú/dots-de-sección/CTA van al overlay.
   EXCEPCIÓN: los dots de SLIDER quedan en la barra (indicador del panel activo, junto al
   burger). Se auto-ocultan vía `:empty` si el slider no emite contrato. */
.onda-cnav.is-mobile .onda-cnav__set.is-active > :not(.onda-cnav__brand) { display: none; }
/* Los dots de SLIDER quedan CENTRADOS en la barra (absolute al centro de la píldora), así el
   logo va a la izquierda y el burger a la derecha sin que los dots desplacen el layout.
   Para que el centro sea el de la PÍLDORA (no el de .onda-cnav__sets, que no abarca el burger),
   en móvil dejamos sets/set ESTÁTICOS → el ancestro posicionado más cercano es la píldora.
   (En móvil los sets no-activos están display:none, así que no se necesita el position:relative
   de sets que usa el morph en desktop.) */
.onda-cnav.is-mobile .onda-cnav__sets { position: static; }
.onda-cnav.is-mobile .onda-cnav__set.is-active > .onda-cnav__nav--slider {
	display: flex;
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	margin: 0;
}
.onda-cnav.is-mobile .onda-cnav__sets { flex: 1 1 auto; min-width: 0; }
.onda-cnav.is-mobile .onda-cnav__pill {
	position: relative;              /* ancla los dots-slider centrados (absolute) */
	width: var(--onda-cnav-bar-w, 92%);   /* ancho configurable (control "Ancho de la barra") */
	max-width: 100%;
	min-height: 0;
	padding: 10px 14px 10px 18px;
	flex: 0 0 auto;
	justify-content: space-between;  /* logo izquierda (si hay) · burger derecha */
	gap: 10px;
}
.onda-cnav.is-mobile .onda-cnav__burger { display: flex; }
/* AL ABRIR (estilo umano): la barra se va "hacia atrás" (fade) y queda SOLO la X del overlay.
   La barra deja de capturar clicks; el cierre lo maneja el botón .onda-cnav__m-close. */
.onda-cnav.is-mobile-open .onda-cnav__pill {
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transition: opacity 0.25s ease, visibility 0.25s ease;
}

.onda-cnav__mobile[hidden] { display: none; }
/* OVERLAY a pantalla completa. El `display` se gatea con :not([hidden]) para que la barra
   cerrada NO muestre el overlay (display:flex en la base ganaría por especificidad a [hidden]). */
.onda-cnav.is-mobile .onda-cnav__mobile {
	position: fixed;
	inset: 0;
	z-index: 2147483001;
	flex-direction: column;
	margin: 0;
	padding: calc(env(safe-area-inset-top, 0px) + 24px) 28px calc(env(safe-area-inset-bottom, 0px) + 28px);
	border-radius: 0;
	background-color: #111;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
}
.onda-cnav.is-mobile .onda-cnav__mobile:not([hidden]) { display: flex; }

/* Botón CERRAR (X) arriba a la derecha del overlay. */
.onda-cnav__m-close {
	position: absolute;
	top: calc(env(safe-area-inset-top, 0px) + 22px);
	right: 24px;
	width: 40px;
	height: 40px;
	padding: 0;
	border: 0;
	background: transparent;
	cursor: pointer;
	z-index: 3;
}
/* Neutraliza el recuadro de color (rosa) que el tema mete a button:hover/:focus. */
.onda-cnav__m-close:hover,
.onda-cnav__m-close:focus,
.onda-cnav__m-close:active {
	background: transparent;
	outline: none;
	box-shadow: none;
}
.onda-cnav__m-close span {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 26px;
	height: 2.5px;
	border-radius: 2px;
	background-color: var(--onda-cnav-close, #fff);
}
.onda-cnav__m-close span:nth-child(1) { transform: translate(-50%, -50%) rotate(45deg); }
.onda-cnav__m-close span:nth-child(2) { transform: translate(-50%, -50%) rotate(-45deg); }

/* ÍTEMS: wrapper que ocupa el alto disponible → su posición VERTICAL y la ALINEACIÓN del
   texto se controlan con las clases raíz onda-cnav--mvalign-* / onda-cnav--malign-*. */
.onda-cnav.is-mobile .onda-cnav__m-items {
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-height: 0;
	padding-top: 56px;  /* deja aire bajo la X cuando los ítems van arriba */
}
.onda-cnav--mvalign-top .onda-cnav__m-items { justify-content: flex-start; }
.onda-cnav--mvalign-center .onda-cnav__m-items { justify-content: center; }
.onda-cnav--mvalign-bottom .onda-cnav__m-items { justify-content: flex-end; }
.onda-cnav--malign-left .onda-cnav__m-items { align-items: flex-start; }
.onda-cnav--malign-left .onda-cnav__m-item { text-align: left; }
.onda-cnav--malign-center .onda-cnav__m-items { align-items: center; }
.onda-cnav--malign-center .onda-cnav__m-item { text-align: center; }
.onda-cnav--malign-right .onda-cnav__m-items { align-items: flex-end; }
.onda-cnav--malign-right .onda-cnav__m-item { text-align: right; }
.onda-cnav.is-mobile .onda-cnav__m-item {
	display: block;
	padding: 8px 2px;
	color: #fff;
	text-decoration: none;
	font-size: clamp(1.9rem, 9vw, 2.6rem);
	font-weight: 500;
	line-height: 1.15;
	letter-spacing: -0.01em;
	transition: color 0.2s ease, opacity 0.2s ease;
}
/* Hover de los ÍTEMS del menú móvil: color propio (control "Color de los ítems al hover").
   Sin color seteado → leve atenuación para que SIEMPRE haya feedback. El selector incluye
   .onda-cnav__mobile a propósito (especificidad 0,5,0) para GANARLE al color base que Elementor
   emite por-página con 0,4,0 (.elementor-XXXX .elementor-element-YYY .onda-cnav__m-item) y que,
   al cargar después, empataba y se imponía dejando el hover sin efecto. */
.onda-cnav.is-mobile .onda-cnav__mobile .onda-cnav__m-item:hover { color: var(--onda-cnav-m-item-hover, inherit); opacity: 0.85; }
/* CTA: usa la etiqueta + enlace del CTA de la SECCIÓN, pero como BOTÓN SÓLIDO (umano):
   pinneado abajo, full-width, siempre visible (no depende de un hover que en táctil no existe).
   Color/fondo configurables; feedback al pasar/presionar. */
.onda-cnav.is-mobile .onda-cnav__mobile .onda-cnav__m-cta {
	flex: 0 0 auto;
	margin-top: 24px;
	display: block;
	width: 100%;
	box-sizing: border-box;
	padding: 17px 20px;
	border-radius: 999px;
	background-color: #fff;  /* lo sobreescribe el control "Fondo del CTA" */
	color: #111;             /* lo sobreescribe el control "Color de texto del CTA" */
	text-align: center;
	text-decoration: none;
	font-size: 1.05rem;
	font-weight: 600;
	letter-spacing: 0;
	transition: transform 0.2s ease, filter 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease, color 0.2s ease;
}
.onda-cnav.is-mobile .onda-cnav__mobile .onda-cnav__m-cta:active { transform: scale(0.97); }
/* Hover del CTA configurable (control "Hover del CTA" → clase raíz onda-cnav--mctahover-*). */
.onda-cnav--mctahover-lift.is-mobile .onda-cnav__m-cta:hover { transform: translateY(-3px); box-shadow: 0 10px 24px -8px rgba(0, 0, 0, 0.45); }
.onda-cnav--mctahover-grow.is-mobile .onda-cnav__m-cta:hover { transform: scale(1.03); }
.onda-cnav--mctahover-darken.is-mobile .onda-cnav__m-cta:hover { filter: brightness(0.9); }
/* onda-cnav--mctahover-none → sin efecto. Los controles de color hover (si se setean) igual aplican. */

/* Editor: muestra el estado por defecto estático (el JS no corre). El panel
   móvil queda oculto y el burger no se fuerza. */
.onda-cnav--editor .onda-cnav__mobile { display: none; }
.onda-cnav-empty {
	padding: 12px 16px;
	border: 1px dashed rgba(0, 0, 0, 0.25);
	border-radius: 8px;
	color: #555;
	font-size: 13px;
}

@media (prefers-reduced-motion: reduce) {
	.onda-cnav__pill { will-change: auto; }
	.onda-cnav__item, .onda-cnav__item::after, .onda-cnav__cta { transition: none; }
}


/* ── Fase 4: Text ────────────────────────────────────────────────────────── */
.onda-blurtext { margin: 0; }
.onda-blurtext .onda-word { display: inline-block; }

.onda-linereveal { margin: 0; }
/* Máscara con SANGRADO: el line-box recorta glifos que lo desbordan (itálicas y
 * swashes por los LADOS, descendentes g/p/y por ABAJO con line-height apretado).
 * padding = ventana de recorte más grande; margen negativo igual = layout idéntico.
 * El estado oculto compensa con translateY(calc(100% + 0.3em)) en el JS. */
.onda-linereveal .onda-line { display: block; overflow: hidden; padding: 0.15em 0.35em 0.25em; margin: -0.15em -0.35em -0.25em; }
.onda-linereveal .onda-line-inner { display: inline-block; will-change: transform; }

.onda-textloop { display: block; }
.onda-textloop__slot {
	display: inline-flex;
	overflow: hidden;
	vertical-align: bottom;
	line-height: 1.1;
}
.onda-textloop__track {
	display: flex;
	flex-direction: column;
	will-change: transform;
}
.onda-textloop__word {
	display: block;
	white-space: nowrap;
	line-height: 1.1;
}

/* ── Fase 5: Components ──────────────────────────────────────────────────── */
/* Testimonial Slider (prefijo .onda-ts) — slide-up por palabra + avatar shrink/grow.
   Técnica replicada del Testimonial Slider de ProEffects; código propio. */
.onda-ts {
	--onda-ts-tx-dur: 700ms;
	--onda-ts-tx-ease: cubic-bezier(.76, 0, .24, 1);
	--onda-ts-stagger: 80ms;
	--onda-ts-avatar-w: 48px;
	--onda-ts-avatar-h: 48px;
	--onda-ts-align: flex-start;
	--onda-ts-text: left;
	--onda-ts-quote-size: clamp(1.4rem, 2.6vw + 0.7rem, 2.3rem);
	--onda-ts-col-gap: 48px;
	position: relative; width: 100%; box-sizing: border-box;
	display: flex; flex-direction: column;
}
.onda-ts *, .onda-ts *::before, .onda-ts *::after { box-sizing: border-box; }
.onda-ts[data-arrows="sides"] { padding: 0 56px; }
/* Alineación coherente (cita + autor + avatar) vía data-align. */
.onda-ts[data-align="left"]   { --onda-ts-align: flex-start; --onda-ts-text: left; }
.onda-ts[data-align="center"] { --onda-ts-align: center;     --onda-ts-text: center; }
.onda-ts[data-align="right"]  { --onda-ts-align: flex-end;   --onda-ts-text: right; }

.onda-ts__top {
	display: flex; align-items: center; gap: 24px;
	width: 100%; margin: 0 auto 28px;
}
/* top-left: flechas → meta (todo a la izquierda). top-right: meta (izq) → flechas (der). */
.onda-ts[data-arrows="top-right"] .onda-ts__arrows { order: 2; }
.onda-ts__meta { display: flex; align-items: center; gap: 16px; flex: 1; }
.onda-ts__counter { font-variant-numeric: tabular-nums; font-size: 0.85rem; white-space: nowrap; opacity: 0.85; }
.onda-ts__eyebrow { font-size: 0.78rem; letter-spacing: 1.5px; text-transform: uppercase; white-space: nowrap; opacity: 0.65; }

.onda-ts__content { width: 100%; margin: 0 auto; display: flex; flex-direction: column; align-items: var(--onda-ts-align); }
.onda-ts__stage { position: relative; width: 100%; min-height: 320px; }
.onda-ts__slide { position: absolute; inset: 0; pointer-events: none; opacity: 0; visibility: hidden; display: flex; flex-direction: column; align-items: var(--onda-ts-align); }
.onda-ts__slide.is-active { position: relative; pointer-events: auto; opacity: 1; visibility: visible; }

.onda-ts__avatar-top { margin-bottom: 22px; }
.onda-ts__quote-wrap { margin-bottom: 36px; }
.onda-ts__quote { margin: 0; font-size: var(--onda-ts-quote-size); line-height: 1.25; font-weight: 500; letter-spacing: -0.01em; text-align: var(--onda-ts-text); }
.onda-ts__word { display: inline-block; overflow: hidden; white-space: nowrap; vertical-align: bottom; line-height: inherit; }
.onda-ts__word-inner { display: inline-block; transform: translateY(0); transition: transform var(--onda-ts-tx-dur) var(--onda-ts-tx-ease); will-change: transform; }

.onda-ts__author { display: inline-flex; align-items: center; gap: 14px; }
.onda-ts__avatar-wrap {
	flex: 0 0 auto; width: var(--onda-ts-avatar-w); height: var(--onda-ts-avatar-h);
	align-self: center; overflow: hidden; border-radius: 50%;
	transform: scale(1); transform-origin: center;
	transition: transform var(--onda-ts-tx-dur) var(--onda-ts-tx-ease); will-change: transform;
}
.onda-ts .onda-ts__avatar-wrap img.onda-ts__avatar {
	width: 100% !important; height: 100% !important; max-width: none !important;
	object-fit: cover; display: block; border-radius: inherit;
}
.onda-ts__author-text-wrap { overflow: hidden; }
.onda-ts__author-text {
	display: flex; flex-direction: column; gap: 2px; align-items: var(--onda-ts-align); text-align: var(--onda-ts-text);
	transform: translateY(0); transition: transform var(--onda-ts-tx-dur) var(--onda-ts-tx-ease);
	transition-delay: var(--onda-ts-stagger); will-change: transform;
}

/* Layout 2 columnas (cita | autor). */
.onda-ts[data-layout="split"] .onda-ts__content { align-items: stretch; }
.onda-ts[data-layout="split"] .onda-ts__slide {
	display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
	gap: var(--onda-ts-col-gap); align-items: start;
}
.onda-ts[data-layout="split"] .onda-ts__avatar-top { grid-column: 2; grid-row: 1; margin-bottom: 0; }
.onda-ts[data-layout="split"] .onda-ts__quote-wrap { grid-column: 1; grid-row: 1 / span 2; align-self: center; margin-bottom: 0; }
.onda-ts[data-layout="split"] .onda-ts__author { grid-column: 2; grid-row: 2; align-self: start; }
.onda-ts__author-name { font-size: 0.95rem; font-weight: 600; }
.onda-ts__author-role { font-size: 0.85rem; opacity: 0.7; }

/* Estados de transición (aplicados por JS). */
.onda-ts__slide.is-exit .onda-ts__word-inner { transform: translateY(-110%); }
.onda-ts__slide.is-exit .onda-ts__author-text { transform: translateY(-110%); }
.onda-ts__slide.is-exit .onda-ts__avatar-wrap { transform: scale(0); }
.onda-ts__slide.is-enter-from .onda-ts__word-inner { transform: translateY(110%); transition: none; }
.onda-ts__slide.is-enter-from .onda-ts__author-text { transform: translateY(110%); transition: none; }
.onda-ts__slide.is-enter-from .onda-ts__avatar-wrap { transform: scale(0); transition: none; }

.onda-ts__arrows { display: flex; gap: 8px; flex: 0 0 auto; }
.onda-ts__arrow {
	width: 38px; height: 38px; display: inline-flex; align-items: center; justify-content: center;
	background: transparent; border: 1px solid currentColor; border-radius: 8px; color: inherit;
	cursor: pointer; padding: 0; opacity: 0.9;
	transition: background-color .2s ease, color .2s ease, opacity .2s ease;
}
.onda-ts__arrow:hover { opacity: 1; }
.onda-ts__arrow:disabled { opacity: 0.3; cursor: not-allowed; }
.onda-ts__arrow--lateral { position: absolute; top: 50%; transform: translateY(-50%); z-index: 3; border-radius: 50%; }
.onda-ts__arrow--lateral.onda-ts__arrow--prev { left: 0; }
.onda-ts__arrow--lateral.onda-ts__arrow--next { right: 0; }

.onda-ts__bottom { display: flex; align-items: center; justify-content: center; gap: 24px; margin-top: 28px; }
.onda-ts__dots { display: flex; gap: 8px; }
.onda-ts__dot {
	width: 8px; height: 8px; border-radius: 50%; background: currentColor; opacity: 0.3;
	border: 0; padding: 0; cursor: pointer;
	transition: opacity .2s ease, width .25s ease, border-radius .25s ease;
}
.onda-ts__dot.is-on { opacity: 1; width: 22px; border-radius: 4px; }

@media (max-width: 767px) {
	/* En móvil el layout de 2 columnas colapsa a apilado. */
	.onda-ts[data-layout="split"] .onda-ts__slide { display: flex; flex-direction: column; align-items: var(--onda-ts-align); gap: 0; }
	.onda-ts[data-layout="split"] .onda-ts__avatar-top { margin-bottom: 22px; }
	.onda-ts[data-layout="split"] .onda-ts__quote-wrap { margin-bottom: 36px; align-self: auto; }
}
@media (max-width: 640px) {
	.onda-ts__top { flex-wrap: wrap; gap: 12px 16px; }
	.onda-ts__stage { min-height: 260px; }
	.onda-ts[data-arrows="sides"] { padding: 0; }
}
@media (prefers-reduced-motion: reduce) {
	.onda-ts__word-inner, .onda-ts__author-text, .onda-ts__avatar-wrap { transition: none !important; }
}

/* Ancho completo del wrapper de Elementor: dentro de un contenedor FLEX-ROW, el ítem
   flex se encoge a su contenido mínimo (la etiqueta vertical ≈24px) y el accordion
   "desaparece". width:100% lo evita en cualquier dirección de flex (mismo fix que
   gallery-3d). */
.elementor-widget-onda-accordion {
	width: 100%;
}

/* Expandable Accordion horizontal (prefijo .onda-eacc).
   Técnica replicada del Expandable Accordion de ProEffects; código propio. */
.onda-eacc {
	--onda-eacc-gap: 8px;
	--onda-eacc-radius: 14px;
	--onda-eacc-ratio: 5;
	--onda-eacc-h: 480px;
	--onda-eacc-content-w: 340px;
	--onda-eacc-peek: 0px;
	--onda-eacc-overlay-a: 0.45;
	--onda-eacc-overlay-c: 0, 0, 0;
	--onda-eacc-tx-dur: 600ms;
	--onda-eacc-tx-ease: cubic-bezier(.22, .8, .25, 1);
	--onda-eacc-fade-dur: 220ms;
	position: relative; width: 100%; box-sizing: border-box;
}
.onda-eacc *, .onda-eacc *::before, .onda-eacc *::after { box-sizing: border-box; }
.onda-eacc__viewport { overflow: hidden; width: 100%; }
.onda-eacc__track {
	display: flex; gap: var(--onda-eacc-gap); height: var(--onda-eacc-h); width: 100%;
	transition: transform var(--onda-eacc-tx-dur) var(--onda-eacc-tx-ease); will-change: transform;
}
.onda-eacc__item {
	position: relative; overflow: hidden; border-radius: var(--onda-eacc-radius);
	flex: 1 1 0; min-width: 0; cursor: pointer;
	transition: flex var(--onda-eacc-tx-dur) var(--onda-eacc-tx-ease);
	background-size: cover; background-position: center; background-color: #1a1a1a;
}
.onda-eacc__item.is-active { flex: var(--onda-eacc-ratio) 1 0; }
.onda-eacc[data-layout="carousel"] .onda-eacc__track { width: auto; }
.onda-eacc[data-layout="carousel"] .onda-eacc__item {
	flex: 0 0 var(--onda-eacc-carousel-w, 200px);
	transition: flex-basis var(--onda-eacc-tx-dur) var(--onda-eacc-tx-ease);
}
.onda-eacc[data-layout="carousel"] .onda-eacc__item.is-active {
	flex: 0 0 calc(var(--onda-eacc-carousel-w, 200px) * var(--onda-eacc-ratio));
}

.onda-eacc__overlay { position: absolute; inset: 0; pointer-events: none; }
.onda-eacc[data-overlay="none"] .onda-eacc__overlay { display: none; }
.onda-eacc[data-overlay="flat"] .onda-eacc__overlay { background: rgba(var(--onda-eacc-overlay-c), var(--onda-eacc-overlay-a)); }
.onda-eacc[data-overlay="gradient"] .onda-eacc__overlay {
	background: linear-gradient(180deg,
		rgba(var(--onda-eacc-overlay-c), calc(var(--onda-eacc-overlay-a) * 0.20)) 0%,
		rgba(var(--onda-eacc-overlay-c), var(--onda-eacc-overlay-a)) 100%);
}

.onda-eacc__label {
	position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
	opacity: 0; pointer-events: none; transition: opacity 320ms ease calc(var(--onda-eacc-tx-dur) * 0.4);
}
.onda-eacc[data-show-label="on"] .onda-eacc__label { opacity: 1; }
.onda-eacc__label span {
	writing-mode: vertical-rl; transform: rotate(180deg);
	font-size: 0.85rem; letter-spacing: 3px; text-transform: uppercase; font-weight: 600;
	color: #fff; opacity: 0.9;
}
.onda-eacc[data-show-label="on"] .onda-eacc__item.is-active .onda-eacc__label {
	opacity: 0; transition: opacity var(--onda-eacc-fade-dur) ease 0ms;
}

.onda-eacc__content {
	position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: flex-end;
	padding: 24px; color: #fff;
}
.onda-eacc__expanded {
	width: var(--onda-eacc-content-w); max-width: 100%;
	opacity: 0; transform: translateY(8px); pointer-events: none;
	transition: opacity var(--onda-eacc-fade-dur) ease 0ms, transform var(--onda-eacc-fade-dur) ease 0ms;
}
.onda-eacc__item.is-active .onda-eacc__expanded {
	opacity: 1; transform: translateY(0); pointer-events: auto;
	transition:
		opacity 380ms ease calc(var(--onda-eacc-tx-dur) * 0.45),
		transform 480ms var(--onda-eacc-tx-ease) calc(var(--onda-eacc-tx-dur) * 0.45);
}
.onda-eacc__eyebrow { display: block; font-size: 0.7rem; letter-spacing: 2.5px; text-transform: uppercase; opacity: 0.85; font-weight: 600; margin: 0 0 10px; }
.onda-eacc__title { font-size: 1.8rem; line-height: 1.1; letter-spacing: -0.4px; font-weight: 700; margin: 0 0 10px; }
.onda-eacc__desc { margin: 0 0 18px; opacity: 0.82; max-width: 40ch; font-size: 0.95rem; }
.onda-eacc__btn {
	display: inline-flex; align-items: center; gap: 7px; padding: 10px 20px; border-radius: 999px;
	background: #fff; color: #0a0a0a; text-decoration: none; font-size: 0.82rem; font-weight: 600;
	width: fit-content; pointer-events: none;
	transition: background-color .2s ease, color .2s ease, transform .2s ease;
}
.onda-eacc__item.is-active .onda-eacc__btn { pointer-events: auto; }
.onda-eacc__btn:hover { transform: translateX(2px); }
.onda-eacc__btn-icon { display: inline-flex; align-items: center; justify-content: center; line-height: 0; flex-shrink: 0; }
.onda-eacc__btn-icon > svg { display: block; }

.onda-eacc__nav { display: flex; align-items: center; gap: 16px; justify-content: center; margin-top: 22px; }
.onda-eacc__nav.is-hidden { display: none; }
.onda-eacc__dots { display: flex; gap: 8px; }
.onda-eacc__dot {
	width: 8px; height: 8px; border-radius: 50%; background: currentColor; opacity: 0.3;
	cursor: pointer; border: 0; padding: 0; color: inherit;
	transition: opacity 250ms ease, width 250ms ease, border-radius 250ms ease;
}
.onda-eacc__dot.is-on { opacity: 1; width: 22px; border-radius: 4px; }
.onda-eacc__arrow {
	width: 38px; height: 38px; border-radius: 50%;
	background: rgba(0, 0, 0, 0.08); border: 1px solid rgba(0, 0, 0, 0.12); color: inherit;
	display: flex; align-items: center; justify-content: center; cursor: pointer;
	font-size: 18px; line-height: 1; user-select: none;
	transition: background-color .2s ease, opacity .2s ease;
}
.onda-eacc__arrow:hover { background: rgba(0, 0, 0, 0.14); }
.onda-eacc__arrow:disabled { opacity: 0.3; cursor: not-allowed; }
.onda-eacc__arrow--lateral {
	position: absolute; top: 50%; transform: translateY(-50%); z-index: 5;
	background: #fff; color: #0a0a0a; border-color: rgba(255, 255, 255, 0.5);
	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.onda-eacc__arrow--lateral.onda-eacc__arrow--prev { left: -18px; }
.onda-eacc__arrow--lateral.onda-eacc__arrow--next { right: -18px; }

@media (prefers-reduced-motion: reduce) {
	.onda-eacc__track, .onda-eacc__item, .onda-eacc__label, .onda-eacc__expanded,
	.onda-eacc__dot, .onda-eacc__arrow { transition: none !important; }
}
/* Móvil: el acordeón HORIZONTAL cae a un apilado vertical limpio (las tarjetas
   horizontales se aplastarían en pantallas chicas). Todos los paneles visibles con su
   contenido; sin pin/carrusel/nav. !important para pisar los estilos inline del JS. */
@media (max-width: 767px) {
	.onda-eacc__track {
		flex-direction: column !important;
		height: auto !important;
		width: 100% !important;
		transform: none !important;
		gap: 12px;
	}
	.onda-eacc__item,
	.onda-eacc[data-layout="carousel"] .onda-eacc__item,
	.onda-eacc__item.is-active,
	.onda-eacc[data-layout="carousel"] .onda-eacc__item.is-active {
		flex: 0 0 auto !important;
		width: 100% !important;
		min-height: 240px;
	}
	.onda-eacc__expanded {
		opacity: 1 !important;
		transform: none !important;
		pointer-events: auto !important;
		width: 100%;
	}
	.onda-eacc__btn { pointer-events: auto !important; }
	.onda-eacc__label { display: none !important; }
	.onda-eacc__nav { display: none !important; }
}

/* Progress Line */
.onda-progressline { position: relative; width: 100%; overflow: hidden; border-radius: 999px; }
.onda-progressline--sticky { position: fixed; top: 0; left: 0; z-index: 99999; border-radius: 0; }
.onda-progressline__bar { display: block; height: 100%; width: 0; will-change: width; }

/* Blob Shape (máscara orgánica con contenido adentro) */
/* El widget toma el ancho completo del contenedor → no colapsa a 0 en flex-row /
   mobile (mismo patrón que gallery-3d/accordion). Si no, .onda-morph con max-width:100%
   de un padre 0px se reduce a 0 y "no aparece" en mobile. */
.elementor-widget-onda-morph-shape { width: 100%; }
.onda-morph-wrap { display: flex; width: 100%; justify-content: center; }
.onda-morph {
	max-width: 100%;
	/* Fallbacks: si el control de tamaño/color no emite su CSS (p.ej. widget
	   inyectado por JSON o sin configurar), el blob igual se ve — nunca vacío.
	   El control, cuando está, pisa estos valores (mayor especificidad). */
	width: 280px;
	aspect-ratio: 1 / 1;
	flex-shrink: 0; /* que no se comprima el ancho en el flex (causaba elipse en mobile). */
	background-color: #ff3d00;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden; /* recorta el contenido a la forma del blob (máscara). */
	border-radius: 42% 58% 70% 30% / 45% 45% 55% 55%;
	animation: onda-morph var(--onda-morph-speed, 12s) ease-in-out infinite;
	will-change: border-radius;
}
.onda-morph--static { animation: none; }
/* Editor: blob estático (sin morph en loop) para mostrar un frame representativo. */
.onda-morph--editor { animation: none; }
.onda-morph__text { padding: 12%; text-align: center; line-height: 1.2; }
/* Morph MÁS marcado: variación de border-radius más extrema + más keyframes → el
   blob se deforma de forma claramente perceptible (antes era muy sutil). */
@keyframes onda-morph {
	0%, 100% { border-radius: 38% 62% 63% 37% / 41% 44% 56% 59%; }
	25%      { border-radius: 70% 30% 28% 72% / 66% 62% 38% 34%; }
	50%      { border-radius: 33% 67% 56% 44% / 28% 70% 30% 72%; }
	75%      { border-radius: 62% 38% 36% 64% / 58% 28% 72% 42%; }
}
@media (prefers-reduced-motion: reduce) { .onda-morph { animation: none; } }

/* ── Mouse effects ───────────────────────────────────────────────────────── */
.onda-cursor {
	position: fixed;
	top: 0;
	left: 0;
	border-radius: 50%;
	pointer-events: none;
	z-index: 2147483600;
	opacity: 0;
	transition: opacity 0.25s ease, width 0.2s ease, height 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
	will-change: transform;
}
.onda-cursor--ring { border: 2px solid currentColor; background: transparent; }
/* Modo imagen: sin borde ni recorte (el "cuadro", si lo hubiera, está en el PNG). */
.onda-cursor--image { border-radius: 0; }
.onda-cursor.is-visible { opacity: 1; }

/* ── Magnetic Reveal (canvas dot-grid que revela el media) ──────────────── */
/* El canvas cubre el contenedor (opaco: pinta su propio fondo) y dibuja el
 * media recortado a la grilla de puntos. El contenido del contenedor queda
 * por encima del canvas. */
.onda-mr {
	position: relative;
}
.onda-mr__canvas {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	z-index: 0;
	pointer-events: none;
}
/* Todo lo que NO es el canvas (widgets/contenido del contenedor) por encima. */
.onda-mr > .e-con-inner,
.onda-mr > .elementor-widget,
.onda-mr > .e-con,
.onda-mr > .elementor-element {
	position: relative;
	z-index: 1;
}

/* ── Text Loop (marquee horizontal) ── */
.onda-marquee { display: block; overflow: hidden; white-space: nowrap; }
.onda-marquee__track {
	display: flex;
	width: max-content;
	animation: onda-marquee-scroll var(--onda-marquee-dur, 10s) linear infinite;
}
.onda-marquee__track[data-direction="right"] { animation-name: onda-marquee-scroll-right; }
.onda-marquee__track[data-pause="true"]:hover { animation-play-state: paused; }
.onda-marquee__item { white-space: nowrap; flex-shrink: 0; }
@keyframes onda-marquee-scroll { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }
@keyframes onda-marquee-scroll-right { 0% { transform: translateX(-50%); } 100% { transform: translateX(0); } }

/* ── Slide Reveal (texto por carácter) ── */
.onda-slidereveal { margin: 0; }
/* Sangrado por CARÁCTER: la máscara por letra decapita el voladizo de las
 * itálicas sobre la letra vecina y los descendentes (ver nota en line-reveal). */
.onda-sr-wrap { display: inline-block; overflow: hidden; vertical-align: top; padding: 0.15em 0.2em 0.25em; margin: -0.15em -0.2em -0.25em; }
.onda-sr-char { display: inline-block; will-change: transform; }

/* ── Block Reveal (línea + bloque de color) ── */
.onda-blockreveal { margin: 0; }
/* Mismo sangrado que line-reveal (el bloque de color cubre el padding-box, así
 * que el barrido tapa también el sangrado). */
.onda-blockreveal .onda-line { display: block; position: relative; overflow: hidden; padding: 0.15em 0.35em 0.25em; margin: -0.15em -0.35em -0.25em; }
.onda-blockreveal .onda-line-inner { display: block; position: relative; z-index: 0; }
.onda-blockreveal .onda-br-block {
	position: absolute;
	top: -1px;
	left: -1px;
	width: calc(100% + 2px);
	height: calc(100% + 2px);
	z-index: 1;
	will-change: transform;
}

/* ── Scroll Fill (relleno de texto) ── */
.onda-scrollfill-text { margin: 0; }
.onda-scrollfill-text .onda-sf-char { display: inline-block; }

/* ── Reveals como EFECTO SOBRE WIDGET (R2 ola 2) ──
 * Cuando blur-text / line-reveal / slide-reveal / block-reveal se aplican como
 * efecto sobre un Heading/Button/Text (data-onda-fx en el wrapper del widget), el
 * texto NO lleva las clases .onda-blurtext/.onda-linereveal/etc., así que las reglas
 * de máscara/overflow scopeadas arriba no alcanzan. Estas reglas, keyeadas por
 * data-onda-fx (presente tanto en el widget standalone como en el wrapper), garantizan
 * el recorte/inline-block de los spans internos en AMBOS contextos. */
[data-onda-fx="blur-text"] .onda-word { display: inline-block; }
/* Mismo SANGRADO de máscara que las variantes standalone (ver nota en
 * .onda-linereveal): sin él, itálicas y descendentes quedan decapitados. */
[data-onda-fx="line-reveal"] .onda-line { display: block; overflow: hidden; padding: 0.15em 0.35em 0.25em; margin: -0.15em -0.35em -0.25em; }
[data-onda-fx="line-reveal"] .onda-line-inner { display: inline-block; will-change: transform; }
[data-onda-fx="slide-reveal"] .onda-sr-wrap { display: inline-block; overflow: hidden; vertical-align: top; padding: 0.15em 0.2em 0.25em; margin: -0.15em -0.2em -0.25em; }
[data-onda-fx="slide-reveal"] .onda-sr-char { display: inline-block; will-change: transform; }
[data-onda-fx="block-reveal"] .onda-line { display: block; position: relative; overflow: hidden; padding: 0.15em 0.35em 0.25em; margin: -0.15em -0.35em -0.25em; }
[data-onda-fx="block-reveal"] .onda-line-inner { display: block; position: relative; z-index: 0; }
[data-onda-fx="block-reveal"] .onda-br-block {
	position: absolute;
	top: -1px;
	left: -1px;
	width: calc(100% + 2px);
	height: calc(100% + 2px);
	z-index: 1;
	will-change: transform;
}


/* ── Hover Underline (v1.1.0, portado de Oncenueve FX) ──
 * Subrayado que CRECE al pasar el mouse (y se retrae al salir). El JS del efecto
 * hover-underline marca .onda-ul en el host y setea las custom props; acá vive toda
 * la animación (CSS puro, scaleX). Ancho vía --onda-ul-w (100% del texto en modo
 * "text", o % del contenedor); color/espesor/duración/separación por custom prop. */
.onda-ul { position: relative; }
.onda-ul.onda-ul--text { display: inline-block; }
.onda-ul::after {
	content: '';
	position: absolute;
	left: 0;
	bottom: calc(-1 * var(--onda-ul-off, 0px));
	width: var(--onda-ul-w, 100%);
	height: var(--onda-ul-h, 2px);
	background: var(--onda-ul-color, currentColor);
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform var(--onda-ul-dur, 0.4s) cubic-bezier(0.76, 0, 0.24, 1);
	pointer-events: none;
}
.onda-ul:hover::after,
.onda-ul:focus-visible::after { transform: scaleX(1); }
.onda-ul[data-onda-ul-origin="center"]::after { transform-origin: center center; }
.onda-ul[data-onda-ul-origin="right"]::after  { transform-origin: right center; }
/* reverse: entra desde la izquierda, se retrae hacia la derecha (flip de origen). */
.onda-ul[data-onda-ul-origin="reverse"]::after       { transform-origin: right center; }
.onda-ul[data-onda-ul-origin="reverse"]:hover::after { transform-origin: left center; }
@media (prefers-reduced-motion: reduce) {
	.onda-ul::after { transition: none; }
}


/* ── Elastic Title (título elástico horizontal por velocidad de scroll) ──
 * El JS aplica el transform inline frame a frame; aquí sólo el recorte para que
 * el paneo no genere scroll horizontal y el will-change/contención de layout. */
[data-onda-fx="elastic-title"] {
	overflow-x: hidden;
}
[data-onda-fx="elastic-title"] .elementor-heading-title,
[data-onda-fx="elastic-title"] h1,
[data-onda-fx="elastic-title"] h2,
[data-onda-fx="elastic-title"] h3 {
	transform-origin: center center;
	backface-visibility: hidden;
}
/* reduced-motion: texto estático, sin skew/estiramiento/paneo (el JS ya no corre,
 * esto es red de seguridad por si quedó un transform inline previo). */
@media (prefers-reduced-motion: reduce) {
	[data-onda-fx="elastic-title"] .elementor-heading-title,
	[data-onda-fx="elastic-title"] h1,
	[data-onda-fx="elastic-title"] h2,
	[data-onda-fx="elastic-title"] h3 {
		transform: none !important;
		will-change: auto !important;
	}
}

/* ── Word Assembly (palabras dispersas que se ensamblan por scroll) ──────
 * El JS parte el texto en spans por PALABRA y aplica el transform/blur/opacity
 * inline frame a frame durante el scrub. Acá sólo aseguramos el inline-block de
 * cada palabra (aunque algún reset lo pise), preservamos los espacios y damos
 * contención para que el desplazamiento en X no genere scroll horizontal. */
.onda-wa-word {
	display: inline-block;
	will-change: transform, filter, opacity;
}
.onda-wa-space {
	white-space: pre;
}
[data-onda-fx="word-assembly"] {
	overflow-x: hidden;
}
/* reduced-motion: texto estático ensamblado, sin dispersión/blur (el JS ya no
 * corre; esto es red de seguridad por si quedó algún estilo inline previo). */
@media (prefers-reduced-motion: reduce) {
	[data-onda-fx="word-assembly"] .onda-wa-word {
		transform: none !important;
		filter: none !important;
		opacity: 1 !important;
		will-change: auto !important;
	}
}

/* ── 3D Names Tunnel (nombres que vuelan hacia la cámara por scroll) ─────
 * El JS arma el stage 3D (perspective + preserve-3d), una capa por nombre a
 * distinto translateZ, y aplica transform/blur/opacity inline frame a frame
 * durante el scrub del pin. Acá: reforzamos preserve-3d, evitamos scroll
 * horizontal y damos tipografía base grande (el autor la pisa por Elementor).
 * Los nombres heredan color/tipografía del contenedor cuando es posible. */
.onda-nt-stage {
	-webkit-perspective: 1000px;
	perspective: 1000px;
	-webkit-transform-style: preserve-3d;
	transform-style: preserve-3d;
}
.onda-nt-scene {
	-webkit-transform-style: preserve-3d;
	transform-style: preserve-3d;
}
.onda-nt-name {
	font-size: clamp(2.2rem, 7vw, 7rem);
	font-weight: 700;
	line-height: 1;
	letter-spacing: -0.02em;
	color: inherit;
	will-change: transform, filter, opacity;
}
[data-onda-fx="names-tunnel"] {
	overflow-x: hidden;
}
/* Móvil: nombres algo más chicos para que el del foco no se corte. */
@media (max-width: 767px) {
	.onda-nt-name {
		font-size: clamp(1.6rem, 11vw, 4rem);
	}
}
/* reduced-motion / disableMobile: lista estática legible apilada y centrada
 * (sin 3D, sin vuelo). El JS arma .onda-nt-static; esto la presenta limpia. */
.onda-nt-static {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0.4em;
	height: 100%;
	min-height: 50vh;
	text-align: center;
}
.onda-nt-name--static {
	position: static;
	transform: none;
	opacity: 1;
	filter: none;
	white-space: normal;
}
@media (prefers-reduced-motion: reduce) {
	.onda-nt-name {
		filter: none !important;
	}
}
/* WIDGET 3D Names Tunnel (R5 Wave 4, 2.88.0): el contenedor del widget hereda el
 * fondo oscuro por defecto y centra el túnel. En el frontend el motor JS arma el
 * stage adentro; el widget solo aporta la caja. */
.onda-nt {
	position: relative;
	width: 100%;
	min-height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	/* SAFARI: `overflow:hidden` NO recorta los hijos con transform 3D dentro de un
	 * subárbol preserve-3d (sí lo hace Chrome) → los nombres que vuelan hacia la cámara
	 * (sobre todo con una fuente grande del autor) se SALÍAN de la caja y sangraban en
	 * iPhone. `clip-path: inset(0)` recorta el contenido 3D en TODOS los navegadores
	 * (no cambia el render en Chrome, donde ya recortaba). */
	-webkit-clip-path: inset(0);
	clip-path: inset(0);
	color: #fff;
}
/* Vista del WIDGET en el EDITOR (patrón Onda editor-preview): el motor pinta un
 * frame estático del túnel 3D "desde dentro" (.onda-nt-stage--editor con varios
 * nombres a profundidades representativas) — sin pin, sin scroll, sin rAF. Acá solo
 * neutralizamos el alto (natural, no 100vh de pin) y dejamos que el stack 3D se vea.
 * Las clases .onda-nt-stage / .onda-nt-name son las MISMAS del front → los controles
 * de estilo (color, tipografía, fondo) aplican en vivo. En el frontend este stage no
 * existe (lo arma el JS animado). */
.onda-nt--editor {
	min-height: 60vh;
}
.onda-nt-stage--editor {
	min-height: 60vh;
}
.onda-nt-empty {
	padding: 18px;
	border: 1px dashed rgba(127, 127, 127, 0.4);
	border-radius: 8px;
	text-align: center;
	font-size: 13px;
	opacity: 0.7;
}

/* Color Scrub (slug "color-scrub") — el JS interpola el background-color con el
   scroll. Acá sólo damos el overlay de GRANO / ruido tv-static (SVG feTurbulence
   tileado), que el JS monta como .onda-clr-grain encima del color. El color de
   fondo lo pone el JS inline (no hay regla de color aquí). */
.onda-clr-grain {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
	z-index: 2;
	mix-blend-mode: overlay;
	background-repeat: repeat;
	background-size: 180px 180px;
	/* Ruido SVG feTurbulence embebido (sin pedidos de red). */
	background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
	will-change: background-position;
}
/* Target body: el grano cubre toda la pantalla (fijo). */
.onda-clr-grain--fixed {
	position: fixed;
	z-index: 0;
}
/* Móvil: grano más liviano (menos blend cost). */
@media (max-width: 767px) {
	.onda-clr-grain {
		mix-blend-mode: soft-light;
		background-size: 140px 140px;
	}
}
/* reduced-motion: el grano no se anima (el JS ya no desplaza la posición). */
@media (prefers-reduced-motion: reduce) {
	.onda-clr-grain {
		will-change: auto;
	}
}

/* ── Works Showcase (slug "works-showcase") ─────────────────────────────
 * El JS arma el split-screen: .onda-ws-stage (flex) con .onda-ws-left (fija,
 * preview + meta + año) y .onda-ws-right (lista de títulos gigantes que sube
 * con el scroll del pin). El preview hace crossfade entre dos capas .onda-ws-pane.
 * El título activo lleva .is-active (opacidad plena; los demás atenúan vía
 * --onda-ws-dim). El año se descifra por dígito en .onda-ws-year-char.
 * Móvil: .onda-ws-mobile = acordeón/apilado de .onda-ws-card. */
[data-onda-fx="works-showcase"] {
	overflow-x: hidden;
}
.onda-ws-stage {
	color: inherit;
}
.onda-ws-left {
	/* Ancho por CSS-var (control "Ancho columna izquierda %"), fallback 42%. */
	flex: 0 0 var(--onda-ws-left, 42%);
	max-width: var(--onda-ws-left, 42%);
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 1.4rem;
	padding: clamp(1.5rem, 4vw, 4rem);
	box-sizing: border-box;
}
.onda-ws-year {
	font-size: clamp(1.4rem, 2.4vw, 2.4rem);
	font-weight: 600;
	letter-spacing: 0.04em;
	line-height: 1;
	opacity: 0.85;
}
.onda-ws-year-char {
	font-variant-numeric: tabular-nums;
}
.onda-ws-preview {
	position: relative;
	width: 100%;
	aspect-ratio: 4 / 3;
	border-radius: 14px;
	overflow: hidden;
	background: rgba(127, 127, 127, 0.12);
}
.onda-ws-pane {
	position: absolute;
	inset: 0;
	opacity: 0;
	transform: scale(1.04);
	filter: blur(8px);
	will-change: opacity, transform, filter;
}
.onda-ws-pane.is-on {
	opacity: 1;
	transform: scale(1);
	filter: blur(0);
}
.onda-ws-pane img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
.onda-ws-pane video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
.onda-ws-ph {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	padding: 1rem;
	box-sizing: border-box;
	font-size: clamp(1.4rem, 3vw, 2.6rem);
	font-weight: 700;
	letter-spacing: -0.02em;
	text-align: center;
	opacity: 0.55;
}
.onda-ws-meta {
	display: flex;
	flex-direction: column;
	gap: 0.7rem;
	transition: opacity 320ms cubic-bezier(0.16, 1, 0.3, 1);
}
.onda-ws-meta-row {
	display: grid;
	grid-template-columns: 90px 1fr;
	gap: 0.6rem 1rem;
	align-items: start;
	padding-top: 0.55rem;
	border-top: 1px solid currentColor;
	border-image: linear-gradient(90deg, currentColor, transparent) 1;
	opacity: 0.9;
}
.onda-ws-meta-key {
	font-size: 0.72rem;
	text-transform: uppercase;
	letter-spacing: 0.1em;
	opacity: 0.55;
	font-weight: 600;
}
.onda-ws-meta-val {
	font-size: 0.95rem;
	line-height: 1.4;
}
/* Botón "Explore the case" al pie de la columna izquierda (link al proyecto activo,
 * como sirnik). Subrayado que crece en hover + flecha que avanza. */
.onda-ws-link {
	display: inline-flex;
	align-items: center;
	gap: 0.5em;
	margin-top: clamp(20px, 3vh, 44px);
	color: inherit;
	text-decoration: none;
	font-size: 0.95rem;
	font-weight: 500;
	letter-spacing: 0.01em;
	position: relative;
	width: fit-content;
	transition: color 0.3s ease;
}
.onda-ws-link .onda-ws-link-label {
	position: relative;
	transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
/* Efecto hover del botón: el texto se corre un poco (la flecha avanza con su propia
 * regla). El color de hover lo pone el control "Color del botón (hover)". */
.onda-ws-link:hover .onda-ws-link-label,
.onda-ws-link:focus-visible .onda-ws-link-label {
	transform: translateX(3px);
}
.onda-ws-link .onda-ws-link-arrow {
	display: inline-block;
	transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.onda-ws-link:hover .onda-ws-link-arrow,
.onda-ws-link:focus-visible .onda-ws-link-arrow {
	transform: translateX(0.35em);
}
/* Ícono elegible (Elementor Icons): hereda tamaño y color del botón. */
.onda-ws-link-arrow svg { width: 1em; height: 1em; fill: currentColor; vertical-align: middle; }
.onda-ws-link-arrow i { font-size: 1em; font-style: normal; line-height: 1; }
/* Línea divisoria del botón al PIE (border-bottom), para que CIERRE el módulo de la
 * columna izquierda. Mismo degradado que las filas de meta; full-width para abarcar la
 * columna. El subrayado del label se quitó (ya no crece en hover). */
.onda-ws-link--div {
	display: flex;
	width: 100%;
	border-bottom: 1px solid currentColor;
	border-image: linear-gradient(90deg, currentColor, transparent) 1;
	padding-bottom: 0.9rem;
}
.onda-ws-right {
	/* Ancho por CSS-var (control "Ancho columna derecha %"), fallback 58%. */
	flex: 1 1 var(--onda-ws-right, 58%);
	display: flex;
	flex-direction: column;
	justify-content: flex-start;
	overflow: hidden;
}
.onda-ws-list {
	display: flex;
	flex-direction: column;
	/* Padding superior e inferior de ~media pantalla: el 1er título arranca cerca
	   de la línea de foco y el último la alcanza al final del recorrido. */
	padding: 45vh clamp(1rem, 3vw, 3rem) 45vh 0;
	gap: clamp(0.4rem, 1.5vh, 1.4rem);
}
.onda-ws-title {
	font-size: clamp(2.4rem, 6.5vw, 7rem);
	font-weight: 700;
	line-height: 1.02;
	letter-spacing: -0.025em;
	opacity: var(--onda-ws-dim, 0.25);
	transition: opacity 360ms cubic-bezier(0.16, 1, 0.3, 1);
	will-change: opacity;
}
.onda-ws-title.is-active {
	opacity: 1;
}

/* Alineación de los títulos gigantes (control "Alineación de los títulos").
   Va por clase en el wrapper (.onda-ws-align-left|center|right) → se refleja EN
   VIVO en el editor. Afecta el text-align de cada título y el align-items de la
   lista (para que la línea quede pegada al borde elegido). */
.onda-ws-align-center .onda-ws-list { align-items: center; text-align: center; }
.onda-ws-align-center .onda-ws-list { padding-left: clamp(1rem, 3vw, 3rem); }
.onda-ws-align-right .onda-ws-list  { align-items: flex-end; text-align: right; }
.onda-ws-align-right .onda-ws-list  { padding-left: clamp(1rem, 3vw, 3rem); padding-right: 0; }
.onda-ws-align-left .onda-ws-list   { align-items: flex-start; text-align: left; }
.onda-ws-align-center .onda-ws-title,
.onda-ws-align-right .onda-ws-title  { width: 100%; }

/* Divisor entre filas de meta (control "Divisor entre filas de meta"). Off =
   sin la línea superior. */
.onda-ws-meta--nodiv .onda-ws-meta-row {
	border-top: 0;
	border-image: none;
	padding-top: 0;
}

/* ── Móvil / disableMobile: APILADO en FICHAS (acordeón o stacked). ──────────
   Sin pin, sin split. Cada proyecto = una tarjeta limpia y premium: cabecera con
   título grande + año, y un panel (preview + meta) que se expande con transición.
   Rediseño 2.95.0: fichas con respiro real, preview redondeado destacado, meta
   legible, sin solapes ni saltos. */
.onda-ws-mobile {
	display: flex;
	flex-direction: column;
	width: 100%;
	/* Centrado en pantalla: caja contenida + auto-márgenes (queda centrado aunque el
	 * autor le ponga padding lateral al widget). */
	max-width: 560px;
	margin-inline: auto;
	gap: clamp(0.9rem, 3vw, 1.4rem);
	padding: 0;
}
/* En MÓVIL el split pasa a acordeón centrado: neutralizamos el padding HORIZONTAL que
 * el autor le haya puesto al widget (pensado para el split de desktop, p.ej. 100px a un
 * lado) → el acordeón queda centrado en pantalla, no corrido. Scoped a este widget; el
 * padding vertical del autor se respeta. */
@media (max-width: 767px) {
	.elementor-widget-onda-works-showcase > .elementor-widget-container {
		padding-left: 0 !important;
		padding-right: 0 !important;
	}
}
.onda-ws-card {
	border: 1px solid rgba(127, 127, 127, 0.18);
	border-radius: 16px;
	overflow: hidden;
	background: rgba(127, 127, 127, 0.04);
	transition: border-color 320ms ease, background 320ms ease;
}
.onda-ws-card.is-open {
	border-color: rgba(127, 127, 127, 0.32);
	background: rgba(127, 127, 127, 0.07);
}
.onda-ws-card-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	width: 100%;
	padding: clamp(1rem, 4vw, 1.4rem);
	background: none;
	border: 0;
	color: inherit;
	font: inherit;
	text-align: left;
	cursor: pointer;
}
/* El card-head es un <button>: blindarlo del fondo de hover/focus/active que muchos
 * temas/kits aplican a TODOS los botones (p.ej. el acento del sitio, que se veía como
 * un rosa al tocar la pestaña). Especificidad 0,3,0 para ganarle a `[type=submit]:hover`.
 * El fondo de la pestaña lo decide la card (controlable con "Color de la pestaña"). */
.onda-ws-card .onda-ws-card-head,
.onda-ws-card .onda-ws-card-head:hover,
.onda-ws-card .onda-ws-card-head:focus,
.onda-ws-card .onda-ws-card-head:active {
	background: transparent;
	box-shadow: none;
}
.onda-ws-card-title {
	/* El acordeón es SOLO móvil. La tipografía del título del widget (control del autor)
	 * apunta a .onda-ws-title Y .onda-ws-card-title; un tamaño grande pensado para los
	 * títulos gigantes del desktop (p.ej. 101px) reventaba el acordeón en móvil (título
	 * desbordando/tapando el header). Capeamos el TAMAÑO acá (la familia/peso/espaciado
	 * del autor se conservan) para que el acordeón quede legible y nunca sangre. */
	font-size: clamp(1.7rem, 8vw, 2.6rem) !important;
	font-weight: 700;
	letter-spacing: -0.02em;
	line-height: 1.05;
	flex: 1 1 auto;
	min-width: 0;
	overflow-wrap: anywhere;
	position: relative;
	transition: color 280ms cubic-bezier(0.16, 1, 0.3, 1), transform 360ms cubic-bezier(0.16, 1, 0.3, 1);
}
.onda-ws-card-year {
	font-size: 0.95rem;
	font-weight: 600;
	opacity: 0.55;
	flex: 0 0 auto;
	font-variant-numeric: tabular-nums;
}
/* Chevron de afordancia: gira 180° al abrir la pestaña (señala que despliega). */
.onda-ws-card-chevron {
	flex: 0 0 auto;
	width: 0.62em;
	height: 0.62em;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	transform: rotate(45deg);
	opacity: 0.6;
	transition: transform 360ms cubic-bezier(0.16, 1, 0.3, 1), opacity 280ms ease, color 280ms ease;
	margin-top: -0.18em;
}
.onda-ws-card-head:hover .onda-ws-card-chevron { opacity: 1; }
.onda-ws-card.is-open .onda-ws-card-chevron {
	transform: rotate(-135deg);
	margin-top: 0.18em;
	opacity: 1;
}

/* ── Animación del título del acordeón (control "Animación del título") ──────
 * Afordancia premium de que la pestaña es interactiva: micro-animación al hover y
 * al abrir. Clase .onda-ws-anim-{preset} en el wrapper; aplica al .onda-ws-card-title. */
/* Subrayado que crece desde la izquierda (default). */
.onda-ws-anim-underline .onda-ws-card-title::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: -0.06em;
	width: 100%;
	height: 2px;
	background: currentColor;
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform 380ms cubic-bezier(0.16, 1, 0.3, 1);
}
.onda-ws-anim-underline .onda-ws-card-head:hover .onda-ws-card-title::after,
.onda-ws-anim-underline .onda-ws-card.is-open .onda-ws-card-title::after {
	transform: scaleX(1);
}
/* Deslizar a la derecha. */
.onda-ws-anim-slide .onda-ws-card-head:hover .onda-ws-card-title,
.onda-ws-anim-slide .onda-ws-card.is-open .onda-ws-card-title {
	transform: translateX(0.35em);
}
/* Escala leve (desde la izquierda, sin empujar el layout). */
.onda-ws-anim-scale .onda-ws-card-title { transform-origin: left center; }
.onda-ws-anim-scale .onda-ws-card-head:hover .onda-ws-card-title,
.onda-ws-anim-scale .onda-ws-card.is-open .onda-ws-card-title {
	transform: scale(1.05);
}
/* "color": sin transform; el realce lo da el control "Color del título en hover/activo"
 * (si el autor no eligió color, igual el chevron + borde de la card señalan el estado). */
.onda-ws-card-panel {
	display: grid;
	grid-template-rows: 0fr;
	overflow: hidden;
	transition: grid-template-rows 420ms cubic-bezier(0.16, 1, 0.3, 1);
}
.onda-ws-card.is-open .onda-ws-card-panel {
	grid-template-rows: 1fr;
}
/* El ÚNICO hijo directo del panel es .onda-ws-card-panel-inner (img+meta envueltos);
   con un solo hijo + min-height:0 + overflow:hidden, el grid 0fr cierra del TODO la
   pestaña cerrada (solo queda el título). Antes había 2 hijos directos y el panel no
   colapsaba → se veía un peek de resumen/etiquetas/imagen con la pestaña cerrada. */
.onda-ws-card-panel-inner {
	min-height: 0;
	overflow: hidden;
}
/* El respiro lateral va en .onda-ws-card-img / .onda-ws-card-meta (ahora dentro del
   wrapper interno). */
.onda-ws-card-img {
	border-radius: 12px;
	overflow: hidden;
	margin: 0 clamp(1rem, 4vw, 1.4rem) 1rem;
	background: rgba(127, 127, 127, 0.1);
	aspect-ratio: 4 / 3;
}
.onda-ws-card-img img,
.onda-ws-card-img video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
.onda-ws-card-meta {
	padding: 0 clamp(1rem, 4vw, 1.4rem) clamp(1.2rem, 4vw, 1.5rem);
	display: flex;
	flex-direction: column;
	gap: 0.65rem;
}
.onda-ws-card-meta .onda-ws-meta-row {
	grid-template-columns: minmax(72px, 30%) 1fr;
}
/* Tablet (768–1024): el split SÍ aplica; damos algo más de aire al preview. */
@media (max-width: 1024px) and (min-width: 768px) {
	.onda-ws-left {
		flex-basis: var(--onda-ws-left, 46%);
		max-width: var(--onda-ws-left, 46%);
	}
	.onda-ws-meta-row {
		grid-template-columns: 76px 1fr;
	}
}
/* reduced-motion: sin transiciones (el JS hace swap directo + año final). */
@media (prefers-reduced-motion: reduce) {
	.onda-ws-pane,
	.onda-ws-meta,
	.onda-ws-title,
	.onda-ws-card-panel {
		transition: none !important;
	}
	.onda-ws-pane {
		filter: none;
		transform: none;
	}
}

/* Vista del WIDGET en el EDITOR de Elementor (patrón editor-preview, ver
   docs/REF-editor-preview.md): el motor JS no corre ahí, pero el split-screen NO
   necesita JS para verse — el PHP renderiza el MISMO markup del front
   (.onda-ws-stage > .onda-ws-left + .onda-ws-right) con el ítem 1 activo. Acá solo
   neutralizamos lo que en el front hace el pin/scroll: el stage no se pinea, fluye
   con alto natural; la lista no lleva el padding de 45vh (que en el editor dejaría
   los títulos fuera de vista); el preview no necesita la animación de crossfade.
   Como reusa las clases reales, TODOS los controles de estilo aplican en vivo. */
.onda-ws--editor .onda-ws-stage--editor {
	display: flex;
	width: 100%;
	min-height: 70vh;
	overflow: hidden;
	align-items: stretch;
}
/* Ancho completo (edge-to-edge) en el EDITOR: el wrapper rompe a 100vw con el truco
   full-bleed. En el FRONT lo hace el spacer por JS (no esta clase), por eso esta regla
   queda acotada a .onda-ws--editor. */
.onda-ws--editor.onda-ws--full {
	width: 100vw;
	max-width: 100vw;
	margin-left: calc(50% - 50vw);
	margin-right: calc(50% - 50vw);
}
.onda-ws--editor .onda-ws-list--editor {
	padding-top: clamp(1rem, 3vh, 3rem);
	padding-bottom: clamp(1rem, 3vh, 3rem);
}
.onda-ws--editor .onda-ws-pane {
	transition: none;
	opacity: 1;
	transform: none;
	filter: none;
}
/* En editor estrecho (panel angosto) el split puede apretar: damos un mínimo
   legible a la columna izquierda sin romper el porcentaje del front. */
@media (max-width: 700px) {
	.onda-ws--editor .onda-ws-stage--editor {
		flex-direction: column;
		min-height: 0;
	}
	.onda-ws--editor .onda-ws-left,
	.onda-ws--editor .onda-ws-right {
		flex: 1 1 auto;
		max-width: 100%;
	}
}
.onda-ws-empty {
	padding: 18px;
	border: 1px dashed rgba(127, 127, 127, 0.4);
	border-radius: 8px;
	text-align: center;
	font-size: 13px;
	opacity: 0.7;
}

/* Stacked Cards — modo Card Toss 3D (exit=toss). Perspectiva en el escenario y
   preserve-3d para que la inclinación de las cards se vea con profundidad real.
   El JS también la pone inline por card; estas reglas son refuerzo/respaldo. */
.onda-stack-stage--toss {
	-webkit-perspective: 1200px;
	perspective: 1200px;
	-webkit-transform-style: preserve-3d;
	transform-style: preserve-3d;
}
.onda-stack-stage--toss > * {
	-webkit-transform-style: preserve-3d;
	transform-style: preserve-3d;
	-webkit-backface-visibility: hidden;
	backface-visibility: hidden;
}

/* =====================================================================
 * Scroll Sequence (Componente, slug "scroll-sequence") — Fase 4.
 * Sección pineada que avanza cuadro por cuadro (canvas) o scrubbea un video.
 * El widget usa .onda-sequence.onda-seq-widget; el shortcode legacy usa
 * .onda-sequence a secas (estilos base en onda-motion-presets.css).
 * ===================================================================*/
.onda-sequence.onda-seq-widget {
	position: relative;
	width: 100%;
	height: 100vh;
	overflow: hidden;
	background: #000;
}

.onda-seq__stage {
	position: absolute;
	inset: 0;
	overflow: hidden;
}

/* Canvas (imágenes), video y póster: todos llenan el stage. El "fit" lo aplica
   el canvas al dibujar; en video/póster lo manda object-fit (cover por defecto,
   contain con el modificador). */
.onda-seq__canvas,
.onda-seq__video,
.onda-seq__poster {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	margin: 0;
}
.onda-seq__video,
.onda-seq__poster {
	object-fit: cover;
}
.onda-seq--contain .onda-seq__video,
.onda-seq--contain .onda-seq__poster {
	object-fit: contain;
}

/* Póster: paint instantáneo; el JS lo funde a 0 cuando el canvas/video pinta. */
.onda-seq__poster {
	z-index: 1;
	transition: opacity 0.4s ease;
}
.onda-seq__canvas,
.onda-seq__video {
	z-index: 0;
}

/* Pasos de texto. Posición vertical por modificador de alineación. */
.onda-seq-widget .onda-seq-step {
	position: absolute;
	left: 50%;
	transform: translateX(-50%);
	z-index: 3;
	max-width: 28ch;
	text-align: center;
	pointer-events: none;
}
.onda-seq--align-bottom .onda-seq-step { bottom: 12vh; }
.onda-seq--align-top    .onda-seq-step { top: 12vh; }
.onda-seq--align-center .onda-seq-step { top: 50%; transform: translate(-50%, -50%); }

.onda-seq-widget .onda-seq-step .k {
	display: block;
	font-weight: 800;
	font-size: clamp(2rem, 5vw, 3.6rem);
	line-height: 1.04;
	letter-spacing: -0.02em;
	color: currentColor;
}
.onda-seq-widget .onda-seq-step .s {
	display: block;
	margin-top: 0.5rem;
	font-size: 1.05rem;
	line-height: 1.5;
	opacity: 0.85;
}

/* Modo estático (móvil / reduce-motion): un solo cuadro, sin pin. Los pasos se
   muestran fijos (sin animación de scrub) para que el mensaje no se pierda. */
.onda-seq--static .onda-seq-step {
	position: static;
	transform: none;
	max-width: none;
	margin: 1.2rem auto 0;
	padding: 0 1rem;
}
.onda-seq--static .onda-seq__stage {
	position: relative;
}
.onda-seq--static.onda-seq-widget {
	height: auto;
	min-height: 60vh;
}

/* Editor: la escena no corre; mostramos el póster/primer frame quieto. */
.onda-seq--editor .onda-seq__poster { opacity: 1 !important; }

/* =====================================================================
 * Scroll Sequence — MODO FONDO DE CONTENEDOR (efecto scroll-sequence-bg).
 * La secuencia (canvas/video) es la CAPA STICKY de fondo de un contenedor ALTO;
 * el contenido del autor scrollea/revela ENCIMA mientras la secuencia avanza.
 * Pin nativo: `position: sticky; top:0; height:100vh` + `margin-bottom:-100vh`
 * (la capa no ocupa alto de flujo → el contenido arranca arriba y pasa por
 * encima del fondo pineado). El host se fuerza a block para que el truco del
 * margen negativo sea robusto (evita rarezas del flex de Elementor); los hijos
 * reales van por encima (z-index 1). El scrub va atado al scroll del contenedor
 * (motor JS). Requiere que NINGÚN ancestro tenga overflow:hidden (rompe sticky).
 * ===================================================================*/
.onda-seqbg-host {
	position: relative !important;
	display: block !important;
	overflow: visible !important;
}
.onda-seqbg-layer {
	position: sticky;
	top: 0;
	width: 100%;
	height: 100vh;
	margin-bottom: -100vh;
	overflow: hidden;
	z-index: 0;
	background: #000;
	pointer-events: none;
}
.onda-seqbg-layer .onda-seq__stage {
	position: absolute;
	inset: 0;
}
.onda-seqbg-host > :not(.onda-seqbg-layer) {
	position: relative;
	z-index: 1;
}

/* ====================================================================
 * Card Slider — slider horizontal pineado de fichas (video/foto).
 * Widget Onda_Motion_Widget_Card_Slider · efecto card-slider.
 * Ref umanodesign.studio. El motor JS pinea y traslada la tira; acá vive
 * el look de las fichas + los modos sin pin (swipe/apilado) + el progreso.
 * ==================================================================== */
.onda-cs {
	--onda-cs-cardw: 34vw;
	--onda-cs-gap: 28px;
	--onda-cs-radius: 18px;
	--onda-cs-progress: currentColor;
	position: relative;
	width: 100%;
}
.onda-cs-track {
	display: flex;
	flex-wrap: nowrap;
	/* stretch: todas las fichas toman la MISMA altura (la de la más alta) → el media
	   queda "pineado" arriba alineado entre fichas y lo que varía es el espacio del
	   texto/abajo; sin esto (center) las fichas quedan escalonadas según el largo del texto. */
	align-items: stretch;
	gap: var(--onda-cs-gap);
	width: max-content;
	will-change: transform;
}
.onda-cs-card {
	flex: 0 0 var(--onda-cs-cardw);
	max-width: var(--onda-cs-cardw);
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	gap: 1.1rem;
	border-radius: var(--onda-cs-radius);
	text-decoration: none;
	color: inherit;
	overflow: visible;
}
.onda-cs-card * { box-sizing: border-box; }

/* Slide de DISEÑO LIBRE (plantilla de Elementor): el contenido manda su propio layout. */
.onda-cs-card--tpl {
	display: block;
	padding: 0;
	gap: 0;
	overflow: hidden;
}
.onda-cs-tpl {
	width: 100%;
	height: 100%;
}
.onda-cs-card--tpl.onda-cs-card--fs .onda-cs-tpl,
.onda-cs-card--fs .onda-cs-tpl { height: 100%; }

/* Alineación de la info (izquierda / centro / derecha) */
.onda-cs-align-center .onda-cs-caption { align-items: center; }
.onda-cs-align-right .onda-cs-caption { align-items: flex-end; }
.onda-cs-media {
	position: relative;
	width: 100%;
	aspect-ratio: 3 / 4;
	overflow: hidden;
	border-radius: var(--onda-cs-radius);
	background: rgba(0, 0, 0, 0.05);
}
.onda-cs-video,
.onda-cs-img {
	position: absolute !important;
	inset: 0;
	/* !important: gana a la regla global img{height:auto} de Elementor/Hello que si no
	   deja la imagen a su alto natural y NO llena la ficha. object-fit queda sin important
	   para que el control Cubrir/Contener pueda cambiarlo. */
	width: 100% !important;
	height: 100% !important;
	max-width: none !important;
	object-fit: cover;
	display: block;
}
.onda-cs-ph {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 700;
	opacity: 0.4;
	padding: 1rem;
	text-align: center;
}
.onda-cs-scrim {
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: linear-gradient(to top, rgba(0, 0, 0, 0.85) 0%, rgba(0, 0, 0, 0) 55%);
	opacity: 0.45;
}
.onda-cs-caption {
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
}
.onda-cs-eyebrow {
	font-size: 0.72em;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	opacity: 0.7;
}
.onda-cs-title {
	margin: 0;
	font-size: 1.5rem;
	line-height: 1.1;
	font-weight: 700;
}
.onda-cs-desc {
	margin: 0;
	font-size: 0.95rem;
	line-height: 1.45;
	opacity: 0.85;
}

/* Texto sobre el media (overlay) */
.onda-cs--overlay .onda-cs-card,
.onda-cs-card--overlay {
	gap: 0;
}
.onda-cs-card--overlay .onda-cs-caption {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 2;
	padding: 1.4rem;
	color: #fff;
}
.onda-cs-card--overlay .onda-cs-eyebrow,
.onda-cs-card--overlay .onda-cs-desc {
	opacity: 0.92;
}

/* Escenario pineado (lo crea el JS). El indicador queda fijo abajo durante el pin. */
.onda-cs-stage {
	display: flex;
	align-items: center;
}
.onda-cs-progress {
	position: absolute;
	left: 50%;
	bottom: 4vh;
	transform: translateX(-50%);
	z-index: 30;
}
.onda-cs-progress--bar {
	width: clamp(140px, 24vw, 360px);
	height: 4px;
	border-radius: 99px;
	background: color-mix(in srgb, var(--onda-cs-progress) 22%, transparent);
	overflow: hidden;
}
.onda-cs-progress-fill {
	display: block;
	width: 100%;
	height: 100%;
	border-radius: 99px;
	background: var(--onda-cs-progress);
	transform: scaleX(0);
	transform-origin: left center;
}
.onda-cs-progress--dots {
	display: flex;
	gap: 0.55rem;
}
.onda-cs-dot {
	width: 8px;
	height: 8px;
	border-radius: 99px;
	background: color-mix(in srgb, var(--onda-cs-progress) 30%, transparent);
	transition: background 0.25s ease, transform 0.25s ease;
}
.onda-cs-dot.is-active {
	background: var(--onda-cs-progress);
	transform: scale(1.3);
}

/* La ficha en pantalla completa (la pone el JS con position:fixed; acá el look). */
.onda-cs-card--fs {
	overflow: hidden;
	/* El JS fija width/height inline; hay que liberar el tope de ancho de la ficha
	   (max-width:var(--onda-cs-cardw)) y el flex, o el fullscreen no crece a lo ancho. */
	max-width: none !important;
	flex: none !important;
	padding: 0 !important;
}
.onda-cs-card--fs .onda-cs-media {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	aspect-ratio: auto !important;
	border-radius: inherit;
}
/* En pantalla completa el texto se desvanece (como umano: solo queda el media). */
.onda-cs-card--fs .onda-cs-caption {
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.35s ease;
}
.onda-cs-card--fs .onda-cs-scrim { opacity: 0 !important; transition: opacity 0.35s ease; }

/* ── Editor: tira scrollable (sin pin/JS), para ver y estilar las fichas ── */
.onda-cs--editor .onda-cs-track {
	width: 100%;
	overflow-x: auto;
	padding-bottom: 0.5rem;
	scrollbar-width: thin;
}

/* ── Sin pin: móvil / tablet / reduced-motion ── */
.onda-cs--swipe .onda-cs-track {
	width: 100%;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	-webkit-overflow-scrolling: touch;
	padding-bottom: 0.5rem;
}
.onda-cs--swipe .onda-cs-card {
	scroll-snap-align: start;
	flex-basis: clamp(220px, 78vw, 420px);
	max-width: clamp(220px, 78vw, 420px);
}
.onda-cs--stacked .onda-cs-track {
	flex-direction: column;
	width: 100%;
	align-items: stretch;
	gap: clamp(1.5rem, 6vw, 3rem);
}
.onda-cs--stacked .onda-cs-card {
	flex-basis: auto;
	max-width: 100%;
}
/* Sin pin (swipe/stacked) NO hay scrub → la barra/dots de progreso no se llenan y, como
   están en position:absolute; bottom:4vh relativo al .onda-cs (que acá mide lo que la tira,
   no 100vh), caerían ENCIMA del texto de la ficha. Se ocultan: en móvil/tablet la posición
   la dan el scroll-snap nativo (y, en su caso, los dots del Contextual Nav). */
.onda-cs--swipe .onda-cs-progress,
.onda-cs--stacked .onda-cs-progress {
	display: none;
}

@media (prefers-reduced-motion: reduce) {
	.onda-cs-dot { transition: none; }
}

/* ============================================================================
 * Scroll Stories (widget unificado) — .onda-ss-*
 * UN widget; el motor reusado (horizontal-slider / stacked-cards / scroll-stack /
 * sticky-scroll) posiciona los ítems en runtime (inline styles). Este CSS da el
 * LOOK del ítem + el fallback sin-JS (editor / móvil fuera de pin = columna legible).
 * ============================================================================ */
.onda-ss {
	--onda-ss-radius: 0px;
	--onda-ss-itemw: 100vw;
	--onda-ss-gap: 28px;
	position: relative;
}

/* Ítem (card / panel). Alto base = 100vh (alto natural que necesita el modo Stacked
   clásico para MEDIR la card). En horizontal/scroll-stack/toss el motor le fija
   height inline (100% del escenario = alto REAL del viewport) → ese inline gana sobre
   el 100vh y en MÓVIL el media cubre exacto la pantalla (sin el desajuste del vh). */
.onda-ss-item {
	position: relative;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	height: 100vh;
	border-radius: var(--onda-ss-radius);
	box-sizing: border-box;
}
/* El PANEL (media+caption) LLENA el ítem (absolute), cualquiera sea el alto que el
   motor le ponga al ítem → el media cubre exacto, sin desajuste de vh en móvil. */
.onda-ss-panel {
	position: absolute;
	inset: 0;
	overflow: hidden;
	border-radius: inherit;
}
.onda-ss-media {
	position: relative;
	overflow: hidden;
	border-radius: inherit;
}
/* Especificidad 0,2,0 para ganarle a reglas del tema/Elementor (`.elementor img
   { height:auto }`) que dejaban la imagen a su alto NATURAL → en móvil (angosto) no
   cubría y aparecía un hueco debajo. Con el ancestro .onda-ss el alto 100% gana. */
.onda-ss .onda-ss-img,
.onda-ss .onda-ss-video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}
.onda-ss-ph {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	min-height: 160px;
	background: #ececec;
	color: #999;
	font-size: 0.85rem;
}
.onda-ss-tpl { width: 100%; height: 100%; }

/* Caption */
.onda-ss-caption {
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 16px 4px 0;
}
.onda-ss-eyebrow {
	font-size: 0.72rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	opacity: 0.7;
}
.onda-ss-title { margin: 0; font-size: 1.35rem; line-height: 1.15; }
.onda-ss-desc { margin: 0; font-size: 0.95rem; line-height: 1.45; opacity: 0.85; }

/* Botón (paridad con un botón con efecto Onda Motion del sitio). flex:0 0 auto +
   width:auto evitan que el align-items:stretch de la caption lo estire a full-width
   (visto en móvil). align-self lo da la posición horizontal del bloque. */
.onda-ss-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	margin-top: 12px;
	align-self: flex-start;
	flex: 0 0 auto;
	width: auto;
	max-width: 100%;
	padding: 0.7em 1.4em;
	color: #fff;
	background: #111;
	text-decoration: none;
	font-size: 0.9rem;
	border-radius: 999px;
	cursor: pointer;
	transition: color 0.25s ease, background-color 0.25s ease, border-color 0.25s ease, transform 0.25s ease;
}
.onda-ss-align-center .onda-ss-btn { align-self: center; }
.onda-ss-align-right .onda-ss-btn { align-self: flex-end; }
/* Los hijos de la caption con ancho propio (p.ej. la descripción, capada a 48ch)
   deben SEGUIR la alineación del bloque: en una columna flex, un hijo con max-width
   se queda en el inicio (izquierda) aunque el bloque esté centrado/a la derecha. */
.onda-ss-align-center .onda-ss-caption { align-items: center; }
.onda-ss-align-right .onda-ss-caption { align-items: flex-end; }

/* ── Modos no-sticky = PANEL FULL-SCREEN (media a sangre + caption overlay) ───
   Horizontal/Stacked/Scroll-stack son efectos de PANTALLA COMPLETA: cada ítem es
   un panel del tamaño del viewport (el motor le fija el alto a 100vh y, en
   horizontal, el ancho a 100vw). NO son fichas chicas (eso es el Card Slider). */
.onda-ss--horizontal .onda-ss-item {
	width: var(--onda-ss-itemw, 100vw);
}
.onda-ss--stacked .onda-ss-item,
.onda-ss--scroll-stack .onda-ss-item,
.onda-ss--switcher .onda-ss-item {
	width: 100%;
}
.onda-ss--horizontal .onda-ss-media,
.onda-ss--stacked .onda-ss-media,
.onda-ss--scroll-stack .onda-ss-media,
.onda-ss--switcher .onda-ss-media {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
}
/* OVERLAY posicionable: contiene el bloque de info y lo ubica en cualquiera de las
   9 posiciones (clases onda-ss-align-* horizontal × onda-ss-valign-* vertical). El
   padding del overlay = distancia del bloque a los bordes (que no quede pegado). */
.onda-ss--horizontal .onda-ss-overlay,
.onda-ss--stacked .onda-ss-overlay,
.onda-ss--scroll-stack .onda-ss-overlay,
.onda-ss--switcher .onda-ss-overlay {
	position: absolute;
	inset: 0;
	z-index: 2;
	display: flex;
	padding: clamp(20px, 5%, 64px);
	color: #fff;
	pointer-events: none;
}
.onda-ss-overlay .onda-ss-caption { pointer-events: auto; max-width: 60ch; background: none; padding: 0; }
.onda-ss-align-left .onda-ss-overlay   { justify-content: flex-start; }
.onda-ss-align-center .onda-ss-overlay { justify-content: center; }
.onda-ss-align-right .onda-ss-overlay  { justify-content: flex-end; }
.onda-ss-valign-top .onda-ss-overlay    { align-items: flex-start; }
.onda-ss-valign-middle .onda-ss-overlay { align-items: center; }
.onda-ss-valign-bottom .onda-ss-overlay { align-items: flex-end; }
/* Scrim para legibilidad, en la dirección del bloque (suave). */
.onda-ss--horizontal .onda-ss-media::after,
.onda-ss--stacked .onda-ss-media::after,
.onda-ss--scroll-stack .onda-ss-media::after,
.onda-ss--switcher .onda-ss-media::after {
	content: '';
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: linear-gradient(to top, rgba(0,0,0,0.55), rgba(0,0,0,0) 55%);
}
.onda-ss-valign-top .onda-ss-media::after    { background: linear-gradient(to bottom, rgba(0,0,0,0.55), rgba(0,0,0,0) 55%); }
.onda-ss-valign-middle .onda-ss-media::after { background: radial-gradient(ellipse at center, rgba(0,0,0,0.5), rgba(0,0,0,0) 72%); }
.onda-ss--horizontal .onda-ss-title,
.onda-ss--stacked .onda-ss-title,
.onda-ss--scroll-stack .onda-ss-title,
.onda-ss--switcher .onda-ss-title { font-size: clamp(1.8rem, 4.5vw, 3.4rem); }
.onda-ss--horizontal .onda-ss-desc,
.onda-ss--stacked .onda-ss-desc,
.onda-ss--scroll-stack .onda-ss-desc,
.onda-ss--switcher .onda-ss-desc { max-width: 48ch; font-size: clamp(0.95rem, 1.3vw, 1.15rem); }

/* Scroll Stack = TRANSFORMACIÓN: cada panel CUBRE por completo al anterior
   (full-bleed, sin gaps) → cuando se despega aparece el siguiente y se siente que
   el panel anterior "se transformó" en el nuevo (1 → pasa 2 → 3). Por eso NO lleva
   inset/sombra (eso dejaría ver el de atrás y rompería la transformación). */

/* Stacked Cards = MAZO de cards: cada card llena el ítem (full) con esquinas
   redondeadas + sombra; el motor las apila con "peek" (asoman arriba) y las escala
   hacia atrás → se ve el mazo (la de adelante completa, las de atrás asomando con su
   tope redondeado). El inset grande rompía el peek (asomaba el margen), por eso full. */
.onda-ss--stacked .onda-ss-item { overflow: visible; }
.onda-ss--stacked .onda-ss-panel {
	box-shadow: 0 30px 70px -24px rgba(0, 0, 0, 0.6);
}

/* ── STICKY Reveal (= ProEffects + pausa). Card PINEADA con la foto CONTENIDA (centrada
   con márgenes, clip-reveal) y el TEXTO CENTRADO que VIAJA y se QUEDA (pausa) en el
   centro antes de cruzar al siguiente (sensación paso a paso, lo maneja el motor).
   Desktop = 2 columnas (media | texto); móvil = apilado (media arriba / texto). ────── */
.onda-ss--sticky {
	--onda-ss-sticky-mediaw: 46%;     /* ancho de la columna del media (desktop) */
	--onda-ss-sticky-photoh: 72vh;    /* alto de la foto contenida */
	--onda-ss-sticky-photow: 100%;    /* ancho de la foto dentro de su columna */
	--onda-ss-sticky-top: 0px;
	--onda-ss-sticky-steph: 110vh;    /* scroll por paso (más alto = pausa más larga) */
}
.onda-ss--sticky.is-built { display: block; }

/* Card pineada (a pantalla completa) */
.onda-ss-sticky__pcard {
	position: sticky;
	top: var(--onda-ss-sticky-top, 0px);
	height: 100vh;
	display: flex;
	gap: var(--onda-ss-sticky-gap, clamp(24px, 5vw, 80px));
	align-items: center;
}
.onda-ss--sticky.is-built.onda-ss--media-right .onda-ss-sticky__pcard { flex-direction: row-reverse; }

/* Foto CONTENIDA (centrada, con márgenes): la capa clip ocupa su zona, la foto va
   centrada adentro con su alto/ancho. */
.onda-ss-sticky__mwrap { position: relative; flex: 0 0 var(--onda-ss-sticky-mediaw, 46%); height: 100%; }
.onda-ss-sticky__mwrap .onda-ss-sticky__media-item { position: absolute; inset: 0; will-change: clip-path; }
.onda-ss-sticky__media-item {
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
}
.onda-ss-sticky__media-item .onda-ss-img,
.onda-ss-sticky__media-item .onda-ss-video {
	width: var(--onda-ss-sticky-photow, 100%);
	height: var(--onda-ss-sticky-photoh, 72vh);
	max-width: 100%;
	object-fit: cover;
	border-radius: var(--onda-ss-radius, 0);
}

/* Texto: SIEMPRE centrado vertical (Errol). Los pasos se apilan (absolute); el motor
   los mueve (viaja) + pausa + crossfade. */
.onda-ss-sticky__twrap { position: relative; flex: 1 1 auto; align-self: stretch; }
.onda-ss-sticky__tstep {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	justify-content: center;
}
.onda-ss-sticky__tstep .onda-ss-caption { padding: 0; }

/* MÓVIL = apilado (media contenida arriba + texto abajo) */
.onda-ss--sticky.is-built.is-mobile .onda-ss-sticky__pcard {
	flex-direction: column;
	align-items: stretch;
	gap: var(--onda-ss-sticky-gap, clamp(12px, 3vh, 28px));
	padding-bottom: env(safe-area-inset-bottom, 0);
}
/* La zona del media ABRAZA a la foto (misma var de alto): sin bandas muertas, la
   máscara del clip-reveal y el borde donde se esconde el texto miden EXACTO lo que
   mide la foto (antes: caja fija de 50vh con foto de 46vh → máscara más grande). */
.onda-ss--sticky.is-built.is-mobile .onda-ss-sticky__mwrap { flex: 0 0 var(--onda-ss-sticky-photoh, 46vh); }
/* El texto viaja DENTRO de su zona (clip): al salir se esconde en el borde superior
   (visualmente "detrás" de la foto) en vez de pasarle por encima, y el paso nuevo
   sube limpio desde el borde inferior. Desktop no lo necesita (columnas separadas,
   el viaje por todo el viewport es el look buscado ahí). */
.onda-ss--sticky.is-built.is-mobile .onda-ss-sticky__twrap { overflow: hidden; }
/* Centrado SEGURO de la caption en móvil: con márgenes auto (no justify-content),
   que colapsan a 0 si la caption es MÁS ALTA que la zona → una caption corta queda
   centrada, una larga arranca desde arriba con el título completo (el recorte, si
   lo hay, cae al final de la descripción, nunca decapita el título). */
.onda-ss--sticky.is-built.is-mobile .onda-ss-sticky__tstep { justify-content: flex-start; }
.onda-ss--sticky.is-built.is-mobile .onda-ss-sticky__tstep .onda-ss-caption { margin-top: auto; margin-bottom: auto; }
/* "Posición vertical del bloque: Arriba" en móvil pega la caption al borde superior
 * de su zona (justo debajo de la foto + la distancia configurada) en vez de centrarla
 * en el espacio restante. Solo el caso "top": hasta ahora la clase valign era un no-op
 * en este widget, así que Medio/Abajo siguen centrando igual que siempre (sin cambio
 * visual en instancias existentes). */
.onda-ss--sticky.is-built.is-mobile.onda-ss-valign-top .onda-ss-sticky__tstep .onda-ss-caption { margin-top: 0; }
.onda-ss--sticky.is-mobile { --onda-ss-sticky-photoh: 46vh; --onda-ss-sticky-photow: 92%; }

/* Fallback editor / sin-JS: cada paso en flujo = media + su texto debajo.
 * El alto del media y el gap respetan los MISMOS controles que el front (antes
 * estaban fijos en 56vh/16px → en el preview del editor "Alto de la foto" y
 * "Distancia entre foto/video y texto" parecían no hacer nada). */
.onda-ss-sticky__step {
	display: flex;
	flex-direction: column;
	gap: var(--onda-ss-sticky-gap, 16px);
	margin-bottom: 44px;
}
.onda-ss-sticky__step .onda-ss-sticky__media-item {
	position: relative;
	width: 100%;
	height: var(--onda-ss-sticky-photoh, 56vh);
	max-height: 92vh;
	border-radius: var(--onda-ss-radius);
	overflow: hidden;
}
/* EDITOR: el preview de Elementor redimensiona el iframe por dispositivo → con @media
   el sticky se ve 2 columnas en DESKTOP y apilado en MÓVIL dentro del editor. */
@media (min-width: 768px) {
	.onda-ss--editor.onda-ss--sticky .onda-ss-sticky__step {
		flex-direction: row;
		align-items: center;
		gap: var(--onda-ss-sticky-gap, clamp(24px, 4vw, 64px));
	}
	.onda-ss--editor.onda-ss--sticky.onda-ss--media-right .onda-ss-sticky__step { flex-direction: row-reverse; }
	.onda-ss--editor.onda-ss--sticky .onda-ss-sticky__step .onda-ss-sticky__media-item {
		flex: 0 0 var(--onda-ss-sticky-mediaw, 48%);
		height: var(--onda-ss-sticky-photoh, 56vh);
		margin-bottom: 0;
	}
	.onda-ss--editor.onda-ss--sticky .onda-ss-sticky__step .onda-ss-caption { flex: 1 1 auto; }
}

/* ── Fallback sin-JS (editor / móvil fuera de pin): columna legible ────────── */
.onda-ss--editor,
.onda-ss--stacked,
.onda-ss--scroll-stack,
.onda-ss--switcher,
.onda-ss--horizontal {
	display: flex;
	flex-direction: column;
	gap: var(--onda-ss-gap, 28px);
}
.onda-ss--editor .onda-ss-item {
	width: 100% !important;
	height: auto !important;
}
.onda-ss--editor .onda-ss-panel {
	position: relative;
	height: auto;
	box-shadow: none;
}
.onda-ss--editor.onda-ss--horizontal .onda-ss-media,
.onda-ss--editor.onda-ss--stacked .onda-ss-media,
.onda-ss--editor.onda-ss--scroll-stack .onda-ss-media,
.onda-ss--editor.onda-ss--switcher .onda-ss-media {
	position: relative;
	aspect-ratio: 16 / 9;
}
.onda-ss--editor.onda-ss--horizontal .onda-ss-overlay,
.onda-ss--editor.onda-ss--stacked .onda-ss-overlay,
.onda-ss--editor.onda-ss--scroll-stack .onda-ss-overlay,
.onda-ss--editor.onda-ss--switcher .onda-ss-overlay {
	position: relative;
	inset: auto;
	color: inherit;
	padding: 16px 4px 0;
	align-items: stretch;
}

.onda-ss-empty {
	padding: 24px;
	border: 1px dashed #c9c9c9;
	border-radius: 8px;
	color: #888;
	text-align: center;
	font-size: 0.9rem;
}

/* ============================================================================
 * HERO + CAPAS — .onda-hl* (Fullscreen Grow / Curtain Reveal)
 * El motor (fullscreen-grow / curtain-reveal) lee los .onda-hl-layer (sub-containers
 * .e-con hijos directos del root): 1º hero (fondo), resto capas. En runtime cuelga las
 * capas de un "stage" y les fija position:absolute + 100%×100% + clip-path. Este CSS da
 * el LOOK (media a sangre, caption posicionable, scrim) + el fallback sin-JS (editor /
 * fuera de pin = columnas legibles apiladas).
 * ============================================================================ */
.onda-hl {
	position: relative;
}
/* Capa (.e-con). Fallback sin-JS = bloque alto legible; el motor la pasa a absolute
   100%×100% (inline) cuando el efecto corre → su inline gana. overflow hidden para que
   el clip-path recorte el media. */
.onda-hl-layer {
	position: relative;
	min-height: 70vh;
	overflow: hidden;
	box-sizing: border-box;
	display: flex;
}
.onda-hl-media {
	position: absolute;
	inset: 0;
	overflow: hidden;
	border-radius: inherit;
}
/* Especificidad 0,2,0 para ganarle a `.elementor img { height:auto }` del tema. */
.onda-hl .onda-hl-img,
.onda-hl .onda-hl-video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}
.onda-hl-tpl { width: 100%; height: 100%; }
.onda-hl-ph {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	min-height: 200px;
	background: #ececec;
	color: #999;
	font-size: 0.85rem;
}
/* Scrim para legibilidad (en la dirección del bloque). */
.onda-hl-layer--scrim .onda-hl-media::after {
	content: '';
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: linear-gradient(to top, rgba(0,0,0,0.55), rgba(0,0,0,0) 55%);
}
.onda-hl-valign-top .onda-hl-layer--scrim .onda-hl-media::after    { background: linear-gradient(to bottom, rgba(0,0,0,0.55), rgba(0,0,0,0) 55%); }
.onda-hl-valign-middle .onda-hl-layer--scrim .onda-hl-media::after { background: radial-gradient(ellipse at center, rgba(0,0,0,0.5), rgba(0,0,0,0) 72%); }

/* OVERLAY posicionable: ubica el bloque de info en cualquiera de las 9 posiciones
   (onda-hl-align-* horizontal × onda-hl-valign-* vertical). */
.onda-hl-overlay {
	position: absolute;
	inset: 0;
	z-index: 2;
	display: flex;
	padding: clamp(20px, 5%, 64px);
	color: #fff;
	pointer-events: none;
}
.onda-hl-align-left .onda-hl-overlay   { justify-content: flex-start; }
.onda-hl-align-center .onda-hl-overlay { justify-content: center; }
.onda-hl-align-right .onda-hl-overlay  { justify-content: flex-end; }
.onda-hl-valign-top .onda-hl-overlay    { align-items: flex-start; }
.onda-hl-valign-middle .onda-hl-overlay { align-items: center; }
.onda-hl-valign-bottom .onda-hl-overlay { align-items: flex-end; }
.onda-hl-caption {
	pointer-events: auto;
	max-width: 60ch;
	display: flex;
	flex-direction: column;
	gap: 10px;
}
.onda-hl-eyebrow {
	font-size: 0.72rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	opacity: 0.8;
}
.onda-hl-title { margin: 0; font-size: clamp(1.8rem, 4.5vw, 3.4rem); line-height: 1.1; }
.onda-hl-desc { margin: 0; max-width: 48ch; font-size: clamp(0.95rem, 1.3vw, 1.15rem); line-height: 1.45; opacity: 0.92; }
.onda-hl-align-center .onda-hl-caption { align-items: center; }
.onda-hl-align-right .onda-hl-caption  { align-items: flex-end; }
.onda-hl-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	margin-top: 4px;
	flex: 0 0 auto;
	width: auto;
	max-width: 100%;
	padding: 0.7em 1.4em;
	color: #fff;
	background: #111;
	text-decoration: none;
	font-size: 0.9rem;
	border-radius: 999px;
	cursor: pointer;
}

/* Fallback sin-JS (editor / fuera de pin): capas apiladas en columna legible. */
.onda-hl--editor {
	display: flex;
	flex-direction: column;
	gap: 0;
}
.onda-hl--editor .onda-hl-layer {
	min-height: 60vh;
}
.onda-hl-empty {
	padding: 24px;
	border: 1px dashed #c9c9c9;
	border-radius: 8px;
	color: #888;
	text-align: center;
	font-size: 0.9rem;
}

/* ── Hover BG Zoom (el fondo del contenedor escala al hover) ──
 * La capa .onda-bgz__layer hereda el fondo COMPUTADO del contenedor EN VIVO
 * (background: inherit → sobrevive al lazy-load de Elementor) y escala detrás
 * del contenido. isolation crea el stacking context: la capa con z -1 pinta
 * ENCIMA del fondo propio del contenedor pero DEBAJO de todo su contenido.
 * overflow: clip (no hidden) recorta sin crear scroll container. */
.onda-bgz {
	position: relative;
	overflow: clip;
	isolation: isolate;
}
.onda-bgz > .onda-bgz__layer {
	position: absolute;
	inset: 0;
	z-index: -1;
	background: inherit;
	border-radius: inherit;
	pointer-events: none;
	transform: scale(1);
	transition: transform var(--onda-bgz-dur, 0.7s) cubic-bezier(0.22, 0.61, 0.36, 1);
	will-change: transform;
}
.onda-bgz:hover > .onda-bgz__layer {
	transform: scale(var(--onda-bgz-scale, 1.08));
}
/* Zoom OUT: reposa acercado y se aleja al hover. */
.onda-bgz--out > .onda-bgz__layer {
	transform: scale(var(--onda-bgz-scale, 1.08));
}
.onda-bgz--out:hover > .onda-bgz__layer {
	transform: scale(1);
}
@media (prefers-reduced-motion: reduce) {
	.onda-bgz > .onda-bgz__layer,
	.onda-bgz--out > .onda-bgz__layer {
		transition: none;
		transform: none;
	}
}
