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.
This commit is contained in:
tanweer919
2026-07-18 00:06:53 +05:30
parent f634312b83
commit a459fefb7f
67 changed files with 31188 additions and 4 deletions
+239
View File
@@ -0,0 +1,239 @@
/* 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'}, '*');
})();