Files
eazy-design-export/assets/mobile-chrome.js
T

160 lines
12 KiB
JavaScript

/* eazy mobile chrome: hamburger + fullscreen menu + bottom action bar (mobile only) */
(function () {
if (customElements.get('ez-mobile-chrome')) return;
const HOME = 'index.html';
const LINKS = [
['אמירנט', 'amirnet.html'],
['הקורסים', 'compare.html'],
['מתחילים בחינם', 'free.html'],
['ללמוד אנגלית', 'learn.html'],
['מחשבון פטור מאנגלית', 'technion.html'],
['עלינו', 'about.html'],
];
class EzMobileChrome extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<style>
ez-mobile-chrome .ezc { display: none; }
@media (max-width: 820px) {
ez-mobile-chrome .ezc { display: block; font-family: var(--font-body); }
ez-mobile-chrome .ezc-burger { position: fixed; top: 14px; left: 14px; z-index: 90; width: 42px; height: 42px; border: none; border-radius: 0; background: transparent; color: var(--ez-ink); font-size: 26px; display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0; }
ez-mobile-chrome .ezc-bar { position: fixed; bottom: 0; right: 0; left: 0; z-index: 80; background: #fff; border-top: 1px solid var(--ez-line); display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 10px 16px calc(12px + env(safe-area-inset-bottom, 0px)); }
ez-mobile-chrome .ezc-bar a { display: flex; align-items: center; justify-content: center; gap: 7px; border-radius: 999px; padding: 13px 0; font-family: var(--font-display); font-weight: 700; font-size: 17px; color: #fff; border-bottom: none; min-height: 48px; box-sizing: border-box; }
ez-mobile-chrome .ezc-menu { position: fixed; inset: 0; z-index: 100; background: #fff; display: none; flex-direction: column; padding: 18px 22px calc(18px + env(safe-area-inset-bottom, 0px)); overflow-y: auto; }
ez-mobile-chrome .ezc-menu.open { display: flex; }
ez-mobile-chrome .ezc-menu a.ezc-nav { font-family: var(--font-display); font-weight: 700; font-size: 24px; color: var(--ez-ink); padding: 16px 0; border-bottom: 1px solid var(--ez-line); }
ez-mobile-chrome .ezc-menu a.ezc-nav.active { color: var(--ez-orange); }
}
</style>
<div class="ezc">
<button class="ezc-burger" aria-label="תפריט"><i class="ph ph-list"></i></button>
<div class="ezc-menu" dir="rtl">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:14px">
<a href="${HOME}" style="border-bottom:none"><img src="assets/logo-mark-orange.png" alt="eazy" style="width:44px;height:44px"></a>
<button class="ezc-close" aria-label="סגירה" style="width:42px;height:42px;border:1px solid var(--ez-line);border-radius:10px;background:#fff;font-size:20px;cursor:pointer"><i class="ph ph-x"></i></button>
</div>
${LINKS.map(([t, h]) => `<a class="ezc-nav${decodeURIComponent(location.pathname).endsWith('/' + h) ? ' active' : ''}" href="${h}">${t}</a>`).join('')}
<div style="margin-top:auto;display:grid;gap:10px;padding-bottom:10px">
<a href="contact.html" style="background:var(--ez-orange);color:#fff;border-radius:999px;padding:14px 0;text-align:center;font-family:var(--font-display);font-weight:700;font-size:16px;border-bottom:none">שיחת ייעוץ חינם</a>
<span dir="ltr" style="text-align:center;font-family:var(--font-mono);font-size:14px;color:var(--ez-fg-2)">1-700-70-70-07</span>
</div>
</div>
<div class="ezc-bar" dir="rtl">
<a href="contact.html" style="background:var(--ez-orange)"><i class="ph ph-phone" style="font-size:17px"></i>שיחת ייעוץ</a>
<a href="contact.html" style="background:#25D366"><i class="ph ph-whatsapp-logo" style="font-size:19px"></i>וואטסאפ</a>
</div>
</div>`;
const menu = this.querySelector('.ezc-menu');
const setOpen = open => { menu.classList.toggle('open', open); document.body.style.overflow = open ? 'hidden' : ''; };
this.querySelector('.ezc-burger').addEventListener('click', () => setOpen(true));
this.querySelector('.ezc-close').addEventListener('click', () => setOpen(false));
menu.querySelectorAll('a').forEach(a => a.addEventListener('click', () => setOpen(false)));
}
}
customElements.define('ez-mobile-chrome', EzMobileChrome);
/* Runtime mobile fixes: applied directly to inline styles so they work
regardless of how the runtime serializes the style attribute. */
function applyMobileFixes() {
if (window.innerWidth > 820) return;
document.body.style.fontSize = '20px';
document.querySelectorAll('[style]').forEach(el => {
const s = el.style;
// floating whatsapp button: bottom bar replaces it
if (s.position === 'fixed' && el.tagName === 'A' && el.getAttribute('aria-label') === 'וואטסאפ') { s.display = 'none'; return; }
// font floor (breadcrumbs stay compact)
const fs = parseFloat(s.fontSize);
if (s.display === 'flex' && s.gap === '8px' && fs === 13.5) { s.fontSize = '15px'; el.dataset.ezKeep = '1'; return; }
if (fs && fs < 20 && el.tagName !== 'I' && !el.dataset.ezKeep) {
if (el.closest('footer')) { if (fs < 16) s.fontSize = '16px'; }
else if (el.closest('label')) { s.fontSize = '16px'; }
else { s.fontSize = '20px'; if (!s.lineHeight || parseFloat(s.lineHeight) < 1.5) s.lineHeight = '1.55'; }
}
// grid collapse
const g = s.gridTemplateColumns;
if (g && !g.startsWith('14px')) {
if (el.closest('footer')) s.gridTemplateColumns = '1fr 1fr';
else if (g.includes('repeat(10')) s.gridTemplateColumns = 'repeat(5, 1fr)';
else if (g.includes('repeat(4')) s.gridTemplateColumns = 'repeat(2, 1fr)';
else if (g.trim().split(/\s+/).length > 1 || g.includes('repeat(')) s.gridTemplateColumns = '1fr';
}
// breathing room between stacked grid children
if (g && s.gap && parseFloat(s.gap) < 16) s.gap = '18px';
// hero cut-out photos + decorations
if (el.tagName === 'IMG') {
if ((el.getAttribute('src') || '').includes('dot-cluster')) { s.display = 'none'; return; }
const ih = parseFloat(s.height);
if (ih && ih >= 300) { s.height = 'auto'; s.width = '64vw'; s.position = 'static'; s.margin = '0 auto'; }
}
if (parseFloat(s.minHeight) >= 300) s.minHeight = '0';
// primary pill CTAs go full-width for a comfortable thumb target
if ((el.tagName === 'A' || el.tagName === 'BUTTON') && s.borderRadius === '999px' && parseFloat(s.fontSize) >= 14 && !el.closest('ez-mobile-chrome') && !el.closest('header') && !el.closest('#ez-exit-popup')) {
s.display = 'block'; s.width = '100%'; s.textAlign = 'center'; s.boxSizing = 'border-box';
}
// section gutters
if (/^(SECTION|FOOTER|ARTICLE|HEADER)$/.test(el.tagName)) {
if (parseFloat(s.paddingLeft) > 24 || parseFloat(s.paddingRight) > 24 || (s.padding || '').includes('64px')) { s.paddingLeft = '20px'; s.paddingRight = '20px'; }
}
});
}
let raf = 0;
const schedule = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(applyMobileFixes); };
window.addEventListener('resize', schedule);
if (document.readyState !== 'loading') schedule(); else document.addEventListener('DOMContentLoaded', schedule);
new MutationObserver(schedule).observe(document.documentElement, { childList: true, subtree: true });
// hydration can rewrite inline styles after the last DOM mutation settles — re-apply on a short schedule
[400, 1200, 2500, 5000].forEach(t => setTimeout(schedule, t));
/* Exit-intent popup: offered once per session, unless a lead was already sent */
function initExitPopup() {
if (document.getElementById('ez-exit-popup')) return;
const wrap = document.createElement('div');
wrap.id = 'ez-exit-popup';
wrap.setAttribute('dir', 'rtl');
wrap.style.cssText = 'display:none;position:fixed;inset:0;z-index:300;background:rgba(26,26,32,.55);align-items:center;justify-content:center;padding:24px';
wrap.innerHTML = `
<div style="background:#fff;border-radius:14px;max-width:460px;width:100%;padding:32px 32px 28px;position:relative;text-align:center;font-family:var(--font-body);box-shadow:var(--shadow-3)">
<button id="ez-exit-close" aria-label="סגירה" style="position:absolute;top:12px;left:12px;width:36px;height:36px;border:1px solid var(--ez-line);border-radius:10px;background:#fff;cursor:pointer;font-size:16px;color:var(--ez-ink)"><i class="ph ph-x"></i></button>
<img src="assets/logo-mark-orange.png" alt="eazy" style="width:44px;height:44px;margin:0 auto 10px;display:block">
<div style="font-family:var(--font-display);font-weight:700;font-size:24px;line-height:1.25;color:var(--ez-ink)">רגע לפני שיוצאים –<br>רוצים גישה חינמית למשחק חווייתי ללימוד אנגלית?</div>
<p style="font-size:15.5px;line-height:1.55;color:var(--ez-fg-2);margin:10px 0 18px">כולל לימוד מהשגיאות שלכם. רשמו מספר סלולרי – ותקבלו בוואטסאפ גישה למשחק ללא הגבלה.</p>
<div style="display:grid;gap:10px">
<input id="ez-exit-phone" type="tel" placeholder="מספר סלולרי" style="height:46px;border:1px solid var(--ez-line);border-radius:8px;padding:0 14px;font-family:var(--font-body);font-size:15.5px;text-align:right">
<button id="ez-exit-send" style="background:#25D366;color:#fff;border:0;border-radius:999px;padding:13px 0;font-family:var(--font-display);font-weight:700;font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px"><i class="ph ph-whatsapp-logo" style="font-size:19px"></i>שלחו לי גישה בוואטסאפ</button>
</div>
<p style="display:flex;gap:8px;align-items:flex-start;text-align:right;font-size:12px;line-height:1.5;color:var(--ez-fg-3);margin:12px 0 0"><span style="flex:none;width:16px;height:16px;border-radius:4px;background:var(--ez-orange);display:flex;align-items:center;justify-content:center;margin-top:1px"><i class="ph-bold ph-check" style="font-size:10px;color:#fff"></i></span><span>אני מאשר/ת פנייה חוזרת ושמירת הפרטים לצורך כך בלבד, בהתאם למדיניות הפרטיות.</span></p>
</div>`;
document.body.appendChild(wrap);
const done = () => { wrap.style.display = 'none'; };
wrap.addEventListener('click', e => { if (e.target === wrap) done(); });
wrap.querySelector('#ez-exit-close').addEventListener('click', done);
wrap.querySelector('#ez-exit-send').addEventListener('click', () => {
sessionStorage.setItem('ez-lead-sent', '1');
const box = wrap.querySelector('div');
box.innerHTML = '<div style="font-family:var(--font-display);font-weight:700;font-size:24px;color:var(--ez-ink);padding:20px 0">מעולה! בדקו את הוואטסאפ (:</div>';
setTimeout(done, 1800);
});
// any lead-form submit on the page cancels the popup for the session
document.addEventListener('click', e => {
const b = e.target.closest && e.target.closest('button');
if (b && /דברו איתי/.test(b.textContent)) sessionStorage.setItem('ez-lead-sent', '1');
}, true);
const shouldShow = () => !sessionStorage.getItem('ez-exit-popup-shown') && !sessionStorage.getItem('ez-lead-sent');
const show = () => {
if (!shouldShow()) return;
sessionStorage.setItem('ez-exit-popup-shown', '1');
wrap.style.display = 'flex';
};
// desktop: cursor leaves through the top of the window
document.addEventListener('mouseout', e => { if (!e.relatedTarget && e.clientY <= 0) show(); });
// mobile: fast scroll back to the top after real engagement
let maxY = 0;
window.addEventListener('scroll', () => {
const y = window.scrollY;
maxY = Math.max(maxY, y);
if (window.innerWidth <= 820 && maxY > 900 && y < 80 && maxY - y > 800) show();
}, { passive: true });
}
if (document.readyState !== 'loading') initExitPopup(); else document.addEventListener('DOMContentLoaded', initExitPopup);
})();