Sysdig 2026 Cloud-Native Security and Usage Report
Human-driven security is reaching its limits. See the data behind the shift to faster, AI-driven defense, and why a new model is emerging.
tag */
/* ============================================================
* Sysdig — Headless Cloud Security launch page
* Vanilla JS for: scroll-driven line drawing, sticky green bar,
* hover accordion, and reveal-on-scroll.
* Everything is namespaced under the .headless-launch root.
* ============================================================ */
(function () {
'use strict';
var root = document.querySelector('.headless-launch');
if (!root) return;
/* --------------------------------------------------------
* Helpers
* -------------------------------------------------------- */
function clamp(n, min, max) {
return n < min ? min : n > max ? max : n;
}
// requestAnimationFrame throttle so scroll handlers stay smooth
function rafThrottle(fn) {
var queued = false;
return function () {
if (queued) return;
queued = true;
var args = arguments;
var ctx = this;
window.requestAnimationFrame(function () {
queued = false;
fn.apply(ctx, args);
});
};
}
/* --------------------------------------------------------
* 1) Dashed grid — draw in once when the founder section
* enters the viewport. After that the lines stay fully
* drawn so the static state matches the mockup.
* -------------------------------------------------------- */
var letterSection = root.querySelector('.hl-letter');
if (letterSection) {
if ('IntersectionObserver' in window) {
var lineIO = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
letterSection.classList.add('is-drawn');
lineIO.unobserve(entry.target);
}
});
}, { threshold: 0.05, rootMargin: '0px 0px -8% 0px' });
lineIO.observe(letterSection);
} else {
letterSection.classList.add('is-drawn');
}
}
function updateLineFill() {
// Now only used to keep the green bar in sync on scroll/resize.
if (!letterSection) return;
var rect = letterSection.getBoundingClientRect();
var vh = window.innerHeight || document.documentElement.clientHeight;
updateGreenBar(rect, vh);
}
/* --------------------------------------------------------
* 2) Sticky green bar that "scrolls on top of" the dashed line
* The bar lives inside .hl-vline-host — a zero-width column
* sharing the x-position of one of the dashed verticals.
* We translate the bar on Y so it tracks the viewport while
* staying inside the line's bounds, overlaying the dashes.
* -------------------------------------------------------- */
var greenBar = root.querySelector('#hl-greenbar');
var greenHost = root.querySelector('.hl-vline-host');
function updateGreenBar(letterRect, vh) {
if (!greenBar || !greenHost) return;
var lineRect = greenHost.getBoundingClientRect();
var lineHeight = lineRect.height;
var barHeight = greenBar.offsetHeight || 110;
// Where in the viewport we want the bar to "ride".
// 38% from the top of the viewport reads naturally with the eye.
var anchor = vh * 0.38;
// Position relative to the top of the line.
var y = anchor - lineRect.top;
// Clamp so the bar never escapes the line.
y = clamp(y, 0, Math.max(0, lineHeight - barHeight));
greenBar.style.setProperty('--gy', y.toFixed(1) + 'px');
// Hide the bar entirely when the section isn't on screen.
if (lineRect.bottom < 0 || lineRect.top > vh) {
greenBar.style.opacity = '0';
} else {
greenBar.style.opacity = '1';
}
}
/* --------------------------------------------------------
* 3) Hover accordion for the resources strip
* On hover (or focus / tap), the targeted card gets the
* .hl-card--active class and others lose it. Defaults to
* the first card so the page looks intentional at rest.
* -------------------------------------------------------- */
var accordion = root.querySelector('#hl-accordion');
if (accordion) {
var cards = Array.prototype.slice.call(
accordion.querySelectorAll('.hl-card')
);
var defaultIdx = 0;
var activeIdx = defaultIdx;
function setActive(idx) {
if (idx === activeIdx) return;
activeIdx = idx;
for (var i = 0; i < cards.length; i++) {
if (i === idx) cards[i].classList.add('hl-card--active');
else cards[i].classList.remove('hl-card--active');
}
}
cards.forEach(function (card, idx) {
card.addEventListener('mouseenter', function () { setActive(idx); });
card.addEventListener('focusin', function () { setActive(idx); });
// tap on touch devices
card.addEventListener('click', function (e) {
// don't swallow the inner pill's link
if (e.target.closest('.hl-pill')) return;
setActive(idx);
});
});
// When the cursor leaves the whole strip, drift back to default.
accordion.addEventListener('mouseleave', function () {
setActive(defaultIdx);
});
}
/* --------------------------------------------------------
* 4) Reveal-on-scroll for headline / signature / cta
* Adds a subtle fade-up so the page feels like it's being
* assembled rather than dumped on screen.
* -------------------------------------------------------- */
var revealTargets = root.querySelectorAll(
'.hl-banner__line, .hl-letter__quotemark, ' +
'.hl-letter__person, .hl-letter__label, .hl-letter__signature, ' +
'.hl-letter__cta, .hl-letter__body p'
);
for (var r = 0; r < revealTargets.length; r++) {
revealTargets[r].classList.add('hl-reveal');
}
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add('is-in');
io.unobserve(entry.target);
}
});
}, { threshold: 0.15, rootMargin: '0px 0px -8% 0px' });
for (var t = 0; t < revealTargets.length; t++) io.observe(revealTargets[t]);
} else {
// fallback: just show everything
for (var f = 0; f < revealTargets.length; f++) {
revealTargets[f].classList.add('is-in');
}
}
/* --------------------------------------------------------
* 4b) Green paint-in
* - Founder quote panel wipes in when its viewport
* intersection crosses ~45%.
* - "WITHOUT THE INTERFACE" highlighter wipes in the
* moment the user starts to scroll (the banner is
* already on-screen below the 75vh hero, so we want a
* deliberate scroll-driven reveal, not an idle one).
* -------------------------------------------------------- */
var quoteEl = root.querySelector('.hl-letter__quote');
if (quoteEl) {
if ('IntersectionObserver' in window) {
var quoteIO = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add('is-painted');
quoteIO.unobserve(entry.target);
}
});
}, { threshold: 0.45, rootMargin: '0px 0px -10% 0px' });
quoteIO.observe(quoteEl);
} else {
quoteEl.classList.add('is-painted');
}
}
var markEls = root.querySelectorAll('.hl-mark');
if (markEls.length) {
var paintMarks = function () {
for (var m = 0; m < markEls.length; m++) {
markEls[m].classList.add('is-painted');
}
window.removeEventListener('scroll', paintMarks);
};
if ((window.scrollY || window.pageYOffset || 0) > 0) {
// Page already scrolled (e.g. after a soft-reload) — paint immediately.
paintMarks();
} else {
window.addEventListener('scroll', paintMarks, { passive: true, once: true });
}
}
/* --------------------------------------------------------
* 5) Hero pre-roll — "Introducing" types in, then we cut to
* the video. After the video, the eyebrow / title / CTA
* stagger in (existing behaviour, untouched).
* -------------------------------------------------------- */
var heroVideo = root.querySelector('#hl-hero-video');
var heroEl = root.querySelector('.hl-hero');
var heroIntroEl = root.querySelector('#hl-hero-intro');
var heroCopyEls = root.querySelectorAll(
'.hl-hero__eyebrow, .hl-hero__title, .hl-hero__cta'
);
/* Type "SYSDIG HEADLESS CLOUD SECURITY" character-by-character into
the three pre/accent/post sub-spans of the eyebrow. The accent
span keeps its lumin color (CSS), so "HEADLESS" types in green
between the white "SYSDIG" and " CLOUD SECURITY". The cursor
element is the next sibling of the post span, so as each character
is appended the cursor shifts right with it — same effect as the
"Introducing" pre-roll. */
function typeEyebrow() {
var preEl = root.querySelector('.hl-hero__eyebrow-pre');
var accentEl = root.querySelector('.hl-hero__eyebrow-accent');
var postEl = root.querySelector('.hl-hero__eyebrow-post');
if (!preEl || !accentEl || !postEl) return;
var preText = 'Sysdig ';
var accentText = 'Headless';
var postText = ' Cloud Security';
// Slightly faster than the intro (110ms): the eyebrow is much
// longer (30 chars vs 11), so a tighter cadence keeps the total
// typing duration under 2s and stays in step with the staggered
// reveal of the title + CTAs below it.
var charDelay = 65;
var prefersReducedMotion =
window.matchMedia &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (prefersReducedMotion) {
preEl.textContent = preText;
accentEl.textContent = accentText;
postEl.textContent = postText;
return;
}
function fillSegment(targetEl, text, idx, onDone) {
if (idx >= text.length) {
if (onDone) onDone();
return;
}
targetEl.textContent += text.charAt(idx);
window.setTimeout(function () {
fillSegment(targetEl, text, idx + 1, onDone);
}, charDelay);
}
fillSegment(preEl, preText, 0, function () {
fillSegment(accentEl, accentText, 0, function () {
fillSegment(postEl, postText, 0);
});
});
}
var heroRevealed = false;
function revealHeroCopy() {
if (heroRevealed) return;
heroRevealed = true;
if (heroVideo) {
heroVideo.classList.remove('is-ready');
heroVideo.classList.add('is-done');
}
// Wait until the video has fully faded out, then:
// 1. Reveal all hero copy (CSS transition-delays stagger them).
// 2. Surface the cursor immediately (.is-cursor-on) so it blinks
// throughout the eyebrow's per-character typing — not just
// after, like the old clip-path version.
// 3. Kick off the JS-driven character typing into the eyebrow.
window.setTimeout(function () {
for (var i = 0; i < heroCopyEls.length; i++) {
heroCopyEls[i].classList.add('is-revealed');
}
if (heroEl) heroEl.classList.add('is-cursor-on');
typeEyebrow();
}, 700);
}
function startVideoSequence() {
if (!heroVideo) {
revealHeroCopy();
return;
}
var fadeInVideo = function () { heroVideo.classList.add('is-ready'); };
if (heroVideo.readyState >= 2) {
fadeInVideo();
} else {
heroVideo.addEventListener('loadeddata', fadeInVideo, { once: true });
}
heroVideo.addEventListener('ended', function () {
try {
heroVideo.pause();
heroVideo.currentTime = Math.max(0, heroVideo.duration - 0.05);
} catch (e) { /* no-op */ }
revealHeroCopy();
});
// Autoplay attribute was removed from the video element so the clip
// doesn't run during the intro pre-roll. JS plays it explicitly here.
var playAttempt = heroVideo.play();
if (playAttempt && typeof playAttempt.catch === 'function') {
playAttempt.catch(function () { revealHeroCopy(); });
}
// Safety net: some browsers fire `ended` inconsistently. Reveal
// anyway once we're past the clip's natural duration.
heroVideo.addEventListener('loadedmetadata', function () {
var ms = (heroVideo.duration * 1000) + 600;
if (isFinite(ms) && ms > 0) window.setTimeout(revealHeroCopy, ms);
}, { once: true });
// Hard cap: if metadata never loads in 8s, give up and show copy.
window.setTimeout(revealHeroCopy, 8000);
}
if (heroIntroEl) {
var introWord = 'Introducing';
var introTextEl = heroIntroEl.querySelector('.hl-hero__intro-text');
var charDelay = 110; // ms per character — readable but quick
var holdDelay = 700; // ms to linger on the finished word before cutting
var prefersReducedMotion =
window.matchMedia &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function finishIntro() {
heroIntroEl.classList.add('is-done');
startVideoSequence();
}
if (prefersReducedMotion || !introTextEl) {
// Reduced-motion (or missing text node) — show the whole word
// immediately, hold briefly, then cut.
if (introTextEl) introTextEl.textContent = introWord;
window.setTimeout(finishIntro, holdDelay);
} else {
// Per-character typing loop — appending one letter at a time
// shifts the cursor right with each insertion, so the cursor
// visibly follows the typing instead of sitting parked at the end.
var charIdx = 0;
function typeNext() {
if (charIdx >= introWord.length) {
window.setTimeout(finishIntro, holdDelay);
return;
}
introTextEl.textContent += introWord.charAt(charIdx);
charIdx++;
window.setTimeout(typeNext, charDelay);
}
// Kick off on the next frame so the first character lands after
// the browser has painted the (empty) overlay.
window.requestAnimationFrame(typeNext);
}
} else {
startVideoSequence();
}
/* --------------------------------------------------------
* 6) Wire scroll + resize
* -------------------------------------------------------- */
var onScroll = rafThrottle(updateLineFill);
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll);
// initial paint
updateLineFill();
})();
Test drive the right way to defend the cloud with a security expert