Загрузка...

Шаблон HTML/CSS страницы контактов для SaaS-проектов. Минималистичный дизайн, адаптивный. Идеально для IT-компаний, стартапов.
<html lang="en"><head><meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SIGNAL — Real-time Observability & Incident Intelligence</title>
<meta name="description" content="SIGNAL — the observability platform that connects metrics, traces, and logs across your entire stack. Real-time dashboards, AI-powered anomaly detection, and smart alerting for engineering teams.">
<meta property="og:title" content="SIGNAL — Observability Platform for Engineering Teams">
<meta property="og:description" content="Connect metrics, traces, and logs across your entire stack. Real-time dashboards, AI-powered anomaly detection, and smart alerting.">
<meta property="og:type" content="website">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link rel="preconnect" href="https://code.iconify.design" crossorigin="">
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin="">
<link rel="preconnect" href="https://unpkg.com" crossorigin="">
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=IBM+Plex+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
<script async="" src="https://code.iconify.design/3/3.1.0/iconify.min.js"></script>
<style>
@property --glow-angle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
:root {
--bg: #0a0a0a;
--bg-elevated: #111111;
--bg-card: #111111;
--text: #f0ede6;
--text-muted: #777;
--text-soft: #999999;
--accent: #c9835a;
--accent-light: #e8c4a0;
--accent-dark: #2a1810;
--status-ok: #3dd68c;
--status-warn: #f0a030;
--status-crit: #e84040;
--border: #1e1e1e;
--border-glow: rgba(201, 131, 90, 0.25);
--font-heading: 'Outfit', sans-serif;
--font-body: 'IBM Plex Sans', sans-serif;
--container: 1280px;
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
::selection { background: var(--accent); color: var(--bg); }
html { scrollbar-width: thin; scrollbar-color: var(--accent-dark) var(--bg); }
body {
font-family: var(--font-body);
background: var(--bg);
color: var(--text);
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a { color: inherit; text-decoration: none; }
button { border: none; background: none; color: inherit; cursor: pointer; font-family: inherit; }
.container {
width: 100%; max-width: var(--container);
margin: 0 auto; padding: 0 clamp(1.25rem, 4vw, 3rem);
}
/* -- Canvas ------------------------------------------------ */
#webgl {
position: fixed; top: 0; left: 0;
width: 100%; height: 100%; z-index: 1; pointer-events: none;
}
/* -- Film Grain ------------------------------------------- */
.grain {
position: fixed; inset: 0; z-index: 9998; pointer-events: none;
opacity: 0.035;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
background-repeat: repeat;
background-size: 256px 256px;
}
/* -- Nav -------------------------------------------------- */
.nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
padding: 1.25rem 0;
transition: background 0.4s ease, padding 0.4s ease, backdrop-filter 0.4s ease;
}
.nav.is-scrolled {
background: rgba(10, 10, 10, 0.85);
backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
padding: 0.8rem 0;
border-bottom: 1px solid var(--border);
}
.nav__inner { display: flex; align-items: center; justify-content: space-between; }
.nav__logo {
font-family: var(--font-heading); font-size: 1.15rem;
font-weight: 800; letter-spacing: 0.12em; color: var(--accent);
}
.nav__links { display: flex; align-items: center; gap: 2rem; }
.nav__links a {
font-size: 0.82rem; font-weight: 500; letter-spacing: 0.02em;
color: var(--text-muted); transition: color 0.3s ease; position: relative;
}
.nav__links a::after {
content: ''; position: absolute; bottom: -4px; left: 0;
width: 0; height: 1px; background: var(--accent); transition: width 0.3s ease;
}
.nav__links a:hover { color: var(--text); }
.nav__links a:hover::after { width: 100%; }
.nav__links a.is-active { color: var(--accent); }
.nav__links a.is-active::after { width: 100%; }
.nav__cta {
display: inline-flex; align-items: center; gap: 0.5rem;
font-family: var(--font-heading); font-size: 0.82rem; font-weight: 600;
letter-spacing: 0.02em; padding: 0.6rem 1.4rem;
background: var(--accent); color: var(--bg);
border-radius: 8px; transition: all 0.35s ease;
}
.nav__cta:hover {
background: var(--accent-light);
box-shadow: 0 4px 20px rgba(201, 131, 90, 0.3);
transform: translateY(-1px);
}
.nav__toggle { display: none; font-size: 1.4rem; color: var(--text); }
/* -- Mobile Menu ------------------------------------------- */
.mobile-menu {
position: fixed; inset: 0; z-index: 200; background: var(--bg);
display: flex; flex-direction: column;
padding: 1.25rem clamp(1.25rem, 4vw, 3rem);
transform: translateX(100%);
transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.mobile-menu.is-open { transform: translateX(0); }
.mobile-menu__header { display: flex; align-items: center; justify-content: space-between; }
.mobile-menu__close { font-size: 1.4rem; color: var(--text-muted); }
.mobile-menu__nav { display: flex; flex-direction: column; margin-top: 3rem; }
.mobile-menu__nav a {
font-family: var(--font-heading); font-size: clamp(1.8rem, 7vw, 3rem);
font-weight: 700; letter-spacing: -0.03em; padding: 0.7rem 0;
color: var(--text-muted); transition: color 0.3s ease;
border-bottom: 1px solid var(--border);
}
.mobile-menu__nav a:hover { color: var(--accent); }
/* -- Section Utilities ------------------------------------ */
.section {
position: relative; z-index: 2;
padding: clamp(5rem, 10vw, 8rem) 0;
}
.section--solid { background: var(--bg); }
.section--elevated { background: var(--bg-elevated); }
.section--transparent { background: transparent; }
.section--dots {
background-color: var(--bg);
background-image: radial-gradient(circle, var(--accent) 0.7px, transparent 0.7px);
background-size: 28px 28px;
background-position: 14px 14px;
}
.section--dots::before {
content: ''; position: absolute; inset: 0;
background: var(--bg); opacity: 0.955; pointer-events: none;
}
.section--grid {
background-color: transparent;
background-image:
linear-gradient(var(--accent) 0.5px, transparent 0.5px),
linear-gradient(90deg, var(--accent) 0.5px, transparent 0.5px);
background-size: 60px 60px;
}
.section--grid::before {
content: ''; position: absolute; inset: 0; pointer-events: none;
background:
linear-gradient(180deg,
rgba(10,10,10,0.95) 0%,
rgba(10,10,10,0.75) 20%,
rgba(10,10,10,0.45) 40%,
rgba(10,10,10,0.45) 60%,
rgba(10,10,10,0.75) 80%,
rgba(10,10,10,0.95) 100%
);
}
.section__bg-number {
position: absolute; top: 0.3em; right: 5%;
font-family: var(--font-heading); font-size: clamp(10rem, 22vw, 20rem);
font-weight: 700; color: var(--text); opacity: 0.02;
line-height: 1; pointer-events: none; user-select: none;
z-index: 0;
}
.section-label {
font-family: var(--font-heading);
font-size: 0.7rem; font-weight: 600; letter-spacing: 0.2em;
text-transform: uppercase; color: var(--accent);
margin-bottom: clamp(2rem, 4vw, 3.5rem);
display: flex; align-items: center; gap: 1rem;
position: relative; z-index: 1;
}
.section-label::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.section-title {
font-family: var(--font-heading);
font-size: clamp(1.8rem, 3.5vw, 2.8rem);
font-weight: 700; line-height: 1.1; letter-spacing: -0.035em;
margin-bottom: 1rem; position: relative; z-index: 1;
}
.section-desc {
font-size: clamp(0.9rem, 1.1vw, 1rem); line-height: 1.8;
color: var(--text-muted); max-width: 520px;
position: relative; z-index: 1;
}
/* -- Buttons ---------------------------------------------- */
.btn {
display: inline-flex; align-items: center; gap: 0.6rem;
font-family: var(--font-heading); font-size: 0.88rem;
font-weight: 600; letter-spacing: 0.01em; padding: 0.85rem 1.8rem;
border-radius: 10px; position: relative; overflow: hidden;
transition: all 0.4s cubic-bezier(0.22, 1, 0.36, 1);
will-change: transform;
}
.btn--primary {
background: var(--accent); color: var(--bg);
}
.btn--primary:hover {
background: var(--accent-light);
box-shadow: 0 4px 20px rgba(201, 131, 90, 0.3);
transform: translateY(-2px);
}
.btn--ghost {
background: rgba(201, 131, 90, 0.04); color: var(--text);
border: 1px solid var(--border);
backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.btn--ghost:hover {
border-color: var(--accent); color: var(--accent);
background: rgba(201, 131, 90, 0.08);
box-shadow: 0 0 20px rgba(201, 131, 90, 0.08);
}
.btn--large { font-size: 1rem; padding: 1.15rem 2.6rem; border-radius: 12px; }
/* -- Hero ------------------------------------------------- */
.hero {
position: relative; z-index: 2;
min-height: 100vh; min-height: 100svh;
display: flex; align-items: center;
padding: 8rem 0 4rem;
}
.hero__content { max-width: 760px; }
.hero__badge {
display: inline-flex; align-items: center; gap: 0.5rem;
font-size: 0.75rem; font-weight: 500; letter-spacing: 0.04em;
color: var(--accent); padding: 0.4rem 1rem;
border: 1px solid var(--border-glow); border-radius: 100px;
margin-bottom: 2rem;
background: rgba(201, 131, 90, 0.05);
}
.hero__badge-dot {
width: 6px; height: 6px; border-radius: 50%;
background: var(--status-ok);
box-shadow: 0 0 8px var(--status-ok);
}
.hero__title {
font-family: var(--font-heading);
font-size: clamp(3rem, 7.5vw, 5.5rem);
font-weight: 800; line-height: 1.05; letter-spacing: -0.04em;
margin-bottom: 1.5rem;
}
.hero__title-accent {
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-light) 100%);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
}
.hero__typed-cursor {
display: inline-block; width: 3px; height: 0.85em;
background: var(--accent); margin-left: 4px;
vertical-align: text-bottom;
animation: cursorBlink 0.7s step-end infinite;
}
@keyframes cursorBlink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.hero__subtitle {
font-size: clamp(1rem, 1.3vw, 1.15rem); line-height: 1.7;
color: var(--text-muted); max-width: 480px;
margin-bottom: 2.5rem;
}
.hero__actions { display: flex; gap: 1rem; flex-wrap: wrap; }
/* -- Marquee ---------------------------------------------- */
.marquee {
position: relative; z-index: 2; overflow: hidden;
padding: 1.5rem 0; background: var(--bg);
border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 8%, black 92%, transparent 100%);
mask-image: linear-gradient(to right, transparent 0%, black 8%, black 92%, transparent 100%);
}
.marquee__track { display: flex; width: max-content; will-change: transform; }
.marquee__content { display: flex; align-items: center; white-space: nowrap; flex-shrink: 0; }
.marquee__item {
font-family: var(--font-heading); font-size: clamp(0.9rem, 1.4vw, 1.15rem);
font-weight: 500; letter-spacing: 0.02em;
color: var(--text); opacity: 0.18; padding: 0 2rem;
transition: opacity 0.4s ease;
}
.marquee__item:hover { opacity: 0.5; }
.marquee__sep { color: var(--accent); opacity: 0.3; font-size: 0.5em; padding: 0 0.5rem; }
/* -- Feature Highlights ----------------------------------- */
.features__grid {
display: grid; grid-template-columns: repeat(3, 1fr);
gap: 1.5rem; position: relative; z-index: 1;
}
.feature-card {
position: relative; padding: 2rem;
background: var(--bg-card); border: 1px solid var(--border);
border-radius: 12px; overflow: hidden;
transition: border-color 0.4s ease, box-shadow 0.4s ease;
}
.feature-card:hover {
border-color: var(--border-glow);
box-shadow: 0 4px 30px rgba(201, 131, 90, 0.08);
}
.feature-card__spotlight {
position: absolute; inset: 0; opacity: 0;
transition: opacity 0.4s ease; pointer-events: none;
background: radial-gradient(400px circle at var(--mx, 50%) var(--my, 50%),
rgba(201, 131, 90, 0.06) 0%, transparent 70%);
}
.feature-card:hover .feature-card__spotlight { opacity: 1; }
.feature-card__icon {
width: 44px; height: 44px; border-radius: 10px;
background: var(--accent-dark); color: var(--accent);
display: flex; align-items: center; justify-content: center;
font-size: 1.3rem; margin-bottom: 1.25rem;
transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.feature-card:hover .feature-card__icon { transform: scale(1.08) rotate(4deg); }
.feature-card__title {
font-family: var(--font-heading); font-size: 1.05rem;
font-weight: 600; letter-spacing: -0.01em; margin-bottom: 0.6rem;
}
.feature-card__desc {
font-size: 0.85rem; color: var(--text-muted); line-height: 1.65;
}
/* -- Platform Preview ------------------------------------- */
.platform { text-align: center; position: relative; z-index: 1; }
.platform__frame {
position: relative; margin-top: 3rem;
border-radius: 12px; overflow: hidden;
border: 1px solid var(--border);
background: var(--bg-card);
}
.platform__topbar {
display: flex; align-items: center; gap: 0.5rem;
padding: 0.8rem 1.2rem; border-bottom: 1px solid var(--border);
background: var(--bg-elevated);
}
.platform__dot {
width: 8px; height: 8px; border-radius: 50%;
}
.platform__dot--red { background: var(--status-crit); opacity: 0.6; }
.platform__dot--yellow { background: var(--status-warn); opacity: 0.6; }
.platform__dot--green { background: var(--status-ok); opacity: 0.6; }
.platform__url {
font-size: 0.68rem; color: var(--text-muted); margin-left: 0.8rem;
font-family: var(--font-body);
}
.platform__body {
padding: 2rem;
min-height: 340px;
display: grid; grid-template-columns: 180px 1fr;
gap: 1.5rem;
}
.platform__sidebar {
display: flex; flex-direction: column; gap: 0.4rem;
border-right: 1px solid var(--border); padding-right: 1.5rem;
}
.platform__sidebar-item {
font-size: 0.75rem; color: var(--text-muted); padding: 0.5rem 0.8rem;
border-radius: 6px; display: flex; align-items: center; gap: 0.5rem;
transition: background 0.3s ease;
}
.platform__sidebar-item--active {
background: var(--accent-dark); color: var(--accent);
}
.platform__main { display: flex; flex-direction: column; gap: 1rem; }
.platform__chart {
flex: 1; display: flex; align-items: flex-end; gap: 6px;
padding: 1rem 0;
}
.platform__bar {
flex: 1; border-radius: 3px 3px 0 0;
background: var(--accent);
opacity: 0.6;
transition: height 0.6s ease;
}
.platform__metrics-row {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.8rem;
}
.platform__metric-card {
padding: 0.8rem; border-radius: 8px;
border: 1px solid var(--border);
background: var(--bg);
}
.platform__metric-val {
font-family: var(--font-heading); font-size: 1.1rem;
font-weight: 700; color: var(--accent); letter-spacing: -0.02em;
}
.platform__metric-label {
font-size: 0.65rem; color: var(--text-muted); margin-top: 0.2rem;
}
/* -- Callouts --------------------------------------------- */
.platform__callouts { position: absolute; inset: 0; pointer-events: none; }
.platform__callout {
position: absolute; padding: 0.5rem 0.8rem;
background: rgba(10, 10, 10, 0.9); border: 1px solid var(--border-glow);
border-radius: 8px; font-size: 0.72rem; color: var(--accent-light);
white-space: nowrap; backdrop-filter: blur(8px);
}
.platform__callout--1 { top: 25%; right: -10px; transform: translateX(100%); }
.platform__callout--2 { bottom: 30%; right: -10px; transform: translateX(100%); }
.platform__callout--3 { top: 35%; left: -10px; transform: translateX(-100%); }
/* -- Metrics Bar ------------------------------------------ */
.metrics {
position: relative; z-index: 2; background: var(--bg);
border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
padding: clamp(2.5rem, 5vw, 4rem) 0;
}
.metrics__grid {
display: grid; grid-template-columns: repeat(4, 1fr);
gap: 0; position: relative;
}
.metrics__item {
text-align: center; padding: 1rem 2rem;
position: relative;
}
.metrics__item:not(:last-child)::after {
content: ''; position: absolute; right: 0; top: 15%; height: 70%;
width: 1px; background: var(--border);
}
.metrics__number {
font-family: var(--font-heading);
font-size: clamp(2rem, 4vw, 3rem);
font-weight: 800; letter-spacing: -0.04em; color: var(--accent);
display: block; margin-bottom: 0.3rem;
}
.metrics__label {
font-size: 0.78rem; color: var(--text-muted); letter-spacing: 0.02em;
}
/* -- How It Works ----------------------------------------- */
.steps__grid {
display: grid; grid-template-columns: repeat(3, 1fr);
gap: 0; position: relative; z-index: 1;
}
.step {
text-align: center; padding: 2rem 1.5rem; position: relative;
}
.step__connector {
position: absolute; top: calc(2rem + 27px); left: 50%; right: -50%;
height: 2px; background: var(--border); z-index: 0;
}
.step:last-child .step__connector { display: none; }
.step__connector-fill {
width: 0; height: 100%; background: var(--accent);
transition: width 1.2s cubic-bezier(0.22, 1, 0.36, 1);
}
.step.is-active .step__connector-fill { width: 100%; }
.step__icon {
width: 56px; height: 56px; border-radius: 50%;
background: var(--bg); border: 2px solid var(--border);
display: flex; align-items: center; justify-content: center;
font-size: 1.3rem; color: var(--accent);
margin: 0 auto 1.25rem; position: relative; z-index: 2;
transition: border-color 0.4s ease, box-shadow 0.4s ease;
}
.step.is-active .step__icon {
border-color: var(--accent);
box-shadow: 0 0 0 6px rgba(201, 131, 90, 0.15);
}
.step__pulse {
position: absolute; inset: -6px; border-radius: 50%;
border: 2px solid var(--accent); opacity: 0;
}
.step.is-active .step__pulse {
animation: stepPulse 1.5s ease-out 0.5s;
}
@keyframes stepPulse {
0% { transform: scale(1); opacity: 0.6; }
100% { transform: scale(1.6); opacity: 0; }
}
.step__number {
font-family: var(--font-heading); font-size: 0.65rem;
font-weight: 600; letter-spacing: 0.15em; color: var(--accent);
text-transform: uppercase; margin-bottom: 0.75rem;
}
.step__title {
font-family: var(--font-heading); font-size: 1.1rem;
font-weight: 600; letter-spacing: -0.01em; margin-bottom: 0.6rem;
}
.step__desc {
font-size: 0.82rem; color: var(--text-muted); line-height: 1.65;
max-width: 280px; margin: 0 auto;
}
/* -- Testimonial ------------------------------------------ */
.testimonial {
text-align: center; max-width: 780px; margin: 0 auto;
position: relative; z-index: 1;
}
.testimonial__mark {
font-family: Georgia, serif;
font-size: clamp(5rem, 12vw, 8rem);
font-weight: 400; color: var(--accent); opacity: 0.1;
line-height: 0.4; margin-bottom: 2.5rem; display: block;
}
.testimonial__text {
font-family: var(--font-heading);
font-size: clamp(1.15rem, 2vw, 1.6rem);
font-weight: 400; line-height: 1.55; letter-spacing: -0.01em;
margin-bottom: 2rem; font-style: italic; color: var(--text);
}
.testimonial__author { display: flex; flex-direction: column; gap: 0.2rem; }
.testimonial__name {
font-family: var(--font-heading); font-weight: 600; font-size: 0.88rem;
}
.testimonial__role { font-size: 0.78rem; color: var(--text-muted); }
/* -- Pricing Preview -------------------------------------- */
.pricing-preview__grid {
display: grid; grid-template-columns: repeat(3, 1fr);
gap: 1.5rem; position: relative; z-index: 1;
}
.pricing-card {
position: relative; padding: 2.5rem 2rem;
background: var(--bg-card); border: 1px solid var(--border);
border-radius: 12px;
transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
box-shadow 0.5s ease, border-color 0.4s ease;
transform-style: preserve-3d;
}
.pricing-card:hover {
border-color: var(--border-glow);
box-shadow: 0 8px 40px rgba(201, 131, 90, 0.1);
}
.pricing-card--featured {
border-color: var(--accent);
}
.pricing-card__popular {
position: absolute; top: -12px; left: 50%; transform: translateX(-50%);
font-family: var(--font-heading); font-size: 0.68rem; font-weight: 600;
letter-spacing: 0.08em; text-transform: uppercase;
padding: 0.3rem 1rem; background: var(--accent); color: var(--bg);
border-radius: 100px;
}
.pricing-card__name {
font-family: var(--font-heading); font-size: 0.85rem;
font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
color: var(--text-muted); margin-bottom: 0.6rem;
}
.pricing-card__price {
font-family: var(--font-heading);
font-size: clamp(2rem, 3.5vw, 2.8rem);
font-weight: 800; letter-spacing: -0.04em; margin-bottom: 0.3rem;
}
.pricing-card__price span {
font-size: 0.45em; font-weight: 400; color: var(--text-muted);
}
.pricing-card__desc {
font-size: 0.82rem; color: var(--text-muted); margin-bottom: 1.5rem;
line-height: 1.5;
}
.pricing-card__features { list-style: none; margin-bottom: 2rem; }
.pricing-card__features li {
font-size: 0.82rem; color: var(--text-muted);
padding: 0.4rem 0; display: flex; align-items: center; gap: 0.6rem;
}
.pricing-card__features li .iconify { color: var(--accent); font-size: 1rem; }
.pricing-card .btn { width: 100%; justify-content: center; }
/* -- CTA -------------------------------------------------- */
.cta {
position: relative; z-index: 2; background: transparent;
padding: clamp(6rem, 12vw, 10rem) 0;
text-align: center;
}
.cta__title {
font-family: var(--font-heading);
font-size: clamp(2.2rem, 6vw, 4.5rem);
font-weight: 800; line-height: 1.05;
letter-spacing: -0.04em; margin-bottom: 1.5rem;
}
.cta__title-accent {
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-light) 100%);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
}
.cta__subtitle {
font-size: 1rem; color: var(--text-muted); margin-bottom: 2.5rem;
max-width: 440px; margin-left: auto; margin-right: auto; line-height: 1.6;
}
.cta__form {
display: flex; gap: 0.75rem; max-width: 440px;
margin: 0 auto; position: relative;
}
.cta__input {
flex: 1; padding: 0.9rem 1.2rem;
font-family: var(--font-body); font-size: 0.88rem;
background: var(--bg-card); border: 1px solid var(--border);
border-radius: 8px; color: var(--text); outline: none;
transition: border-color 0.4s ease, box-shadow 0.4s ease;
}
.cta__input::placeholder { color: var(--text-muted); }
.cta__input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(201, 131, 90, 0.15);
}
.cta__input--glow:focus {
border-image: conic-gradient(from var(--glow-angle), var(--accent), var(--accent-light), var(--accent)) 1;
animation: glowRotate 3s linear infinite;
}
@keyframes glowRotate {
to { --glow-angle: 360deg; }
}
/* -- Footer ----------------------------------------------- */
.footer {
position: relative; z-index: 2;
background-color: var(--bg);
background-image: radial-gradient(circle, var(--accent) 0.5px, transparent 0.5px);
background-size: 24px 24px;
border-top: 1px solid var(--border);
padding: clamp(3rem, 6vw, 5rem) 0 2rem;
}
.footer::before {
content: ''; position: absolute; inset: 0;
background: var(--bg); opacity: 0.97; pointer-events: none;
}
.footer__inner { position: relative; z-index: 1; }
.footer__grid {
display: grid; grid-template-columns: 2fr 1fr 1fr 1fr;
gap: clamp(2rem, 4vw, 4rem);
margin-bottom: clamp(3rem, 5vw, 4rem);
}
.footer__logo {
font-family: var(--font-heading); font-size: 1.15rem;
font-weight: 800; letter-spacing: 0.12em; color: var(--accent);
display: block; margin-bottom: 0.8rem;
}
.footer__logo-shimmer {
background: linear-gradient(90deg, var(--accent), var(--accent-light), var(--accent));
background-size: 200% 100%;
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
animation: shimmer 4s ease-in-out infinite;
}
@keyframes shimmer {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
.footer__tagline {
font-size: 0.85rem; color: var(--text-muted);
line-height: 1.6; max-width: 280px;
}
.footer__col h4 {
font-family: var(--font-heading); font-size: 0.7rem;
font-weight: 600; letter-spacing: 0.15em; text-transform: uppercase;
color: var(--text-muted); margin-bottom: 1rem;
}
.footer__col a {
display: block; font-size: 0.85rem; color: var(--text-muted);
padding: 0.3rem 0; transition: color 0.3s ease;
}
.footer__col a:hover { color: var(--accent); }
.footer__bottom {
display: flex; align-items: center; justify-content: space-between;
padding-top: 2rem; border-top: 1px solid var(--border);
font-size: 0.72rem; color: var(--text-muted);
}
.footer__bottom a { transition: color 0.3s ease; }
.footer__bottom a:hover { color: var(--accent); }
/* -- Divider ---------------------------------------------- */
.divider {
position: relative; z-index: 2; background: var(--bg);
padding: 0; height: 1px;
}
.divider__line {
width: 100%; height: 1px;
background: var(--border);
}
/* -- Scroll Progress Bar ---------------------------------- */
.scroll-progress {
position: fixed; top: 0; left: 0; width: 0; height: 2px;
background: linear-gradient(90deg, var(--accent), var(--accent-light));
z-index: 9999; transition: none;
box-shadow: 0 0 8px rgba(201,131,90,0.5);
}
/* -- Responsive ------------------------------------------- */
@media (max-width: 1024px) {
.features__grid { grid-template-columns: repeat(2, 1fr); }
.pricing-preview__grid { grid-template-columns: repeat(2, 1fr); }
.pricing-preview__grid .pricing-card:last-child {
grid-column: 1 / -1; max-width: 400px; margin: 0 auto;
}
.platform__body { grid-template-columns: 1fr; }
.platform__sidebar { flex-direction: row; flex-wrap: wrap; border-right: none; padding-right: 0; border-bottom: 1px solid var(--border); padding-bottom: 1rem; }
.platform__callout { display: none; }
.platform__metrics-row { grid-template-columns: repeat(2, 1fr); }
.footer__grid { grid-template-columns: 1fr 1fr; gap: 2.5rem; }
.section__bg-number { font-size: clamp(8rem, 18vw, 14rem); }
}
@media (max-width: 768px) {
.nav__links, .nav__cta { display: none; }
.nav__toggle { display: block; }
.hero { padding: 7rem 0 3rem; }
.hero__actions { flex-direction: column; }
.hero__actions .btn { width: 100%; justify-content: center; }
.features__grid { grid-template-columns: 1fr; }
.steps__grid { grid-template-columns: 1fr; gap: 2rem; }
.step__connector { display: none; }
.step { padding: 1.5rem 1rem; }
.metrics__grid { grid-template-columns: repeat(2, 1fr); }
.metrics__item:nth-child(2)::after { display: none; }
.pricing-preview__grid { grid-template-columns: 1fr; }
.pricing-preview__grid .pricing-card:last-child {
grid-column: auto; max-width: none; margin: 0; width: 100%;
}
.pricing-card { padding: 2rem 1.5rem; }
.platform__metrics-row { grid-template-columns: repeat(2, 1fr); }
.cta__form { flex-direction: column; }
.cta__form .btn { width: 100%; justify-content: center; }
.footer__grid { grid-template-columns: 1fr; gap: 2rem; }
.section__bg-number { font-size: clamp(6rem, 16vw, 10rem); }
.scroll-progress { height: 1px; }
.testimonial__text { font-size: clamp(1rem, 3.5vw, 1.3rem); }
.testimonial__mark { font-size: clamp(4rem, 10vw, 6rem); }
}
@media (max-width: 480px) {
.hero__title { font-size: clamp(2.2rem, 11vw, 3.2rem); }
.hero__subtitle { font-size: 0.95rem; }
.btn--large { font-size: 0.9rem; padding: 1rem 1.8rem; }
.metrics__grid { grid-template-columns: 1fr 1fr; }
.metrics__item::after { display: none; }
.metrics__number { font-size: clamp(1.6rem, 8vw, 2.2rem); }
.platform__metrics-row { grid-template-columns: 1fr 1fr; gap: 0.5rem; }
.platform__metric-val { font-size: 0.9rem; }
.pricing-card { padding: 1.8rem 1.2rem; }
.pricing-card__price { font-size: clamp(1.8rem, 8vw, 2.4rem); }
.cta__title { font-size: clamp(1.8rem, 9vw, 2.8rem); }
.footer__bottom { flex-direction: column; gap: 0.5rem; text-align: center; }
.section__bg-number { font-size: clamp(4rem, 14vw, 7rem); opacity: 0.015; }
.testimonial__text { font-size: 0.95rem; }
.step__desc { font-size: 0.78rem; }
}
@media (hover: none) {
.feature-card:hover .feature-card__icon { transform: none; }
.feature-card__spotlight { display: none; }
.nav__cta:hover { background: var(--accent); transform: none; box-shadow: none; }
.btn--primary:hover { transform: none; box-shadow: none; }
.btn--ghost:hover { box-shadow: none; }
.pricing-card:hover { transform: none; box-shadow: none; }
.marquee__item { opacity: 0.25; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
/* -- Skip Link -- */
.skip-link { position: absolute; top: -100%; left: 1rem; z-index: 9999; padding: 0.75rem 1.5rem; background: var(--accent); color: var(--bg); border-radius: 0 0 8px 8px; font-size: 0.85rem; font-weight: 600; text-decoration: none; transition: top 0.2s; }
.skip-link:focus { top: 0; }
</style>
<script src="https://cdn.tailwindcss.com"></script></head>
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<div class="grain"></div>
<canvas id="webgl" role="img" aria-label="Animated 3D visualization"></canvas>
<div class="scroll-progress" id="scrollProgress"></div>
<!-- Navigation -->
<nav class="nav" id="nav">
<div class="container nav__inner">
<a href="/home" class="nav__logo">SIGNAL</a>
<div class="nav__links">
<a href="/features">Features</a>
<a href="/pricing">Pricing</a>
<a href="/integrations">Integrations</a>
<a href="/blog">Blog</a>
<a href="/about">About</a>
<a href="/changelog">Changelog</a>
</div>
<a href="/contact" class="nav__cta">
Get Started
<span class="iconify" data-icon="solar:arrow-right-linear" data-width="14"></span>
</a>
<button class="nav__toggle" id="navToggle" aria-label="Open menu">
<span class="iconify" data-icon="solar:hamburger-menu-linear" data-width="24"></span>
</button>
</div>
</nav>
<!-- Mobile Menu -->
<div class="mobile-menu" id="mobileMenu">
<div class="mobile-menu__header">
<a href="/home" class="nav__logo">SIGNAL</a>
<button class="mobile-menu__close" id="mobileClose" aria-label="Close menu">
<span class="iconify" data-icon="solar:close-circle-linear" data-width="26"></span>
</button>
</div>
<nav class="mobile-menu__nav">
<a href="/features" data-mobile-link="">Features</a>
<a href="/pricing" data-mobile-link="">Pricing</a>
<a href="/integrations" data-mobile-link="">Integrations</a>
<a href="/blog" data-mobile-link="">Blog</a>
<a href="/about" data-mobile-link="">About</a>
<a href="/changelog" data-mobile-link="">Changelog</a>
<a href="/contact" data-mobile-link="">Contact</a>
</nav>
</div>
<main id="main-content">
<!-- Hero -->
<section class="hero" id="hero">
<div class="container">
<div class="hero__content">
<div class="hero__badge">
<span class="hero__badge-dot"></span>
Trusted by 500+ engineering teams
</div>
<h1 class="hero__title" id="heroTitle">
Observe Everything.<br>
Miss <span class="hero__title-accent" id="heroAccent">Nothing</span><span class="hero__typed-cursor" id="heroCursor"></span>
</h1>
<p class="hero__subtitle">Real-time observability powered by AI. Ingest logs, metrics, and traces -- detect anomalies before they become incidents.</p>
<div class="hero__actions">
<a href="/contact" class="btn btn--primary btn--large">
Start Free Trial
<span class="iconify" data-icon="solar:arrow-right-linear" data-width="16"></span>
</a>
<a href="/features" class="btn btn--ghost btn--large">
<span class="iconify" data-icon="solar:play-circle-linear" data-width="18"></span>
Watch Demo
</a>
</div>
</div>
</div>
</section>
<!-- Logo Marquee -->
<div class="marquee" id="marquee">
<div class="marquee__track" id="marqueeTrack">
<div class="marquee__content">
<span class="marquee__item">Stripe</span><span class="marquee__sep">•</span>
<span class="marquee__item">Vercel</span><span class="marquee__sep">•</span>
<span class="marquee__item">Linear</span><span class="marquee__sep">•</span>
<span class="marquee__item">Notion</span><span class="marquee__sep">•</span>
<span class="marquee__item">Figma</span><span class="marquee__sep">•</span>
<span class="marquee__item">Shopify</span><span class="marquee__sep">•</span>
<span class="marquee__item">GitLab</span><span class="marquee__sep">•</span>
<span class="marquee__item">Datadog</span><span class="marquee__sep">•</span>
</div>
<div class="marquee__content" aria-hidden="true">
<span class="marquee__item">Stripe</span><span class="marquee__sep">•</span>
<span class="marquee__item">Vercel</span><span class="marquee__sep">•</span>
<span class="marquee__item">Linear</span><span class="marquee__sep">•</span>
<span class="marquee__item">Notion</span><span class="marquee__sep">•</span>
<span class="marquee__item">Figma</span><span class="marquee__sep">•</span>
<span class="marquee__item">Shopify</span><span class="marquee__sep">•</span>
<span class="marquee__item">GitLab</span><span class="marquee__sep">•</span>
<span class="marquee__item">Datadog</span><span class="marquee__sep">•</span>
</div>
</div>
</div>
<!-- Feature Highlights -->
<section class="section section--dots" id="features">
<div class="container">
<span class="section__bg-number">01</span>
<div class="section-label">Core Capabilities</div>
<div class="section-title" style="max-width: 560px; margin-bottom: 2.5rem;">Everything you need to keep your systems running flawlessly.</div>
<div class="features__grid" id="featuresGrid">
<div class="feature-card">
<div class="feature-card__spotlight"></div>
<div class="feature-card__icon">
<span class="iconify" data-icon="solar:chart-2-bold-duotone" data-width="22"></span>
</div>
<h3 class="feature-card__title">Real-time Dashboards</h3>
<p class="feature-card__desc">Live infrastructure views with sub-second refresh rates. Custom layouts, drag-and-drop widgets, instant queries.</p>
</div>
<div class="feature-card">
<div class="feature-card__spotlight"></div>
<div class="feature-card__icon">
<span class="iconify" data-icon="solar:shield-warning-bold-duotone" data-width="22"></span>
</div>
<h3 class="feature-card__title">AI Anomaly Detection</h3>
<p class="feature-card__desc">Machine learning models trained on your baseline metrics. Catch issues before alerts fire.</p>
</div>
<div class="feature-card">
<div class="feature-card__spotlight"></div>
<div class="feature-card__icon">
<span class="iconify" data-icon="solar:bell-bing-bold-duotone" data-width="22"></span>
</div>
<h3 class="feature-card__title">Smart Alerting</h3>
<p class="feature-card__desc">Context-aware alerts with automatic deduplication, severity ranking, and intelligent routing to the right on-call.</p>
</div>
<div class="feature-card">
<div class="feature-card__spotlight"></div>
<div class="feature-card__icon">
<span class="iconify" data-icon="solar:routing-2-bold-duotone" data-width="22"></span>
</div>
<h3 class="feature-card__title">Distributed Tracing</h3>
<p class="feature-card__desc">Follow requests across microservices. Identify bottlenecks with flame graphs and service dependency maps.</p>
</div>
<div class="feature-card">
<div class="feature-card__spotlight"></div>
<div class="feature-card__icon">
<span class="iconify" data-icon="solar:document-text-bold-duotone" data-width="22"></span>
</div>
<h3 class="feature-card__title">Log Aggregation</h3>
<p class="feature-card__desc">Centralize logs from every source. Full-text search, pattern recognition, and structured parsing at petabyte scale.</p>
</div>
<div class="feature-card">
<div class="feature-card__spotlight"></div>
<div class="feature-card__icon">
<span class="iconify" data-icon="solar:server-square-cloud-bold-duotone" data-width="22"></span>
</div>
<h3 class="feature-card__title">Infrastructure Mapping</h3>
<p class="feature-card__desc">Auto-discover topology. Visualize your entire stack -- from Kubernetes pods to database clusters -- in one view.</p>
</div>
</div>
</div>
</section>
<!-- Divider -->
<div class="divider"><div class="container"><div class="divider__line"></div></div></div>
<!-- Platform Preview -->
<section class="section section--solid" id="platform">
<div class="container">
<span class="section__bg-number">02</span>
<div class="platform">
<div class="section-label" style="justify-content: center;">Platform</div>
<h2 class="section-title" style="text-align: center; max-width: 600px; margin: 0 auto 0.8rem;">Built for engineers who demand clarity.</h2>
<p class="section-desc" style="text-align: center; max-width: 500px; margin: 0 auto;">One unified platform for logs, metrics, traces, and incidents. No context-switching.</p>
<div class="platform__frame" id="platformFrame">
<div class="platform__topbar">
<span class="platform__dot platform__dot--red"></span>
<span class="platform__dot platform__dot--yellow"></span>
<span class="platform__dot platform__dot--green"></span>
<span class="platform__url">app.signal.dev/dashboard</span>
</div>
<div class="platform__body">
<div class="platform__sidebar">
<div class="platform__sidebar-item platform__sidebar-item--active">
<span class="iconify" data-icon="solar:chart-2-linear" data-width="14"></span> Overview
</div>
<div class="platform__sidebar-item">
<span class="iconify" data-icon="solar:document-text-linear" data-width="14"></span> Logs
</div>
<div class="platform__sidebar-item">
<span class="iconify" data-icon="solar:tuning-2-linear" data-width="14"></span> Metrics
</div>
<div class="platform__sidebar-item">
<span class="iconify" data-icon="solar:routing-2-linear" data-width="14"></span> Traces
</div>
<div class="platform__sidebar-item">
<span class="iconify" data-icon="solar:bell-linear" data-width="14"></span> Alerts
</div>
</div>
<div class="platform__main">
<div class="platform__metrics-row">
<div class="platform__metric-card">
<div class="platform__metric-val">99.98%</div>
<div class="platform__metric-label">Uptime</div>
</div>
<div class="platform__metric-card">
<div class="platform__metric-val" style="color: var(--status-ok);">42ms</div>
<div class="platform__metric-label">P99 Latency</div>
</div>
<div class="platform__metric-card">
<div class="platform__metric-val">2.4B</div>
<div class="platform__metric-label">Events/Day</div>
</div>
<div class="platform__metric-card">
<div class="platform__metric-val" style="color: var(--status-warn);">3</div>
<div class="platform__metric-label">Active Alerts</div>
</div>
</div>
<div class="platform__chart" id="platformChart"></div>
</div>
</div>
<div class="platform__callouts">
<div class="platform__callout platform__callout--1">
<span class="iconify" data-icon="solar:bolt-linear" data-width="12"></span> AI-powered anomaly detection
</div>
<div class="platform__callout platform__callout--2">
<span class="iconify" data-icon="solar:graph-linear" data-width="12"></span> Real-time data streaming
</div>
<div class="platform__callout platform__callout--3">
<span class="iconify" data-icon="solar:shield-check-linear" data-width="12"></span> SOC 2 compliant
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Metrics Bar -->
<div class="metrics" id="metrics">
<div class="container">
<div class="metrics__grid">
<div class="metrics__item">
<span class="metrics__number" data-count="99.9">0</span>
<span class="metrics__label">% Uptime SLA</span>
</div>
<div class="metrics__item">
<span class="metrics__number" data-count="2" data-suffix="B+">0</span>
<span class="metrics__label">Events Processed Daily</span>
</div>
<div class="metrics__item">
<span class="metrics__number" data-count="500" data-suffix="+">0</span>
<span class="metrics__label">Engineering Teams</span>
</div>
<div class="metrics__item">
<span class="metrics__number" data-count="100" data-prefix="<" data-suffix="ms">0</span>
<span class="metrics__label">Query Latency P99</span>
</div>
</div>
</div>
</div>
<!-- How It Works -->
<section class="section section--grid" id="howItWorks">
<div class="container">
<span class="section__bg-number">03</span>
<div class="section-label">How It Works</div>
<h2 class="section-title" style="text-align: center; max-width: 500px; margin: 0 auto 3rem;">Three steps to complete observability.</h2>
<div class="steps__grid" id="stepsGrid">
<div class="step">
<div class="step__connector"><div class="step__connector-fill"></div></div>
<div class="step__icon">
<span class="iconify" data-icon="solar:plug-circle-bold-duotone" data-width="24"></span>
<div class="step__pulse"></div>
</div>
<div class="step__number">Step 01</div>
<h3 class="step__title">Connect</h3>
<p class="step__desc">Install our lightweight agent or use 120+ native integrations. Data flows in minutes, not days.</p>
</div>
<div class="step">
<div class="step__connector"><div class="step__connector-fill"></div></div>
<div class="step__icon">
<span class="iconify" data-icon="solar:eye-bold-duotone" data-width="24"></span>
<div class="step__pulse"></div>
</div>
<div class="step__number">Step 02</div>
<h3 class="step__title">Detect</h3>
<p class="step__desc">AI models analyze patterns across logs, metrics, and traces. Anomalies surface automatically.</p>
</div>
<div class="step">
<div class="step__icon">
<span class="iconify" data-icon="solar:verified-check-bold-duotone" data-width="24"></span>
<div class="step__pulse"></div>
</div>
<div class="step__number">Step 03</div>
<h3 class="step__title">Resolve</h3>
<p class="step__desc">Get AI-generated root cause analysis, runbooks, and one-click remediation. MTTR drops dramatically.</p>
</div>
</div>
</div>
</section>
<!-- Divider -->
<div class="divider"><div class="container"><div class="divider__line"></div></div></div>
<!-- Testimonial -->
<section class="section section--solid" id="testimonial">
<div class="container">
<div class="testimonial">
<span class="testimonial__mark" id="testimonialMark">“</span>
<blockquote class="testimonial__text">SIGNAL cut our mean-time-to-resolution by 73%. The AI root cause analysis is genuinely magic -- it correlates signals we'd never connect manually across our 200+ microservices.</blockquote>
<div class="testimonial__author">
<span class="testimonial__name">Marcus Rivera</span>
<span class="testimonial__role">VP Engineering, Stripe Infrastructure</span>
</div>
</div>
</div>
</section>
<!-- Pricing Preview -->
<section class="section section--dots" id="pricingPreview">
<div class="container">
<span class="section__bg-number">04</span>
<div class="section-label">Pricing</div>
<div style="display: flex; align-items: flex-end; justify-content: space-between; gap: 2rem; margin-bottom: 3rem; flex-wrap: wrap; position: relative; z-index: 1;">
<div>
<h2 class="section-title" style="margin-bottom: 0.5rem;">Simple, transparent pricing.</h2>
<p class="section-desc">Start free. Scale as you grow. No surprises.</p>
</div>
<a href="/pricing" class="btn btn--ghost" style="flex-shrink: 0;">
See Full Pricing
<span class="iconify" data-icon="solar:arrow-right-linear" data-width="14"></span>
</a>
</div>
<div class="pricing-preview__grid" id="pricingGrid">
<div class="pricing-card" id="pricingCard0">
<div class="pricing-card__name">Starter</div>
<div class="pricing-card__price">$29 <span>/mo</span></div>
<p class="pricing-card__desc">For small teams getting started with observability.</p>
<ul class="pricing-card__features">
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> 5 team members</li>
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> 10M events/month</li>
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> 7-day retention</li>
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> Email support</li>
</ul>
<a href="/contact" class="btn btn--ghost">Get Started</a>
</div>
<div class="pricing-card pricing-card--featured" id="pricingCard1">
<div class="pricing-card__popular">Most Popular</div>
<div class="pricing-card__name">Pro</div>
<div class="pricing-card__price">$99 <span>/mo</span></div>
<p class="pricing-card__desc">For growing teams that need deeper insights.</p>
<ul class="pricing-card__features">
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> 25 team members</li>
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> 100M events/month</li>
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> 30-day retention</li>
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> AI root cause analysis</li>
</ul>
<a href="/contact" class="btn btn--primary">Start Free Trial</a>
</div>
<div class="pricing-card" id="pricingCard2">
<div class="pricing-card__name">Enterprise</div>
<div class="pricing-card__price">Custom</div>
<p class="pricing-card__desc">For organizations with advanced security and scale needs.</p>
<ul class="pricing-card__features">
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> Unlimited members</li>
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> Unlimited events</li>
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> Custom retention</li>
<li><span class="iconify" data-icon="solar:check-circle-bold" data-width="16"></span> Dedicated support</li>
</ul>
<a href="/contact" class="btn btn--ghost">Contact Sales</a>
</div>
</div>
</div>
</section>
<!-- CTA -->
<section class="cta" id="cta">
<div class="container">
<h2 class="cta__title">
Start Monitoring<br>
in <span class="cta__title-accent">60 Seconds</span>
</h2>
<p class="cta__subtitle">No credit card required. Free for up to 5 team members and 10M events per month.</p>
<div class="cta__form">
<input type="email" class="cta__input cta__input--glow" placeholder="Enter your work email" aria-label="Email address">
<button class="btn btn--primary">
Get Started
<span class="iconify" data-icon="solar:arrow-right-linear" data-width="14"></span>
</button>
</div>
</div>
</section>
</main>
<!-- Footer -->
<footer class="footer">
<div class="container footer__inner">
<div class="footer__grid">
<div>
<span class="footer__logo footer__logo-shimmer">SIGNAL</span>
<p class="footer__tagline">Real-time observability and incident intelligence for modern engineering teams.</p>
</div>
<div class="footer__col">
<h4>Product</h4>
<a href="/features">Features</a>
<a href="/pricing">Pricing</a>
<a href="/integrations">Integrations</a>
<a href="/changelog">Changelog</a>
</div>
<div class="footer__col">
<h4>Company</h4>
<a href="/about">About</a>
<a href="/blog">Blog</a>
<a href="/contact">Contact</a>
<a href="#">Careers</a>
</div>
<div class="footer__col">
<h4>Legal</h4>
<a href="#">Privacy Policy</a>
<a href="#">Terms of Service</a>
<a href="#">Security</a>
<a href="#">DPA</a>
</div>
</div>
<div class="footer__bottom">
<span>© 2026 SIGNAL. All rights reserved.</span>
<span>SOC 2 Type II Certified</span>
</div>
</div>
</footer>
<!-- Scripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
<script src="https://unpkg.com/lenis@1.1.18/dist/lenis.min.js"></script>
<script>
(() => {
'use strict';
gsap.registerPlugin(ScrollTrigger);
// -- Lenis Smooth Scroll -----------------------------------
const lenis = new Lenis({ lerp: 0.12, wheelMultiplier: 1.0, smoothWheel: true });
lenis.on('scroll', () => ScrollTrigger.update());
gsap.ticker.add((t) => lenis.raf(t * 1000));
gsap.ticker.lagSmoothing(0);
// -- Navigation --------------------------------------------
const nav = document.getElementById('nav');
const toggle = document.getElementById('navToggle');
const menu = document.getElementById('mobileMenu');
const closeBtn = document.getElementById('mobileClose');
const mobileLinks = document.querySelectorAll('[data-mobile-link]');
window.addEventListener('scroll', () => {
nav.classList.toggle('is-scrolled', window.scrollY > 60);
}, { passive: true });
toggle.addEventListener('click', () => menu.classList.add('is-open'));
closeBtn.addEventListener('click', () => menu.classList.remove('is-open'));
mobileLinks.forEach(l => l.addEventListener('click', () => menu.classList.remove('is-open')));
// -- Infinite Marquee (velocity-reactive) ------------------
class InfiniteMarquee {
constructor(el) {
this.el = el;
this.track = el.querySelector('.marquee__track');
this.contents = el.querySelectorAll('.marquee__content');
this.baseSpeed = 0.6;
this.currentSpeed = this.baseSpeed;
this.targetSpeed = this.baseSpeed;
this.position = 0;
this.singleWidth = 0;
this.scrollVelocity = 0;
this.lastScrollY = window.scrollY;
this.init();
}
init() {
this.measure();
window.addEventListener('resize', () => this.measure());
window.addEventListener('scroll', () => {
const vel = Math.abs(window.scrollY - this.lastScrollY);
this.scrollVelocity = vel;
this.lastScrollY = window.scrollY;
}, { passive: true });
this.animate();
}
measure() { this.singleWidth = this.contents[0].offsetWidth; }
animate() {
const boost = Math.min(this.scrollVelocity * 0.5, 14);
this.targetSpeed = this.baseSpeed + boost;
this.currentSpeed += (this.targetSpeed - this.currentSpeed) * 0.08;
this.scrollVelocity *= 0.92;
this.position -= this.currentSpeed;
if (Math.abs(this.position) >= this.singleWidth) this.position += this.singleWidth;
this.track.style.transform = `translate3d(${this.position}px, 0, 0)`;
requestAnimationFrame(() => this.animate());
}
}
const marqueeEl = document.getElementById('marquee');
if (marqueeEl) new InfiniteMarquee(marqueeEl);
// -- Simplex Noise 3D (CPU port for vertex displacement) -----
const SimplexNoise3D = (() => {
const F3 = 1.0 / 3.0, G3 = 1.0 / 6.0;
const grad3 = [
[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],
[1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],
[0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]
];
const p = [];
for (let i = 0; i < 256; i++) p[i] = (Math.random() * 256) | 0;
const perm = new Array(512), permMod12 = new Array(512);
for (let i = 0; i < 512; i++) { perm[i] = p[i & 255]; permMod12[i] = perm[i] % 12; }
function dot3(g, x, y, z) { return g[0]*x + g[1]*y + g[2]*z; }
return function noise(x, y, z) {
const s = (x + y + z) * F3;
const i = Math.floor(x + s), j = Math.floor(y + s), k = Math.floor(z + s);
const t = (i + j + k) * G3;
const X0 = i - t, Y0 = j - t, Z0 = k - t;
const x0 = x - X0, y0 = y - Y0, z0 = z - Z0;
let i1, j1, k1, i2, j2, k2;
if (x0 >= y0) {
if (y0 >= z0) { i1=1;j1=0;k1=0;i2=1;j2=1;k2=0; }
else if (x0 >= z0) { i1=1;j1=0;k1=0;i2=1;j2=0;k2=1; }
else { i1=0;j1=0;k1=1;i2=1;j2=0;k2=1; }
} else {
if (y0 < z0) { i1=0;j1=0;k1=1;i2=0;j2=1;k2=1; }
else if (x0 < z0) { i1=0;j1=1;k1=0;i2=0;j2=1;k2=1; }
else { i1=0;j1=1;k1=0;i2=1;j2=1;k2=0; }
}
const x1 = x0-i1+G3, y1 = y0-j1+G3, z1 = z0-k1+G3;
const x2 = x0-i2+2*G3, y2 = y0-j2+2*G3, z2 = z0-k2+2*G3;
const x3 = x0-1+3*G3, y3 = y0-1+3*G3, z3 = z0-1+3*G3;
const ii = i & 255, jj = j & 255, kk = k & 255;
const gi0 = permMod12[ii + perm[jj + perm[kk]]];
const gi1 = permMod12[ii+i1 + perm[jj+j1 + perm[kk+k1]]];
const gi2 = permMod12[ii+i2 + perm[jj+j2 + perm[kk+k2]]];
const gi3 = permMod12[ii+1 + perm[jj+1 + perm[kk+1]]];
let n0, n1, n2, n3;
let t0 = 0.6 - x0*x0 - y0*y0 - z0*z0;
n0 = t0 < 0 ? 0 : (t0 *= t0, t0 * t0 * dot3(grad3[gi0], x0, y0, z0));
let t1 = 0.6 - x1*x1 - y1*y1 - z1*z1;
n1 = t1 < 0 ? 0 : (t1 *= t1, t1 * t1 * dot3(grad3[gi1], x1, y1, z1));
let t2 = 0.6 - x2*x2 - y2*y2 - z2*z2;
n2 = t2 < 0 ? 0 : (t2 *= t2, t2 * t2 * dot3(grad3[gi2], x2, y2, z2));
let t3 = 0.6 - x3*x3 - y3*y3 - z3*z3;
n3 = t3 < 0 ? 0 : (t3 *= t3, t3 * t3 * dot3(grad3[gi3], x3, y3, z3));
return 32.0 * (n0 + n1 + n2 + n3);
};
})();
// -- Three.js Scene (Editor-Compatible PBR) -----------------
class SignalScene {
constructor() {
this.canvas = document.getElementById('webgl');
this.width = window.innerWidth;
this.height = window.innerHeight;
this.mouse = { x: 0, y: 0, tx: 0, ty: 0 };
this.scroll = { current: 0, target: 0 };
this.init();
this.createLights();
this.createBlob();
this.createWireframe();
this.createParticles();
this.createFloatingShapes();
this.setupEvents();
this.animate();
}
init() {
this.scene = new THREE.Scene();
this.scene.name = 'SignalScene';
this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 0.1, 100);
this.camera.position.set(0, 0, 5.5);
this.camera.name = 'MainCamera';
this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas, antialias: true, alpha: true });
this.renderer.setSize(this.width, this.height);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
this.clock = new THREE.Clock();
}
createLights() {
const ambLight = new THREE.AmbientLight(0xffeedd, 0.7);
ambLight.name = 'AmbientFill';
this.scene.add(ambLight);
const dirLight = new THREE.DirectionalLight(0xffd5b0, 0.25);
dirLight.position.set(2, 3, 5);
dirLight.name = 'MainDirectionalLight';
this.scene.add(dirLight);
const dirLight2 = new THREE.DirectionalLight(0xc9835a, 0.1);
dirLight2.position.set(-3, -1, 2);
dirLight2.name = 'FillDirectionalLight';
this.scene.add(dirLight2);
const pointLight = new THREE.PointLight(0xc9835a, 0.15, 20);
pointLight.position.set(-2, 1, 3);
pointLight.name = 'TerracottaAccentLight';
this.scene.add(pointLight);
this.pointLight = pointLight;
}
createBlob() {
const geo = new THREE.IcosahedronGeometry(1.8, 18);
const posAttr = geo.getAttribute('position');
const normAttr = geo.getAttribute('normal');
this.blobOrigPos = new Float32Array(posAttr.array);
this.blobOrigNorm = new Float32Array(normAttr.array);
const colorArr = new Float32Array(posAttr.count * 3);
geo.setAttribute('color', new THREE.BufferAttribute(colorArr, 3));
this.colorDark = new THREE.Color('#1e1008');
this.colorMid = new THREE.Color('#c9835a');
this.colorLight = new THREE.Color('#f0ede6');
this.blobMat = new THREE.MeshPhysicalMaterial({
vertexColors: true,
emissive: 0x150a03,
emissiveIntensity: 0.15,
metalness: 0.02,
roughness: 0.65,
clearcoat: 0.25,
clearcoatRoughness: 0.3,
transparent: true,
opacity: 0.80,
depthWrite: false,
});
this.blob = new THREE.Mesh(geo, this.blobMat);
this.blob.name = 'MainSphere';
this.blob.position.set(2.2, 0.2, 0);
this.scene.add(this.blob);
}
createWireframe() {
const geo = new THREE.IcosahedronGeometry(1.84, 6);
const mat = new THREE.MeshBasicMaterial({
color: new THREE.Color('#c9835a'), wireframe: true,
transparent: true, opacity: 0.05, depthWrite: false,
});
this.wireframe = new THREE.Mesh(geo, mat);
this.wireframe.name = 'WireframeOverlay';
this.wireframe.position.copy(this.blob.position);
this.scene.add(this.wireframe);
}
createParticles() {
const count = 1800;
const positions = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
const r = 6 + Math.random() * 12;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1);
positions[i * 3] = r * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta);
positions[i * 3 + 2] = r * Math.cos(phi);
}
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const mat = new THREE.PointsMaterial({
size: 0.5, color: new THREE.Color('#c9835a'),
transparent: true, opacity: 0.35, sizeAttenuation: true, depthWrite: false,
});
this.particles = new THREE.Points(geo, mat);
this.particles.name = 'ParticleField';
this.scene.add(this.particles);
}
createFloatingShapes() {
this.floaters = [];
const shapes = [
new THREE.OctahedronGeometry(0.12, 0),
new THREE.TetrahedronGeometry(0.1, 0),
new THREE.IcosahedronGeometry(0.08, 0),
];
const mat = new THREE.MeshBasicMaterial({
color: new THREE.Color('#c9835a'), wireframe: true,
transparent: true, opacity: 0.15,
});
for (let i = 0; i < 14; i++) {
const mesh = new THREE.Mesh(shapes[i % shapes.length], mat.clone());
mesh.name = 'FloatingShape_' + i;
mesh.position.set(
(Math.random() - 0.5) * 12,
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 6 - 2
);
mesh.userData = {
speedX: (Math.random() - 0.5) * 0.3,
speedY: (Math.random() - 0.5) * 0.3,
origY: mesh.position.y,
phase: Math.random() * Math.PI * 2,
};
this.floaters.push(mesh);
this.scene.add(mesh);
}
}
setupEvents() {
window.addEventListener('mousemove', (e) => {
this.mouse.tx = (e.clientX / this.width) * 2 - 1;
this.mouse.ty = -(e.clientY / this.height) * 2 + 1;
});
window.addEventListener('resize', () => {
this.width = window.innerWidth;
this.height = window.innerHeight;
this.camera.aspect = this.width / this.height;
this.camera.updateProjectionMatrix();
this.renderer.setSize(this.width, this.height);
});
ScrollTrigger.create({
trigger: document.body, start: 'top top', end: 'bottom bottom',
onUpdate: (self) => { this.scroll.target = self.progress; },
});
}
displaceBlob(time, intensity, speed, mx, my) {
const geo = this.blob.geometry;
const posArr = geo.getAttribute('position').array;
const colorArr = geo.getAttribute('color').array;
const origPos = this.blobOrigPos;
const origNorm = this.blobOrigNorm;
const count = geo.getAttribute('position').count;
const noise = SimplexNoise3D;
const ts = time * speed;
const ts06 = ts * 0.6;
const ts03 = ts * 0.3;
const i50 = intensity * 0.5;
const i25 = intensity * 0.25;
const th = time * 0.5;
const t3 = time * 0.3;
const dr = this.colorDark.r, dg = this.colorDark.g, db = this.colorDark.b;
const mr = this.colorMid.r - dr, mg = this.colorMid.g - dg, mb = this.colorMid.b - db;
const cr = this.colorLight.r, cg = this.colorLight.g, cb = this.colorLight.b;
const tr = this.colorMid.r, tg = this.colorMid.g, tb = this.colorMid.b;
const camPos = this.camera.position;
const blobPos = this.blob.position;
const cdx0 = camPos.x - blobPos.x;
const cdy0 = camPos.y - blobPos.y;
const cdz0 = camPos.z - blobPos.z;
const camLen = Math.sqrt(cdx0 * cdx0 + cdy0 * cdy0 + cdz0 * cdz0);
const cdx = cdx0 / camLen, cdy = cdy0 / camLen, cdz = cdz0 / camLen;
const qx = -this.blob.quaternion.x;
const qy = -this.blob.quaternion.y;
const qz = -this.blob.quaternion.z;
const qw = this.blob.quaternion.w;
const tx = 2 * (qy * cdz - qz * cdy);
const ty = 2 * (qz * cdx - qx * cdz);
const tz = 2 * (qx * cdy - qy * cdx);
const ovdx = cdx + qw * tx + (qy * tz - qz * ty);
const ovdy = cdy + qw * ty + (qz * tx - qx * tz);
const ovdz = cdz + qw * tz + (qx * ty - qy * tx);
for (let i = 0; i < count; i++) {
const i3 = i * 3;
const ox = origPos[i3], oy = origPos[i3+1], oz = origPos[i3+2];
const nx = origNorm[i3], ny = origNorm[i3+1], nz = origNorm[i3+2];
const n1 = noise(ox*0.8+ts, oy*0.8+ts, oz*0.8+ts) * intensity;
const n2 = noise(ox*1.6+ts06, oy*1.6+ts06, oz*1.6+ts06) * i50;
const n3 = noise(ox*3.2+ts03, oy*3.2+ts03, oz*3.2+ts03) * i25;
let disp = n1 + n2 + n3;
disp += mx * Math.sin(oy * 2.5 + th) * 0.1;
disp += my * Math.cos(ox * 2.5 + t3) * 0.1;
posArr[i3] = ox + nx * disp;
posArr[i3 + 1] = oy + ny * disp;
posArr[i3 + 2] = oz + nz * disp;
const ct = disp < -0.15 ? 0 : disp > 0.35 ? 1 : (disp + 0.15) * 2;
let colR = dr + mr * ct;
let colG = dg + mg * ct;
let colB = db + mb * ct;
const dot = nx * ovdx + ny * ovdy + nz * ovdz;
const fBase = 1.0 - Math.abs(dot);
const fresnel = fBase * fBase * fBase;
const f35 = fresnel * 0.35;
const f1 = 1.0 - f35;
colR = colR * f1 + cr * f35;
colG = colG * f1 + cg * f35;
colB = colB * f1 + cb * f35;
const f25 = fresnel * 0.25;
colR += tr * f25;
colG += tg * f25;
colB += tb * f25;
const fakeAlpha = 0.82 + fresnel * 0.18;
colR *= fakeAlpha;
colG *= fakeAlpha;
colB *= fakeAlpha;
colorArr[i3] = colR;
colorArr[i3 + 1] = colG;
colorArr[i3 + 2] = colB;
}
geo.getAttribute('position').needsUpdate = true;
geo.getAttribute('color').needsUpdate = true;
}
animate() {
requestAnimationFrame(() => this.animate());
const t = this.clock.getElapsedTime();
this.mouse.x += (this.mouse.tx - this.mouse.x) * 0.04;
this.mouse.y += (this.mouse.ty - this.mouse.y) * 0.04;
this.scroll.current += (this.scroll.target - this.scroll.current) * 0.06;
const sp = this.scroll.current;
const intensity = 0.38 + Math.sin(sp * Math.PI * 2) * 0.12;
this.displaceBlob(t, intensity, 0.22, this.mouse.x, this.mouse.y);
this.blob.position.x = 2.2 - sp * 4.4;
this.blob.position.y = 0.2 + Math.sin(sp * Math.PI) * 0.8;
const scale = 1.0 + Math.sin(sp * Math.PI) * 0.25;
this.blob.scale.setScalar(scale);
this.blob.rotation.x = t * 0.06 + this.mouse.y * 0.3;
this.blob.rotation.y = t * 0.09 + this.mouse.x * 0.3;
const opacityCurve = 1.0 - Math.sin(sp * Math.PI) * 0.3;
this.blobMat.opacity = 0.80 * opacityCurve;
this.wireframe.position.copy(this.blob.position);
this.wireframe.rotation.copy(this.blob.rotation);
this.wireframe.scale.copy(this.blob.scale);
this.wireframe.material.opacity = 0.05 * opacityCurve;
this.pointLight.position.set(
this.blob.position.x - 2,
this.blob.position.y + 1,
3
);
this.particles.rotation.y = t * 0.015;
this.particles.rotation.x = t * 0.008 + sp * 0.3;
this.floaters.forEach((f) => {
const d = f.userData;
f.rotation.x = t * d.speedX;
f.rotation.y = t * d.speedY;
f.position.y = d.origY + Math.sin(t * 0.5 + d.phase) * 0.3;
});
this.renderer.render(this.scene, this.camera);
}
}
if (typeof THREE !== 'undefined') new SignalScene();
// -- Feature Card Spotlight --------------------------------
document.querySelectorAll('.feature-card').forEach(card => {
card.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
card.style.setProperty('--mx', (e.clientX - rect.left) + 'px');
card.style.setProperty('--my', (e.clientY - rect.top) + 'px');
});
});
// -- Platform Chart Bars -----------------------------------
function createChartBars() {
const chart = document.getElementById('platformChart');
if (!chart) return;
for (let i = 0; i < 24; i++) {
const bar = document.createElement('div');
bar.className = 'platform__bar';
bar.style.height = (20 + Math.random() * 80) + '%';
bar.style.opacity = 0.3 + Math.random() * 0.4;
chart.appendChild(bar);
}
}
createChartBars();
// -- Magnetic Buttons --------------------------------------
function initMagneticButtons() {
if (matchMedia('(hover: none)').matches) return;
document.querySelectorAll('.btn').forEach(btn => {
btn.addEventListener('mousemove', (e) => {
const rect = btn.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
gsap.to(btn, { x: x * 0.15, y: y * 0.15, duration: 0.3, ease: 'power2.out' });
});
btn.addEventListener('mouseleave', () => {
gsap.to(btn, { x: 0, y: 0, duration: 0.5, ease: 'elastic.out(1, 0.4)' });
});
});
}
// -- Pricing Card Tilt -------------------------------------
function initPricingTilt() {
if (matchMedia('(hover: none)').matches) return;
document.querySelectorAll('.pricing-card').forEach(card => {
card.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width - 0.5;
const y = (e.clientY - rect.top) / rect.height - 0.5;
gsap.to(card, {
rotateY: x * 8, rotateX: -y * 8,
duration: 0.4, ease: 'power2.out',
transformPerspective: 600,
});
});
card.addEventListener('mouseleave', () => {
gsap.to(card, { rotateX: 0, rotateY: 0, duration: 0.6, ease: 'elastic.out(1, 0.5)' });
});
});
}
// -- Section Animations ------------------------------------
function initAnimations() {
// Hero entrance — fast and punchy
const heroTl = gsap.timeline({ delay: 0.5 });
heroTl
.fromTo('.hero__badge', { y: 15, opacity: 0 }, { y: 0, opacity: 1, duration: 0.4, ease: 'power3.out' })
.fromTo('.hero__title', { y: 30, opacity: 0 }, { y: 0, opacity: 1, duration: 0.6, ease: 'power3.out' }, '-=0.15')
.fromTo('.hero__subtitle', { y: 20, opacity: 0 }, { y: 0, opacity: 1, duration: 0.45, ease: 'power3.out' }, '-=0.25')
.fromTo('.hero__actions .btn--primary', { y: 15, opacity: 0 }, { y: 0, opacity: 1, duration: 0.4, ease: 'power3.out' }, '-=0.2')
.fromTo('.hero__actions .btn--ghost', { y: 15, opacity: 0 }, { y: 0, opacity: 1, duration: 0.4, ease: 'power3.out' }, '-=0.3');
// Feature cards -- spotlight entrance
gsap.utils.toArray('.feature-card').forEach((card, i) => {
gsap.fromTo(card,
{ y: 40, opacity: 0 },
{
y: 0, opacity: 1, duration: 0.8, delay: i * 0.1,
ease: 'power3.out',
scrollTrigger: { trigger: card, start: 'top 88%' },
}
);
});
// Platform preview -- clip-path reveal
gsap.fromTo('#platformFrame',
{ clipPath: 'inset(20% 20% 20% 20%)' },
{
clipPath: 'inset(0% 0% 0% 0%)', duration: 1.4,
ease: 'power3.inOut',
scrollTrigger: { trigger: '#platformFrame', start: 'top 80%' },
}
);
gsap.utils.toArray('.platform__callout').forEach((el, i) => {
gsap.fromTo(el,
{ opacity: 0, x: i === 2 ? -20 : 20 },
{
opacity: 1, x: i === 2 ? '-100%' : '100%',
duration: 0.8, delay: 0.8 + i * 0.2,
ease: 'power3.out',
scrollTrigger: { trigger: '#platformFrame', start: 'top 75%' },
}
);
});
// Metrics -- odometer counter
gsap.utils.toArray('.metrics__number').forEach(el => {
const endVal = parseFloat(el.dataset.count);
const suffix = el.dataset.suffix || '';
const prefix = el.dataset.prefix || '';
const isFloat = endVal % 1 !== 0;
ScrollTrigger.create({
trigger: el, start: 'top 90%', once: true,
onEnter: () => {
gsap.to({ val: 0 }, {
val: endVal, duration: 2.2, ease: 'power2.out',
onUpdate: function() {
const v = this.targets()[0].val;
el.textContent = prefix + (isFloat ? v.toFixed(1) : Math.round(v)) + suffix;
},
});
},
});
});
// Steps -- progressive fill (connectors stay visible behind icons)
const stepsGrid = document.getElementById('stepsGrid');
if (stepsGrid) {
// Set initial states: connectors visible, content hidden
gsap.set(stepsGrid.querySelectorAll('.step__icon, .step__number, .step__title, .step__desc'), { opacity: 0, y: 30 });
gsap.set(stepsGrid.querySelectorAll('.step__connector'), { opacity: 1 });
ScrollTrigger.create({
trigger: stepsGrid, start: 'top 75%', once: true,
onEnter: () => {
const steps = stepsGrid.querySelectorAll('.step');
steps.forEach((step, i) => {
const delay = i * 0.3;
// Animate icon first
gsap.fromTo(step.querySelector('.step__icon'),
{ y: 30, opacity: 0, scale: 0.8 },
{ y: 0, opacity: 1, scale: 1, duration: 0.6, delay: delay, ease: 'back.out(1.4)' }
);
// Then text content
gsap.fromTo(step.querySelectorAll('.step__number, .step__title, .step__desc'),
{ y: 20, opacity: 0 },
{ y: 0, opacity: 1, duration: 0.6, delay: delay + 0.15, ease: 'power3.out', stagger: 0.06 }
);
// Activate connector fill after icon lands
setTimeout(() => step.classList.add('is-active'), (delay + 0.4) * 1000);
});
},
});
}
// Testimonial -- parallax quote mark
gsap.fromTo('.testimonial__mark',
{ y: 0 },
{
y: -60, ease: 'none',
scrollTrigger: {
trigger: '.testimonial', start: 'top bottom', end: 'bottom top', scrub: 1.5,
},
}
);
const testTl = gsap.timeline({
scrollTrigger: { trigger: '.testimonial', start: 'top 78%' },
});
testTl
.fromTo('.testimonial__mark', { scale: 0.6, opacity: 0 }, { scale: 1, opacity: 0.1, duration: 0.8, ease: 'back.out(1.4)' })
.fromTo('.testimonial__text', { y: 25, opacity: 0, rotateX: -2 }, { y: 0, opacity: 1, rotateX: 0, duration: 1, ease: 'power3.out' }, '-=0.3')
.fromTo('.testimonial__author', { y: 15, opacity: 0 }, { y: 0, opacity: 1, duration: 0.6, ease: 'power3.out' }, '-=0.4');
// Pricing preview -- entrance
gsap.utils.toArray('.pricing-card').forEach((card, i) => {
gsap.fromTo(card,
{ y: 50, opacity: 0 },
{
y: 0, opacity: 1, duration: 0.9, delay: i * 0.15,
ease: 'power3.out',
scrollTrigger: { trigger: '.pricing-preview__grid', start: 'top 82%' },
}
);
});
// CTA entrance
gsap.fromTo('.cta__title',
{ y: 60, opacity: 0 },
{ y: 0, opacity: 1, duration: 1, ease: 'power4.out',
scrollTrigger: { trigger: '.cta', start: 'top 75%' } }
);
gsap.fromTo('.cta__subtitle',
{ y: 30, opacity: 0 },
{ y: 0, opacity: 1, duration: 0.8, delay: 0.2, ease: 'power3.out',
scrollTrigger: { trigger: '.cta', start: 'top 75%' } }
);
gsap.fromTo('.cta__form',
{ y: 20, opacity: 0 },
{ y: 0, opacity: 1, duration: 0.7, delay: 0.4, ease: 'power3.out',
scrollTrigger: { trigger: '.cta', start: 'top 70%' } }
);
// Footer
gsap.utils.toArray('.footer__grid > *').forEach((el, i) => {
gsap.fromTo(el,
{ y: 25, opacity: 0 },
{ y: 0, opacity: 1, duration: 0.7, delay: i * 0.1, ease: 'power2.out',
scrollTrigger: { trigger: '.footer', start: 'top 92%' } }
);
});
// Section labels -- draw accent line
gsap.utils.toArray('.section-label').forEach(label => {
gsap.fromTo(label,
{ opacity: 0, x: -20 },
{ opacity: 1, x: 0, duration: 0.8, ease: 'power3.out',
scrollTrigger: { trigger: label, start: 'top 90%' } }
);
});
}
// -- Parallax (selective — bg numbers, titles, testimonial, CTA) --
function initParallax() {
// --- Background numbers (deep layer, slow drift) ---
gsap.utils.toArray('.section__bg-number').forEach((el) => {
gsap.fromTo(el,
{ y: 0 },
{
y: -120, ease: 'none',
scrollTrigger: {
trigger: el.parentElement,
start: 'top bottom', end: 'bottom top', scrub: 1,
},
}
);
});
// --- Testimonial layers (quote mark has its own in initAnimations) ---
gsap.fromTo('.testimonial__text',
{ y: 45 },
{ y: -25, ease: 'none',
scrollTrigger: { trigger: '.testimonial__text', start: 'top bottom', end: 'bottom top', scrub: 1.2 } }
);
gsap.fromTo('.testimonial__author',
{ y: 25 },
{ y: -10, ease: 'none',
scrollTrigger: { trigger: '.testimonial__author', start: 'top bottom', end: 'bottom top', scrub: 1.8 } }
);
// --- CTA (title fastest, form slowest — layered depth) ---
gsap.fromTo('.cta__title',
{ y: 70 },
{ y: -35, ease: 'none',
scrollTrigger: { trigger: '.cta__title', start: 'top bottom', end: 'bottom top', scrub: 0.8 } }
);
gsap.fromTo('.cta__subtitle',
{ y: 45 },
{ y: -20, ease: 'none',
scrollTrigger: { trigger: '.cta__subtitle', start: 'top bottom', end: 'bottom top', scrub: 1.2 } }
);
gsap.fromTo('.cta__form',
{ y: 30 },
{ y: -10, ease: 'none',
scrollTrigger: { trigger: '.cta__form', start: 'top bottom', end: 'bottom top', scrub: 1.8 } }
);
}
// -- Scroll Progress Bar -----------------------------------
function initScrollProgress() {
const bar = document.getElementById('scrollProgress');
if (!bar) return;
ScrollTrigger.create({
trigger: document.body,
start: 'top top',
end: 'bottom bottom',
onUpdate: (self) => {
bar.style.width = (self.progress * 100) + '%';
},
});
}
// -- Boot --------------------------------------------------
document.addEventListener('DOMContentLoaded', () => {
requestAnimationFrame(() => {
initAnimations();
initMagneticButtons();
initPricingTilt();
initParallax();
initScrollProgress();
});
});
})();
</script>
</body></html>