1/* ============================================================ 2 âï¸ CONFIG â EDIT EVERYTHING HERE. One place, propagates everywhere. 3 ============================================================ */ 4var CONFIG = { 5 /* Headline numbers â you said these have grown. Update them here. */ 6 stats: { 7 followers: "48 300", // sledujÃcÃch 8 views: "18 M", // zhlédnutà za 4 mÄsÃce 9 engagement: "6,8 %", // průmÄrný engagement 10 waitlist: "500+", // na waitlistu / ÄekaÄce 11 seatsTvurce: "0", // volná mÃsta v tarifu Tvůrce â VYPRODÃNO 12 seatsFeedback: "0", // volná mÃsta v tarifu Tvůrce + Feedback â VYPRODÃNO 13 seatsMentor: "0" // mÃsta v Mentoring 1:1 â VYPRODÃNO 14 }, 15 16 /* Honest countdown â set to the EXACT moment sales open + 72 hours (CET/CEST). 17 Example: opening 2026-07-06T18:00 â deadline 2026-07-09T18:00:00+02:00. */ 18 deadline: "2026-07-09T18:00:00+02:00", 19 20 /* Stripe Payment Links â paste the 3 URLs. Empty = button stays a safe placeholder. */ 21 stripe: { 22 tvurce: "", // AI Tvůrce â VYPRODÃNO (payment link odstránený) 23 feedback: "", // AI ÄarodÄj â VYPRODÃNO (payment link odstránený) 24 mentor: "" // 1:1 Mentoring â VYPRODÃNO (payment link odstránený) 25 }, 26 27 /* Social profile URLs (canonical, no tracking params). */ 28 social: { 29 instagram: "https://www.instagram.com/adela.vs.historie/", 30 tiktok: "https://www.tiktok.com/@adela.vs.historie", 31 youtube: "https://www.youtube.com/@adela.vs.historie" 32 }, 33 34 /* Currency. Default shown on load; 'auto' would need geo â kept manual + persistent. */ 35 currency: { default: "czk" }, 36 37 /* IG-safe analytics. Vercel Web Analytics is wired automatically when deployed on 38 Vercel (see the insights <script> in index.html). Plausible/GA4/Meta pixel are 39 picked up automatically if you paste their snippet into <head>. */ 40 analyticsId: "" 41}; 42 43/* Vercel Web Analytics bootstrap (safe no-op anywhere else). */ 44window.va = window.va || function(){ (window.vaq = window.vaq || []).push(arguments); }; 45 46(function(){ 47 "use strict"; 48 var reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches; 49 var $ = function(s,c){return (c||document).querySelector(s);}; 50 var $$ = function(s,c){return Array.prototype.slice.call((c||document).querySelectorAll(s));}; 51 52 /* ---------- 0. ANALYTICS (IG-webview-safe; degrades to no-op) ---------- */ 53 function track(event, props){ 54 try{ 55 // Vercel Web Analytics custom events 56 if(window.va) window.va('event', {name:event, data:props||{}}); 57 // Plausible 58 if(window.plausible) window.plausible(event, {props:props||{}}); 59 // GA4 (gtag) 60 if(window.gtag) window.gtag('event', event, props||{}); 61 // Meta Pixel 62 if(window.fbq) window.fbq('trackCustom', event, props||{}); 63 // Always available for debugging / your own collector: 64 (window.dataLayer = window.dataLayer || []).push(Object.assign({event:event}, props||{})); 65 }catch(e){/* never let tracking break the page in the IG webview */} 66 } 67 window.__track = track; 68 69 /* ---------- 1. INJECT CONFIG VALUES ---------- */ 70 // stats 71 Object.keys(CONFIG.stats).forEach(function(k){ 72 $$('[data-stat="'+k+'"]').forEach(function(el){ 73 // skip the count-up element's initial text (handled in §6) but still set it as fallback 74 el.textContent = CONFIG.stats[k]; 75 }); 76 }); 77 // social links 78 $$('[data-social]').forEach(function(a){ 79 var url = CONFIG.social[a.getAttribute('data-social')]; 80 if(url) a.setAttribute('href', url); 81 }); 82 // stripe buy buttons 83 $$('[data-buy]').forEach(function(a){ 84 var url = CONFIG.stripe[a.getAttribute('data-buy')]; 85 if(url){ a.setAttribute('href', url); a.removeAttribute('aria-disabled'); } 86 else { a.setAttribute('aria-disabled','true'); a.setAttribute('title','DoplÅ Stripe odkaz v CONFIG'); } 87 a.addEventListener('click', function(){ track('cta_buy', {tier:a.getAttribute('data-buy')}); }); 88 }); 89 // generic CTAs (anchors to pricing) 90 $$('[data-cta]').forEach(function(a){ 91 a.addEventListener('click', function(){ track('cta_click', {where:a.getAttribute('data-cta')}); }); 92 }); 93 94 /* ---------- 2. STICKY NAV: hide on scroll-down, show on scroll-up (rAF-throttled) ---------- */ 95 var nav = $('#nav'), lastY = window.pageYOffset, navTick=false; 96 window.addEventListener('scroll', function(){ 97 if(navTick) return; navTick=true; 98 requestAnimationFrame(function(){ 99 var y = window.pageYOffset; 100 if(y > lastY && y > 200) nav.classList.add('hide'); else nav.classList.remove('hide'); 101 lastY = y; navTick=false; 102 }); 103 }, {passive:true}); 104 105 /* ---------- 3. MOBILE MENU ---------- */ 106 var burger=$('#burger'), sheet=$('#sheet'); 107 function setMenu(open){ 108 sheet.classList.toggle("open",open); 109 sheet.setAttribute("aria-hidden",String(!open)); 110 burger.setAttribute("aria-expanded",String(open)); 111 document.body.style.overflow=open?"hidden":""; 112 } 113 burger.addEventListener("click",function(){setMenu(!sheet.classList.contains("open"));}); 114 $$("[data-close]").forEach(function(a){ a.addEventListener("click",function(){setMenu(false);}); }); 115 116 /* ---------- 4. ACCORDIONS (modules + faq) ---------- */ 117 function wireAccordion(sel){ 118 $$(sel).forEach(function(item){ 119 var head=item.querySelector("button"); 120 head.addEventListener("click",function(){ 121 var open = item.classList.toggle("open"); 122 head.setAttribute("aria-expanded", String(open)); 123 if(open) track('accordion_open',{section:sel,label:(head.textContent||'').trim().slice(0,40)}); 124 setTimeout(measureDoc,450); /* perf(P2.3): akordeón menà výšku dokumentu (transition .4s) â obnov scroll-depth cache po rozbalenà */ 125 }); 126 }); 127 } 128 wireAccordion("[data-mod]"); 129 wireAccordion("[data-faq]"); 130 var firstMod=$("[data-mod]"); 131 if(firstMod){ firstMod.classList.add("open"); firstMod.querySelector('button').setAttribute('aria-expanded','true'); } 132 133 /* ---------- 5. SCROLL REVEALS ---------- */ 134 if("IntersectionObserver" in window && !reduce){ 135 var io=new IntersectionObserver(function(entries){
136 entries.forEach(function(e){ if(e.isIntersecting){ e.target.classList.add("is-visible"); io.unobserve(e.target);} }); 137 },{threshold:0.14, rootMargin:"0px 0px -8% 0px"}); 138 $$(".reveal").forEach(function(el){io.observe(el);}); 139 } else { 140 $$(".reveal").forEach(function(el){el.classList.add("is-visible");}); 141 } 142 143 /* ---------- 6. COUNT-UP for the followers stat ---------- */ 144 var counted=false, cEl=$("[data-count]"); 145 function parseNum(s){ return parseInt(String(s).replace(/[^\d]/g,''),10)||0; } 146 function runCount(){ 147 if(counted||!cEl) return; counted=true; 148 var finalText = CONFIG.stats.followers, target = parseNum(finalText); 149 if(reduce||!target){ cEl.textContent=finalText; return; } 150 var start=null, dur=1400; 151 function step(ts){ if(!start)start=ts; var p=Math.min((ts-start)/dur,1); 152 var val=Math.floor((1-Math.pow(1-p,3))*target); 153 cEl.textContent=val.toLocaleString("cs-CZ"); 154 if(p<1) requestAnimationFrame(step); else cEl.textContent=finalText; 155 } 156 requestAnimationFrame(step); 157 } 158 if("IntersectionObserver" in window && cEl){ 159 var io2=new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting){runCount();io2.disconnect();}});},{threshold:.6}); 160 io2.observe(cEl); 161 } else { runCount(); } 162 163 /* ---------- 7. STICKY MOBILE CTA after hero ---------- 164 Hysteréza: show fire až 80 px ZA hranicou (rozÅ¡Ãrený root), hide pri návrate na hranicu = 80px dead zone, žiadne blikanie. 165 Footer sentinel: keÄ je footer vo viewporte, bar sa schová, aby neprekrýval závereÄný obsah. */ 166 var bar=$('#stickybar'), heroEnd=$('#heroEnd'), footerEl=$('.footer'), barShown=false, pastHero=false, atFooter=false; 167 function syncBar(){ 168 var show = pastHero && !atFooter; 169 bar.classList.toggle("show", show); 170 if(show && !barShown){ barShown=true; track('sticky_cta_shown'); } 171 } 172 if("IntersectionObserver" in window && heroEnd){ 173 var ioShow=new IntersectionObserver(function(es){ 174 es.forEach(function(e){ if(!e.isIntersecting && e.boundingClientRect.top<0){ pastHero=true; syncBar(); } }); 175 },{threshold:0, rootMargin:"80px 0px 0px 0px"}); 176 var ioHide=new IntersectionObserver(function(es){ 177 es.forEach(function(e){ if(e.isIntersecting){ pastHero=false; syncBar(); } }); 178 },{threshold:0}); 179 ioShow.observe(heroEnd); ioHide.observe(heroEnd); 180 if(footerEl){ 181 var ioFoot=new IntersectionObserver(function(es){ 182 es.forEach(function(e){ atFooter=e.isIntersecting; syncBar(); }); 183 },{threshold:0}); 184 ioFoot.observe(footerEl); 185 } 186 } 187 188 /* ---------- 8. CURRENCY TOGGLE (CZK / EUR), persistent ---------- */ 189 var curBtns=$$("[data-cur]"); 190 function setCur(cur){ 191 try{ localStorage.setItem('adela_cur', cur); }catch(e){} 192 curBtns.forEach(function(b){b.classList.toggle("active",b.getAttribute("data-cur")===cur);}); 193 // price/value cells with a <small> sub-line 194 $$("[data-"+cur+"]").forEach(function(el){ 195 var v=el.getAttribute("data-"+cur); 196 if(el.querySelector("small")) el.firstChild.nodeValue=v; else el.textContent=v; 197 }); 198 // plain text swaps (e.g. sticky bar) 199 $$("[data-"+cur+"-text]").forEach(function(el){ el.textContent=el.getAttribute("data-"+cur+"-text"); }); 200 } 201 curBtns.forEach(function(b){b.addEventListener("click",function(){ setCur(b.getAttribute("data-cur")); track('currency_switch',{cur:b.getAttribute('data-cur')}); });}); 202 var savedCur; try{ savedCur=localStorage.getItem('adela_cur'); }catch(e){} 203 setCur(savedCur || CONFIG.currency.default || 'czk'); 204 205 /* ---------- 9. COUNTDOWN (honest) + closed state ---------- */ 206 var cd=$('#countdown'), DEADLINE=new Date(CONFIG.deadline), finalSec=$('#finalCta'); 207 function pad(n){return (n<10?"0":"")+n;} 208 function setCd(d,h,m,s){ 209 cd.querySelector('[data-cd="d"]').textContent=d; 210 cd.querySelector('[data-cd="h"]').textContent=h; 211 cd.querySelector('[data-cd="m"]').textContent=m; 212 cd.querySelector('[data-cd="s"]').textContent=s; 213 } 214 function tick(){ 215 var diff=DEADLINE-Date.now(); 216 if(diff<=0){ setCd("00","00","00","00"); finalSec.classList.add('closed'); return false; } 217 var s=Math.floor(diff/1000); 218 setCd(pad(Math.floor(s/86400)),pad(Math.floor(s%86400/3600)),pad(Math.floor(s%3600/60)),pad(s%60)); 219 return true; 220 } 221 if(cd && !isNaN(DEADLINE.getTime())){ if(tick()) var cdTimer=setInterval(function(){ if(!tick()) clearInterval(cdTimer); },1000); } 222
223 /* ---------- 10. VIDEO MODAL (facade + focus trap + restore) ---------- */ 224 var modal=$('#videoModal'), closeBtn=$('#closeVideo'), modalBox=$('#modalBox'), lastFocus=null; 225 /* Bunny Stream VSL â iframe se vytvoÅà až po otevÅenà (nic se netáhne pÅi naÄtenà stránky). */ 226 var VSL_SRC="https://player.mediadelivery.net/embed/697464/49b44806-c767-43ae-9b7c-9d1597f2e743?autoplay=true&muted=false&loop=false&preload=true&responsive=true"; 227 function mountVsl(){ 228 if(!modalBox || modalBox.querySelector('iframe')) return; 229 var f=document.createElement('iframe'); 230 f.src=VSL_SRC; 231 f.setAttribute('allow','autoplay; encrypted-media; picture-in-picture; fullscreen'); 232 f.setAttribute('allowfullscreen','true'); 233 f.setAttribute('referrerpolicy','strict-origin-when-cross-origin'); 234 f.setAttribute('title','Adéla: Virálnà s AI â video'); 235 modalBox.appendChild(f); 236 track('vsl_play'); 237 } 238 function unmountVsl(){ if(modalBox){ var f=modalBox.querySelector('iframe'); if(f) f.remove(); } } 239 function openModal(){ lastFocus=document.activeElement; modal.classList.add("open"); document.body.style.overflow="hidden"; mountVsl(); closeBtn.focus(); track('video_open'); } 240 function closeModal(){ modal.classList.remove("open"); document.body.style.overflow=""; unmountVsl(); if(lastFocus) lastFocus.focus(); } 241 ["openVideo","openVideo2"].forEach(function(id){var el=$('#'+id); 242 if(el){ el.addEventListener("click",openModal); el.addEventListener("keydown",function(e){if(e.key==="Enter"||e.key===" "){e.preventDefault();openModal();}});}}); 243 closeBtn.addEventListener("click",closeModal); 244 modal.addEventListener("click",function(e){ if(e.target===modal) closeModal(); }); 245 document.addEventListener("keydown",function(e){ if(e.key==="Escape"){ closeModal(); setMenu(false);} }); 246 247 /* ---------- 11. "VÃce" cloud reveal ---------- */ 248 $('#moreBtn').addEventListener("click",function(){ 249 var p=$(".proof"); if(p) p.scrollIntoView({behavior:reduce?"auto":"smooth"}); 250 }); 251 252 /* ---------- 12. (parallax odstránený â layout read/write na scroll = jank; skill §8.4 ho zakazuje) ---------- */ 253 254 /* ---------- 13. SCROLL DEPTH tracking (50% / 90%) â cached výška, žiadny forced reflow v scroll callbacku ---------- */ 255 var depthHits={}, docH=0, depthTick=false; 256 function measureDoc(){ docH=document.documentElement.scrollHeight; } 257 measureDoc(); 258 window.addEventListener('resize', measureDoc, {passive:true}); 259 window.addEventListener('scroll', function(){ 260 if(depthTick) return; depthTick=true; 261 requestAnimationFrame(function(){ 262 var sc=(window.pageYOffset+window.innerHeight)/(docH||1)*100; 263 [50,90].forEach(function(t){ if(sc>=t && !depthHits[t]){ depthHits[t]=true; track('scroll_depth',{percent:t}); } }); 264 depthTick=false; 265 }); 266 }, {passive:true}); 267 268 /* ---------- 14. HERO ENTRANCE ---------- */ 269 requestAnimationFrame(function(){ $('#hero').classList.add('in'); }); 270 271 /* ---------- 14b. perf(P0.2): pauza nekoneÄných animácià (marquee, skydrift) mimo viewportu ---------- */ 272 var ioAnim=new IntersectionObserver(function(es){ 273 es.forEach(function(e){ e.target.classList.toggle('anim-paused', !e.isIntersecting); }); 274 },{threshold:0}); 275 $$('.band').forEach(function(b){ ioAnim.observe(b); }); 276 ioAnim.observe($('#hero')); 277 278 /* ---------- 15. PRÃVNà MODÃLY â nativnà <dialog>, deep-link pÅes hash ---------- */ 279 var legalDlg=$('#legalDialog'), legalTitle=$('#legalTitle'), legalBody=$('#legalBody'); 280 var LEGAL_SLUGS=['obchodni-podminky','ochrana-soukromi','zasady-cookies']; 281 var legalInvoker=null, legalPushed=false; 282 function legalOpen(slug, push){ 283 var tpl=document.getElementById('tpl-'+slug); 284 if(!tpl || !legalDlg || !legalDlg.showModal) return; 285 legalInvoker=document.activeElement; 286 legalTitle.textContent=tpl.getAttribute('data-title'); 287 legalBody.innerHTML=''; 288 legalBody.appendChild(tpl.content.cloneNode(true)); 289 legalBody.scrollTop=0; 290 legalDlg.showModal(); 291 legalPushed=false; 292 if(push!==false && location.hash!=='#'+slug){ history.pushState({legal:slug},'','#'+slug); legalPushed=true; } 293 track('legal_open',{doc:slug}); 294 } 295 if(legalDlg && legalDlg.showModal){ 296 $$('[data-legal]').forEach(function(a){ 297 a.addEventListener('click',function(e){ e.preventDefault(); legalOpen(a.getAttribute('data-legal')); }); 298 }); 299 $('#legalClose').addEventListener('click',function(){ legalDlg.close(); }); 300 legalDlg.addEventListener('click',function(e){ if(e.target===legalDlg) legalDlg.close(); }); // klik na backdrop 301 legalDlg.addEventListener('close',function(){ 302 // hash uklidit jen když zavÅenà nepÅiÅ¡lo z back buttonu (tam už je hash pryÄ) 303 if(LEGAL_SLUGS.indexOf(location.hash.slice(1))>-1){ 304 if(legalPushed){ history.back(); } else { history.replaceState(null,'',location.pathname+location.search); } 305 } 306 legalPushed=false; 307 if(legalInvoker && legalInvoker.focus) legalInvoker.focus(); 308 }); 309 window.addEventListener('popstate',function(){ 310 var h=location.hash.slice(1); 311 if(legalDlg.open && LEGAL_SLUGS.indexOf(h)===-1){ legalPushed=false; legalDlg.close(); } 312 else if(!legalDlg.open && LEGAL_SLUGS.indexOf(h)>-1){ legalOpen(h,false); } 313 }); 314 // deep-link: sdÃlená URL otevÅe modal hned po naÄtenà 315 var h0=location.hash.slice(1); 316 if(LEGAL_SLUGS.indexOf(h0)>-1) legalOpen(h0,false); 317 } 318 319 /* ---------- 16. COOKIE CONSENT â opt-in, reject stejnÄ snadný, gating budoucÃch skriptů ---------- */ 320 var CB_KEY='cookie-consent-v1', CB_VERSION=1; // pÅi zmÄnÄ znÄnà zásad zvyÅ¡ verzi â liÅ¡ta se ukáže znovu 321 var cb=$('#cookiebar'); 322 function cbRead(){ 323 try{ var v=JSON.parse(localStorage.getItem(CB_KEY));
323 return (v && v.version===CB_VERSION) ? v : null; } 324 catch(e){ return null; } 325 } 326 function cbApply(c){ 327 /* GATING: ne-nezbytné skripty se vkládajà VÃHRADNÄ tady, až po souhlasu. 328 Vercel Analytics je cookieless (bez souhlasu OK). Až pÅibude GA4/Meta Pixel: 329 if(c.analytics){ ...inject GA4 script... } 330 if(c.marketing){ ...inject Meta Pixel... } */ 331 if(typeof window.gtag==='function'){ // Google Consent Mode v2 hook 332 window.gtag('consent','update',{ 333 analytics_storage:c.analytics?'granted':'denied', 334 ad_storage:c.marketing?'granted':'denied', 335 ad_user_data:c.marketing?'granted':'denied', 336 ad_personalization:c.marketing?'granted':'denied' 337 }); 338 } 339 } 340 function cbHide(){ 341 cb.hidden=true; cb.classList.remove('cfg'); 342 $('#cbSave').hidden=true; $('#cbCfg').setAttribute('aria-expanded','false'); 343 document.documentElement.classList.remove('consent-pending'); // sticky bar zpÄt 344 } 345 function cbShow(withPanel){ 346 var c=cbRead(); 347 if(c){ $('#cbFunctional').checked=!!c.functional; $('#cbAnalytics').checked=!!c.analytics; $('#cbMarketing').checked=!!c.marketing; } 348 if(withPanel){ cb.classList.add('cfg'); $('#cbSave').hidden=false; $('#cbCfg').setAttribute('aria-expanded','true'); } 349 cb.hidden=false; 350 document.documentElement.classList.add('consent-pending'); // liÅ¡ta má pÅednost pÅed sticky CTA barem 351 } 352 function cbStore(functional,analytics,marketing){ 353 var c={necessary:true,functional:!!functional,analytics:!!analytics,marketing:!!marketing,ts:new Date().toISOString(),version:CB_VERSION}; 354 try{ localStorage.setItem(CB_KEY,JSON.stringify(c)); }catch(e){} 355 cbApply(c); cbHide(); 356 track('cookie_consent',{functional:c.functional,analytics:c.analytics,marketing:c.marketing}); 357 } 358 if(cb){ 359 $('#cbAccept').addEventListener('click',function(){ cbStore(true,true,true); }); 360 $('#cbReject').addEventListener('click',function(){ cbStore(false,false,false); }); 361 $('#cbClose').addEventListener('click',function(){ cbStore(false,false,false); }); // kÅÞek = odmÃtnutÃ, NE tichý souhlas 362 $('#cbCfg').addEventListener('click',function(){ 363 var on=cb.classList.toggle('cfg'); 364 $('#cbSave').hidden=!on; this.setAttribute('aria-expanded',on?'true':'false'); 365 }); 366 $('#cbSave').addEventListener('click',function(){ 367 cbStore($('#cbFunctional').checked,$('#cbAnalytics').checked,$('#cbMarketing').checked); 368 }); 369 var cs=$('#openCookieSettings'); 370 if(cs) cs.addEventListener('click',function(e){ e.preventDefault(); cbShow(true); }); 371 window.cookieConsent={ 372 get:cbRead, 373 has:function(cat){ var c=cbRead(); return cat==='necessary' ? true : !!(c && c[cat]); }, 374 open:function(){ cbShow(true); } 375 }; 376 var stored=cbRead(); 377 if(stored){ cbApply(stored); } else { cbShow(false); } 378 } 379 380})();
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.