Privacy Policy

Last updated: 2025-10-01

Theme
Next review in

We collect account and usage information you provide (e.g., email) to deliver courses, personalize content, and support your learning. We minimize retention and implement reasonable security controls.

We use cookies to keep you signed in, remember preferences, and, with your consent, understand aggregate usage. You can adjust cookie preferences in the footer’s Cookie Settings.

You may contact us at [email protected] or +1 (415) 562-8394 to exercise your rights, including access, correction, and deletion of your data, where applicable.

We do not sell personal data. We share data only with service providers required to operate the platform and only as necessary.

Policy updates will be posted here with a new “last updated” date.

Data we handle

  • Account: email, name, authentication identifiers.
  • Usage: pages viewed, device and approximate location (city-level) when analytics consent is given.
  • Support: messages and attachments you voluntarily provide.

Retention

We retain data only as long as necessary for service delivery, legal obligations, and dispute resolution, then delete or anonymize it.

Legal bases

Depending on your region, we rely on contract performance, legitimate interests, consent, and legal compliance.

Your controls

  • Access, correction, deletion, and portability.
  • Withdraw consent (analytics/marketing) at any time.
  • Object to certain processing where permitted.
') ]); document.querySelector('header').innerHTML = h; document.querySelector('footer').innerHTML = f; window.dispatchEvent(new Event('layout:ready')); } injectLayout(); const brief = document.getElementById('pp-brief'); document.getElementById('toggle-brief').addEventListener('click', ()=> brief.classList.toggle('hidden')); const metaScheme = document.querySelector('meta[name="color-scheme"]'); const htmlEl = document.documentElement; const themeBtn = document.getElementById('ww-theme-toggle'); function applyTheme(t){ htmlEl.setAttribute('data-theme', t); document.body.classList.toggle('dark', t==='dark'); themeBtn.textContent = t==='dark' ? 'Dark' : 'Light'; metaScheme.setAttribute('content', t); localStorage.setItem('ww_theme', t); } applyTheme(localStorage.getItem('ww_theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')); themeBtn.addEventListener('click', ()=> applyTheme(htmlEl.getAttribute('data-theme')==='dark'?'light':'dark')); const lastUpdatedStr = document.getElementById('last-updated').textContent.trim(); const reviewEl = document.getElementById('review-countdown'); function nextReviewFrom(dateStr){ const base = new Date(dateStr+'T00:00:00Z'); const next = new Date(base.getTime()+90*24*60*60*1000); return next; } function renderCountdown(){ const target = nextReviewFrom(lastUpdatedStr); const now = new Date(); let diff = target-now; if(diff<0) diff=0; const d = Math.floor(diff/(24*60*60*1000)); const h = Math.floor((diff%(24*60*60*1000))/(60*60*1000)); const m = Math.floor((diff%(60*60*1000))/(60*1000)); reviewEl.textContent = `${d}d ${h}h ${m}m`; } renderCountdown(); setInterval(renderCountdown, 60000); const WW_CONSENT_KEY = 'ww_consent_v1'; const cookieBanner = document.getElementById('cookie-banner'); const floatingCookieBtn = document.getElementById('floating-cookie'); const modalCookie = document.getElementById('modal-cookie'); const modalCookieCard = modalCookie.querySelector('.t3wq9'); const cbAnalytics = document.getElementById('consent-analytics'); const cbMarketing = document.getElementById('consent-marketing'); const modalAnalytics = document.getElementById('modal-analytics'); const modalMarketing = document.getElementById('modal-marketing'); function getConsent(){ try { return JSON.parse(localStorage.getItem(WW_CONSENT_KEY)) || null } catch(e){ return null } } function setConsent(c){ localStorage.setItem(WW_CONSENT_KEY, JSON.stringify({...c, ts: Date.now()})); document.cookie = `ww_consent=${encodeURIComponent(`a=${c.analytics?'1':'0'}&m=${c.marketing?'1':'0'}`)}; path=/; max-age=${60*60*24*180}`; } function showBanner(){ cookieBanner.classList.remove('hidden'); const c = getConsent(); cbAnalytics.checked = !!(c&&c.analytics); cbMarketing.checked = !!(c&&c.marketing); } function hideBanner(){ cookieBanner.classList.add('hidden'); } function openCookieModal(){ const c = getConsent() || {analytics:false, marketing:false}; modalAnalytics.checked = !!c.analytics; modalMarketing.checked = !!c.marketing; modalCookie.classList.remove('hidden'); requestAnimationFrame(()=>modalCookieCard.setAttribute('data-open','true')); trapFocus(modalCookie); } function closeCookieModal(){ modalCookieCard.setAttribute('data-open','false'); setTimeout(()=>modalCookie.classList.add('hidden'),120); releaseFocus(); } document.getElementById('cookie-accept-all').addEventListener('click', ()=>{ setConsent({analytics:true, marketing:true}); hideBanner(); startOptionalTools(); }); document.getElementById('cookie-reject').addEventListener('click', ()=>{ setConsent({analytics:false, marketing:false}); hideBanner(); stopOptionalTools(); }); document.getElementById('cookie-save').addEventListener('click', ()=>{ setConsent({analytics:cbAnalytics.checked, marketing:cbMarketing.checked}); hideBanner(); startOptionalTools(); }); document.getElementById('cookie-manage').addEventListener('click', openCookieModal); floatingCookieBtn.addEventListener('click', openCookieModal); document.addEventListener('click', (e)=>{ if(e.target && e.target.id==='open-cookie-settings') openCookieModal(); }); modalCookie.addEventListener('click', (e)=>{ if(e.target===modalCookie.querySelector('.z9tqg')) closeCookieModal(); }); modalCookie.querySelector('[data-close-cookie]').addEventListener('click', closeCookieModal); document.getElementById('cookie-modal-accept').addEventListener('click', ()=>{ setConsent({analytics:true, marketing:true}); closeCookieModal(); hideBanner(); startOptionalTools(); }); document.getElementById('cookie-modal-reject').addEventListener('click', ()=>{ setConsent({analytics:false, marketing:false}); closeCookieModal(); hideBanner(); stopOptionalTools(); }); document.getElementById('cookie-modal-save').addEventListener('click', ()=>{ setConsent({analytics:modalAnalytics.checked, marketing:modalMarketing.checked}); closeCookieModal(); hideBanner(); startOptionalTools(); }); function startOptionalTools(){ const c = getConsent(); if(c && c.analytics){ if(!window.wwAnalytics){ window.wwAnalytics = {log:(ev,data)=>console.debug('[Analytics]',ev,data||{})}; } window.wwAnalytics.log('page_view',{path:location.pathname, ts:Date.now()}); } if(!(c&&c.marketing)){ // placeholder disable marketing pixels if any were set } } function stopOptionalTools(){ // turn off optional analytics/marketing stubs } (function initConsent(){ const c = getConsent(); if(!c){ showBanner() } else { startOptionalTools() } })(); const modalRequest = document.getElementById('modal-request'); const modalReqCard = modalRequest.querySelector('.t3wq9'); const reqForm = document.getElementById('reqForm'); const reqSuccess = document.getElementById('reqSuccess'); const reqIdEl = document.getElementById('reqId'); function openRequestModal(){ reqForm.reset(); reqSuccess.classList.add('hidden'); modalRequest.classList.remove('hidden'); requestAnimationFrame(()=>modalReqCard.setAttribute('data-open','true')); trapFocus(modalRequest); } function closeRequestModal(){ modalReqCard.setAttribute('data-open','false'); setTimeout(()=>modalRequest.classList.add('hidden'),120); releaseFocus(); } document.getElementById('open-data-request').addEventListener('click', openRequestModal); document.getElementById('open-data-request-2').addEventListener('click', openRequestModal); modalRequest.addEventListener('click', (e)=>{ if(e.target===modalRequest.querySelector('.z9tqg')) closeRequestModal(); }); modalRequest.querySelectorAll('[data-close-request]').forEach(btn=>btn.addEventListener('click', closeRequestModal)); function validateForm(){ const fd = new FormData(reqForm); const name = (fd.get('name')||'').trim(); const email = (fd.get('email')||'').trim(); const type = (fd.get('type')||'').trim(); const message = (fd.get('message')||'').trim(); const agree = document.getElementById('agree').checked; const err = { name: name.length<2, email: !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email), type: !type, message: message.length<20, agree: !agree }; reqForm.querySelector('[data-err-name]').classList.toggle('hidden', !err.name); reqForm.querySelector('[data-err-email]').classList.toggle('hidden', !err.email); reqForm.querySelector('[data-err-type]').classList.toggle('hidden', !err.type); reqForm.querySelector('[data-err-message]').classList.toggle('hidden', !err.message); reqForm.querySelector('[data-err-agree]').classList.toggle('hidden', !err.agree); return {valid: !Object.values(err).some(Boolean), data:{name,email,type,message,ref:(fd.get('ref')||'').trim()}}; } let submitting=false; reqForm.addEventListener('submit', (e)=>{ e.preventDefault(); if(submitting) return; const {valid, data} = validateForm(); if(!valid) return; submitting=true; const btn = document.getElementById('reqSubmit'); const prev = btn.textContent; btn.textContent = 'Submitting…'; btn.disabled = true; setTimeout(()=>{ const id = 'REQ-' + Math.random().toString(36).slice(2,8).toUpperCase() + '-' + Date.now().toString().slice(-4); const stored = JSON.parse(localStorage.getItem('ww_requests')||'[]'); stored.push({id, ...data, ts: Date.now()}); localStorage.setItem('ww_requests', JSON.stringify(stored)); reqIdEl.textContent = id; reqSuccess.classList.remove('hidden'); btn.textContent = prev; btn.disabled = false; submitting=false; }, 1200); }); function trapFocus(container){ const focusable = container.querySelectorAll('a[href],button,textarea,input,select,[tabindex]:not([tabindex="-1"])'); const list = Array.from(focusable).filter(el=>!el.disabled && el.offsetParent!==null); const first = list[0], last = list[list.length-1]; function onKey(e){ if(e.key==='Escape'){ if(container===modalCookie) closeCookieModal(); if(container===modalRequest) closeRequestModal(); } if(e.key==='Tab'){ if(list.length===0) return; if(e.shiftKey && document.activeElement===first){ e.preventDefault(); last.focus() } else if(!e.shiftKey && document.activeElement===last){ e.preventDefault(); first.focus() } } } container.__trap = onKey; document.addEventListener('keydown', onKey); setTimeout(()=> first?.focus(), 10); } function releaseFocus(){ document.removeEventListener('keydown', modalCookie.__trap || (()=>{})); document.removeEventListener('keydown', modalRequest.__trap || (()=>{})); } window.addEventListener('layout:ready', ()=>{ const cookieLink = document.querySelector('#open-cookie-settings'); if(cookieLink){ cookieLink.addEventListener('click', (e)=>{ e.preventDefault(); openCookieModal(); }) } });