PageSourceSearch

https://europianmedia.com/about/

html europianmedia.com collected 2026-10-07 16:30:29 UTC 78,201 bytes, 1,028 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en" data-page-group="policy">
3<head>
4<meta charset="utf-8">
5<meta name="rsoc:post-id" content="about">
6<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=5">
7
8<title>About — europianmedia</title>
9<meta name="description" content="About for europianmedia.">
10<meta name="robots" content="index,follow,max-image-preview:large">
11<link rel="canonical" href="https://europianmedia.com/about/">
12<meta property="og:type" content="website"><meta property="og:title" content="About — europianmedia">
13<meta property="og:description" content="About for europianmedia."><meta property="og:url" content="https://europianmedia.com/about/">
14<meta property="og:site_name" content="europianmedia">
15<meta name="twitter:card" content="summary">
16
17<script>
18window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}
19gtag('consent','default',{ad_storage:'denied',ad_user_data:'denied',ad_personalization:'denied',analytics_storage:'denied',wait_for_update:500,region:['AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR','HU','IS','IE','IT','LV','LI','LT','LU','MT','NL','NO','PL','PT','RO','SK','SI','ES','SE','GB','CH']});
20gtag('consent','default',{ad_storage:'granted',ad_user_data:'granted',ad_personalization:'granted',analytics_storage:'granted',functionality_storage:'granted',security_storage:'granted'});
21</script>
vendor: 1 bytes, line 21
21
22<script>(function(){
23var channels={"google_paid":"8584923757","other_paid":"9836979022","organic":"6173066189","direct_referral":"8523897350"},storeChannels={"aldi":"8942934831","lidl":"7651792051","publix":"4745335807","acme":"7362532927","smart-final":"8174536236","wegmans":"8836630221","food-lion":"4067770268","festival-foods":"5358913047","bi-mart":"1229948426","ace-hardware":"2164203251","jcpenney":"5756357115","avon":"5911876577","hmart":"7399777127","weis":"7268648677","target":"1509055535","hannaford":"4825309001","cub-foods":"1763282969","walgreens":"7336931434","price-chopper":"8552052594","family-fare":"9331530220","shoprite":"9695602124","kroger":"2586158477","meijer":"8751363142","foodmaxx":"9894812014","stater-bros":"6473350440","super-king":"4230330484","smith-s":"2408526089","family-dollar":"2008583137","harris-teeter":"4642485338","safeway":"2016321990","hyvee":"2773860731","ollie-s":"7096424138","save-a-lot":"2561359315","h-e-b":"4625741291","albertsons":"1170238674","key-food":"5531886012","fresco-y-m-s":"1856364884","schnucks":"2569098172","jewel-osco":"6480423020","vons":"4055327822","el-rancho":"2905722679","abc-warehouse":"6633931302","big-y":"9735124409","giant-eagle":"8360480904","united-supermarkets":"3516096763","lowes-foods":"8915014678","lucky-supermarkets":"7923254797","grocery-outlet":"5734317562","save-mart":"3108154224","99-ranch-market":"8534950733","price-rite":"9889933421","giant":"8764554017","fiesta-mart":"1800699579","el-super":"2743174798","rural-king":"5736075151","food-city":"6855827543","iga":"8853193083","winn-dixie":"9694492304","fry-s-food":"2916582537","mills-fleet-farm":"2096213046","cardenas":"7795356777","basha-s":"3449364768","sedano-s":"9506790043","fareway":"1453040208","market-basket":"9219849588","food-universe":"9296046210","coborn-s":"5032196712","dillons":"7710330940","central-market":"7117277882","food-4-less":"2231607704","foodland":"5804196214","fred-meyer":"6786105392","fresh-thyme":"6977677088","homeland":"2015985284","ingles":"8581393632","king-soopers":"3724827783","mariano-s":"6745567785","pick-n-save":"2399128668","piggly-wiggly":"1225647796","raley-s":"1852333759","ralphs":"5164892801","rouses":"2032472167","shaw-s":"8157139223","sprouts":"4822287031","star-market":"3287955927","stop-shop":"2908681974"},q=new URLSearchParams(location.search),source=(q.get('utm_source')||'').toLowerCase(),medium=(q.get('utm_medium')||'').toLowerCase();
24var bucket='direct_referral',paid=/^(cpc|ppc|paid|paid_social|paid_search|display|cpm|social_paid)$/.test(medium);
25function click(k){var v=q.get(k)||'';return !!v&&!/[{}]/.test(v)&&!/^__.+__$/.test(v)}
26var google=['gclid','gbraid','wbraid'].some(click),other=['fbclid','ttclid','ScCid','sccid','msclkid'].some(click);
27var ref='';try{ref=new URL(document.referrer).hostname}catch(e){}
28var internal=ref===location.hostname||ref==='www.'+location.hostname||location.hostname==='www.'+ref;
29if(google||(paid&&/^(google|adwords|youtube)$/.test(source)))bucket='google_paid';
30else if(paid||['msclkid','ttclid','ScCid','sccid'].some(click))bucket='other_paid';
31else if(medium==='organic'||(!source&&!other&&!internal&&/(^|\.)(google\.[a-z.]+|bing\.com|duckduckgo\.com|search\.yahoo\.com)$/.test(ref)))bucket='organic';
32else if(!source&&!medium&&!other&&internal){
33 try{var old=JSON.parse(sessionStorage.getItem('em_source_v1')||'null');if(old&&Date.now()-old.at<1800000&&/^(google_paid|other_paid|organic|direct_referral)$/.test(old.bucket))bucket=old.bucket}catch(e){}
34}
35try{sessionStorage.setItem('em_source_v1',JSON.stringify({bucket:bucket,at:Date.now()}))}catch(e){}
36var shop='';try{shop=document.documentElement.getAttribute('data-shop')||''}catch(e){}
37var storeChannel=/^(google_paid|other_paid)$/.test(bucket)?String(storeChannels[shop]||''):'';
38window.__EM_STORE_PAID_CHANNEL=storeChannel;
39window.__EM_AD_CHANNEL=storeChannel||channels[bucket]||'';window.__EM_TRAFFIC_BUCKET=bucket;
40})();</script>
vendor: 64 bytes, lines 40-41
40
41<script async src="https://www.googletagmanager.com/gtag/js?id=
41G-813T551WZB
vendor: 12 bytes, line 41
41"></script>
42<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag('js',new Date());gtag('set',{page_group:document.documentElement.getAttribute('data-page-group')||'',content_group:document.documentElement.getAttribute('data-page-group')||'',shop:document.documentElement.getAttribute('data-shop')||'',traffic_bucket:window.__EM_TRAFFIC_BUCKET||'unclassified'});gtag('config','G-813T551WZB',{send_page_view:true});gtag('config','AW-18353253259');gtag('config','AW-18353250616');gtag('config','AW-18303349347');gtag('config','AW-18303341653');</script>
42
43
44
45<link rel="preconnect" href="https://securepubads.g.doubleclick.net" crossorigin>
46<link rel="dns-prefetch" href="https://securepubads.g.doubleclick.net">
47
48
49<meta name="google-adsense-account" content="ca-pub-5570591959688035">
50
51
52<link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
53<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='9' fill='%23E4572E'/%3E%3Cpath d='M17.3 5.5H24a2.5 2.5 0 0 1 2.5 2.5v6.7a2.5 2.5 0 0 1-.73 1.77l-9.5 9.5a2.5 2.5 0 0 1-3.54 0l-6.7-6.7a2.5 2.5 0 0 1 0-3.54l9.5-9.5a2.5 2.5 0 0 1 1.77-.73z' fill='%23fff'/%3E%3Ccircle cx='21.3' cy='10.7' r='2.15' fill='%23E4572E'/%3E%3Cpath d='M12.4 16.3l3.3 3.3M15.6 13.1l3.3 3.3' stroke='%23E4572E' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E">
54<link rel="preconnect" href="https://cdn.deepdivetech.app" crossorigin="">
55<link rel="dns-prefetch" href="https://securepubads.g.doubleclick.net">
56<link rel="preconnect" href="https://fonts.googleapis.com">
57<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
58<link href="https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@700;800&family=Source+Sans+3:wght@400;600;700&display=swap" rel="stylesheet">
59<style>*{box-sizing:border-box}
60body{margin:0;background:#FFFFFF;color:#16130F;font-family:'Source Sans 3',system-ui,-apple-system,sans-serif;-webkit-font-smoothing:antialiased}
61a{color:inherit;text-decoration:none}
62a:hover{color:#E4572E}
63img{display:block}
64button{font-family:inherit}
65::selection{background:#FBD9CB}
66nav[aria-label="Breadcrumb"] a{display:inline-flex;align-items:center;min-height:44px}
67
68@media (max-width:1024px){
69  main{gap:26px!important}
70  main>div,main>article{order:1;max-width:100%!important;flex-basis:100%!important}
71  main>aside{order:2;flex:1 1 100%!important;width:100%!important;max-width:100%!important;margin-left:auto!important;margin-right:auto!important}
72  main>aside>div{width:100%;max-width:340px;margin-left:auto;margin-right:auto}
73  main>aside>div[style*="sticky"],main>aside[style*="sticky"]{position:static!important}
74}
75@media (max-width:767px){
76  header nav>div{flex-wrap:nowrap!important;justify-content:flex-start!important;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
77  header nav>div::-webkit-scrollbar{display:none}
78  header nav>div>a{flex:0 0 auto;padding-left:11px!important;padding-right:11px!important}
79  h1{font-size:29px!important;line-height:1.18!important;text-align:center}
80  main>div>section>h2,main>div>section>h3{text-align:center}
81  main>div>section>p:first-of-type{text-align:center}
82  h2{font-size:23px!important}
83  header>div:first-child{padding-top:10px!important;padding-bottom:10px!important;gap:10px 14px!important}
84  header nav>div>a{padding-top:12px!important;padding-bottom:12px!important;font-size:15px!important}
85  main{padding-top:16px!important}
86  [id^="div-gpt-ad"]:not([data-anchor-slot]){aspect-ratio:auto!important;min-height:0!important;height:auto!important;width:100%!important;max-width:100vw!important;margin-left:0!important;margin-right:0!important}
87  [data-viewer]{margin-left:calc(-1 * clamp(20px,4.4vw,96px))!important;margin-right:calc(-1 * clamp(20px,4.4vw,96px))!important;border-radius:0!important;border-left:0!important;border-right:0!important}
88  [data-thumbs]{display:none!important}
89  [data-pager] button{min-width:32px!important;height:32px!important;padding:0 8px!important;font-size:13px!important;border-radius:8px!important}[data-pager]{padding:6px 12px!important;gap:5px!important}
90  header nav{display:none}
91  header nav[data-open="1"]{display:block}
92  header nav[data-open="1"]>div{flex-wrap:wrap!important;overflow:visible!important;padding-bottom:10px}
93  header nav[data-open="1"]>div>a{flex:1 1 44%}
94  header form{padding:3px 3px 3px 12px!important}
95  header form input{font-size:16px!important;padding-top:3px!important;padding-bottom:3px!important;min-height:34px!important;line-height:1.2!important}
96  header form button{padding:0 12px!important;font-size:13px!important;min-height:34px!important}
97  
98  
99  [data-hero-flyer]{flex:1 1 calc(50% - 7px)!important;max-width:none!important}
100  footer>div:first-child{gap:28px!important}
101}
102@media (min-width:768px){[data-burger]{display:none!important}}
103
104
105[data-hv="h1"]:hover{background:#E4572E}
106[data-hv="h2"]:hover{border-color:#E4572E;box-shadow:0 6px 18px rgba(22,19,15,.09)}
107/* --- platform additions (not from the prototype) ------------------------ */
108/* ---------- breakpoints -----------------------------------------------------
109   Four edges, not ten. We had 760/761, 767/768, 900/901, 1000/1001 and
110   1024/1025 all live at once, which is how a slot's width band could change
111   without its column changing — the ad bands sit at 767 and 1024, so a rule at
112   900 put the two out of step. One of them was already wrong: rails hide at
113   <=1024 but the flyer asides were made sticky from 901, so between 901 and
114   1024 the stylesheet was sticking an aside that was not there.
115
116   The surviving edges are offermate's, which is also what Bootstrap's md/lg
117   split gives: 767/768 phone-to-tablet, 1024/1025 tablet-to-desktop. Full-bleed
118   ad bands and the halved section gaps are phone treatments; at 768 their ads
119   run at container width, so ours do too. */
120
121/* Title band, halved (store + flyer pages).
122   Measured live before the change: 88px between the site header and the H1 —
123   18px wrapper padding + a 44px breadcrumb row (min-height:44px on its links)
124   + 26px of main padding. The three cuts below land at ~46px, i.e. the
125   requested half, without touching the 64px section rhythm further down. */
126html[data-page-group="shop"] header+div,
127html[data-page-group="brochure"] header+div{padding-top:8px!important}
128/* > * not just a: the "/" separator spans carry their own inline min-height:44px,
129   which kept the row at 44 after the links shrank. */
130html[data-page-group="shop"] nav[aria-label="Breadcrumb"]>*,
131html[data-page-group="brochure"] nav[aria-label="Breadcrumb"]>*{min-height:28px!important}
132html[data-page-group="shop"] main,
133html[data-page-group="brochure"] main{padding-top:10px!important}
134html[data-page-group="shop"] main h1{margin-bottom:28px!important}
135html[data-page-group="brochure"] main h1{margin-bottom:4px!important}
136html[data-page-group="brochure"] [data-flyer-note]{margin:6px 0 0!important}
137html[data-page-group="brochure"] [data-page-note]:empty{display:none}
138@media(min-width:768px){html[data-page-group="brochure"] main>div{gap:24px!important}}
139/* Flyer page, below-title slot: the declared formats are now 280 tall, and the
140   prototype reserved 100px — the 180px difference would shove the flyer down on
141   every fill. Desktop only: the phone list still includes the 100px formats. */
142
143/* Phone header: icon-only search on the logo line.
144   The toggle is injected by the header script above; these rules decide when it
145   is the search control and when the real form is. Above 760px nothing changes —
146   the burger is hidden there too, and the wide header has room for the field. */
147[data-search-toggle]{display:none}
148@media (max-width:767px){
149  /* the form only exists once asked for, and then it takes the whole row */
150  header form{display:none!important}
151  header form[data-open="1"]{display:flex!important;flex:1 1 100%!important;max-width:none!important;margin:0!important}
152  [data-search-toggle]{
153    display:flex!important;align-items:center;justify-content:center;
154    flex:0 0 auto;width:44px;height:44px;margin-left:auto;
155    border:1px solid #E7DFD3;border-radius:12px;background:#fff;color:#16130F;cursor:pointer;padding:0}
156  /* the burger owned margin-left:auto; the toggle takes it so the pair sits
157     together at the right instead of being pushed apart by the free space */
158  header [data-burger]{margin-left:8px!important}
159}
160
161/* A rail exists to fill desktop margin. Below the three-column breakpoint it
162   has no margin to fill, so it is removed rather than reshaped into a square:
163   no ad request, no wasted viewport, nothing for the reader to scroll past.
164   Its wrapper goes too, so the "Advertisement" label does not strand. */
165@media (max-width:1024px){
166  [data-desktop-only]{display:none!important}
167  /* Any wrapper, not just <section> — a div-wrapped rail was leaving its
168     label behind on phones as a stub above the content. */
169  [aria-label="Advertisement"]:has([data-rsoc-ad][data-desktop-only]){display:none!important}
170}
171/* --- phone treatment ----------------------------------------------------
172   On a phone the reader is inside a single column with 20px of padding, so a
173   300x250 landed 38px in from each edge and read as a small boxed thing sitting
174   in a hole. Two separate fixes:
175
176   1. Full-bleed the wrapper. The creative is centred in a 100vw band instead of
177      a 335px one, so the widest size that clears (336x280, or a fluid creative)
178      runs edge to edge with nothing drawn around it. calc(50% - 50vw) cancels
179      the container padding without needing to know what it is.
180   2. Cut the surrounding space. 20px above / 24px below is enough to separate an
181      ad from content; more of it is what made the page feel like a stack of
182      boxes rather than a page with ads in it.
183
184   The label stays. It is 12px of the page and it is the one thing here that
185   AdSense actually requires — our competitor omits it entirely. */
186@media (max-width:767px){
187  /* Every wrapper, not just <section> — the home and store pages wrap some of
188     their units in a <div>, which is why those two pages kept their gutters
189     while the catalog page went full width.
190
191     :has([data-rsoc-ad]) is load-bearing, not tidiness. GPT labels the ad
192     IFRAME it injects aria-label="Advertisement" too, so the bare attribute
193     selector also matched the creative itself: a 100vw width and a
194     calc(50% - 50vw) margin computed against a 300px slot shifted the ad ~38px
195     left and clipped it at the viewport edge. Only our wrappers contain a
196     [data-rsoc-ad] element, so this matches us and never Google's markup. */
197  [aria-label="Advertisement"]:has([data-rsoc-ad]){
198    /* !important throughout: these wrappers carry inline geometry from the
199       prototype — the store page's in-content wrapper ships
200       flex:0 0 min(300px,100%);max-width:300px inline, which is what kept that
201       one unit at 300px with gutters while its neighbours went full width.
202       Inline styles outrank a stylesheet rule regardless of media query. */
203    width:100vw!important;max-width:100vw!important;
204    flex:0 0 auto!important;
205    margin-left:calc(50% - 50vw)!important;margin-right:calc(50% - 50vw)!important;
206    margin-top:var(--rhythm)!important;margin-bottom:var(--rhythm)!important;
207  }
208  /* !important because the tag sets an inline width on the placeholder once a
209     creative lands; without it the band collapses back to the creative's own
210     width and the ad re-acquires the gutters this rule exists to remove. */
211  [aria-label="Advertisement"]:has([data-rsoc-ad]) > [data-rsoc-ad]{
212    width:100%!important;max-width:100vw!important;margin:0!important
213  }
214}
215/* Page turns are the primary interaction here, so the control appears above and
216   below the flyer. Nine 34px targets wrapped onto four rows on a phone (119px
217   of pager); one nowrap row that scrolls keeps it at ~34px, like the reference. */
218[data-pager]{scrollbar-width:none}
219[data-pager]::-webkit-scrollbar{display:none}
220@media (max-width:767px){
221  [data-pager]{flex-wrap:nowrap;overflow-x:auto;justify-content:flex-start;
222    gap:5px;-webkit-overflow-scrolling:touch}
223  [data-pager] > *{flex:0 0 auto}
224}
225/* One rhythm for the whole ad/pager/flyer stack, so the gap from an ad to the
226   pager, from the pager to the flyer, and from the flyer to the next pager are
227   all the same measure. The pager carries its spacing as padding (it sits
228   inside the viewer card, against the flyer) while the ads carry theirs as
229   margin, so both are set from the same variable rather than eyeballed apart.
230   !important because the pager's padding is inline on the element. */
231:root{--rhythm:26px}
232/* Base first: the per-pager rules below are the same specificity (0,1,0), so
233   source order is what decides between them. With this rule trailing, it
234   silently overrode both mirrored pagers back to a uniform 26px. */
235[data-pager]{padding:var(--rhythm) 14px!important}
236@media (max-width:767px){
237  /* The main column is a flex column with gap:64px, and flex margins do not
238     collapse — so every ad section carried 64px of gap PLUS its own 18px margin
239     on each side: 82px of nothing above and below each unit. The gap is halved
240     here and the ads give up their margins, so the rhythm below is the only
241     thing setting these distances.
242
243     --rhythm is the gap a reader actually SEES, not a padding value, and the
244     three sides are derived from it rather than guessed:
245
246       ad bottom  → pager button = (column gap) + (gap-side padding)
247       pager btn  → flyer edge   = (flyer-side padding) + 1px card border
248
249     so gap-side padding = --rhythm/2 (matching the halved column gap) and
250     flyer-side padding = --rhythm - 1px. The two pagers are mirror images of
251     each other because they sit on opposite sides of the flyer. */
252  main > div, main > div > div{gap:calc(var(--rhythm) / 2)!important}
253  [aria-label="Advertisement"]:has([data-rsoc-ad]){
254    margin-top:0!important;margin-bottom:0!important;
255  }
256  [data-pager-top]{
257    padding:calc(var(--rhythm) / 2) 14px calc(var(--rhythm) - 1px)!important;
258  }
259  [data-pager]:not([data-pager-top]){
260    padding:var(--rhythm) 14px calc(var(--rhythm) / 2 - 1px)!important;
261  }
262}
263/* The top pager stays at both widths. It was written as a phone-only affordance,
264   but the flyer image runs past the fold on desktop too, so the bottom control is
265   equally out of reach there — and the rule never actually applied, because the
266   element carries an inline display:flex that no stylesheet rule can outrank. */
267/* A slot that takes no fill leaves its "Advertisement" label stranded over
268   nothing — 19px of chrome announcing an ad that is not there. The tag sets
269   data-filled on the placeholder when a creative lands, so the label rides on
270   that. Only the label is withheld, never the slot's reserved height, so this
271   costs 19px of shift on fill and never the full creative height. */
272[aria-label="Advertisement"]:not(:has([data-filled])) > div:first-child{display:none}
273/* The prototypes are inconsistent about the label: 7 of the wrappers across the
274   six templates ship it without text-align:center, so on those the word sat hard
275   against the left edge of a full-bleed band and read as clipped. Normalised
276   here rather than in the templates, so a re-imported prototype cannot bring it
277   back. The slot is centred the same way — a creative narrower than its band
278   (a 300x250 in a 527px column) would otherwise sit left. */
279[aria-label="Advertisement"]:has([data-rsoc-ad]) > div:first-child{
280  text-align:center!important;width:100%!important}
281[aria-label="Advertisement"]:has([data-rsoc-ad]) > [data-rsoc-ad]{
282  text-align:center!important}
283[aria-label="Advertisement"]:has([data-rsoc-ad]) > [data-rsoc-ad] > *{
284  margin-left:auto!important;margin-right:auto!important}
285/* A slot the auction declined is hidden by the tag, but its wrapper stays — the
286   label and the wrapper's own margins keep occupying the page, which is the
287   "vacant space where an ad should be". Collapse the whole wrapper instead.
288   Safe for CLS: this fires on the render callback, after the reserve has already
289   done its job of holding the space while the auction ran. */
290[aria-label="Advertisement"]:has([data-rsoc-ad][data-empty]){display:none!important}
291/* A position switched off for this page kind (see limitFlyerAds): the wrapper
292   goes with it, so the label and its margins do not hold open a gap where no ad
293   was ever going to be requested. */
294[aria-label="Advertisement"]:has([data-rsoc-ad-off]){display:none!important}
295/* Reserve the label, not the creative. A slot the auction declines used to hold
296   280px of empty page open until the collapse fired — and if AdSense never
297   answered at all, forever. offermate reserve ~18px of label and nothing else,
298   so an unanswered slot costs a line of text. Trades a little layout shift on
299   fill for never showing a hole. */
300[aria-label="Advertisement"]:has([data-rsoc-ad]) > [data-rsoc-ad]{min-height:0!important}
301/* The box the creative actually lands in has to be an ordinary block that does
302   not clip. Every placeholder here is a compiled GPT div, and the prototype
303   styled those as flex boxes with overflow:hidden — they held a dashed
304   "GPT slot 300x600" preview that wanted centring and clipping. Harmless for a
305   GPT slot, which renders into its own div; fatal for an AdSense <ins>, which
306   has to insert an iframe.
307
308   Measured on the flyer pages: the unit under the scan sat as a flex item in an
309   overflow:hidden nowrap row, reported 671px wide, and AdSense created its host
310   container and then never inserted an iframe and never set a verdict — an
311   empty 280px box, still pending 135 seconds after load and unchanged by
312   scrolling it into view. The same slot pushed into a plain block container on
313   the same page answered in under ten seconds. That is the whole difference.
314
315   This is also why it looked like the store page worked and flyer pages did
316   not: the position that lands in a clipping flex box differs by template. */
317[aria-label="Advertisement"] [data-rsoc-ad]{
318  display:block!important;overflow:visible!important;width:100%;min-width:0;max-width:100%}
319/* The masthead scrolls away with the page, on every device. The prototype pins
320   it at top:0 in all six templates; targeting the inline rule rather than the
321   element keeps this from catching any other <header> on the page. The sticky
322   rails were offset 112px to clear it, which would otherwise leave that much
323   dead space above them, so they move up too. */
324header[style*="position:sticky"]{position:static!important;top:auto!important}
325[style*="position:sticky"][style*="top:112px"]{top:12px!important}
326/* Both flyer-page columns travel with the reader. The prototype sticks the
327   right aside and, on the left, only the newsletter box inside it — so the
328   store card and the "all flyers" button scrolled away while the right rail
329   stayed. Sticky needs the flex parent's align-items:flex-start, which main
330   already sets, and it is scoped to the brochure page group so the store and
331   home layouts are untouched. Desktop only: on a phone the columns are stacked
332   and a sticky one would sit on top of the flyer. */
333@media (min-width:1025px){
334  html[data-page-group="brochure"] main > aside,
335  html[data-page-group="shop"] main > aside{
336    position:sticky!important;top:12px!important;align-self:flex-start!important}
337  html[data-page-group="shop"] main > aside > [style*="position:sticky"]{position:static!important}
338}
339/* The scan sits in a flex-wrap:nowrap row, so the unit injected after it cannot
340   take its own line however large a flex-basis it asks for — it just shrinks,
341   87px beside a 671px scan, which is both the ad-beside-the-flyer look and the
342   reason AdSense had nothing to serve it. Let that one row wrap, and the
343   100%-basis child drops below the scan at full column width. Targeted at the
344   row that actually contains the injected unit so no other flex row on the page
345   changes behaviour. */
346html[data-page-group="brochure"] div:has(> [aria-label="Advertisement"] [data-rsoc-ad]){
347  flex-wrap:wrap!important}
348/* Separation for the unit under the scan, on a phone.
349   The mobile block zeroes every ad wrapper's margins — deliberately, because
350   spacing there comes from the flex column's gap and margins were doubling it —
351   and it does so with !important, so the inline margin that works on desktop
352   was being discarded: measured 0px between the scan and the ad, and 27px to
353   the "Prev" button. This is more specific than that rule and trails it, so it
354   wins on both counts. Below stays the larger of the two: that is the side the
355   pagination is on, and a thumb reaching for "Next" should not find an ad. */
356@media (max-width:767px){
357  html[data-page-group="brochure"] [aria-label="Advertisement"]:has([data-rsoc-ad-name="mid"]){
358    margin-top:24px!important; margin-bottom:32px!important}
359}
360
361html[data-page-group="brochure"] [data-rsoc-ad][data-rsoc-ad-name="header"],
362html[data-page-group="brochure"] [data-rsoc-ad][data-rsoc-ad-name="mid"]{min-height:1px!important;max-height:none!important;overflow:visible!important}
363
364/* Reserve space without constraining the height Google actually renders. */
365html [aria-label="Advertisement"]:has([data-rsoc-ad]){clear:both;margin-top:24px!important;margin-bottom:32px!important;min-width:0;isolation:isolate}
366html [aria-label="Advertisement"]:has([data-rsoc-ad]) [data-rsoc-ad]{aspect-ratio:auto!important;height:auto!important}
367/* Reserve only once a request is actually in flight.
368   The units below the fold are lazy — they are requested when the reader comes
369   near them — so an unconditional reserve held blank page open on every slot
370   nobody ever scrolled to. Measured on the flyer viewer: the unit under the
371   scan sat at 673x280 on desktop and 390x250 on a phone, visible, empty, with
372   no <ins> in it at all, and it stayed that way for the whole visit. Before the
373   request there is nothing to reserve for, and the label alone is enough.
374   data-em-requested is stamped by the loader at request time. */
375html [aria-label="Advertisement"]:has([data-rsoc-ad]) [data-rsoc-ad][data-em-requested]{min-height:280px!important}
376html [aria-label="Advertisement"]:has([data-rsoc-ad]) [data-rsoc-ad][data-em-requested][data-rsoc-ad-name="square"]{min-height:250px!important}
377html [aria-label="Advertisement"]:has([data-rsoc-ad]) [data-rsoc-ad][data-em-requested][data-rsoc-ad-name="sidebar"],
378html [aria-label="Advertisement"]:has([data-rsoc-ad]) [data-rsoc-ad][data-em-requested][data-rsoc-ad-name="sidebarLeft"]{min-height:600px!important}
379html [aria-label="Advertisement"]:has([data-rsoc-ad]) > div:first-child{display:block;min-height:12px}
380html[data-page-group="home"] [aria-label="Advertisement"]:has([data-rsoc-ad]) [data-rsoc-ad][data-em-requested][data-rsoc-ad-name="header"]{min-height:100px!important}
381@media(max-width:767px){
382html [aria-label="Advertisement"]:has([data-rsoc-ad]) [data-rsoc-ad][data-em-requested]{min-height:250px!important}
383}
384/* Once Google answers, its real height replaces the reservation. This rule used
385   to sit inside the phone-only block above, so on desktop a 280px reserve stayed
386   at 280 even when the creative came back shorter. Keyed on an attribute the
387   loader sets rather than :has(), so it cannot lose on specificity to the
388   per-position reserves above (the rail's is three attributes deep). */
389html [aria-label="Advertisement"]:has([data-rsoc-ad]) [data-rsoc-ad][data-em-requested][data-em-answered][data-rsoc-ad-name]{
390  min-height:0!important;aspect-ratio:auto!important}
391
392/* ---------- horizontal overflow guard --------------------------------------
393   An ad creative wider than its column drags the whole page into horizontal
394   scroll: the layout shifts left, the sticky header slides out of frame, and the
395   reader can swipe into empty space. It has happened with a wide product-carousel
396   creative, and the container it arrived in was Auto Ads' own — so this cannot be
397   fixed by sizing our slots better. The page has to be unable to scroll sideways
398   no matter what any ad does inside it.
399
400   clip, NOT hidden: overflow-x:hidden establishes a scroll container, which
401   breaks position:sticky for every descendant — and this page has three sticky
402   elements including the header and the rail. clip contains the overflow
403   without creating that container, so sticky keeps working.
404
405   Nothing is concealed that a reader could otherwise have reached: the scroll it
406   removes only ever led to empty page. Ad containers are separately capped to the
407   page width so a creative is contained rather than clipped where possible. */
408html,body{overflow-x:clip;max-width:100%}
409[hidden]{display:none!important}
410.google-auto-placed{max-width:100%!important}
411body[data-anchor-spacing]{padding-top:var(--em-anchor-top,0px)!important;padding-bottom:var(--em-anchor-bottom,0px)!important}
412/* Store branding and a quieter desktop header. */
413[data-em-brand]>img{width:214px!important;height:38px!important;object-fit:contain}
414html[data-header-color] header>div:first-child{background:var(--store-header)!important}
415@media(min-width:1025px){
416 header>div:first-child{padding-top:6px!important;padding-bottom:6px!important;gap:12px 22px!important}
417 header form{max-width:580px!important;padding:3px 3px 3px 14px!important}
418 header form input{min-height:32px!important;padding:5px 0!important;font-size:15px!important}
419 header form button{padding:9px 20px!important}
420 header nav>div>a{padding:10px 12px!important;font-size:14px!important;min-height:38px!important}
421 html[data-page-group="home"] main{width:100%!important;max-width:1440px!important;padding-left:clamp(56px,6vw,112px)!important;padding-right:clamp(56px,6vw,112px)!important}
422 html[data-page-group="home"] main h1{font-size:clamp(30px,2.6vw,40px)!important;font-weight:600!important;line-height:1.18!important;letter-spacing:-.025em!important}
423 [data-home-store-grid]{grid-template-columns:repeat(6,minmax(0,1fr))!important}
424 [data-home-store-grid]>a:nth-child(n+13){display:none!important}
425 [data-home-store-grid]:not(:has(>a:nth-child(13)))+div{display:none!important}
426}
427[data-home-store-grid]>a{padding:12px 8px!important;min-height:114px!important;gap:10px!important}
428[data-home-store-grid]>a>img{width:100%!important;max-width:112px!important;height:54px!important;object-fit:contain}
429[data-home-store-grid]>a>span{font-size:15px!important;font-weight:650!important}
430[data-all-stores]{border:1px solid #E7DFD3;border-radius:24px;padding:0 18px!important;background:white}
431/* Compact previews fill their frames from the top. Full pages remain in the viewer. */
432[data-flyer-cover]{aspect-ratio:4/5!important;background:#f7f5f0!important}
433[data-flyer-cover]>img{display:block;width:100%;height:100%;object-fit:cover!important;object-position:center top!important}
434[data-flyer-card]:has(>[data-flyer-cover]){width:100%;max-width:220px;justify-self:center;border-radius:10px!important}
435html [data-store-flyer-grid]>[data-hero-flyer="1"]{max-width:220px!important;width:100%!important}
436/* Fit columns to the available content width, not merely the screen width.
437   A two-column 1280px desktop layout previously squeezed this ad to 252px. */
438@media(min-width:1025px){
439 html [data-store-flyer-grid]{display:grid!important;grid-template-columns:minmax(0,1fr)!important;gap:18px!important;justify-items:center}
440 html[data-page-group="shop"] main>div{--store-card-width:220px}
441 @container(min-width:592px){
442   html[data-page-group="shop"] [data-flyer-card]{--store-card-width:clamp(128px,calc((100cqw - 336px)/2),220px);max-width:var(--store-card-width)!important}
443   html [data-store-flyer-grid]{grid-template-columns:300px repeat(2,clamp(128px,calc((100cqw - 336px)/2),220px))!important;justify-content:center!important}
444   html [data-store-flyer-grid]>[data-store-square]{width:100%!important}
445 }
446 html [data-store-rest-grid]{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:18px;justify-items:center}
447 html [data-store-square] [data-rsoc-ad]{min-height:300px}
448 html [data-store-square] [data-rsoc-ad][data-em-answered]{min-height:0}
449}
450/* Preserve useful content width on compact desktops by stacking both rails
451   in the left column. The ad stays 300px instead of squeezing the flyer row. */
452@media(min-width:1025px) and (max-width:1423px){
453 html[data-page-group="shop"] main{display:grid!important;grid-template-columns:300px minmax(0,1fr)!important;grid-template-rows:auto 1fr;gap:24px 20px!important}
454 html[data-page-group="shop"] main>div{grid-column:2;grid-row:1/3;max-width:100%!important;width:100%}
455 html[data-page-group="shop"] main>aside:first-child{grid-column:1;grid-row:1;position:static!important}
456 html[data-page-group="shop"] main>aside:last-child{grid-column:1;grid-row:2;position:sticky!important}
457}
458[data-flyer-cover]+div{padding:12px!important;gap:6px!important}
459[data-flyer-cover]+div>span:first-child{font-size:15px!important;line-height:1.35!important;height:2.7em!important;min-height:2.7em!important;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
460[data-flyer-cover]+div>span:last-child{font-size:12px!important;line-height:1.5!important;height:3em;min-height:3em;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
461[data-more-store-sets]{grid-column:1/-1;justify-self:start}
462[data-related-flyers]>div{grid-template-columns:repeat(3,minmax(0,1fr))!important;gap:18px!important}
463@media(max-width:767px){
464  /* Cards share two equal tracks; ad hosts always span the complete row.
465     Avoid viewport-wide/negative-margin card grids affecting ad measurements. */
466  div:has(>[data-flyer-card]>[data-flyer-cover]){display:grid!important;width:100%!important;max-width:100%!important;margin-inline:0!important;grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:12px!important;align-items:start}
467  html [data-flyer-card]:has(>[data-flyer-cover]),html [data-store-flyer-grid]>[data-hero-flyer="1"]{width:100%!important;max-width:none!important;min-width:0!important;flex-basis:auto!important;border:1px solid #E7DFD3!important;border-radius:10px!important}
468  html [data-store-flyer-grid]>[aria-label="Advertisement"]{grid-column:1/-1!important;width:100vw!important;max-width:100vw!important;min-width:0!important;justify-self:stretch}
469  [data-flyer-cover]{aspect-ratio:4/5!important}
470  [data-flyer-cover]+div{padding:10px!important;gap:6px!important}
471  [data-flyer-cover]+div>span:first-child{font-size:14px!important}
472  [data-flyer-cover]+div>span:last-child{font-size:11px!important;height:4.5em;min-height:4.5em;-webkit-line-clamp:3}
473  html[data-page-group="shop"] main h1{width:calc(100% + 16px);max-width:none;margin-left:-8px!important;margin-right:-8px!important;text-wrap:wrap!important;overflow-wrap:break-word}
474}
475
476/* Sponsored flyers sit in the organic grid and must be labelled as such. */
477.em-sponsored figcaption::after{content:"Sponsored";display:block;margin-top:2px;
478  font-size:12px;font-weight:600;letter-spacing:.02em;color:#6B6259}
479</style>
480
481<link rel="manifest" href="/manifest.webmanifest">
482
483</head>
484<body>
485<div style="min-height:100vh;display:flex;flex-direction:column">
486<header style="position:sticky;top:0;z-index:50;background:#fff;border-bottom:1px solid #E7DFD3">
487  <div style="width:min(1560px,100%);margin:0 auto;padding:16px clamp(20px,4.4vw,96px);
487display:flex;flex-wrap:wrap;align-items:center;gap:12px 28px">
488    <a data-em-brand aria-label="Europianmedia home" href="/" style="display:flex;align-items:center;gap:9px;min-height:44px;flex:0 0 auto"><img src="/logo.svg" alt="Europianmedia" width="214" height="38"></a>
489    <button type="button" data-burger="1" onClick="" aria-label="Menu" aria-expanded="" style="flex:0 0 auto;width:44px;height:44px;margin-left:auto;border:1px solid #E7DFD3;border-radius:12px;background:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;cursor:pointer">
490      <span style="width:18px;height:2px;background:#16130F;border-radius:2px"></span>
491      <span style="width:18px;height:2px;background:#16130F;border-radius:2px"></span>
492      <span style="width:18px;height:2px;background:#16130F;border-radius:2px"></span>
493    </button>
494    <form onSubmit="" style="flex:1 1 300px;min-width:0;max-width:640px;margin:0 auto;display:flex;align-items:center;gap:8px;background:#FFFFFF;border:1px solid #E7DFD3;border-radius:999px;padding:5px 5px 5px 15px">
495      <svg width="15" height="15" viewBox="0 0 16 16" fill="none" style="flex:0 0 auto" aria-hidden="true"><circle cx="7" cy="7" r="5.2" stroke="#6B6259" stroke-width="1.5"></circle><path d="M11 11l3.2 3.2" stroke="#6B6259" stroke-width="1.5" stroke-linecap="round"></path></svg>
496      <input type="search" placeholder="Search stores, flyers, products" aria-label="Search stores, flyers, products" style="flex:1 1 auto;min-width:0;border:0;background:none;outline:none;font-size:16.5px;color:#16130F;padding:12px 0;min-height:44px">
497      <button type="submit" style="flex:0 0 auto;border:0;border-radius:999px;background:#16130F;color:#fff;font-size:15px;font-weight:700;padding:11px 22px;cursor:pointer" data-hv="h1">Search</button>
498    </form>
499  </div>
500  <nav data-open="" style="border-top:1px solid #F0E9DE">
501    <div style="width:min(1560px,100%);margin:0 auto;padding:0 clamp(20px,4.4vw,96px);display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:2px 4px">
502      <a href="/search/" style="display:flex;align-items:center;gap:6px;padding:15px 14px;font-size:16px;font-weight:700;color:#E4572E">
503        <svg width="13" height="13" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M8.6 1.8H14v5.4l-6.4 6.4a1.4 1.4 0 0 1-2 0L2.2 10.2a1.4 1.4 0 0 1 0-2L8.6 1.8z" stroke="#E4572E" stroke-width="1.4" stroke-linejoin="round"></path><circle cx="11.2" cy="4.7" r="1" fill="#E4572E"></circle></svg>
504        Today's deals
505      </a>
506      <a href="/supermarkets/" data-category-nav="supermarkets" style="padding:15px 14px;font-size:16px;color:#6B6259;font-weight:400;border-bottom:2px solid transparent">Supermarkets</a>
507      <a href="/home-garden/" data-category-nav="home-garden" style="padding:15px 14px;font-size:16px;color:#6B6259;font-weight:400;border-bottom:2px solid transparent">Home &amp; Garden</a>
508      <a href="/clothing-footwear-sport/" data-category-nav="clothing-footwear-sport" style="padding:15px 14px;font-size:16px;color:#6B6259;font-weight:400;border-bottom:2px solid transparent">Clothing &amp; Sport</a>
509      <a href="/health-beauty/" data-category-nav="health-beauty" style="padding:15px 14px;font-size:16px;color:#6B6259;font-weight:400;border-bottom:2px solid transparent">Health &amp; Beauty</a>
510      <a href="/electronics-hobby-everyday/" data-category-nav="electronics-hobby-everyday" style="padding:15px 14px;font-size:16px;color:#6B6259;font-weight:400;border-bottom:2px solid transparent">Electronics</a>
511      
512      
513      <a href="/search/" style="padding:15px 14px;font-size:16px;color:#6B6259">All stores</a>
514    </div>
515  </nav>
516</header>
517<div style="width:min(1560px,100%);margin:0 auto;padding:18px clamp(20px,4.4vw,96px) 0">
518  
519</div>
520<main style="width:min(1560px,100%);margin:0 auto;padding:26px clamp(20px,4.4vw,96px) 60px;display:flex;flex-wrap:wrap;justify-content:center;gap:32px;align-items:flex-start;flex:1 1 auto">
521
522  <article style="flex:1 1 620px;max-width:900px"><h1 style="font-family:'Libre Franklin',system-ui,sans-serif;font-weight:800;font-size:clamp(32px,3.8vw,48px);line-height:1.08;margin:0 0 14px">About</h1><div style="font-size:18px;line-height:1.7;color:#3D3730"><p>europianmedia is an independent directory of retailer circulars. Browse stores and categories, open a flyer and use Previous and Next to read each scan.</p><h2>How to use the flyers</h2><p>Check the start and end dates before shopping. Upcoming previews are not yet valid, while expired circulars are kept for reference. Price
522s, availability, loyalty requirements and regional restrictions are determined by the retailer. Check the scan and your local store for the final offer terms.</p><p>Text recognised from scans is automated and may contain errors. It is a browsing aid, not a verified price list. We earn revenue from labelled advertisements; these are separate from the retailer circulars. We are not affiliated with the stores listed.</p><p><a href="/search/">Browse the store directory and flyers</a>.</p></div></article>
523  <aside style="flex:0 1 300px;max-width:320px;min-width:0;position:sticky;top:112px;display:flex;flex-direction:column;gap:18px">
524    
525  </aside>
526</main>
527<footer style="background:#16130F;color:#C8BFB2;margin-top:auto">
528  <div style="width:min(1560px,100%);margin:0 auto;padding:52px clamp(20px,4.4vw,96px) 26px;display:flex;flex-wrap:wrap;gap:32px">
529    <div style="flex:1 1 260px;min-width:0">
530      <div style="display:flex;align-items:center;gap:9px;margin-bottom:12px">
531        <svg width="28" height="28" viewBox="0 0 32 32" aria-hidden="true"><rect width="32" height="32" rx="9" fill="#E4572E"></rect><path d="M17.3 5.5H24a2.5 2.5 0 0 1 2.5 2.5v6.7a2.5 2.5 0 0 1-.73 1.77l-9.5 9.5a2.5 2.5 0 0 1-3.54 0l-6.7-6.7a2.5 2.5 0 0 1 0-3.54l9.5-9.5a2.5 2.5 0 0 1 1.77-.73z" fill="#fff"></path><circle cx="21.3" cy="10.7" r="2.15" fill="#E4572E"></circle><path d="M12.4 16.3l3.3 3.3M15.6 13.1l3.3 3.3" stroke="#E4572E" stroke-width="1.5" stroke-linecap="round"></path></svg>
532        <span style="font-family:'Libre Franklin',system-ui,sans-serif;font-size:18px;letter-spacing:-0.018em;color:#fff"><span style="font-weight:800">europian</span><span style="font-weight:500;color:#8D857B">media</span></span>
533      </div>
534      <p style="margin:0 0 16px;font-size:15px;line-height:1.7;max-width:34ch">Every weekly ad in one place. Independent, free to use, and never affiliated with the retailers we list.</p>
535      <div style="display:flex;flex-wrap:wrap;gap:8px">
536        
537        
538        
539      </div>
540    </div>
541
542    <div style="flex:1 1 140px;min-width:0">
543      <h4 style="font-family:'Libre Franklin',system-ui,sans-serif;font-weight:700;font-size:16px;color:#fff;margin:0 0 12px">Company</h4>
544      <div style="display:flex;flex-direction:column;gap:2px;font-size:15px">
545        <a href="/about/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">About us</a>
546        <a href="/contact/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Contact</a>
547        <a href="/contact/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Advertise with us</a>
548        
549        <a href="/contact/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Press</a>
550      </div>
551    </div>
552
553    <div style="flex:1 1 140px;min-width:0">
554      <h4 style="font-family:'Libre Franklin',system-ui,sans-serif;font-weight:700;font-size:16px;color:#fff;margin:0 0 12px">Legal</h4>
555      <div style="display:flex;flex-direction:column;gap:2px;font-size:15px">
556        <a href="/terms/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Terms of use</a>
557        <a href="/privacy/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Privacy policy</a>
558        <a href="/cookie-policy/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Cookie Policy</a>
559<a href="/cookie-policy/#manage-choices" data-consent-open style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Privacy choices</a>
560<a href="/disclaimer/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Disclaimer</a>
561        <a href="/advertising-disclosure/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Advertising disclosure</a>
562        <a href="/trademarks/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Trademarks &amp; DMCA</a>
563      </div>
564    </div>
565
566    <div style="flex:1 1 150px;min-width:0">
567      <h4 style="font-family:'Libre Franklin',system-ui,sans-serif;font-weight:700;font-size:16px;color:#fff;margin:0 0 12px">Popular categories</h4>
568      <div style="display:flex;flex-direction:column;gap:2px;font-size:15px">
569        <a href="/supermarkets/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Supermarket weekly ads</a>
570        <a href="/health-beauty/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Pharmacy circulars</a>
571        <a href="/home-garden/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Hardware &amp; garden</a>
572        <a href="/electronics-hobby-everyday/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Electronics deals</a>
573        <a href="/search/" style="display:flex;align-items:center;min-height:44px;color:#C8BFB2">Store directory A&ndash;Z</a>
574      </div>
575    </div>
576  </div>
577
578  <div style="border-top:1px solid #2B2620">
579    <div style="width:min(1560px,100%);margin:0 auto;padding:18px clamp(20px,4.4vw,96px);
579display:flex;flex-wrap:wrap;gap:10px 20px;align-items:center;justify-content:space-between;font-size:13.5px;color:#8D857B">
580      <span>&copy; 2026 europianmedia.com &mdash; all retailer names and logos are trademarks of their respective owners.</span>
581      <span>Ads served by Google Ad Manager &amp; AdSense</span>
582    </div>
583  </div>
584</footer>
585
586</div>
587
588<script>(function(){var d=document;
589var PG=d.documentElement.getAttribute('data-page-group')||'';
590var SHOP=d.documentElement.getAttribute('data-shop')||'';
591var scan=d.querySelector('[data-em-scan]');
592var pages=[];try{pages=JSON.parse(scan&&scan.getAttribute('data-em-pages')||'[]')}catch(e){}
593var pageCount=Number(scan&&scan.getAttribute('data-em-page-count'))||pages.length||1;
594var pn=Math.max(1,Math.min(pageCount,Number(scan&&scan.getAttribute('data-em-page-number'))||1));
595if(pages.length)pn=Math.max(1,Math.min(pageCount,parseInt(new URLSearchParams(location.search).get('page'),10)||pn));
596var flyerBase=scan&&(scan.getAttribute('data-em-base')||location.pathname);
597function observe(action,extra){
598  try{
599    extra=Object.assign({gate:PG,page_number:PG==='brochure'?pn:null,target:reportSession&&reportSession.id?'flyer-session:'+reportSession.id:''},extra||{});
600    if(window.RSOC&&window.RSOC.track)window.RSOC.track(action,extra);
601    else (window.__EM_TELEMETRY=window.__EM_TELEMETRY||[]).push({action:action,extra:extra});
602  }catch(e){}
603}
604// These stages describe browser execution, never Google's attribution or acceptance.
605function adsDiagnostic(stage,destination){observe(stage,{target:'google-ads:'+destination});}
606function ev(name,params){
607  params=Object.assign({send_to:"G-813T551WZB",page_group:PG,shop:SHOP},params||{});
608  if(scan){params.flyer_id=flyerBase;params.page_number=pn;params.page_count=pageCount;}
609  try{if("G-813T551WZB"&&window.gtag)window.gtag('event',name,params)}catch(e){}
610  try{if(name==='first_flyer_reached'||name==='brochure_visit')(window.dataLayer=window.dataLayer||[]).push(Object.assign({event:name},params))}catch(e){}
611  observe(name);
612}
613// A separate reporting session does not alter the existing Google Ads trigger.
614// Its landing event is emitted once, only for Google-paid entries on a store.
615var reportSession=null;
616try{
617  reportSession=JSON.parse(sessionStorage.getItem('em_report_session_v1')||'null');
618  var entryQuery=new URLSearchParams(location.search),entryClick='';
619  ['gclid','gbraid','wbraid','fbclid','ttclid','ScCid','sccid','msclkid'].some(function(k){var v=entryQuery.get(k);if(v&&!/[{}]/.test(v)&&!/^__.+__$/.test(v)){entryClick=k+':'+v;return true}return false});
620  if(!reportSession||Date.now()-reportSession.last>=1800000||(entryClick&&entryClick!==reportSession.entryClick)){
621    reportSession={entryClick:entryClick,id:window.crypto&&window.crypto.randomUUID?window.crypto.randomUUID():Date.now().toString(36)+'-'+Math.random().toString(36).slice(2),last:Date.now()};
622    sessionStorage.setItem('em_report_session_v1',JSON.stringify(reportSession));
623    ev('flyer_session_start');
624    if(PG==='shop'&&window.__EM_TRAFFIC_BUCKET==='google_paid')ev('flyer_store_landing');
625  }
626  reportSession.last=Date.now();sessionStorage.setItem('em_report_session_v1',JSON.stringify(reportSession));
627}catch(e){reportSession=null;}
628// Match the brochure-visit goal: one brochure arrival per tab session,
629// including direct/deep reader landings. No image, timer or ad condition.
630// Keep this flag independent of the 30-minute reporting-session window.
631if(true&&PG==='brochure'){
632  var newBrochureVisit=false;
633  try{
634    if(!sessionStorage.getItem('em_brochure_visit_v1')){
635      // Preserve a conversion already sent under the previous definition in
636      // this tab; switching the goal must not replay the same destination.
637      var priorConversion=false;
638      try{priorConversion=JSON.parse(sessionStorage.getItem('em_first_flyer_v1')||'null')?.converted===true;}catch(e){}
639      sessionStorage.setItem('em_brochure_visit_v1','1');newBrochureVisit=!priorConversion;
640    }
641  }catch(e){}
642  if(newBrochureVisit){
643    ev('brochure_visit',{qualification:'brochure_page_arrival',flyer_id:flyerBase||location.pathname});
644    
645(function(){
646 var destination="AW-18353253259/k3PTCI2vifIcEIvXwa9E",callbackSeen=false;
647 function diagnostic(stage){if(typeof adsDiagnostic==='function')adsDiagnostic(stage,destination);}
648 try{
649  if(typeof gtag!=='function'){diagnostic('google_ads_tag_error');return;}
650  gtag('event','conversion',{send_to:destination,
651   event_callback:function(){if(callbackSeen)return;callbackSeen=true;
651diagnostic('google_ads_tag_callback');},event_timeout:2000});
652  diagnostic('google_ads_tag_queued');
653  setTimeout(function(){if(!callbackSeen)diagnostic('google_ads_tag_no_callback');},5000);
654 }catch(e){diagnostic('google_ads_tag_error');}
655})();
656(function(){
657 var destination="AW-18353250616/mVz8CNDMgvMcELjCwa9E",callbackSeen=false;
658 function diagnostic(stage){if(typeof adsDiagnostic==='function')adsDiagnostic(stage,destination);}
659 try{
660  if(typeof gtag!=='function'){diagnostic('google_ads_tag_error');return;}
661  gtag('event','conversion',{send_to:destination,
662   event_callback:function(){if(callbackSeen)return;callbackSeen=true;diagnostic('google_ads_tag_callback');},event_timeout:2000});
663  diagnostic('google_ads_tag_queued');
664  setTimeout(function(){if(!callbackSeen)diagnostic('google_ads_tag_no_callback');},5000);
665 }catch(e){diagnostic('google_ads_tag_error');}
666})();
667(function(){
668 var destination="AW-18303349347/CgrsCJr73YodEOPk25dE",callbackSeen=false;
669 function diagnostic(stage){if(typeof adsDiagnostic==='function')adsDiagnostic(stage,destination);}
670 try{
671  if(typeof gtag!=='function'){diagnostic('google_ads_tag_error');return;}
672  gtag('event','conversion',{send_to:destination,
673   event_callback:function(){if(callbackSeen)return;callbackSeen=true;
673diagnostic('google_ads_tag_callback');},event_timeout:2000});
674  diagnostic('google_ads_tag_queued');
675  setTimeout(function(){if(!callbackSeen)diagnostic('google_ads_tag_no_callback');},5000);
676 }catch(e){diagnostic('google_ads_tag_error');}
677})();
678(function(){
679 var destination="AW-18303341653/3snDCIyW74odENWo25dE",callbackSeen=false;
680 function diagnostic(stage){if(typeof adsDiagnostic==='function')adsDiagnostic(stage,destination);}
681 try{
682  if(typeof gtag!=='function'){diagnostic('google_ads_tag_error');return;}
683  gtag('event','conversion',{send_to:destination,
684   event_callback:function(){if(callbackSeen)return;callbackSeen=true;diagnostic('google_ads_tag_callback');},event_timeout:2000});
685  diagnostic('google_ads_tag_queued');
686  setTimeout(function(){if(!callbackSeen)diagnostic('google_ads_tag_no_callback');},5000);
687 }catch(e){diagnostic('google_ads_tag_error');}
688})();
689  }
690}
691var foregroundMs=0,readingMs=0,lastTick=Date.now(),minuteSent=false,viewSent=false,qualified=false;
692var session={last:Date.now(),pages:[],converted:false};
693try{var old=JSON.parse(sessionStorage.getItem('em_engagement_v1')||'null');if(old&&Date.now()-old.last<1800000)session=old}catch(e){}
694// Only an actual same-store flyer link arms the conversion. A direct landing,
695// store thumbnail, ad click or pagination view cannot qualify on its own.
696var funnel={last:Date.now(),converted:false,pending:null},firstVisibleSince=null,imageLoadedSent=false,imageVisibleSent=false;
697try{var saved=JSON.parse(sessionStorage.getItem('em_first_flyer_v1')||'null');if(saved&&Date.now()-saved.last<1800000)funnel=saved}catch(e){}
698if(PG==='brochure'&&pn===1){
699  observe('flyer_reader_arrival');
700  if(!funnel.pending)observe('flyer_qualification_blocked',{gate:'missing_store_intent'});
701}
702function saveFunnel(){funnel.last=Date.now();try{sessionStorage.setItem('em_first_flyer_v1',JSON.stringify(funnel))}catch(e){}}
703function armFlyer(e){
704  if(PG!=='shop'||funnel.converted||(e.type==='auxclick'&&e.button!==1))return;
705  var a=e.target.closest&&e.target.closest('a[href]');if(!a)return;
706  try{
707    var u=new URL(a.href,location.href),parts=u.pathname.split('/').filter(Boolean);
708    if(u.origin!==location.origin||parts.length!==2||parts[0]!==SHOP||(u.searchParams.has('page')&&u.searchParams.get('page')!=='1'))return;
709    funnel.pending={base:'/'+parts.join('/')+'/',shop:SHOP,at:Date.now()};saveFunnel();
710    observe('flyer_link_open');
711  }catch(e){}
712}
713// Capture the content-link intent before a vignette/router intercepts the
714// navigation. Arming alone never converts: the matching loaded destination
715// must still pass the first-image visibility and session checks below.
716d.addEventListener('click',armFlyer,{passive:true,capture:true});
717d.addEventListener('auxclick',armFlyer,{passive:true,capture:true});
718function firstFlyer(now){
719  var p=funnel.pending;
720  if(funnel.converted||pn!==1||!p||p.base!==flyerBase||p.shop!==SHOP||now-p.at>=1800000)return;
721  // A full-screen ad or menu can cover a geometrically visible image. Wait
722  // until the reader can actually see the scan; never inspect an ad iframe.
723  var r=scan.getBoundingClientRect(),left=Math.max(0,r.left),right=Math.min(innerWidth,r.right);
724  var top=Math.max(0,r.top),bottom=Math.min(innerHeight,r.bottom);
725  if(right<=left||d.elementFromPoint((left+right)/2,(top+bottom)/2)!==scan){firstVisibleSince=null;return;}
726  if(firstVisibleSince===null){firstVisibleSince=now;return;}
727  if(now-firstVisibleSince<2000)return;
728  // Queue a single direct Google Ads event; GA4 receives a separate observation.
729  // No arbitrary monetary value or inferred AdSense click is attached.
730  funnel.converted=true;saveFunnel();
731  ev("first_flyer_image_viewed",{qualification:'store_to_first_visible_image'});
732  
733}
734function persist(){session.last=Date.now();try{sessionStorage.setItem('em_engagement_v1',JSON.stringify(session))}catch(e){}}
735function qualify(reason){
736  if(qualified||session.converted)return;
737  qualified=true;session.converted=true;persist();
738  ev("engaged_flyer_visit",{qualification:reason});
739  
740}
741function scanVisible(){
742  if(!scan||!scan.complete||!scan.naturalWidth)return false;
743  var r=scan.getBoundingClientRect();
744  return r.width>0&&r.height>0&&Math.min(r.bottom,innerHeight)-Math.max(r.top,0)>=Math.min(r.height,innerHeight)*0.25;
745}
746function tick(){
747  var now=Date.now(),delta=Math.min(Math.max(now-lastTick,0),1500);lastTick=now;
748  if(d.visibilityState!=='visible'){firstVisibleSince=null;return;}
749  foregroundMs+=delta;
750  if(!minuteSent&&foregroundMs>=60000){minuteSent=true;ev('one_minute_on_site')}
751  if(PG==='brochure'&&pn===1&&scan&&scan.complete&&scan.naturalWidth&&!imageLoadedSent){imageLoadedSent=true;observe('flyer_image_loaded');}
752  if(PG!=='brochure'||!scanVisible()){firstVisibleSince=null;return;}
753  if(pn===1&&!imageVisibleSent){imageVisibleSent=true;observe('flyer_image_in_viewport');}
754  firstFlyer(now);
755  readingMs+=delta;
756  if(!viewSent){
757    viewSent=true;ev('flyer_page_view');
758    var key=flyerBase+'#'+pn;
759    if(session.pages.indexOf(key)<0)session.pages.push(key);
760    session.pages=session.pages.slice(-50);persist();
761    if(session.pages.length>=2)qualify('second_page');
762  }
763  if(readingMs>=30000)qualify('30_seconds_reading');
764}
765var lastActivity=0,maxScroll=0;
766function measureScroll(){
767 var height=Math.max(d.documentElement.scrollHeight||0,d.body&&d.body.scrollHeight||0);
768 maxScroll=Math.max(maxScroll,height<=innerHeight?100:Math.min(100,Math.round(100*(window.scrollY||0)/(height-innerHeight))));
769}
770function activity(){
771 measureScroll();
772 observe('flyer_activity',{active_ms:Math.round(foregroundMs),reading_ms:Math.round(readingMs),scroll_depth:maxScroll});
773 lastActivity=Date.now();
774}
775var timer=setInterval(function(){tick();if(d.visibilityState==='visible'&&Date.now()-lastActivity>=15000)activity()},1000);
776d.addEventListener('visibilitychange',function(){if(d.visibilityState==='hidden')activity();lastTick=Date.now();firstVisibleSince=null});
777d.addEventListener('scroll',function(){measureScroll();if(!scanVisible())firstVisibleSince=null},{passive:true});
778window.addEventListener('pagehide',function(){
779  activity();clearInterval(timer);persist();saveFunnel();firstVisibleSince=null;
780  if(readingMs>0)ev('flyer_reading_time',{reading_time_ms:Math.round(readingMs)});
781});
782window.addEventListener('pageshow',function(e){if(e.persisted){
783  // Back/forward cache restores old JS state; read the newer conversion flag.
784  try{var fresh=JSON.parse(sessionStorage.getItem('em_first_flyer_v1')||'null');if(fresh&&Date.now()-fresh.last<1800000)funnel=fresh}catch(e){}
785  firstVisibleSince=null;lastTick=Date.now();timer=setInterval(function(){tick();if(d.visibilityState==='visible'&&Date.now()-lastActivity>=15000)activity()},1000);
786}});
787d.addEventListener('click',function(e){
788  var a=e.target.closest&&e.target.closest('a[href]');if(!a)return;
789  var card=a.closest('[data-flyer-card],[data-store-card],[data-hero-flyer]');if(!card)return;
790  var siblings=card.parentElement?Array.from(card.parentElement.children):[];
791  ev('select_flyer',{placement:card.getAttribute('data-placement')||PG,position:Math.max(1,siblings.indexOf(card)+1)});
792},{passive:true});
793
794function on(el,ev,fn){if(el)el.addEventListener(ev,fn)}
795/* Policy links always navigate; only the separate privacy control opens the CMP. */
796d.querySelectorAll('a[data-consent-open]').forEach(function(a){on(a,'click',function(e){if(window.googlefc&&typeof window.googlefc.showRevocationMessage==='function'){try{window.googlefc.showRevocationMessage();e.preventDefault()}catch(_){/* Follow the policy-page fallback if the CMP fails. */}}})});
797/* Google owns consent state. Open its documented UI only after a user action. */
798on(d.querySelector('[data-consent-settings]'),'click',function(){
799  var msg=d.querySelector('[data-consent-message]');
800  var fc=window.googlefc;
801  if(fc&&typeof fc.showRevocationMessage==='function'){
802    fc.showRevocationMessage();
803    if(msg)msg.textContent='Opening the available consent message. If it does not appear, use the provider and browser controls below.';
804  }else if(msg)msg.textContent='Consent controls are not available in this browser or region. Use the provider and browser controls below, or contact us for help.';
805});
806/* Hide newly expired offers on cached discovery pages; archives remain available.
807   Date comparison uses the same US reporting day as the server build. */
808function dateKey(s){var p=(s||'').split('/');return p.length===3&&p.every(function(x){return x&&!isNaN(Number(x))})?p[2]+'-'+p[0].padStart(2,'0')+'-'+p[1].padStart(2,'0'):''}
809function updateFreshness(){
810  var today=new Intl.DateTimeFormat('en-CA',{timeZone:'America/Ne
810w_York',year:'numeric',month:'2-digit',day:'2-digit'}).format(new Date());
811  d.querySelectorAll('[data-flyer-to]').forEach(function(a){
812    var end=dateKey(a.getAttribute('data-flyer-to')),from=dateKey(a.getAttribute('data-flyer-from'));
813    var expired=a.getAttribute('data-flyer-retired')==='1'||(end&&end<today);
814    if(a.closest('[data-flyer-archive]'))a.hidden=!expired;else if(expired)a.hidden=true;
815    if(!expired&&from&&from<=today){
816      var walk=d.createTreeWalker(a,NodeFilter.SHOW_TEXT),node;
817      while((node=walk.nextNode()))if(node.nodeValue.indexOf('Upcoming')>=0)node.nodeValue=node.nodeValue.replace('Upcoming','Current');
818    }
819  });
820  var first=d.querySelector('[data-store-flyer-grid]'),rest=d.querySelector('[data-store-rest-grid]');
821  if(first){
822    var cards=Array.from(d.querySelectorAll('[data-store-flyer-grid] > [data-hero-flyer], [data-store-rest-grid] > [data-hero-flyer]'));
823    var manual=cards.filter(function(a){return Number(a.getAttribute('data-flyer-rank'))>=0}).sort(function(a,b){return Number(a.getAttribute('data-flyer-rank'))-Number(b.getAttribute('data-flyer-rank'))}).map(function(a){return a.getAttribute('data-flyer-slug')});
824    function record(a){return {slug:a.getAttribute('data-flyer-slug'),validFrom:a.getAttribute('data-flyer-from'),validTo:a.getAttribute('data-flyer-to'),expired:a.getAttribute('data-flyer-retired')==='1',pages:[1]}}
825    cards.sort(function(a,b){return (function compareStoreFlyers(a, b, manual, today) {
826  const day = value => {
827    const s=String(value||'').trim(),m=/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/.exec(s);
828    return m ? m[3]+'-'+m[1].padStart(2,'0')+'-'+m[2].padStart(2,'0') : /^\d{4}-\d{2}-\d{2}$/.test(s) ? s : '';
829  };
830  const group = f => f.contentStatus==='placeholder'||!f.pages?.length ? 4 : f.expired||(day(f.validTo)&&day(f.validTo)<today) ? 3 : !day(f.validFrom) ? 4 : day(f.validFrom)>today ? 2 : 1;
831  const rank = f => {const i=(manual||[]).indexOf(f.slug);return i<0 ? Number.MAX_SAFE_INTEGER : i;};
832  const state=group(a)-group(b); if(state)return state;
833  const chosen=rank(a)-rank(b); if(chosen)return chosen;
834  // Current sets expire soonest first; open-ended sets follow dated ones.
835  // Upcoming sets follow all current sets, then nearest expiry/start first.
836  const endA=day(a.validTo),endB=day(b.validTo);
837  const expiry=(endA||'9999-12-31').localeCompare(endB||'9999-12-31');
838  if(expiry)return group(a)===3 ? -expiry : expiry;
839  return day(a.validFrom).localeCompare(day(b.validFrom)) || String(a.slug).localeCompare(String(b.slug));
840})(record(a),record(b),manual,today)});
841    var visible=cards.filter(function(a){return !a.hidden});
842    function place(parent,list){if(!parent)return;list.forEach(function(a,i){var at=parent.querySelectorAll(':scope > [data-hero-flyer]')[i];if(at!==a){if(at)parent.insertBefore(a,at);else parent.appendChild(a)}})}
843    // Hidden cards stay in the second grid so they cannot displace live cards.
844    if(rest){place(first,visible.slice(0,2));place(rest,visible.slice(2).concat(cards.filter(function(a){return a.hidden})));rest.hidden=visible.length<=2}
845    else place(first,visible.concat(cards.filter(function(a){return a.hidden})));
846    var more=d.querySelector('[data-more-store-sets]');if(more)more.hidden=visible.length<=2;
847  }
848  d.querySelectorAll('[data-archive-section]').forEach(function(section){section.hidden=!Array.from(section.querySelectorAll('[data-flyer-archive]')).some(function(a){return !a.hidden})});
849  var note=d.querySelector('[data-flyer-note]');
850  if(note){
851    var end=dateKey(note.getAttribute('data-to')),from=dateKey(note.getAttribute('data-from'));
852    if(note.getAttribute('data-retired')==='1'||(end&&end<today))note.textContent='Archived flyer — these offers have ended.';
853    else if(from&&from>today)note.textContent='Preview — these offers start on '+note.getAttribute('data-from')+'.';
854    else if(from&&!end)note.textContent='End date not supplied. Confirm these offers with the retailer before shopping.';
855    else if(!from)note.textContent='The validity dates are incomplete. Confirm this offer with the retailer before shopping.';
856    else note.textContent='Check the printed prices, pack sizes and offer conditions before shopping.';
857  }
858}
859updateFreshness();
860addEventListener('pageshow',updateFreshness);
861d.addEventListener('visibilitychange',function(){if(!d.hidden)updateFreshness()});
862/* Saving a flyer is a browser bookmark; no account or network request. */
863var save=d.querySelector('[data-save]');
864if(save){
865  var saveKey='em:saved:'+location.pathname;
866  function saved(){try{return localStorage.getItem(saveKey)==='1'}catch(e){return false}}
867  function paint(){save.textContent=saved()?'Saved on this device':'Save';save.setAttribute('aria-pressed',String(saved()))}
868  paint();on(save,'click',function(){try{if(saved())localStorage.removeItem(saveKey);else localStorage.setItem(saveKey,'1');paint()}catch(e){save.textContent='Saving unavailable'}});
869}
870
871d.querySelectorAll('[data-follow-store]').forEach(function(b){on(b,'click',function(){var input=d.querySelector('[data-newsletter] input[type=email]');if(input){input.scrollIntoView({block:'center'});input.focus()}else{location.href='/contact/'}})});
872/* category nav behind the hamburger below 760px */
873var burger=d.querySelector('[data-burger]'),nav=d.querySelector('header nav');
874on(burger,'click',function(){var open=nav.getAttribute('data-open')==='1';nav.setAttribute('data-open',open?'0':'1');burger.setAttribute('aria-expanded',String(!open));});
875/* Phone header: search is an icon, not a field.
876   The prototype's search form is flex:1 1 300px inside a flex-wrap container, so
877   on a 375px screen it wrapped onto its own line — a second full-width row under
878   the logo, on top of a 44px input. That is most of the first screen spent on a
879   control almost nobody uses on arrival. The icon sits on the logo line next to
880   the burger and reveals the real field on tap, so the default state costs one
881   44px square instead of a whole row. */
882var sform=d.querySelector('header form');
883if(sform){sform.setAttribute('action','/search/');sform.setAttribute('method','get');var qinput=sform.querySelector('input');if(qinput)qinput.name='q'}
884/* Search filters real prerendered results; user input is only assigned as text. */
885if(d.querySelector('[data-search-heading]')){
886  var query=(new URLSearchParams(location.search).get('q')||'').trim(),active='All';
887  if(sform&&qinput)qinput.value=query;
888
889  d.querySelector('[data-search-heading]').textContent=query?'Results for “'+query+'”':'All stores and flyers';
890  function filterResults(){
891    var counts={Stores:0,Flyers:0,Blogs:0};
892    var terms=query.toLowerCase().split(/\s+/).filter(Boolean);
893    var today=new Intl.DateTimeFormat('en-CA',{timeZone:'America/New_York',year:'numeric',month:'2-digit',day:'2-digit'}).format(new Date());
894    d.querySelectorAll('[data-search-item]').forEach(function(a){
895      var kind=a.getAttribute('data-search-item');
896      var hay=(a.textContent+' '+(a.getAttribute('data-search-words')||'')).toLowerCase();
897      var end=dateKey(a.getAttribute('data-flyer-to'));
898      var match=terms.every(function(term){return hay.indexOf(term)>=0})&&!(end&&end<today);
899      if(match)counts[kind]++;
900      a.hidden=!match||(active!=='All'&&active!==kind);
901    });
902    var total=counts.Stores+counts.Flyers+counts.Blogs,shown=active==='All'?total:counts[active];
903    d.querySelector('[data-search-summary]').textContent=query?total+' results across stores, flyer sets and articles.':'Browse stores, explore current and upcoming flyer sets, and find shopping advice.';
904    d.querySelectorAll('[data-search-filter]').forEach(function(b){
905      var kind=b.getAttribute('data-search-filter'),selected=kind===active;
906      b.setAttribute('aria-pressed',String(selected));
907      var count=b.querySelector('[data-search-count]');if(count)count.textContent=kind==='All'?total:counts[kind];
908    });
909    d.querySelectorAll('[data-search-section]').forEach(function(section){var kind=section.getAttribute('data-search-section');section.hidden=counts[kind]===0||(active!=='All'&&active!==kind)});
910    var empty=d.querySelector('[data-search-empty]');if(empty)empty.hidden=shown!==0;
911  }
912  d.querySelectorAll('[data-search-filter]').forEach(function(b){on(b,'click',function(){active=b.getAttribute('data-search-filter');filterResults()})});
913  filterResults();
914}
915if(sform&&burger){
916  var stog=d.createElement('button');
917  stog.type='button';
918  stog.setAttribute('data-search-toggle','1');
919  stog.setAttribute('aria-label','Search');
920  stog.setAttribute('aria-expanded','false');
921  stog.innerHTML='<svg width="19" height="19" viewBox="0 0 16 16" fill="none" aria-hidden="true">'
922    +'<circle cx="7" cy="7" r="5.2" stroke="currentColor" stroke-width="1.6"></circle>'
923    +'<path d="M11 11l3.2 3.2" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"></path></svg>';
924  burger.parentNode.insertBefore(stog,burger);
925  on(stog,'click',function(){
926    var open=sform.getAttribute('data-open')==='1';
927    sform.setAttribute('data-open',open?'0':'1');
928    stog.setAttribute('aria-expanded',String(!open));
929    // opening is a deliberate tap; put the caret where the reader is looking
930    if(!open){var i=sform.querySelector('input');if(i){try{i.focus()}catch(e){}}}
931  });
932}
933/* anchor unit must be dismissible — Better Ads requires it for sticky units */
934/* The prototype's inline anchor bar is stripped at build time (stripInlineAnchor),
935   so there is no bar to close and no body reserve to release. */
936/* Scroll position is deliberately NOT persisted. We used to store it per path in
937   localStorage and replay it on load, which restored the *last* position for that
938   URL on every visit — arriving at a page fresh, from a link or a new session,
939   dropped the reader at the footer where they happened to stop days earlier.
940   The browser already restores scroll for back/forward, keyed to the history
941   entry rather than the path, which is the behaviour that was actually wanted.
942   Removing this also drops a scroll listener and a localStorage write per frame.
943
944   Nothing replaces it: a fresh load already starts at the top, and forcing
945   scrollTo(0,0) here would fight the browser on a genuine back/forward.
946
947   Anyone who visited while the old code shipped still has em:scroll:* keys in
948   their browser. They are inert now, but sweep them so we are not leaving
949   storage behind on readers' devices for a feature that no longer exists. */
950try{for(var i=localStorage.length-1;i>=0;i--){var k=localStorage.key(i);
951  if(k&&k.indexOf('em:scroll:')===0)localStorage.removeItem(k)}}catch(e){}
952/* Newsletter capture — an email is worth more than one 300x250, and it is
953   inventory we own rather than rent. Posts to the platform collector so the
954   address lands in the same place as every other event. */
955d.querySelectorAll('[data-newsletter]').forEach(function(f){
956  f.addEventListener('submit',function(e){
957    e.preventDefault();
958    var input=f.querySelector('input[type=email]'), msg=f.querySelector('[data-newsletter-msg]');
959    var email=(input&&input.value||'').trim();
960    if(!email){return}
961    if(msg)msg.textContent='Subscribing…';
962    /* The address goes to /api/subscribe, which stores it in a private object.
963       It deliberately does NOT go through the analytics beacon: that would put
964       raw PII into the events table next to IP and user-agent. The analytics
965       event records that a signup happened and nothing about who. */
966    var sdk=d.querySelector('script[src*="sdk.js"]');
967    fetch('/api/subscribe',{method:'POST',headers:{'Content-Type':'application/json'},
968      body:JSON.stringify({email:email,site:(sdk&&sdk.getAttribute('data-site'))||'',
969                           key:(sdk&&sdk.getAttribute('data-key'))||'',store:d.documentElement.getAttribute('data-shop')||'',src:location.pathname})})
970      .then(function(r){
971        if(!r.ok)throw new Error('failed');
972        try{ window.RSOC&&window.RSOC.track&&window.RSOC.track('newsletter_signup'); }catch(err){}
973        if(msg)msg.textContent='Check your inbox to confirm your subscription.';
974        if(window.gtag)window.gtag('event','generate_lead',{event_source:f.getAttribute('data-placement')||'rail'});
975        f.querySelector('button[type=submit]').disabled=true;
976        if(input)input.disabled=true;
977      })
978      .catch(function(){ if(msg)msg.textContent='Sorry — that did not go through. Try again?'; });
979  });
980});
981/* Page turns are real navigations — that is the point: each one is a fresh
982   pageview and a fresh set of ad requests. The controls are buttons in the
983   design, so the click sends the browser to the page's own URL. */
984d.addEventListener('click',function(e){
985  var b=e.target.closest('[data-href]');
986  if(!b||b.disabled)return;
987  var href=b.getAttribute('data-href');
988  if(href){e.preventDefault();location.href=href;}
989});
990})();</script>
vendor: 1 bytes, line 990
990
991<script>(function(){
992var site="europianmedia",shop=document.documentElement.getAttribute('data-shop')||'',box=document.querySelector('[data-push-box]'),button=box&&box.querySelector('button'),msg=box&&box.querySelector('[role=status]'),cfg;
993function api(action,body){return fetch('/api/audience/'+action,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(Object.assign({site:site},body))}).then(function(r){return r.json().then(function(d){if(!r.ok)throw Error(d.error||'Please try again');return d})})}
994fetch('/api/audience/config?site='+encodeURIComponent(site)).then(function(r){return r.json()}).then(function(c){cfg=c;if(box&&c.pushEnabled)box.hidden=false;document.querySelectorAll('[data-newsletter]').forEach(function(f){if(!c.emailEnabled){var b=f.querySelector('button[type=submit]'),m=f.querySelector('[data-newsletter-msg]');if(b){b.disabled=true;b.textContent='Email updates coming soon'}if(m)m.textContent='Email delivery is being prepared.'}})}).catch(function(){});
995if(!button)return;
996function state(){try{return JSON.parse(localStorage.getItem('em:push')||'null')}catch(e){return null}}
997if(state())button.textContent='Turn off browser alerts';
998button.addEventListener('click',async function(){button.disabled=true;try{
999 if(!('serviceWorker'in navigator)||!('PushManager'in window)||!('Notification'in window))throw Error('This browser does not support alerts here. On iPhone, add this website to your Home Screen and open it there.');
1000 var saved=state();
1001 if(saved){await api('unsubscribe',saved);var reg=await navigator.serviceWorker.getRegistration('/'),sub=reg&&await reg.pushManager.getSubscription();if(sub)await sub.unsubscribe();localStorage.removeItem('em:push');button.textContent='Get browser alerts';msg.textContent='Browser alerts turned off.';return}
1002 var permission=await Notification.requestPermission();if(permission!=='granted')throw Error('Notifications were not enabled. You can change this in your browser site settings.');
1003 var registration=await navigator.serviceWorker.register('/flyer-sw.js',{scope:'/'});await navigator.serviceWorker.ready;
1004 var sub=await registration.pushManager.getSubscription();if(!sub)sub=await registration.pushManager.subscribe({userVisibleOnly:true,applicationServerKey:cfg.publicKey});
1005 var result;try{result=await api('push-subscribe',{subscription:sub.toJSON(),store:shop,src:location.pathname})}catch(e){await sub.unsubscribe();throw e}
1006 localStorage.setItem('em:push',JSON.stringify({id:result.id,token:result.token}));button.textContent='Turn off browser alerts';msg.textContent='Alerts enabled'+(shop?' for this store.':'.');
1007 }catch(e){msg.textContent=e.message}finally{button.disabled=false}
1008});})();</script>
vendor: 1 bytes, line 1008
1008
1009<script>(function(){
1010var body=document.body,seen=false,timer=0,watched=new WeakSet();
1011function schedule(){if(!timer)timer=setTimeout(sync,80)}
1012function sync(){timer=0;var top=0,bottom=0;
1013 document.querySelectorAll('ins.adsbygoogle[data-anchor-shown]').forEach(function(el){
1014  if(!watched.has(el)){watched.add(el);new MutationObserver(schedule).observe(el,{attributes:true,attributeFilter:['style','class','data-anchor-shown']});if(window.ResizeObserver)new ResizeObserver(schedule).observe(el)}
1015  seen=true;var s=getComputedStyle(el);if(s.display==='none'||s.visibility==='hidden'||s.position!=='fixed')return;
1016  var r=el.getBoundingClientRect(),visible=Math.max(0,Math.min(innerHeight,r.bottom)-Math.max(0,r.top));
1017  // The collapse/reopen handle projects 25px outside Google's frame.
1018  var reserve=Math.ceil(visible+25);if(s.top!=='auto'&&r.top<innerHeight/2)top=Math.max(top,reserve);else bottom=Math.max(bottom,reserve);
1019 });
1020 if(seen){body.setAttribute('data-anchor-spacing','');body.style.setProperty('--em-anchor-top',top+'px');body.style.setProperty('--em-anchor-bottom',bottom+'px')}
1021}
1022new MutationObserver(schedule).observe(body,{childList:true,subtree:true});window.addEventListener('resize',schedule,{passive:true});window.addEventListener('hashchange',schedule);schedule();
1023})();</script>
1023
1024
1025
1026<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4bc70e2c01a94c73b74392e4234840661791215815920" integrity="sha512-L0ha0OXavK/8okipN9F8BtP84dg9DUhPERbBXzwI6dgTA55d2+yweo3pn5CSFYs45/r8md2+xvUPtTdvTNRfjA==" data-cf-beacon='{"version":"2024.11.0","token":"7f9dcd1694004350bd6108f17126a67c","r":1,"spa":2}' crossorigin="anonymous"></script>
1026
1027</body>
1028</html>

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.