1/** 2 * WIRE Miami - Cookie Consent Banner 3 * ==================================== 4 * Requires: wire-config.js loaded BEFORE this file. 5 * 6 * ADA compliant: role="dialog", aria-label, 44px minimum tap targets. 7 * GDPR compliant: explicit accept/decline with cookie persistence. 8 * 9 * WIRE fix 2026-08-17 10 * Layout. The banner was position:fixed;bottom:0 at z-index 9999. The mobile 11 * action bar sits at bottom:0 z9995 and the accessibility toggle at z9998, so 12 * on every first mobile visit the banner buried both until it was dismissed. 13 * Measured at 390x844: banner 390x220 at y445, action bar 390x65 at y599, 14 * a11y toggle 48x48 at y536. The banner now sits ABOVE the action bar and 15 * lifts the accessibility toggle above itself, so nothing is covered and 16 * nothing is hidden. Both are restored on dismiss. 17 * 18 * Palette. Dropped the accent dependency (it rendered gold on most sites and 19 * sage green on The Well) and the 2px radius. Canonical grayscale only: 20 * #212121 surface, white text, white accept button. This also collapses the 21 * five drifted copies of this file into one. 22 * 23 * Privacy link. Was relative, so it 404'd from every blog subfolder page. 24 * 41 sites load this file from /blog/. Now root-absolute. 25 */ 26 27(function () { 28 'use strict'; 29 30 // Check if consent already given 31 if (document.cookie.indexOf('cookie_consent=accepted') !== -1) return; 32 if (document.cookie.indexOf('cookie_consent=declined') !== -1) return; 33 34 var banner = document.createElement('div'); 35 banner.id = 'cookie-consent'; 36 banner.setAttribute('role', 'dialog'); 37 banner.setAttribute('aria-label', 'Cookie consent'); 38 banner.style.cssText = 'position:fixed;bottom:0;left:0;right:0;background:#212121;' + 39 'color:rgba(255,255,255,0.85);padding:16px 24px;display:flex;align-items:center;' + 40 'justify-content:space-between;gap:16px;z-index:9999;' + 41 'font-family:"Instrument Sans",sans-serif;' + 42 'font-size:0.85rem;box-shadow:0 -4px 20px rgba(0,0,0,0.15);flex-wrap:wrap;'; 43 44 var text = document.createElement('p'); 45 text.style.cssText = 'margin:0;flex:1;
45min-width:200px;line-height:1.5;'; 46 text.innerHTML = 'This website uses cookies to enhance your experience and analyze site traffic. ' + 47 'By continuing to browse, you consent to our use of cookies. ' + 48 '<a href="/privacy-policy.html" style="color:#FFFFFF;text-decoration:underline;">Privacy Policy</a>'; 49 50 var btnWrap = document.createElement('div'); 51 btnWrap.style.cssText = 'display:flex;gap:8px;flex-shrink:0;'; 52 53 var acceptBtn = document.createElement('button'); 54 acceptBtn.textContent = 'Accept'; 55 acceptBtn.style.cssText = 'background:#FFFFFF;color:#212121;border:none;padding:10px 24px;' + 56 'font-size:0.78rem;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;' + 57 'cursor:pointer;border-radius:0;min-height:44px;'; 58 59 var declineBtn = document.createElement('button'); 60 declineBtn.textContent = 'Decline'; 61 declineBtn.style.cssText = 'background:transparent;color:rgba(255,255,255,0.7);' + 62 'border:1px solid rgba(255,255,255,0.25);padding:10px 24px;font-size:0.78rem;font-weight:700;' + 63 'letter-spacing:0.06em;text-transform:uppercase;cursor:pointer;border-radius:0;min-height:44px;'; 64 65 // --- layout: never cover the action bar or the accessibility toggle ------- 66 67 // Both of these carry "bottom: 80px !important" from per-site CSS, so the 68 // lift has to be set as !important too or the inline value is ignored. 69 // Measured 2026-08-17 on thewellcocogrove.com: inline bottom:271px was set 70 // and lost to "#a11y-toggle { bottom: 80px !important }". 71 var LIFT = ['#a11y-toggle', '#wc-bubble']; 72 var lifted = []; 73 74 function ctaHeight() { 75 var bar = document.getElementById('wire-mobile-cta') || 76 document.getElementById('mobile-cta-bar') || 77 document.querySelector('.mobile-cta-bar'); 78 if (!bar) return 0; 79 var cs = window.getComputedStyle(bar); 80 if (cs.position !== 'fixed' || cs.display === 'none' || cs.visibility === 'hidden') return 0; 81 return Math.round(bar.getBoundingClientRect().height); 82 } 83 84 function layout() { 85 if (!banner.isConnected) return; 86 var offset = ctaHeight(); 87 banner.style.bottom = offset + 'px'; 88 89 var top = offset + Math.round(banner.getBoundingClientRect().height) + 12; 90 for (var i = 0; i < LIFT.length; i++) { 91 // These widgets are deferred and may mount after this file runs, so 92 // re-resolve every pass rather than caching once. 93 var el = document.querySelector(LIFT[i]); 94 if (!el) continue; 95 var cs = window.getComputedStyle(el); 96 if (cs.position !== 'fixed' || cs.display === 'none' || cs.visibility === 'hidden') continue; 97 if (lifted.indexOf(el) === -1) lifted.push(el); 98 el.style.setProperty('bottom', top + 'px', 'important'); 99 } 100 } 101 102 function restoreChrome() { 103 for (var i = 0; i < lifted.length; i++) { 104 lifted[i].style.removeProperty('bottom'); 105 } 106 lifted.length = 0; 107 window.removeEventListener('resize', layout); 108 window.removeEventListener('orientationchange', layout); 109 window.removeEventListener('load', layout); 110 } 111 112 function dismiss(value) { 113 document.cookie = 'cookie_consent=' + value + ';path=/;max-age=31536000;SameSite=Lax'; 114 banner.remove(); 115 restoreChrome(); 116 } 117 118 acceptBtn.addEventListener('click', function () { dismiss('accepted'); }); 119 declineBtn.addEventListener('click', function () { dismiss('declined'); }); 120 121 btnWrap.appendChild(acceptBtn); 122 btnWrap.appendChild(declineBtn); 123 banner.appendChild(text); 124 banner.appendChild(btnWrap); 125 document.body.appendChild(banner); 126 127 layout(); 128 if (window.requestAnimationFrame) window.requestAnimationFrame(layout); 129 window.addEventListener('load', layout); 130 window.addEventListener('resize', layout); 131 window.addEventListener('orientationchange', layout); 132 // The action bar and the a11y toggle are both deferred; re-measure as they mount. 133 setTimeout(layout, 400); 134 setTimeout(layout, 1200); 135 setTimeout(layout, 2500); 136})();
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.