Files
lynkeduppro-crm/public/page/lp-v3.js
T
tanweer919 a459fefb7f feat(landing): port the full marketing site into the CRM (static)
Copies the goutam-pages landing (React marketing home + /page/1..19 + assets)
into /public and adds beforeFiles rewrites so '/' serves the marketing home and
'/1'..'/19' + '/thanks' serve the static pages — reproducing the landing's
vercel.json clean URLs. /portal/* (login/register) is untouched.
2026-07-18 00:06:53 +05:30

240 lines
9.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* LynkedUp Pro v3 — interactivity */
// ---------- Mobile nav (hamburger toggle) ----------
(function() {
const toggle = document.getElementById('navToggle');
const links = document.getElementById('navLinks');
const overlay = document.getElementById('navOverlay');
if (!toggle || !links) return;
function setOpen(open) {
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
toggle.classList.toggle('is-open', open);
links.classList.toggle('is-open', open);
if (overlay) overlay.classList.toggle('is-open', open);
document.body.classList.toggle('nav-locked', open);
}
toggle.addEventListener('click', () => {
const open = toggle.getAttribute('aria-expanded') !== 'true';
setOpen(open);
});
if (overlay) overlay.addEventListener('click', () => setOpen(false));
// Close on any link tap inside the panel
links.querySelectorAll('a').forEach(a => {
a.addEventListener('click', () => setOpen(false));
});
// Close when crossing back to desktop width
const mq = window.matchMedia('(min-width: 1024px)');
mq.addEventListener('change', e => { if (e.matches) setOpen(false); });
// Close on Escape
document.addEventListener('keydown', e => {
if (e.key === 'Escape') setOpen(false);
});
})();
// ---------- Theme toggle (light/dark) ----------
(function() {
const root = document.documentElement;
const key = 'lp-theme';
const saved = localStorage.getItem(key);
if (saved === 'light' || saved === 'dark') {
root.setAttribute('data-theme', saved);
} else {
root.setAttribute('data-theme', 'dark'); // default
}
const btn = document.getElementById('themeToggle');
if (btn) {
btn.addEventListener('click', () => {
const current = root.getAttribute('data-theme') || 'dark';
const next = current === 'dark' ? 'light' : 'dark';
root.setAttribute('data-theme', next);
localStorage.setItem(key, next);
});
}
})();
// ---------- Countdown timer (both final card + sticky bar) ----------
(function() {
let secs = 42, mins = 27, hours = 14, days = 3;
const get = id => document.getElementById(id);
const elFinal = { s: get('cdS'), m: get('cdM'), h: get('cdH'), d: get('cdD') };
const elStk = { s: get('ssS'), m: get('ssM'), h: get('ssH'), d: get('ssD') };
function pad(n) { return String(n).padStart(2, '0'); }
function render() {
const v = { d: pad(days), h: pad(hours), m: pad(mins), s: pad(secs) };
for (const k of ['d','h','m','s']) {
if (elFinal[k]) elFinal[k].textContent = v[k];
if (elStk[k]) elStk[k].textContent = v[k];
}
}
setInterval(() => {
secs--;
if (secs < 0) { secs = 59; mins--;
if (mins < 0) { mins = 59; hours--;
if (hours < 0) { hours = 23; days = Math.max(0, days - 1); }
}
}
render();
}, 1000);
render();
})();
// ---------- Sticky CTA reveal/hide ----------
(function() {
const el = document.getElementById('stickyCta');
const close = document.getElementById('stickyClose');
if (!el || !close) return;
let dismissed = false;
window.addEventListener('scroll', () => {
if (dismissed) return;
if (window.scrollY > 700) el.classList.add('show');
else el.classList.remove('show');
});
close.addEventListener('click', () => {
dismissed = true;
el.classList.remove('show');
document.body.style.paddingBottom = '0';
});
})();
// ---------- Video player ----------
(function() {
const frame = document.getElementById('videoFrame');
const playBtn = document.getElementById('videoPlay');
const videoEl = document.getElementById('videoEl');
if (!frame || !playBtn || !videoEl) return;
function play(at) {
frame.classList.add('playing');
if (typeof at === 'number' && !isNaN(at)) {
try { videoEl.currentTime = at; } catch (e) {}
}
videoEl.play().catch(() => {});
}
playBtn.addEventListener('click', () => play());
document.querySelectorAll('.vc[data-seek]').forEach(chip => {
chip.addEventListener('click', () => {
const at = parseFloat(chip.dataset.seek);
play(at);
});
});
})();
// ---------- Dashboard product-view tabs ----------
(function() {
const wrap = document.getElementById('dashWindow');
if (!wrap) return;
const tabs = wrap.querySelectorAll('.dw-tab');
const shots = wrap.querySelectorAll('.dw-shot');
const urlText = document.getElementById('dwUrlText');
function activate(name) {
tabs.forEach(t => {
const on = t.dataset.tab === name;
t.classList.toggle('is-active', on);
t.setAttribute('aria-selected', on ? 'true' : 'false');
if (on && urlText && t.dataset.url) urlText.textContent = t.dataset.url;
});
shots.forEach(s => s.classList.toggle('is-active', s.dataset.shot === name));
}
tabs.forEach(t => {
t.addEventListener('click', () => activate(t.dataset.tab));
});
})();
// ---------- Smooth in-page nav ----------
document.querySelectorAll('a[href^="#"]').forEach(a => {
a.addEventListener('click', (e) => {
const id = a.getAttribute('href');
if (id.length < 2) return;
const t = document.querySelector(id);
if (!t) return;
e.preventDefault();
window.scrollTo({ top: t.getBoundingClientRect().top + window.scrollY - 80, behavior: 'smooth' });
});
});
// ---------- Tweaks panel ----------
(function() {
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"accentOrange": "#f97316",
"accentCyan": "#22d3ee",
"bgIntensity": 0.65,
"showGrid": true
}/*EDITMODE-END*/;
let state = { ...TWEAK_DEFAULTS };
let active = false;
const root = document.getElementById('tweaks-root');
function applyState() {
document.documentElement.style.setProperty('--orange', state.accentOrange);
document.documentElement.style.setProperty('--cyan', state.accentCyan);
document.documentElement.style.setProperty('--cyan-2', state.accentCyan);
const orbs = document.querySelectorAll('.bg-orb');
orbs.forEach(o => o.style.opacity = state.bgIntensity);
const grid = document.querySelector('.bg-grid');
if (grid) grid.style.display = state.showGrid ? '' : 'none';
}
applyState();
function render() {
if (!active) { root.innerHTML = ''; return; }
root.innerHTML = `
<div style="width:300px; background:rgba(5,15,36,.96); backdrop-filter:blur(20px); border:1px solid rgba(56,189,248,.3); border-radius:16px; box-shadow:0 30px 60px -20px rgba(0,0,0,.8); padding:18px; font-family:'Plus Jakarta Sans'; color:#e6edf6;">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px;">
<strong style="font-size:14px; letter-spacing:-.01em; color:#fff;">Tweaks</strong>
<button id="tw-close" style="border:0; background:transparent; cursor:pointer; color:#94a3b8; font-size:18px;">×</button>
</div>
<div style="font-size:11px; color:#94a3b8; font-weight:600; text-transform:uppercase; letter-spacing:.05em; margin-bottom:8px;">CTA Orange</div>
<div style="display:flex; gap:6px; margin-bottom:14px;">
${['#f97316','#ef4444','#fbbf24','#ec4899'].map(c => `<button data-orange="${c}" style="flex:1; height:32px; border-radius:8px; border:2px solid ${state.accentOrange===c?'#fff':'transparent'}; background:${c}; cursor:pointer;"></button>`).join('')}
</div>
<div style="font-size:11px; color:#94a3b8; font-weight:600; text-transform:uppercase; letter-spacing:.05em; margin-bottom:8px;">Accent Cyan</div>
<div style="display:flex; gap:6px; margin-bottom:14px;">
${['#22d3ee','#38bdf8','#a78bfa','#10b981'].map(c => `<button data-cyan="${c}" style="flex:1; height:32px; border-radius:8px; border:2px solid ${state.accentCyan===c?'#fff':'transparent'}; background:${c}; cursor:pointer;"></button>`).join('')}
</div>
<div style="font-size:11px; color:#94a3b8; font-weight:600; text-transform:uppercase; letter-spacing:.05em; margin-bottom:8px;">Background glow · ${Math.round(state.bgIntensity*100)}%</div>
<input type="range" min="0" max="1" step="0.05" value="${state.bgIntensity}" id="tw-bg" style="width:100%; margin-bottom:14px;">
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:#cbd5e1; cursor:pointer;">
<input type="checkbox" id="tw-grid" ${state.showGrid?'checked':''}/>
Show background grid
</label>
</div>
`;
root.querySelector('#tw-close').onclick = () => {
active = false; render();
window.parent.postMessage({type:'__edit_mode_dismissed'}, '*');
};
root.querySelectorAll('[data-orange]').forEach(b => b.onclick = () => {
state.accentOrange = b.dataset.orange; applyState(); render();
window.parent.postMessage({type:'__edit_mode_set_keys', edits:{accentOrange: state.accentOrange}}, '*');
});
root.querySelectorAll('[data-cyan]').forEach(b => b.onclick = () => {
state.accentCyan = b.dataset.cyan; applyState(); render();
window.parent.postMessage({type:'__edit_mode_set_keys', edits:{accentCyan: state.accentCyan}}, '*');
});
root.querySelector('#tw-bg').oninput = (e) => {
state.bgIntensity = parseFloat(e.target.value); applyState(); render();
window.parent.postMessage({type:'__edit_mode_set_keys', edits:{bgIntensity: state.bgIntensity}}, '*');
};
root.querySelector('#tw-grid').onchange = (e) => {
state.showGrid = e.target.checked; applyState(); render();
window.parent.postMessage({type:'__edit_mode_set_keys', edits:{showGrid: state.showGrid}}, '*');
};
}
window.addEventListener('message', (e) => {
if (e.data?.type === '__activate_edit_mode') { active = true; render(); }
if (e.data?.type === '__deactivate_edit_mode') { active = false; render(); }
});
window.parent.postMessage({type:'__edit_mode_available'}, '*');
})();