Security hardening + new tools deployment

- Hide Apache version (ServerTokens Prod)
- Add Permissions-Policy header
- Remove deprecated X-XSS-Protection
- Consolidate security headers to .htaccess only (remove duplicates from PHP)
- Deploy free tools: robots-analyzer, data-converter
- Deploy tools announcement blog post
- Update sitemap with new tools and blog post
This commit is contained in:
root
2026-02-05 04:11:15 +00:00
parent 3a0d8034c7
commit b6e39fe0c2
89 changed files with 4866 additions and 1932 deletions

View File

@@ -0,0 +1,158 @@
// CRO Enhancements - Sticky Bar, Exit Intent, Tracking
(function() {
// Only run on blog articles
if (!window.location.pathname.includes("/blog/articles/")) return;
// Check if user already converted or dismissed
var hasConverted = localStorage.getItem("ukds_converted");
var hasDismissed = localStorage.getItem("ukds_dismissed");
var dismissedAt = localStorage.getItem("ukds_dismissed_at");
// Reset dismissal after 7 days
if (dismissedAt && Date.now() - parseInt(dismissedAt) > 7 * 24 * 60 * 60 * 1000) {
localStorage.removeItem("ukds_dismissed");
localStorage.removeItem("ukds_dismissed_at");
hasDismissed = null;
}
// === STICKY CTA BAR ===
var stickyBar = document.createElement("div");
stickyBar.className = "sticky-cta-bar";
stickyBar.innerHTML = '<p>Need expert help with your data project?</p>' +
'<a href="/quote" class="btn" onclick="trackCTA(\'sticky_bar\')">Get Free Consultation</a>' +
'<span class="close-bar" onclick="closeStickyBar()">&times;</span>';
document.body.appendChild(stickyBar);
// Show sticky bar after scrolling 40%
var stickyShown = false;
window.addEventListener("scroll", function() {
var scrollPercent = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;
if (scrollPercent > 40 && !stickyShown && !hasDismissed) {
stickyBar.classList.add("visible");
stickyShown = true;
}
});
window.closeStickyBar = function() {
stickyBar.classList.remove("visible");
localStorage.setItem("ukds_dismissed", "sticky");
localStorage.setItem("ukds_dismissed_at", Date.now().toString());
};
// === EXIT INTENT POPUP ===
if (!hasConverted && !hasDismissed) {
var popup = document.createElement("div");
popup.className = "exit-popup-overlay";
popup.innerHTML = '<div class="exit-popup">' +
'<span class="close-popup" onclick="closeExitPopup()">&times;</span>' +
'<h3>Wait! Before you go...</h3>' +
'<div class="lead-magnet">' +
'<div style="font-size:2.5em;margin-bottom:10px;">📊</div>' +
'<strong>Free Data Project Checklist</strong>' +
'<p style="font-size:0.9em;margin:10px 0 0 0;color:#666;">' +
'The same checklist we use for enterprise projects</p>' +
'</div>' +
'<form id="exit-form" onsubmit="submitExitForm(event)">' +
'<input type="email" name="email" placeholder="Enter your email" required>' +
'<button type="submit" class="btn-primary">Send Me The Checklist</button>' +
'</form>' +
'<span class="no-thanks" onclick="closeExitPopup()">No thanks, I will figure it out myself</span>' +
'</div>';
document.body.appendChild(popup);
var exitShown = false;
document.addEventListener("mouseout", function(e) {
if (e.clientY < 10 && !exitShown && !hasConverted && !hasDismissed) {
popup.classList.add("active");
exitShown = true;
trackCTA("exit_popup_shown");
}
});
window.closeExitPopup = function() {
popup.classList.remove("active");
localStorage.setItem("ukds_dismissed", "popup");
localStorage.setItem("ukds_dismissed_at", Date.now().toString());
};
window.submitExitForm = function(e) {
e.preventDefault();
var email = e.target.email.value;
trackCTA("exit_popup_converted", email);
localStorage.setItem("ukds_converted", "true");
popup.querySelector(".exit-popup").innerHTML =
'<h3>🎉 Check your inbox!</h3>' +
'<p>We sent the checklist to <strong>' + email + '</strong></p>' +
'<p style="margin-top:20px;">' +
'<a href="/quote" class="btn-primary" style="display:inline-block;padding:14px 28px;text-decoration:none;border-radius:6px;">Or talk to us now →</a>' +
'</p>';
// Send to endpoint
fetch("/api/lead-capture.php", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
email: email,
source: "exit_popup",
page: window.location.pathname
})
}).catch(function(err) { console.error(err); });
};
}
// === CTA TRACKING ===
window.trackCTA = function(action, label) {
if (typeof gtag !== "undefined") {
gtag("event", action, {
event_category: "CRO",
event_label: label || window.location.pathname
});
}
console.log("CTA tracked:", action, label);
};
// Track all quote links
document.querySelectorAll('a[href*="/quote"]').forEach(function(link) {
link.addEventListener("click", function() {
var isInlineCta = this.closest(".inline-cta") ? "inline_cta" : "other";
trackCTA("quote_click", isInlineCta);
});
});
})();
// Social Proof Notification
(function() {
if (localStorage.getItem("ukds_social_proof_dismissed")) return;
var messages = [
"3 businesses requested quotes today",
"A London fintech just enquired about data scraping",
"New project started: competitor price monitoring",
"5 quotes sent this week"
];
var notification = document.createElement("div");
notification.id = "social-proof";
notification.style.cssText = "position:fixed;bottom:80px;left:20px;background:#fff;padding:15px 20px;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,0.15);z-index:9998;display:none;max-width:280px;font-size:14px;border-left:4px solid #00cc66;";
notification.innerHTML = '<div style="display:flex;align-items:center;gap:10px;"><span style="font-size:1.5em;">🔔</span><span id="social-proof-text"></span></div><span onclick="closeSocialProof()" style="position:absolute;top:5px;right:10px;cursor:pointer;color:#999;font-size:1.2em;">&times;</span>';
document.body.appendChild(notification);
window.closeSocialProof = function() {
notification.style.display = "none";
localStorage.setItem("ukds_social_proof_dismissed", Date.now());
};
function showNotification() {
var msg = messages[Math.floor(Math.random() * messages.length)];
document.getElementById("social-proof-text").textContent = msg;
notification.style.display = "block";
setTimeout(function() {
notification.style.display = "none";
}, 6000);
}
// Show after 20 seconds, then every 45 seconds
setTimeout(showNotification, 20000);
setInterval(showNotification, 45000);
})();