1/* 2 * Restyle layer for captured WordPress/Elementor pages â the parts that depend 3 * on computed colours, so they can't be plain CSS (see legacy.css): 4 * 1. Buttons get a redesign variant: primary (lilac pill), light, or ghost. 5 * 2. The old white cards (testimonials, reviews, FAQ boxes) become dark 6 * cards, and dark text inside them is lifted to the light text colour. 7 * Re-runs for nodes added later (carousels clone their slides). 8 */ 9(() => { 10 const body = document.body; 11 if (!body || !body.classList.contains("pcx-legacy") || body.dataset.pcxRestyled) return; 12 body.dataset.pcxRestyled = "1"; 13 14 const SKIP = ".pcx-hdr, .pcx-ftr, .pc-wl-popup, astro-dev-toolbar"; 15 const BTN = 16 "a.elementor-button, button.elementor-button, .elementor-button-link, .wp-block-button__link, .wp-element-button, a.ekit-btn, .elementskit-btn, a.eael-creative-button, .ast-button, a.button, button.button"; 17 18 const rgba = (c) => { 19 const m = /rgba?\(([^)]+)\)/.exec(c || ""); 20 if (!m) return null; 21 const [r, g, b, a = 1] = m[1].split(/[\s,/]+/).filter(Boolean).map(Number); 22 return { r, g, b, a }; 23 }; 24 const lum = (c) => (0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) / 255; 25 26 const classifyButton = (el) => { 27 if (el.closest(SKIP) || el.classList.contains("pcx-btn")) return; 28 const cs = getComputedStyle(el); 29 const bg = rgba(cs.backgroundColor); 30 const hasGradient = /gradient/.test(cs.backgroundImage); 31 let variant = "primary"; 32 if (!hasGradient) { 33 if (!bg || bg.a < 0.25) variant = "ghost"; 34 else if (lum(bg) > 0.82) variant = "light"; 35 } 36 el.classList.add("pcx-btn", `pcx-btn--${variant}`); 37 }; 38 39 const CARD_TAGS = "div, section, article, aside, li, figure, blockquote, details, form, table"; 40 const flipCard = (el) => { 41 if (el.closest(SKIP) || el.closest(".pcx-darkcard") || el.matches(BTN) || el.closest(".pcx-btn")) return; 42 const cs = getComputedStyle(el); 43 const bg = rgba(cs.backgroundColor); 44 if (!bg || bg.a < 0.85 || lum(bg) < 0.88) return; 45 if (el.offsetWidth < 120 || el.offsetHeight < 48) return; 46 el.classList.add("pcx-darkcard"); 47 // Lift dark text inside the flipped card. 48 el.querySelectorAll("*").forEach((child) => { 49 const c = rgba(getComputedStyle(child).color); 50 if (c && lum(c) < 0.6) child.style.setProperty("color", "#e5e1f0", "important"); 51 }); 52 const own = rgba(cs.color); 53 if (own && lum(own) < 0.6) el.style.setProperty("color", "#e5e1f0", "important"); 54 }; 55 56 // The old site washed sections in violet glows/gradients; the redesign keeps 57 // surfaces flat with at most a faint lilac glow from the top. 58 const ACCENTS = /rgba?\(\s*(184,\s*118,\s*209|227,\s*159,\s*246|142,\s*86,\s*160|243,\s*217,\s*250)/; 59 const SOFT_GLOW = "radial-gradient(ellipse 60% 55% at 50% 0%, rgba(227, 159, 246, 0.09), transparent 70%)"; 60 const isAccentWash = (cs) => { 61 const img = cs.backgroundImage; 62 if (!img || img === "none" || !/gradient/.test(img) || /url\(/.test(img) || !ACCENTS.test(img)) return false; 63 if (cs.webkitBackgroundClip === "text" || cs.backgroundClip === "text") return false; 64 // Gradient borders (padding-box + border-box layer pairs) are handled in CSS. 65 return !/padding-box/.test(cs.backgroundClip); 66 }; 67 const softenGlow = (el) => { 68 if (el.closest(SKIP) || el.classList.contains("pcx-btn") || el.closest(".pcx-btn")) return; 69 const big = el.offsetWidth > 600 && el.offsetHeight > 240; 70 if (isAccentWash(getComputedStyle(el))) { 71 el.style.setProperty("background-image", big ? SOFT_GLOW : "none", "important"); 72 } 73 // Pseudo-element glows can't take inline styles; flag them for legacy.css. 74 for (const pseudo of ["before", "after"]) { 75 if (isAccentWash(getComputedStyle(el, `::${pseudo}`))) { 76 el.classList.add(`pcx-soft-${pseudo}`, big ? "pcx-soft-big" : "pcx-soft-small"); 77 } 78 } 79 }; 80 81 // Same hue rule as src/lib/legacy-restyle.ts, for colours that only exist at 82 // runtime (third-party embeds such as OptinMonster inject their own styles). 83 const lilacFor = (c) => { 84 if (!c || c.a < 0.05) return null; 85 const r = c.r / 255, g = c.g / 255, b = c.b / 255; 86 const max = Math.max(r, g, b), min = Math.min(r, g, b), l = (max + min) / 2, d = max - min; 87 if (d === 0) return null; 88 const sat = d / (1 - Math.abs(2 * l - 1)); 89 let h = max === r ? ((g - b) / d) % 6 : max === g ? (b - r) / d + 2 : (r - g) / d + 4; 90 h = (h * 60 + 360) % 360; 91 if (h < 250 || h > 300 || sat < 0.45 || l < 0.12 || l > 0.88) return null; 92 const hex = l >= 0.68 ? "#e39ff6" : l >= 0.4 ? "#b876d1" : l >= 0.25 ? "#8e56a0" : "#53335e"; 93 const n = parseInt(hex.slice(1), 16); 94 if ((n >> 16) === c.r && ((n >> 8) & 255) === c.g && (n & 255) === c.b) return null; 95 return hex; 96 }; 97 const fixViolet = (el) => { 98 if (el.closest(SKIP)) return; 99 const cs = getComputedStyle(el); 100 const bg = rgba(cs.backgroundColor); 101 const bgTo = lilacFor(bg); 102 if (bgTo) { 103 el.style.setProperty("background-color", bgTo, "important"); 104 const fg = rgba(cs.color); 105 if (fg && lum(fg) > 0.8) el.style.setProperty("color", "#131313", "important"); 106 } 107 const fgTo = lilacFor(rgba(cs.color)); 108 if (fgTo && !bgTo) el.style.setProperty("color", fgTo === "#b876d1" ? "#e39ff6" : fgTo, "important"); 109 const bdTo = lilacFor(rgba(cs.borderTopColor)); 110 if (bdTo && parseFloat(cs.borderTopWidth) > 0) el.style.setProperty("border-color", "rgba(227, 159, 246, 0.35)", "important"); 111 }; 112
113 // Dark text left sitting on a dark surface (old white widgets restyled dark by 114 // CSS) gets the light text colour. 115 const surfaceOf = (el) => { 116 for (let n = el; n && n !== document.documentElement; n = n.parentElement) { 117 const c = rgba(getComputedStyle(n).backgroundColor); 118 if (c && c.a > 0.5) return c; 119 } 120 return { r: 19, g: 19, b: 19, a: 1 }; 121 }; 122 const hasOwnText = (el) => [...el.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim()); 123 const fixContrast = (el) => { 124 if (el.closest(SKIP) || !hasOwnText(el)) return; 125 const c = rgba(getComputedStyle(el).color); 126 if (!c || lum(c) >= 0.4) return; 127 if (lum(surfaceOf(el)) < 0.3) el.style.setProperty("color", "#e5e1f0", "important"); 128 }; 129 130 const run = (root) => { 131 if (root.nodeType !== 1) return; 132 fixViolet(root); 133 root.querySelectorAll("*").forEach(fixViolet); 134 if (root.matches(BTN)) classifyButton(root); 135 root.querySelectorAll(BTN).forEach(classifyButton); 136 if (root.matches(CARD_TAGS)) flipCard(root); 137 root.querySelectorAll(CARD_TAGS).forEach(flipCard); 138 if (root.matches(CARD_TAGS)) softenGlow(root); 139 root.querySelectorAll(CARD_TAGS).forEach(softenGlow); 140 fixContrast(root); 141 root.querySelectorAll("*").forEach(fixContrast); 142 }; 143 144 run(body); 145 window.addEventListener("load", () => run(body), { once: true }); 146 147 let pending = []; 148 let timer = 0; 149 new MutationObserver((muts) => { 150 for (const m of muts) m.addedNodes.forEach((n) => n.nodeType === 1 && pending.push(n)); 151 clearTimeout(timer); 152 timer = setTimeout(() => { 153 const nodes = pending; 154 pending = []; 155 nodes.forEach((n) => n.isConnected && run(n)); 156 }, 150); 157 }).observe(body, { childList: true, subtree: true }); 158})();
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.