1(function () { 2 function onReady(callback) { 3 var called = false; 4 5 function runOnce() { 6 if (called) { 7 return; 8 } 9 called = true; 10 callback(); 11 } 12 13 if (document.readyState === "loading") { 14 document.addEventListener("DOMContentLoaded", runOnce); 15 window.setTimeout(runOnce, 0); 16 return; 17 } 18 runOnce(); 19 } 20 21 function getTheme() { 22 try { 23 var stored = window.localStorage.getItem("seovault-theme"); 24 if (stored === "light" || stored === "dark") { 25 return stored; 26 } 27 } catch (error) { 28 // Storage can be unavailable in privacy-restricted browser contexts. 29 } 30 return window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"; 31 } 32 33 function setTheme(theme) { 34 document.documentElement.setAttribute("data-theme", theme); 35 try { 36 window.localStorage.setItem("seovault-theme", theme); 37 } catch (error) { 38 // Keep the in-memory theme working when storage is unavailable. 39 } 40 var themeColor = document.querySelector('meta[name="theme-color"]'); 41 if (themeColor) { 42 themeColor.setAttribute("content", theme === "light" ? "#f3f5f9" : "#07111f"); 43 } 44 } 45 46 function toggleTheme() { 47 var current = document.documentElement.getAttribute("data-theme") || "dark"; 48 setTheme(current === "dark" ? "light" : "dark"); 49 } 50 51 function applyEnvironmentLinks() { 52 var config = window.SeoVaultSiteConfig; 53 if (!config) { 54 return; 55 } 56 57 var linkMap = { 58 "start-free": config.startFreeUrl, 59 login: config.loginUrl, 60 blog: config.blogUrl, 61 app: config.appOrigin, 62 pricing: config.pricingUrl, 63 features: config.featuresUrl, 64 billing: config.billingUrl 65 }; 66 67 Object.keys(linkMap).forEach(function (key) { 68 var nodes = document.querySelectorAll('[data-link="' + key + '"]'); 69 nodes.forEach(function (node) { 70 node.setAttribute("href", linkMap[key]); 71 }); 72 }); 73 74 document.querySelectorAll("[data-env-text]").forEach(function (node) { 75 node.textContent = config.environmentLabel; 76 }); 77 78 if (config.environment !== "prod") { 79 var badge = document.querySelector("[data-env-badge]"); 80 if (badge) { 81 badge.hidden = false; 82 badge.textContent = config.environment === "dev" ? "Dev preview" : "Local preview"; 83 } 84 } 85 } 86 87 function trackMarketingEvent(eventName, payload) { 88 try { 89 var consent = window.CookieConsent; 90 if (consent && typeof consent.acceptedCategory === "function" && 91 !consent.acceptedCategory("analytics")) { 92 return false; 93 } 94 95 if (typeof window.gtag === "function") { 96 window.gtag("event", eventName, payload || {}); 97 return true; 98 } 99 100 if (Array.isArray(window.dataLayer)) { 101 window.dataLayer.push(Object.assign({ event: eventName }, payload || {})); 102 return true; 103 } 104 } catch (error) { 105 // Analytics must never interrupt the marketing page. 106 } 107 108 return false; 109 } 110 111 function initPluginProAnalytics() { 112 if (document.body.getAttribute("data-page") !== "pricing-plugin-pro") { 113 return; 114 } 115 116 var tracked = false; 117 var payload = { 118 surface: "marketing_pricing", 119 page_path: window.location.pathname 120 }; 121 122 function trackPricingViewed() { 123 if (tracked || !trackMarketingEvent("plugin_pro_pricing_viewed", payload)) { 124 return; 125 } 126 tracked = true; 127 } 128 129 trackPricingViewed(); 130 window.addEventListener("cc:onConsent", trackPricingViewed); 131 window.addEventListener("cc:onChange", trackPricingViewed); 132 document.addEventListener("cc:onConsent", trackPricingViewed); 133 document.addEventListener("cc:onChange", trackPricingViewed); 134 } 135 136 function applyCanonicalAndRobots() { 137 var config = window.SeoVaultSiteConfig; 138 if (!config) { 139 return; 140 } 141 142 var canonical = document.querySelector('link[rel="canonical"]'); 143 if (canonical) { 144 canonical.setAttribute("href", config.siteOrigin + window.location.pathname); 145 } 146 147 var ogUrl = document.querySelector('meta[property="og:url"]'); 148 if (ogUrl) { 149 ogUrl.setAttribute("content", config.siteOrigin + window.location.pathname); 150 } 151 152 var robots = document.querySelector('meta[name="robots"]'); 153 if (!robots) { 154 robots = document.createElement("meta"); 155 robots.setAttribute("name", "robots"); 156 document.head.appendChild(robots); 157 } 158 159 if (config.environment === "prod") { 160 robots.setAttribute("content", "index,follow"); 161 } else { 162 robots.setAttribute("content", "noindex,nofollow"); 163 } 164 } 165 166 function initMenu() { 167 var toggle = document.querySelector("[data-menu-toggle]"); 168 var panel = document.querySelector("[data-mobile-panel]"); 169 if (!toggle || !panel) { 170 return; 171 } 172 173 toggle.addEventListener("click", function () { 174 var open = panel.classList.toggle("is-open");
175 toggle.classList.toggle("is-open", open); 176 toggle.setAttribute("aria-expanded", open ? "true" : "false"); 177 }); 178 179 panel.querySelectorAll("a").forEach(function (link) { 180 link.addEventListener("click", function () { 181 panel.classList.remove("is-open"); 182 toggle.classList.remove("is-open"); 183 toggle.setAttribute("aria-expanded", "false"); 184 }); 185 }); 186 } 187 188 function initSeoMockupSwitcher() { 189 var root = document.querySelector("[data-seo-mockup]"); 190 if (!root) { 191 return; 192 } 193 194 var mockup = root.closest(".wp-mockup") || root; 195 var triggers = Array.prototype.slice.call(mockup.querySelectorAll("[data-mockup-view-trigger]")); 196 var panels = Array.prototype.slice.call(root.querySelectorAll("[data-mockup-view-panel]")); 197 if (!triggers.length || !panels.length) { 198 return; 199 } 200 201 var current = root.getAttribute("data-mockup-view") || "general"; 202 var timer = null; 203 204 function scheduleNext() { 205 if (timer) { 206 window.clearTimeout(timer); 207 } 208 timer = window.setTimeout(function () { 209 activate(current === "general" ? "seo-analyzer" : "general"); 210 }, 5000); 211 } 212 213 function activate(view) { 214 var found = false; 215 216 triggers.forEach(function (trigger) { 217 var active = trigger.getAttribute("data-mockup-view-trigger") === view; 218 trigger.classList.toggle("current", active); 219 trigger.setAttribute("aria-pressed", active ? "true" : "false"); 220 }); 221 222 panels.forEach(function (panel) { 223 var active = panel.getAttribute("data-mockup-view-panel") === view; 224 panel.classList.toggle("is-active", active); 225 panel.setAttribute("aria-hidden", active ? "false" : "true"); 226 if (active) { 227 found = true; 228 } 229 }); 230 231 if (!found) { 232 return; 233 } 234 235 current = view; 236 root.setAttribute("data-mockup-view", view); 237 root.scrollTop = 0; 238 scheduleNext(); 239 } 240 241 triggers.forEach(function (trigger) { 242 trigger.addEventListener("click", function () { 243 activate(trigger.getAttribute("data-mockup-view-trigger")); 244 }); 245 }); 246 247 activate(current); 248 } 249 250 function initReveal() { 251 var items = document.querySelectorAll(".reveal"); 252 if (!items.length) { 253 return; 254 } 255 256 var observer = new IntersectionObserver(function (entries) { 257 entries.forEach(function (entry) { 258 if (entry.isIntersecting) { 259 entry.target.classList.add("is-visible"); 260 observer.unobserve(entry.target); 261 } 262 }); 263 }, { threshold: 0.18 }); 264 265 items.forEach(function (item) { 266 observer.observe(item); 267 }); 268 } 269 270 function initCounters() { 271 var counters = document.querySelectorAll("[data-counter]"); 272 if (!counters.length) { 273 return; 274 } 275 276 var observer = new IntersectionObserver(function (entries) { 277 entries.forEach(function (entry) { 278 if (!entry.isIntersecting) { 279 return; 280 } 281 282 var element = entry.target; 283 var target = parseInt(element.getAttribute("data-counter"), 10); 284 var current = 0; 285 var duration = 1100; 286 var startTime = null; 287 288 function step(timestamp) { 289 if (!startTime) { 290 startTime = timestamp; 291 } 292 var progress = Math.min((timestamp - startTime) / duration, 1); 293 current = Math.floor(target * progress); 294 element.textContent = current; 295 if (progress < 1) { 296 window.requestAnimationFrame(step); 297 } else { 298 element.textContent = target; 299 } 300 } 301 302 window.requestAnimationFrame(step); 303 observer.unobserve(element); 304 }); 305 }, { threshold: 0.5 }); 306
307 counters.forEach(function (counter) { 308 observer.observe(counter); 309 }); 310 } 311 312 function renderPricingCards() { 313 var containers = document.querySelectorAll("[data-pricing-grid]"); 314 var data = window.SeoVaultPricingData; 315 if (!containers.length || !data) { 316 return; 317 } 318 319 var plans = data.plans; 320 321 containers.forEach(function (container) { 322 container.innerHTML = plans 323 .map(function (plan) { 324 var list = plan.features.map(function (feature) { 325 return "<li>" + feature + "</li>"; 326 }).join(""); 327 328 var meta = plan.meta.map(function (item) { 329 return '<span class="meta-chip">' + item + "</span>"; 330 }).join(""); 331 332 var priceMarkup = ''; 333 334 if (plan.regularPrice) { 335 priceMarkup = '' + 336 '<div class="price-sale">' + 337 '<div class="price-regular">' + 338 '<span>Regular price</span>' + 339 '<s>' + plan.regularPrice + '/mo</s>' + 340 '</div>' + 341 '<div class="price-row"><strong>' + plan.price + '/mo</strong></div>' + 342 '<span class="price-launch-label">Launch price</span>' + 343 '</div>'; 344 } else { 345 priceMarkup = '<div class="price-row"><strong>' + plan.price + "</strong><span>" + plan.period + "</span></div>"; 346 } 347 348 return '' + 349 '<article class="pricing-card ' + (plan.badge ? "featured" : "") + '">' + 350 '<div class="pricing-card-head">' + 351 (plan.badge ? '<span class="pricing-badge">' + plan.badge + "</span>" : "") + 352 "<h3>" + plan.name + "</h3>" + 353 "<p>" + plan.tagline + "</p>" + 354 "</div>" + 355 priceMarkup + 356 '<div class="pricing-meta">' + meta + "</div>" + 357 '<ul class="pricing-list">' + list + "</ul>" + 358 '<a class="button ' + (plan.badge ? "primary" : "secondary") + '" data-link="' + plan.ctaKey + '" href="#">' + plan.ctaLabel + "</a>" + 359 "</article>"; 360 }) 361 .join(""); 362 }); 363 364 applyEnvironmentLinks(); 365 } 366 367 function renderPluginProPricingCards() { 368 var containers = document.querySelectorAll("[data-plugin-pro-pricing-grid]"); 369 var data = window.SeoVaultPluginProPricingData; 370 if (!containers.length || !data || !data.plans) { 371 return; 372 } 373 374 containers.forEach(function (container) { 375 container.innerHTML = data.plans 376 .map(function (plan) { 377 var list = (plan.features || []).map(function (feature) { 378 return "<li>" + feature + "</li>"; 379 }).join(""); 380 381 var meta = (plan.meta || []).map(function (item) { 382 return '<span class="meta-chip">' + item + "</span>"; 383 }).join(""); 384 385 var buttonClass = plan.badge ? "primary" : "secondary"; 386 var priceMarkup = plan.renewalPrice 387 ? '<div class="price-sale">' + 388 '<div class="price-row"><strong>' + plan.price + "</strong>" + 389 (plan.period ? "<span>" + plan.period + "</span>" : "") + 390 "</div>" + 391 '<div class="price-renewal"><span>Then renews at</span><strong>' + plan.renewalPrice + "</strong></div>" + 392 '<span class="price-launch-label">Founding price · first payment only</span>' + 393 "</div>" 394 : '<div class="price-row"><strong>' + plan.price + "</strong>" + 395 (plan.period ? "<span>" + plan.period + "</span>" : "") + 396 "</div>"; 397 var cta = plan.checkoutAvailable && plan.ctaHref 398 ? '<a class="button ' + buttonClass + '" data-plugin-pro-plan-cta="' + plan.id + '" href="' + plan.ctaHref + '">' + 399 plan.ctaLabel + 400 "</a>" 401 : '<button class="button ' + buttonClass + '" type="button" disabled aria-disabled="true" data-plugin-pro-plan-cta="' + plan.id + '">' + 402 plan.ctaLabel + 403 "</button>"; 404 405 return '' + 406 '<article class="pricing-card ' + (plan.badge ? "featured" : "") + '" data-plugin-pro-plan="' + plan.id + '">' + 407 '<div class="pricing-card-head">' + 408 (plan.badge ? '<span class="pricing-badge">' + plan.badge + "</span>" : "") + 409 "<h3>" + plan.name + "</h3>" + 410 "<p>" + plan.tagline + "</p>" + 411 "</div>" + 412 priceMarkup + 413 '<div class="pricing-meta">' + meta + "</div>" + 414 '<ul class="pricing-list">
414' + list + "</ul>" + 415 cta + 416 "</article>"; 417 }) 418 .join(""); 419 }); 420 } 421 422 function renderComparisonTable() { 423 var container = document.querySelector("[data-pricing-table]"); 424 var notesContainer = document.querySelector("[data-pricing-notes]"); 425 var data = window.SeoVaultPricingData; 426 if (!data) { 427 return; 428 } 429 430 if (container) { 431 var header = data.plans.map(function (plan) { 432 return "<th>" + plan.name + "</th>"; 433 }).join(""); 434 435 var rows = data.comparisonRows.map(function (row) { 436 var values = row.values.map(function (value) { 437 return "<td>" + value + "</td>"; 438 }).join(""); 439 440 return "<tr><td>" + row.label + "</td>" + values + "</tr>"; 441 }).join(""); 442 443 container.innerHTML = '' + 444 '<table class="comparison-table">' + 445 "<thead><tr><th>Plan feature</th>" + header + "</tr></thead>" + 446 "<tbody>" + rows + "</tbody>" + 447 "</table>"; 448 } 449 450 if (notesContainer) { 451 notesContainer.innerHTML = window.SeoVaultPricingData.notes 452 .map(function (note) { 453 return "<li>" + note + "</li>"; 454 }) 455 .join(""); 456 } 457 } 458 459 function renderVisuals() { 460 var hero = document.querySelector('[data-visual="hero"]'); 461 if (hero && window.SeoVaultVisuals) { 462 hero.innerHTML = window.SeoVaultVisuals.heroVisual(); 463 } 464 } 465 466 function initGauge() { 467 var gauge = document.querySelector('[data-gauge-target]'); 468 if (!gauge) return; 469 470 var numberEl = gauge.querySelector('[data-gauge-number]'); 471 var target = parseInt(gauge.getAttribute('data-gauge-target'), 10); 472 var peak = 100; 473 var duration1 = 2800; 474 var duration2 = 1600; 475 var startTime = null; 476 var phase = 1; 477 478 function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); } 479 function easeInOutCubic(t) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; } 480 481 function setGauge(v) { 482 gauge.style.setProperty('--value', v); 483 if (numberEl) numberEl.textContent = Math.round(v); 484 } 485 486 function tick(now) { 487 if (!startTime) startTime = now; 488 if (phase === 1) { 489 var t = Math.min((now - startTime) / duration1, 1); 490 setGauge(easeOutCubic(t) * peak); 491 if (t >= 1) { phase = 2; startTime = now; } 492 } else { 493 var t2 = Math.min((now - startTime) / duration2, 1); 494 var eased = easeInOutCubic(t2); 495 setGauge(peak - eased * (peak - target)); 496 if (t2 >= 1) { setGauge(target); return; } 497 } 498 requestAnimationFrame(tick); 499 } 500 501 var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; 502 if (reduceMotion) { 503 setGauge(target); 504 return; 505 } 506 507 // Only start the animation when the gauge scrolls into view 508 var observer = new IntersectionObserver(function (entries) { 509 entries.forEach(function (entry) { 510 if (entry.isIntersecting) { 511 observer.unobserve(gauge); 512 startTime = null; 513 phase = 1; 514 requestAnimationFrame(tick); 515 } 516 }); 517 }, { threshold: 0.4 }); 518 519 observer.observe(gauge); 520 } 521 522 function initFaq() { 523 document.querySelectorAll(".faq-item").forEach(function (item) { 524 var trigger = item.querySelector(".faq-trigger"); 525 var panel = item.querySelector(".faq-panel"); 526 if (!trigger || !panel) { 527 return; 528 } 529 530 trigger.addEventListener("click", function () { 531 var expanded = trigger.getAttribute("aria-expanded") === "true"; 532 trigger.setAttribute("aria-expanded", expanded ? "false" : "true"); 533 panel.classList.toggle("is-open", !expanded); 534 }); 535 }); 536 } 537 538 function initLightbox() { 539 var triggers = document.querySelectorAll(".lightbox-trigger"); 540 if (!triggers.length) { 541 return; 542 } 543
544 var overlay = document.createElement("div"); 545 overlay.className = "lightbox-overlay"; 546 overlay.setAttribute("role", "dialog"); 547 overlay.setAttribute("aria-modal", "true"); 548 overlay.setAttribute("aria-label", "Image preview"); 549 550 var img = document.createElement("img"); 551 overlay.appendChild(img); 552 553 var closeBtn = document.createElement("button"); 554 closeBtn.className = "lightbox-close"; 555 closeBtn.type = "button"; 556 closeBtn.setAttribute("aria-label", "Close image preview"); 557 closeBtn.textContent = "\u00d7"; 558 overlay.appendChild(closeBtn); 559 560 document.body.appendChild(overlay); 561 562 function open(src, alt) { 563 img.src = src; 564 img.alt = alt || ""; 565 overlay.classList.add("is-open"); 566 document.body.style.overflow = "hidden"; 567 } 568 569 function close() { 570 overlay.classList.remove("is-open"); 571 document.body.style.overflow = ""; 572 } 573 574 triggers.forEach(function (trigger) { 575 trigger.addEventListener("click", function (e) { 576 e.preventDefault(); 577 var triggerImg = trigger.querySelector("img"); 578 open(trigger.href, triggerImg ? triggerImg.alt : ""); 579 }); 580 }); 581 582 closeBtn.addEventListener("click", close); 583 overlay.addEventListener("click", function (e) { 584 if (e.target !== img) { 585 close(); 586 } 587 }); 588 document.addEventListener("keydown", function (e) { 589 if (e.key === "Escape" && overlay.classList.contains("is-open")) { 590 close(); 591 } 592 }); 593 } 594 595 function initLanguageMarquee() { 596 var track = document.querySelector("[data-lang-marquee]"); 597 var inner = document.querySelector("[data-lang-marquee-inner]"); 598 if (!track || !inner) { 599 return; 600 } 601 602 var reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; 603 if (reduceMotion) { 604 return; 605 } 606 607 var sourceItems = Array.prototype.slice.call(inner.querySelectorAll("[data-lang-item]")); 608 if (!sourceItems.length) { 609 return; 610 } 611 612 var gap = 30; 613 614 function getItemTotalWidth(item) { 615 var style = window.getComputedStyle(item); 616 var marginLeft = parseFloat(style.marginLeft) || 0; 617 var marginRight = parseFloat(style.marginRight) || 0; 618 return item.offsetWidth + marginLeft + marginRight; 619 } 620 621 var loopWidth = 0; 622 sourceItems.forEach(function (item) { 623 loopWidth += getItemTotalWidth(item); 624 }); 625 loopWidth += gap * sourceItems.length; 626 627 if (loopWidth <= 0) { 628 return; 629 } 630 631 while (inner.children.length > sourceItems.length) { 632 inner.removeChild(inner.lastElementChild); 633 } 634 635 var needed = Math.ceil((track.offsetWidth + loopWidth) / loopWidth) + 1; 636 for (var i = 0; i < needed; i++) { 637 sourceItems.forEach(function (item) { 638 inner.appendChild(item.cloneNode(true)); 639 }); 640 } 641 642 var offset = 0; 643 var speed = window.innerWidth <= 820 ? 0.20 : 0.55; 644 var paused = false; 645 var animationId = null; 646 var lastTime = null; 647 648 function step(timestamp) { 649 if (lastTime === null) { 650 lastTime = timestamp; 651 } 652 653 var delta = timestamp - lastTime; 654 lastTime = timestamp; 655 656 if (!paused) { 657 offset += speed * delta; 658 if (offset >= loopWidth) { 659 offset -= loopWidth; 660 } 661 inner.style.transform = "translate3d(" + (-offset) + "px, 0, 0)"; 662 } 663 664 animationId = window.requestAnimationFrame(step); 665 } 666 667 track.addEventListener("mouseenter", function () { 668 paused = true; 669 }); 670 671 track.addEventListener("mouseleave", function () { 672 paused = false; 673 }); 674 675 if (animationId) { 676 window.cancelAnimationFrame(animationId); 677 } 678 animationId = window.requestAnimationFrame(step); 679 } 680 681 function initTypewriter() { 682 var el = document.getElementById('hero-typewriter'); 683 if (!el || typeof Typed === 'undefined') return; 684 685 new Typed('#hero-typewriter', {
686 strings: [ 687 'Publish faster in WordPress â with AI help, not plugin bloat.', 688 'More than an AI writer â your full WordPress publishing workflow.' 689 ], 690 typeSpeed: 40, 691 backSpeed: 18, 692 backDelay: 2800, 693 startDelay: 400, 694 loop: true, 695 smartBackspace: false, 696 showCursor: true, 697 cursorChar: '|' 698 }); 699 } 700 701 function runSiteEnhancement(label, callback) { 702 try { 703 callback(); 704 } catch (error) { 705 console.error("SEOVault site enhancement failed: " + label, error); 706 } 707 } 708 709 var siteInitialized = false; 710 711 function initializeSite() { 712 if (siteInitialized) { 713 return; 714 } 715 siteInitialized = true; 716 717 runSiteEnhancement("theme", function () { 718 setTheme(getTheme()); 719 document.querySelectorAll("[data-theme-toggle]").forEach(function (button) { 720 button.addEventListener("click", toggleTheme); 721 }); 722 }); 723 runSiteEnhancement("environment links", applyEnvironmentLinks); 724 runSiteEnhancement("plugin pro analytics", initPluginProAnalytics); 725 runSiteEnhancement("canonical and robots", applyCanonicalAndRobots); 726 runSiteEnhancement("visuals", renderVisuals); 727 runSiteEnhancement("gauge", initGauge); 728 runSiteEnhancement("pricing cards", renderPricingCards); 729 runSiteEnhancement("plugin pro pricing cards", renderPluginProPricingCards); 730 runSiteEnhancement("comparison table", renderComparisonTable); 731 runSiteEnhancement("menu", initMenu); 732 runSiteEnhancement("SEO mockup switcher", initSeoMockupSwitcher); 733 runSiteEnhancement("reveal animations", initReveal); 734 runSiteEnhancement("counters", initCounters); 735 runSiteEnhancement("FAQ", initFaq); 736 runSiteEnhancement("lightbox", initLightbox); 737 runSiteEnhancement("language marquee", initLanguageMarquee); 738 runSiteEnhancement("typewriter", initTypewriter); 739 } 740 741 window.setTimeout(initializeSite, 50); 742})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.