diff --git a/index.html b/index.html index 6a52551..b9b1df4 100644 --- a/index.html +++ b/index.html @@ -62,17 +62,21 @@

What Others Are Saying

-
-

"I have never met a yinglet with such presence. Zeppo is a force of nature wrapped in fur."

- — A Very Reliable Source -
-
-

"He once stared down a baxxid and the baxxid blinked first (even though it doesn't have eyelids). Legend."

- — Witness -
-
-

"I asked him to tone it down and he toned it UP. Respect."

- — Anonymous +
+
+
+

"I have never met a yinglet with such presence. Zeppo is a force of nature wrapped in fur."

+ — A Very Reliable Source +
+
+

"He once stared down a baxxid and the baxxid blinked first (even though it doesn't have eyelids). Legend."

+ — Witness +
+
+

"I asked him to tone it down and he toned it UP. Respect."

+ — Anonymous +
+
@@ -108,5 +112,6 @@ + diff --git a/script.js b/script.js new file mode 100644 index 0000000..8acf3bb --- /dev/null +++ b/script.js @@ -0,0 +1,121 @@ +(function () { + 'use strict'; + + var fragments = [ + 'I have never met a yinglet with such presence.', + 'Zeppo is a force of nature wrapped in fur.', + 'He once stared down a baxxid and the baxxid blinked first (even though it doesn\'t have eyelids).', + 'Legend.', + 'I asked him to tone it down and he toned it UP.', + 'Respect.', + 'Zeppo doesn\'t walk into a room \u2014 he arrives.', + 'Whiskers perfectly groomed, tail held high and shelltooth sharp.', + 'He knows he\'s him.', + 'Wits sharper than a creystone blade.', + 'Zeppo could scheme his way out of any jam and still have time to steal your lunch.', + 'Despite being a tiny rodent-person, his heart is the size of a melon.', + 'Loyal, fierce, and ride-or-die for his crew.', + 'Name a better dental situation. You can\'t.', + 'That shelltooth has 10,000 PSI of pure charisma.', + 'Where Zeppo goes, adventure follows.', + 'Not always good adventure, but never boring.', + 'The universe literally cannot ignore him.', + 'No throne needed. Zeppo rules through sheer force of personality.', + 'Say his name three times and he appears to judge you.', + ]; + + var sources = [ + 'A Very Reliable Source', + 'Witness', + 'Anonymous', + 'Local Prophet', + 'The Council of Elders', + 'A Slightly Biased Fan', + 'His Mother', + 'A Former Rival', + 'The Oracle of Weh', + 'Some Guy', + 'An Expert', + 'History', + 'A Completely Unbiased Observer', + 'The Voices', + 'A Very Smart Person', + 'The Echo', + 'A Distant Admiral', + 'A Humble Historian', + 'A Random Vagrant', + 'The Governor', + 'A Self-Proclaimed Expert', + 'The Town Crier', + 'A Very Loud Individual', + ]; + + function randomItem(arr) { + return arr[Math.floor(Math.random() * arr.length)]; + } + + function generateQuote() { + var count = Math.random() > 0.5 ? 2 : 1; + var selected = []; + var available = fragments.slice(); + for (var i = 0; i < count && available.length > 0; i++) { + var idx = Math.floor(Math.random() * available.length); + selected.push(available.splice(idx, 1)[0]); + } + var text = selected.join(' '); + if (Math.random() > 0.3) { + var extra = randomItem(fragments); + if (selected.indexOf(extra) === -1) { + text += ' ' + extra; + } + } + var source = randomItem(sources); + return { text: text, source: source }; + } + + function createElement(data) { + var div = document.createElement('div'); + div.className = 'testimonial'; + var p = document.createElement('p'); + p.textContent = '\u201C' + data.text + '\u201D'; + div.appendChild(p); + var cite = document.createElement('cite'); + cite.textContent = '\u2014 ' + data.source; + div.appendChild(cite); + return div; + } + + var track = document.getElementById('testimonial-track'); + if (!track) return; + + var viewport = track.parentElement; + var paused = false; + var scrollPos = 0; + var speed = 0.35; + + function step() { + try { + if (!paused) { + scrollPos += speed; + track.style.transform = 'translateY(' + (-scrollPos) + 'px)'; + + var first = track.firstElementChild; + var second = first && first.nextElementSibling; + if (first && second) { + var distance = second.offsetTop - first.offsetTop; + if (distance > 0 && scrollPos >= distance) { + scrollPos -= distance; + track.removeChild(first); + track.appendChild(createElement(generateQuote())); + } + } + } + } catch (_) {} + requestAnimationFrame(step); + } + + viewport.addEventListener('mouseenter', function () { paused = true; }); + viewport.addEventListener('mouseleave', function () { paused = false; }); + + step(); +})(); diff --git a/style.css b/style.css index 2c4a23a..06b0bc2 100644 --- a/style.css +++ b/style.css @@ -218,6 +218,30 @@ footer { margin-top: 2rem; } +.testimonial-viewport { + overflow: hidden; + max-height: 320px; + position: relative; +} + +.testimonial-viewport::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 48px; + background: linear-gradient(transparent, #151520); + pointer-events: none; + z-index: 1; +} + +#testimonial-track { + display: flex; + flex-direction: column; + gap: 1rem; +} + @media (max-width: 600px) { h1 { font-size: 3rem; } .hero h2 { font-size: 2rem; }