More Heroes Snippets
Animated Gradient CTA — Free HTML CSS JS Section Snippet
Animated Gradient CTA · Heroes · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Animated Gradient CTA — Spinning Conic Glow with Email Capture

The animated gradient CTA is the eye-catching conversion block at the bottom of modern landing pages: a rounded panel lit from behind by a slowly rotating, blurred rainbow glow, with a headline and an inline email signup. This snippet builds the full section in plain HTML, CSS, and a little vanilla JavaScript — including the layered background, focus states, and a submit flow that confirms in place.
The rotating conic glow
The colorful light is a single .ag-glow element stretched well beyond the panel with inset: -40% and painted with a conic-gradient that cycles indigo, pink, amber, and cyan back to indigo. A heavy filter: blur(70px) melts those color stops into a soft aurora, and an agSpin keyframe rotates it a full 360° over nine seconds. Because it sits at z-index: -2 behind the content and the panel uses overflow: hidden, only the part inside the rounded corners shows, so the glow reads as ambient light leaking from behind the card.
A grain overlay for texture
Flat gradients can look plasticky, so a .ag-grain layer adds subtle noise. It's a tiny radial-gradient dot tiled at 4px via background-size, set to mix-blend-mode: overlay so the dots interact with the colors beneath rather than sitting on top as gray specks. This is a zero-image way to give the gradient a premium, textured finish. isolation: isolate on the panel keeps that blend mode contained to this component.
Layered with z-index and isolation
Three stacked layers compose the look: the spinning glow at -2, the grain at -1, and the content at 1. Keeping the content above its own stacking context (via isolation: isolate) guarantees the overlay blend never bleeds onto the page behind the section, which is a common bug when blend modes escape their container.
The inline signup
The form pairs an email input with a submit button. The input has a clear focus ring — a pink border plus a soft box-shadow halo — so keyboard users always see where they are. On submit, JavaScript prevents the default navigation, disables the button and shows a "Joining…" label, then after a short simulated delay hides the form and replaces the helper note with a success message that echoes the entered address. Swap the setTimeout for a real fetch to your signup endpoint and the UX stays identical.
Respecting the user's battery
A continuously spinning, blurred gradient is GPU work, so a visibilitychange listener pauses the animation by setting animationPlayState to paused whenever the tab is hidden, and resumes it on return. That small courtesy stops the effect from draining battery in a background tab.
Customizing it
Change the conic-gradient color stops to your brand palette, slow or speed the agSpin duration, dial the blur and opacity for a subtler or more intense glow, and adjust the grain dot size and opacity. The button hover lift and the focus ring are independent, so you can restyle them freely. On screens under 520px the form stacks vertically. Pair it with a scroll velocity marquee above or a testimonial wall to close a landing page with momentum.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA rounded CTA panel glows with a slowly spinning rainbow light.
- 2Notice the textureA subtle grain overlay gives the gradient a premium finish.
- 3Focus the email fieldA pink ring and halo make the focus state obvious.
- 4Submit the formThe button shows Joining, then a success note echoes your email.
- 5Switch tabsThe gradient pauses while the tab is hidden to save battery.
- 6Recolor and retimeEdit the conic stops, spin duration, blur, and grain.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
A single oversized element with inset: -40% is filled with a conic-gradient of four colors and blurred heavily with filter: blur(70px), then rotated 360 degrees on a 9-second keyframe. It sits behind the content at z-index -2, and the panel's overflow: hidden clips it to the rounded corners so it reads as ambient backlight.
It tiles a 1px radial-gradient dot every 4 pixels and uses mix-blend-mode: overlay so the noise interacts with the colors beneath instead of sitting on top as gray dots. That breaks up the flat gradient and gives the panel a textured, premium finish with zero image assets.
The grain layer uses an overlay blend mode. Without a new stacking context, that blend can bleed onto whatever sits behind the section on the page. isolation: isolate creates a contained stacking context so the blend only affects the panel's own layers, which is the standard fix for escaping blend modes.
No. A visibilitychange listener sets the glow's animationPlayState to paused whenever document.hidden is true and back to running on return. A blurred, continuously rotating gradient is real GPU work, so pausing it off-screen is a meaningful courtesy on laptops and phones.
The markup and CSS port directly. Handle submit with a framework event handler that calls your signup API and toggles a submitted boolean to swap the form for the success note. Add the visibilitychange listener in a mount effect with cleanup on unmount. In Tailwind, build the glow with an absolute element using bg-[conic-gradient(...)], blur-3xl, and an animate-spin-slow keyframe.
Animated Gradient CTA — Export as HTML, React, Vue, Angular & Tailwind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Animated Gradient CTA</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#06060a;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:30px 16px}
.ag-cta{position:relative;width:100%;max-width:680px;border-radius:26px;overflow:hidden;background:#0d0b1a;border:1px solid rgba(255,255,255,.08);padding:54px 30px;text-align:center;isolation:isolate}
.ag-glow{position:absolute;inset:-40%;z-index:-2;background:conic-gradient(from 0deg,#6366f1,#ec4899,#f59e0b,#22d3ee,#6366f1);filter:blur(70px);opacity:.55;animation:agSpin 9s linear infinite}
@keyframes agSpin{to{transform:rotate(360deg)}}
.ag-grain{position:absolute;inset:0;z-index:-1;opacity:.35;background-image:radial-gradient(rgba(255,255,255,.12) 1px,transparent 1px);background-size:4px 4px;mix-blend-mode:overlay}
.ag-inner{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:16px}
.ag-badge{font-size:12px;font-weight:700;letter-spacing:.04em;color:#fbcfe8;background:rgba(236,72,153,.14);border:1px solid rgba(236,72,153,.35);padding:6px 13px;border-radius:999px}
.ag-title{font-size:clamp(28px,6vw,46px);font-weight:900;letter-spacing:-.03em;line-height:1.06;color:#fff}
.ag-sub{font-size:15px;color:#c7c7d9;max-width:440px;line-height:1.55}
.ag-form{display:flex;gap:9px;width:100%;max-width:420px;margin-top:6px}
.ag-input{flex:1;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.16);border-radius:12px;padding:13px 15px;font-family:inherit;font-size:14px;color:#fff;outline:none;transition:border-color .2s,box-shadow .2s}
.ag-input::placeholder{color:#8b8ba3}
.ag-input:focus{border-color:#ec4899;box-shadow:0 0 0 3px rgba(236,72,153,.22)}
.ag-btn{background:#fff;color:#0d0b1a;border:none;border-radius:12px;padding:13px 20px;font-family:inherit;font-size:14px;font-weight:800;cursor:pointer;white-space:nowrap;transition:transform .15s,box-shadow .15s}
.ag-btn:hover{transform:translateY(-2px);box-shadow:0 12px 30px -10px rgba(255,255,255,.4)}
.ag-note{font-size:12px;color:#9a9ab0}
@media(max-width:520px){.ag-form{flex-direction:column}.ag-cta{padding:42px 22px}}
</style>
</head>
<body>
<section class="ag-cta">
<div class="ag-glow" aria-hidden="true"></div>
<div class="ag-grain" aria-hidden="true"></div>
<div class="ag-inner">
<span class="ag-badge">✦ Limited beta</span>
<h2 class="ag-title">Build interfaces<br>at the speed of thought</h2>
<p class="ag-sub">Drop in production-ready components and ship your next idea this weekend — no design debt.</p>
<form class="ag-form" id="agForm">
<input type="email" class="ag-input" placeholder="[email protected]" required aria-label="Email address">
<button type="submit" class="ag-btn">Get early access</button>
</form>
<p class="ag-note" id="agNote">Join 12,400 builders. No spam, ever.</p>
</div>
</section>
<script>
var form = document.getElementById('agForm');
var note = document.getElementById('agNote');
form.addEventListener('submit', function (e) {
e.preventDefault();
var input = form.querySelector('.ag-input');
var btn = form.querySelector('.ag-btn');
if (!input.value) return;
btn.disabled = true;
btn.textContent = 'Joining…';
// Simulate an async signup; replace with a real fetch to your API.
setTimeout(function () {
form.style.display = 'none';
note.innerHTML = '✓ You are on the list — check ' +
'<strong style="color:#fff">' + input.value + '</strong>';
note.style.color = '#a7f3d0';
}, 800);
});
// Pause the gradient spin when the tab is hidden to save battery.
document.addEventListener('visibilitychange', function () {
var glow = document.querySelector('.ag-glow');
glow.style.animationPlayState = document.hidden ? 'paused' : 'running';
});
</script>
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Animated Gradient CTA</title>
<!-- Tailwind CSS v3+ — arbitrary value classes (e.g. bg-[#0f172a]) are valid -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
@keyframes agSpin{to{transform:rotate(360deg)}}
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,-apple-system,sans-serif;background:#06060a;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:30px 16px
}
.ag-input::placeholder {
color:#8b8ba3
}
</style>
</head>
<body>
<section class="relative w-full max-w-[680px] rounded-[26px] overflow-hidden bg-[#0d0b1a] border border-[rgba(255,255,255,.08)] py-[54px] px-[30px] text-center [isolation:isolate] max-[520px]:py-[42px] max-[520px]:px-[22px]">
<div class="ag-glow absolute inset-[-40%] z-[-2] [background:conic-gradient(from_0deg,#6366f1,#ec4899,#f59e0b,#22d3ee,#6366f1)] [filter:blur(70px)] opacity-55 [animation:agSpin_9s_linear_infinite]" aria-hidden="true"></div>
<div class="absolute inset-0 z-[-1] opacity-35 [background-image:radial-gradient(rgba(255,255,255,.12)_1px,transparent_1px)] bg-[size:4px_4px] [mix-blend-mode:overlay]" aria-hidden="true"></div>
<div class="relative z-[1] flex flex-col items-center gap-4">
<span class="text-xs font-bold tracking-[.04em] text-[#fbcfe8] bg-[rgba(236,72,153,.14)] border border-[rgba(236,72,153,.35)] py-1.5 px-[13px] rounded-[999px]">✦ Limited beta</span>
<h2 class="text-[clamp(28px,6vw,46px)] font-black tracking-[-.03em] leading-[1.06] text-[#fff]">Build interfaces<br>at the speed of thought</h2>
<p class="text-[15px] text-[#c7c7d9] max-w-[440px] leading-[1.55]">Drop in production-ready components and ship your next idea this weekend — no design debt.</p>
<form class="flex gap-[9px] w-full max-w-[420px] mt-1.5 max-[520px]:flex-col" id="agForm">
<input type="email" class="ag-input flex-1 bg-[rgba(255,255,255,.06)] border border-[rgba(255,255,255,.16)] rounded-xl py-[13px] px-[15px] font-[inherit] text-sm text-[#fff] outline-none [transition:border-color_.2s,box-shadow_.2s] focus:border-[#ec4899] focus:shadow-[0_0_0_3px_rgba(236,72,153,.22)]" placeholder="[email protected]" required aria-label="Email address">
<button type="submit" class="ag-btn bg-[#fff] text-[#0d0b1a] border-0 rounded-xl py-[13px] px-5 font-[inherit] text-sm font-extrabold cursor-pointer whitespace-nowrap [transition:transform_.15s,box-shadow_.15s] hover:[transform:translateY(-2px)] hover:shadow-[0_12px_30px_-10px_rgba(255,255,255,.4)]">Get early access</button>
</form>
<p class="text-xs text-[#9a9ab0]" id="agNote">Join 12,400 builders. No spam, ever.</p>
</div>
</section>
<script>
var form = document.getElementById('agForm');
var note = document.getElementById('agNote');
form.addEventListener('submit', function (e) {
e.preventDefault();
var input = form.querySelector('.ag-input');
var btn = form.querySelector('.ag-btn');
if (!input.value) return;
btn.disabled = true;
btn.textContent = 'Joining…';
// Simulate an async signup; replace with a real fetch to your API.
setTimeout(function () {
form.style.display = 'none';
note.innerHTML = '✓ You are on the list — check ' +
'<strong style="color:#fff">' + input.value + '</strong>';
note.style.color = '#a7f3d0';
}, 800);
});
// Pause the gradient spin when the tab is hidden to save battery.
document.addEventListener('visibilitychange', function () {
var glow = document.querySelector('.ag-glow');
glow.style.animationPlayState = document.hidden ? 'paused' : 'running';
});
</script>
</body>
</html>import React, { useEffect } from 'react';
// CSS — optionally move to AnimatedGradientCTA.module.css
const css = `
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#06060a;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:30px 16px}
.ag-cta{position:relative;width:100%;max-width:680px;border-radius:26px;overflow:hidden;background:#0d0b1a;border:1px solid rgba(255,255,255,.08);padding:54px 30px;text-align:center;isolation:isolate}
.ag-glow{position:absolute;inset:-40%;z-index:-2;background:conic-gradient(from 0deg,#6366f1,#ec4899,#f59e0b,#22d3ee,#6366f1);filter:blur(70px);opacity:.55;animation:agSpin 9s linear infinite}
@keyframes agSpin{to{transform:rotate(360deg)}}
.ag-grain{position:absolute;inset:0;z-index:-1;opacity:.35;background-image:radial-gradient(rgba(255,255,255,.12) 1px,transparent 1px);background-size:4px 4px;mix-blend-mode:overlay}
.ag-inner{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:16px}
.ag-badge{font-size:12px;font-weight:700;letter-spacing:.04em;color:#fbcfe8;background:rgba(236,72,153,.14);border:1px solid rgba(236,72,153,.35);padding:6px 13px;border-radius:999px}
.ag-title{font-size:clamp(28px,6vw,46px);font-weight:900;letter-spacing:-.03em;line-height:1.06;color:#fff}
.ag-sub{font-size:15px;color:#c7c7d9;max-width:440px;line-height:1.55}
.ag-form{display:flex;gap:9px;width:100%;max-width:420px;margin-top:6px}
.ag-input{flex:1;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.16);border-radius:12px;padding:13px 15px;font-family:inherit;font-size:14px;color:#fff;outline:none;transition:border-color .2s,box-shadow .2s}
.ag-input::placeholder{color:#8b8ba3}
.ag-input:focus{border-color:#ec4899;box-shadow:0 0 0 3px rgba(236,72,153,.22)}
.ag-btn{background:#fff;color:#0d0b1a;border:none;border-radius:12px;padding:13px 20px;font-family:inherit;font-size:14px;font-weight:800;cursor:pointer;white-space:nowrap;transition:transform .15s,box-shadow .15s}
.ag-btn:hover{transform:translateY(-2px);box-shadow:0 12px 30px -10px rgba(255,255,255,.4)}
.ag-note{font-size:12px;color:#9a9ab0}
@media(max-width:520px){.ag-form{flex-direction:column}.ag-cta{padding:42px 22px}}
`;
export default function AnimatedGradientCTA() {
// Auto-generated escape hatch: the original snippet's vanilla JS runs once
// after mount and queries the rendered DOM. For idiomatic React, lift this
// into state + handlers.
useEffect(() => {
const _listeners = [];
const _originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
_listeners.push({ target: this, type, listener, options });
return _originalAddEventListener.call(this, type, listener, options);
};
var form = document.getElementById('agForm');
var note = document.getElementById('agNote');
form.addEventListener('submit', function (e) {
e.preventDefault();
var input = form.querySelector('.ag-input');
var btn = form.querySelector('.ag-btn');
if (!input.value) return;
btn.disabled = true;
btn.textContent = 'Joining…';
// Simulate an async signup; replace with a real fetch to your API.
setTimeout(function () {
form.style.display = 'none';
note.innerHTML = '✓ You are on the list — check ' +
'<strong style="color:#fff">' + input.value + '</strong>';
note.style.color = '#a7f3d0';
}, 800);
});
// Pause the gradient spin when the tab is hidden to save battery.
document.addEventListener('visibilitychange', function () {
var glow = document.querySelector('.ag-glow');
glow.style.animationPlayState = document.hidden ? 'paused' : 'running';
});
// Cleanup: restore addEventListener and remove all listeners
return () => {
EventTarget.prototype.addEventListener = _originalAddEventListener;
for (const { target, type, listener, options } of _listeners) {
target.removeEventListener(type, listener, options);
}
};
}, []);
return (
<>
<style>{css}</style>
<section className="ag-cta">
<div className="ag-glow" aria-hidden="true"></div>
<div className="ag-grain" aria-hidden="true"></div>
<div className="ag-inner">
<span className="ag-badge">✦ Limited beta</span>
<h2 className="ag-title">Build interfaces<br />at the speed of thought</h2>
<p className="ag-sub">Drop in production-ready components and ship your next idea this weekend — no design debt.</p>
<form className="ag-form" id="agForm">
<input type="email" className="ag-input" placeholder="[email protected]" required aria-label="Email address" />
<button type="submit" className="ag-btn">Get early access</button>
</form>
<p className="ag-note" id="agNote">Join 12,400 builders. No spam, ever.</p>
</div>
</section>
</>
);
}import React, { useEffect } from 'react';
// Requires Tailwind CSS v3+ — https://tailwindcss.com/docs/installation
// Arbitrary value classes (e.g. bg-[#0f172a]) are valid Tailwind v3+
export default function AnimatedGradientCTA() {
// Auto-generated escape hatch: the original snippet's vanilla JS runs once
// after mount and queries the rendered DOM. For idiomatic React, lift this
// into state + handlers.
useEffect(() => {
const _listeners = [];
const _originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
_listeners.push({ target: this, type, listener, options });
return _originalAddEventListener.call(this, type, listener, options);
};
var form = document.getElementById('agForm');
var note = document.getElementById('agNote');
form.addEventListener('submit', function (e) {
e.preventDefault();
var input = form.querySelector('.ag-input');
var btn = form.querySelector('.ag-btn');
if (!input.value) return;
btn.disabled = true;
btn.textContent = 'Joining…';
// Simulate an async signup; replace with a real fetch to your API.
setTimeout(function () {
form.style.display = 'none';
note.innerHTML = '✓ You are on the list — check ' +
'<strong style="color:#fff">' + input.value + '</strong>';
note.style.color = '#a7f3d0';
}, 800);
});
// Pause the gradient spin when the tab is hidden to save battery.
document.addEventListener('visibilitychange', function () {
var glow = document.querySelector('.ag-glow');
glow.style.animationPlayState = document.hidden ? 'paused' : 'running';
});
// Cleanup: restore addEventListener and remove all listeners
return () => {
EventTarget.prototype.addEventListener = _originalAddEventListener;
for (const { target, type, listener, options } of _listeners) {
target.removeEventListener(type, listener, options);
}
};
}, []);
return (
<>
<style>{`
@keyframes agSpin{to{transform:rotate(360deg)}}
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,-apple-system,sans-serif;background:#06060a;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:30px 16px
}
.ag-input::placeholder {
color:#8b8ba3
}
`}</style>
<section className="relative w-full max-w-[680px] rounded-[26px] overflow-hidden bg-[#0d0b1a] border border-[rgba(255,255,255,.08)] py-[54px] px-[30px] text-center [isolation:isolate] max-[520px]:py-[42px] max-[520px]:px-[22px]">
<div className="ag-glow absolute inset-[-40%] z-[-2] [background:conic-gradient(from_0deg,#6366f1,#ec4899,#f59e0b,#22d3ee,#6366f1)] [filter:blur(70px)] opacity-55 [animation:agSpin_9s_linear_infinite]" aria-hidden="true"></div>
<div className="absolute inset-0 z-[-1] opacity-35 [background-image:radial-gradient(rgba(255,255,255,.12)_1px,transparent_1px)] bg-[size:4px_4px] [mix-blend-mode:overlay]" aria-hidden="true"></div>
<div className="relative z-[1] flex flex-col items-center gap-4">
<span className="text-xs font-bold tracking-[.04em] text-[#fbcfe8] bg-[rgba(236,72,153,.14)] border border-[rgba(236,72,153,.35)] py-1.5 px-[13px] rounded-[999px]">✦ Limited beta</span>
<h2 className="text-[clamp(28px,6vw,46px)] font-black tracking-[-.03em] leading-[1.06] text-[#fff]">Build interfaces<br />at the speed of thought</h2>
<p className="text-[15px] text-[#c7c7d9] max-w-[440px] leading-[1.55]">Drop in production-ready components and ship your next idea this weekend — no design debt.</p>
<form className="flex gap-[9px] w-full max-w-[420px] mt-1.5 max-[520px]:flex-col" id="agForm">
<input type="email" className="ag-input flex-1 bg-[rgba(255,255,255,.06)] border border-[rgba(255,255,255,.16)] rounded-xl py-[13px] px-[15px] font-[inherit] text-sm text-[#fff] outline-none [transition:border-color_.2s,box-shadow_.2s] focus:border-[#ec4899] focus:shadow-[0_0_0_3px_rgba(236,72,153,.22)]" placeholder="[email protected]" required aria-label="Email address" />
<button type="submit" className="ag-btn bg-[#fff] text-[#0d0b1a] border-0 rounded-xl py-[13px] px-5 font-[inherit] text-sm font-extrabold cursor-pointer whitespace-nowrap [transition:transform_.15s,box-shadow_.15s] hover:[transform:translateY(-2px)] hover:shadow-[0_12px_30px_-10px_rgba(255,255,255,.4)]">Get early access</button>
</form>
<p className="text-xs text-[#9a9ab0]" id="agNote">Join 12,400 builders. No spam, ever.</p>
</div>
</section>
</>
);
}<template>
<section class="ag-cta">
<div class="ag-glow" aria-hidden="true"></div>
<div class="ag-grain" aria-hidden="true"></div>
<div class="ag-inner">
<span class="ag-badge">✦ Limited beta</span>
<h2 class="ag-title">Build interfaces<br>at the speed of thought</h2>
<p class="ag-sub">Drop in production-ready components and ship your next idea this weekend — no design debt.</p>
<form class="ag-form" id="agForm">
<input type="email" class="ag-input" placeholder="[email protected]" required aria-label="Email address">
<button type="submit" class="ag-btn">Get early access</button>
</form>
<p class="ag-note" id="agNote">Join 12,400 builders. No spam, ever.</p>
</div>
</section>
</template>
<script setup>
import { onMounted } from 'vue';
let form;
let note;
onMounted(() => {
form = document.getElementById('agForm');
note = document.getElementById('agNote');
form.addEventListener('submit', function (e) {
e.preventDefault();
var input = form.querySelector('.ag-input');
var btn = form.querySelector('.ag-btn');
if (!input.value) return;
btn.disabled = true;
btn.textContent = 'Joining…';
// Simulate an async signup; replace with a real fetch to your API.
setTimeout(function () {
form.style.display = 'none';
note.innerHTML = '✓ You are on the list — check ' +
'<strong style="color:#fff">' + input.value + '</strong>';
note.style.color = '#a7f3d0';
}, 800);
});
// Pause the gradient spin when the tab is hidden to save battery.
document.addEventListener('visibilitychange', function () {
var glow = document.querySelector('.ag-glow');
glow.style.animationPlayState = document.hidden ? 'paused' : 'running';
});
});
</script>
<style scoped>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#06060a;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:30px 16px}
.ag-cta{position:relative;width:100%;max-width:680px;border-radius:26px;overflow:hidden;background:#0d0b1a;border:1px solid rgba(255,255,255,.08);padding:54px 30px;text-align:center;isolation:isolate}
.ag-glow{position:absolute;inset:-40%;z-index:-2;background:conic-gradient(from 0deg,#6366f1,#ec4899,#f59e0b,#22d3ee,#6366f1);filter:blur(70px);opacity:.55;animation:agSpin 9s linear infinite}
@keyframes agSpin{to{transform:rotate(360deg)}}
.ag-grain{position:absolute;inset:0;z-index:-1;opacity:.35;background-image:radial-gradient(rgba(255,255,255,.12) 1px,transparent 1px);background-size:4px 4px;mix-blend-mode:overlay}
.ag-inner{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:16px}
.ag-badge{font-size:12px;font-weight:700;letter-spacing:.04em;color:#fbcfe8;background:rgba(236,72,153,.14);border:1px solid rgba(236,72,153,.35);padding:6px 13px;border-radius:999px}
.ag-title{font-size:clamp(28px,6vw,46px);font-weight:900;letter-spacing:-.03em;line-height:1.06;color:#fff}
.ag-sub{font-size:15px;color:#c7c7d9;max-width:440px;line-height:1.55}
.ag-form{display:flex;gap:9px;width:100%;max-width:420px;margin-top:6px}
.ag-input{flex:1;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.16);border-radius:12px;padding:13px 15px;font-family:inherit;font-size:14px;color:#fff;outline:none;transition:border-color .2s,box-shadow .2s}
.ag-input::placeholder{color:#8b8ba3}
.ag-input:focus{border-color:#ec4899;box-shadow:0 0 0 3px rgba(236,72,153,.22)}
.ag-btn{background:#fff;color:#0d0b1a;border:none;border-radius:12px;padding:13px 20px;font-family:inherit;font-size:14px;font-weight:800;cursor:pointer;white-space:nowrap;transition:transform .15s,box-shadow .15s}
.ag-btn:hover{transform:translateY(-2px);box-shadow:0 12px 30px -10px rgba(255,255,255,.4)}
.ag-note{font-size:12px;color:#9a9ab0}
@media(max-width:520px){.ag-form{flex-direction:column}.ag-cta{padding:42px 22px}}
</style>// @ts-nocheck
// Note: vanilla JS DOM manipulation is preserved as-is inside ngAfterViewInit().
// For idiomatic Angular, replace document.getElementById() with @ViewChild() refs
// and move state into component properties with two-way binding.
import { Component, AfterViewInit, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-animated-gradient-c-t-a',
standalone: true,
imports: [CommonModule],
encapsulation: ViewEncapsulation.None,
template: `
<section class="ag-cta">
<div class="ag-glow" aria-hidden="true"></div>
<div class="ag-grain" aria-hidden="true"></div>
<div class="ag-inner">
<span class="ag-badge">✦ Limited beta</span>
<h2 class="ag-title">Build interfaces<br>at the speed of thought</h2>
<p class="ag-sub">Drop in production-ready components and ship your next idea this weekend — no design debt.</p>
<form class="ag-form" id="agForm">
<input type="email" class="ag-input" placeholder="[email protected]" required aria-label="Email address">
<button type="submit" class="ag-btn">Get early access</button>
</form>
<p class="ag-note" id="agNote">Join 12,400 builders. No spam, ever.</p>
</div>
</section>
`,
styles: [`
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#06060a;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:30px 16px}
.ag-cta{position:relative;width:100%;max-width:680px;border-radius:26px;overflow:hidden;background:#0d0b1a;border:1px solid rgba(255,255,255,.08);padding:54px 30px;text-align:center;isolation:isolate}
.ag-glow{position:absolute;inset:-40%;z-index:-2;background:conic-gradient(from 0deg,#6366f1,#ec4899,#f59e0b,#22d3ee,#6366f1);filter:blur(70px);opacity:.55;animation:agSpin 9s linear infinite}
@keyframes agSpin{to{transform:rotate(360deg)}}
.ag-grain{position:absolute;inset:0;z-index:-1;opacity:.35;background-image:radial-gradient(rgba(255,255,255,.12) 1px,transparent 1px);background-size:4px 4px;mix-blend-mode:overlay}
.ag-inner{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:16px}
.ag-badge{font-size:12px;font-weight:700;letter-spacing:.04em;color:#fbcfe8;background:rgba(236,72,153,.14);border:1px solid rgba(236,72,153,.35);padding:6px 13px;border-radius:999px}
.ag-title{font-size:clamp(28px,6vw,46px);font-weight:900;letter-spacing:-.03em;line-height:1.06;color:#fff}
.ag-sub{font-size:15px;color:#c7c7d9;max-width:440px;line-height:1.55}
.ag-form{display:flex;gap:9px;width:100%;max-width:420px;margin-top:6px}
.ag-input{flex:1;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.16);border-radius:12px;padding:13px 15px;font-family:inherit;font-size:14px;color:#fff;outline:none;transition:border-color .2s,box-shadow .2s}
.ag-input::placeholder{color:#8b8ba3}
.ag-input:focus{border-color:#ec4899;box-shadow:0 0 0 3px rgba(236,72,153,.22)}
.ag-btn{background:#fff;color:#0d0b1a;border:none;border-radius:12px;padding:13px 20px;font-family:inherit;font-size:14px;font-weight:800;cursor:pointer;white-space:nowrap;transition:transform .15s,box-shadow .15s}
.ag-btn:hover{transform:translateY(-2px);box-shadow:0 12px 30px -10px rgba(255,255,255,.4)}
.ag-note{font-size:12px;color:#9a9ab0}
@media(max-width:520px){.ag-form{flex-direction:column}.ag-cta{padding:42px 22px}}
`]
})
export class AnimatedGradientCTAComponent implements AfterViewInit {
ngAfterViewInit(): void {
var form = document.getElementById('agForm');
var note = document.getElementById('agNote');
form.addEventListener('submit', function (e) {
e.preventDefault();
var input = form.querySelector('.ag-input');
var btn = form.querySelector('.ag-btn');
if (!input.value) return;
btn.disabled = true;
btn.textContent = 'Joining…';
// Simulate an async signup; replace with a real fetch to your API.
setTimeout(function () {
form.style.display = 'none';
note.innerHTML = '✓ You are on the list — check ' +
'<strong style="color:#fff">' + input.value + '</strong>';
note.style.color = '#a7f3d0';
}, 800);
});
// Pause the gradient spin when the tab is hidden to save battery.
document.addEventListener('visibilitychange', function () {
var glow = document.querySelector('.ag-glow');
glow.style.animationPlayState = document.hidden ? 'paused' : 'running';
});
}
}