diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 27745e6..b5ed3f3 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -36,6 +36,7 @@ import GoogleAnalytics from '@/components/GoogleAnalytics';
import CookieConsent from '@/components/CookieConsent';
import NewsletterModal from '@/components/NewsletterModal';
+import BannerTracking from '@/components/BannerTracking';
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1
@@ -150,6 +151,7 @@ export default function RootLayout({
}} />
+
{/* Skip to main content — WCAG 2.4.1 */}
{
+ if (typeof window === "undefined") return;
+
+ let sid = sessionStorage.getItem("rmp.session");
+ if (!sid) {
+ sid =
+ typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
+ ? crypto.randomUUID()
+ : Math.random().toString(36).slice(2) + Date.now().toString(36);
+ sessionStorage.setItem("rmp.session", sid);
+ }
+
+ const fired = new WeakSet();
+ const visibleByKey = new Map();
+ const hiddenByUs = new WeakSet();
+
+ function isNested(el: Element): boolean {
+ return !!el.parentElement?.closest(SELECTOR);
+ }
+
+ function fireBeacon(el: Element): void {
+ if (fired.has(el)) return;
+ if (isNested(el)) return;
+ fired.add(el);
+
+ const campaign_id = el.getAttribute("data-campaign-id") || undefined;
+ const slug = el.getAttribute("data-ad-slug") || undefined;
+ if (!campaign_id && !slug) return;
+
+ const payload = JSON.stringify({
+ campaign_id,
+ slug,
+ property,
+ page_path: location.pathname,
+ page_url: location.href,
+ session_id: sid,
+ viewport: false,
+ });
+
+ try {
+ if (typeof navigator.sendBeacon === "function") {
+ navigator.sendBeacon(
+ ENDPOINT,
+ new Blob([payload], { type: "application/json" })
+ );
+ } else {
+ void fetch(ENDPOINT, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: payload,
+ keepalive: true,
+ }).catch(() => {});
+ }
+ } catch {
+ // never throw to the page
+ }
+ }
+
+ function keyOf(el: Element): string | null {
+ const cid = el.getAttribute("data-campaign-id");
+ if (cid) return `c:${cid}`;
+
+ const anchor =
+ el.matches("a[href]")
+ ? el
+ : el.querySelector("a[href]") ?? el.closest("a[href]");
+ const href = anchor?.getAttribute("href");
+ if (href && !href.startsWith("#")) return `h:${href}`;
+
+ const img = el.querySelector("img[src]") as HTMLImageElement | null;
+ const src = img?.getAttribute("src");
+ if (src) return `i:${src}`;
+
+ return null;
+ }
+
+ const io = new IntersectionObserver(
+ (entries) => {
+ for (const e of entries) {
+ const el = e.target;
+ const key = keyOf(el);
+ if (!key) continue;
+
+ if (e.isIntersecting) {
+ const cur = visibleByKey.get(key);
+ if (cur && cur !== el) {
+ (el as HTMLElement).style.setProperty("display", "none", "important");
+ el.setAttribute("data-deduped", "auto");
+ hiddenByUs.add(el);
+ } else {
+ visibleByKey.set(key, el);
+ }
+ } else {
+ if (hiddenByUs.has(el)) continue;
+ if (visibleByKey.get(key) === el) {
+ visibleByKey.delete(key);
+ const candidates = Array.from(
+ document.querySelectorAll(SELECTOR)
+ ).filter(
+ (c) =>
+ !isNested(c) && keyOf(c) === key && hiddenByUs.has(c)
+ );
+ if (candidates.length > 0) {
+ const c = candidates[0] as HTMLElement;
+ c.style.removeProperty("display");
+ c.removeAttribute("data-deduped");
+ hiddenByUs.delete(c);
+ }
+ }
+ }
+ }
+ },
+ { threshold: 0.3 }
+ );
+
+ function register(el: Element): void {
+ if (isNested(el)) return;
+ fireBeacon(el);
+ io.observe(el);
+ }
+
+ document.querySelectorAll(SELECTOR).forEach(register);
+
+ const mo = new MutationObserver((muts) => {
+ for (const m of muts) {
+ m.addedNodes.forEach((n) => {
+ if (n.nodeType !== 1) return;
+ const el = n as Element;
+ if (el.matches?.(SELECTOR)) register(el);
+ el.querySelectorAll?.(SELECTOR).forEach(register);
+ });
+ }
+ });
+ mo.observe(document.body, { childList: true, subtree: true });
+
+ return () => {
+ io.disconnect();
+ mo.disconnect();
+ };
+ }, [property]);
+
+ return null;
+}