Every post you write is a little invitation to come closer and stay a while.
Place the below code in your header script area this delete this block.
<script>/* Word-by-word reading fade */
(function () {
var START = 0.85; // fading begins when the block's top hits 85% down the screen
var END = 0.35; // ...and wraps up around 35% down the screen
var MIN = 0.18; // starting opacity of each word
var SPREAD = 3; // how many words are mid-fade at once (higher = softer)
var blocks = [];
var ticking = false;
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// Wrap every word in a span while keeping bold, links, highlights, etc.
function splitWords(el) {
var walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null);
var textNodes = [];
while (walker.nextNode()) textNodes.push(walker.currentNode);
textNodes.forEach(function (node) {
if (!node.nodeValue.trim()) return;
var frag = document.createDocumentFragment();
node.nodeValue.split(/(\s+)/).forEach(function (part) {
if (!part) return;
if (/^\s+$/.test(part)) {
frag.appendChild(document.createTextNode(part));
} else {
var span = document.createElement('span');
span.className = 'cc-word';
span.textContent = part;
frag.appendChild(span);
}
});
node.parentNode.replaceChild(frag, node);
});
return Array.prototype.slice.call(el.querySelectorAll('.cc-word'));
}
function updateBlock(b) {
var rect = b.el.getBoundingClientRect();
var vh = window.innerHeight;
var startPx = vh * START;
var range = vh * (START - END) + rect.height * 0.5; // taller text = longer fade
var p = Math.min(1, Math.max(0, (startPx - rect.top) / range));
if (p === b.lastP) return;
b.lastP = p;
var n = b.words.length;
b.words.forEach(function (w, i) {
var t = Math.min(1, Math.max(0, (p * (n + SPREAD) - i) / SPREAD));
var o = (MIN + (1 - MIN) * t).toFixed(3);
if (w._o !== o) {
w.style.opacity = o;
w._o = o;
}
});
}
function updateAll() {
blocks.forEach(updateBlock);
ticking = false;
}
function onScroll() {
if (!ticking) {
ticking = true;
requestAnimationFrame(updateAll);
}
}
function init() {
if (reduce) return; // leave the text fully visible
document.querySelectorAll('.cc-word-fade').forEach(function (el) {
blocks.push({ el: el, words: splitWords(el), lastP: null });
});
if (!blocks.length) return;
updateAll(); // set the correct state right away on load
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();</script>