a459fefb7f
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.
240 lines
9.6 KiB
JavaScript
240 lines
9.6 KiB
JavaScript
/* 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'}, '*');
|
||
})();
|