). A
// direct child of that is neither ours nor Shopify's, and that
// renders -- itself or something inside it -- as a fixed/sticky strip
// spanning the viewport at its top or bottom edge, or that sits ABOVE
// our page root as a visible strip, is a store banner: it is marked
// data-esch-store-bar and the head rule hides it. Left alone: anything
// under #root / #midas-*, Shopify's admin, preview and privacy bars,
// app embeds' own containers, cookie/consent banners and dialogs, and
// anything that isn't bar-shaped (popups, flyouts, chat buttons).
// No Liquid inside this script: it ships verbatim and is unit-tested
// from the file (frontend/src/lib/storeBarGuard.test.ts).
(function () {
var OURS = /^(root$|midas-)/;
var PLATFORM = /^(shopify-|web-pixels-|shop-|admin-bar|preview-bar|AdminBar)/;
var SKIP = /^(SCRIPT|STYLE|LINK|TEMPLATE|NOSCRIPT|META|FORM)$/;
var CONSENT = /cookie|consent|gdpr|ccpa|privacy/i;
var MIN_H = 10, MAX_H = 160, MIN_W = 0.6, EDGE = 8, LOOK = 40;
var MARK = 'data-esch-store-bar';
function viewport() {
var d = document.documentElement;
return { w: window.innerWidth || d.clientWidth, h: window.innerHeight || d.clientHeight };
}
function visibleStrip(el, cs) {
if (cs.display === 'none' || cs.visibility === 'hidden') return false;
var r = el.getBoundingClientRect(), v = viewport();
return r.height >= MIN_H && r.height <= MAX_H && r.width >= v.w * MIN_W;
}
function pinnedBar(el) {
var cs = getComputedStyle(el);
if (cs.position !== 'fixed' && cs.position !== 'sticky') return false;
if (!visibleStrip(el, cs)) return false;
var r = el.getBoundingClientRect(), v = viewport();
return r.top <= EDGE || r.bottom >= v.h - EDGE;
}
function findPinnedBar(node) {
if (pinnedBar(node)) return true;
var walker = document.createTreeWalker(node, 1), n = 0;
while (walker.nextNode() && n++ < LOOK) {
if (pinnedBar(walker.currentNode)) return true;
}
return false;
}
function aboveOurPage(node, root) {
if (!root || !(node.compareDocumentPosition(root) & 4)) return false;
return visibleStrip(node, getComputedStyle(node));
}
function looksLikeConsent(node) {
var role = node.getAttribute('role') || '';
if (/dialog/i.test(role)) return true;
var label = node.id + ' ' + (typeof node.className === 'string' ? node.className : '') + ' ' +
(node.getAttribute('aria-label') || '');
return CONSENT.test(label) || CONSENT.test((node.textContent || '').slice(0, 600));
}
function ours(node) {
if (OURS.test(node.id || '')) return true;
var cls = typeof node.className === 'string' ? node.className : '';
return /(^|\s)template-root(\s|$)/.test(cls) || !!node.querySelector('.template-root');
}
function sweep() {
var root = document.getElementById('root');
var kids = document.body.children;
for (var i = 0; i < kids.length; i++) {
var n = kids[i];
if (n.hasAttribute(MARK) || SKIP.test(n.tagName)) continue;
if (ours(n) || PLATFORM.test(n.id || '') || looksLikeConsent(n)) continue;
if (findPinnedBar(n) || aboveOurPage(n, root)) n.setAttribute(MARK, '');
}
}
// Bar scripts insert their markup first and size it after (config
// fetch, fonts, resize), so every trigger looks now and again shortly
// after. Pending looks are replaced, not stacked.
var raf = window.requestAnimationFrame || function (f) { return setTimeout(f, 16); };
var queued = [];
function later() {
while (queued.length) clearTimeout(queued.pop());
raf(sweep);
for (var i = 0, ms = [300, 1500, 4000]; i < ms.length; i++) queued.push(setTimeout(sweep, ms[i]));
}
later();
if (document.readyState !== 'complete') window.addEventListener('load', later);
if (window.MutationObserver) {
new MutationObserver(function (muts) {
for (var i = 0; i < muts.length; i++) {
if (muts[i].addedNodes.length) { later(); return; }
}
}).observe(document.body, { childList: true });
}
})();