Deploy static website

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Andres Corazon Santana Aquino
2026-10-27 15:47:21 -04:00
parent a78c73c6e5
commit c8ad2d9989
154 changed files with 11058 additions and 6686 deletions
+28
View File
@@ -0,0 +1,28 @@
(function () {
// Without the lead form, drop the calendars before GHL's form_embed.js picks them up.
// The iframes keep their src in the HTML: form_embed.js only sizes iframes that already
// point at /booking when it runs, and an unsized calendar hides its confirm button.
if (document.documentElement.getAttribute('data-gate') !== 'open') {
document.querySelectorAll('iframe.ghl-cal').forEach(function (f) { f.remove(); });
return;
}
var tabs = document.querySelectorAll('[data-call]');
var select = function (id, scroll) {
tabs.forEach(function (t) {
var on = t.getAttribute('data-call') === id;
t.setAttribute('aria-selected', String(on));
var panel = document.getElementById('cal-' + t.getAttribute('data-call'));
if (panel) { if (on) panel.removeAttribute('data-off'); else panel.setAttribute('data-off', ''); }
});
if (scroll) document.getElementById('schedule').scrollIntoView({ behavior: 'smooth', block: 'start' });
};
tabs.forEach(function (t) {
t.addEventListener('click', function () {
var id = t.getAttribute('data-call');
select(id, true);
try { history.replaceState(null, '', '#' + id); } catch (e) {}
});
});
var hash = location.hash.replace('#', '');
if (hash === 'free' || hash === 'vip') select(hash, false);
})();
Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

+65
View File
@@ -0,0 +1,65 @@
/* Inter Tight (SIL Open Font License 1.1) - self-hosted, no Google Fonts request */
@font-face {
font-family: 'Inter Tight';
font-style: normal;
font-display: swap;
font-weight: 300;
src: url(fonts/inter-tight-latin-ext-300-normal.woff2) format('woff2');
unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
font-family: 'Inter Tight';
font-style: normal;
font-display: swap;
font-weight: 300;
src: url(fonts/inter-tight-latin-300-normal.woff2) format('woff2');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
font-family: 'Inter Tight';
font-style: normal;
font-display: swap;
font-weight: 400;
src: url(fonts/inter-tight-latin-ext-400-normal.woff2) format('woff2');
unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
font-family: 'Inter Tight';
font-style: normal;
font-display: swap;
font-weight: 400;
src: url(fonts/inter-tight-latin-400-normal.woff2) format('woff2');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
font-family: 'Inter Tight';
font-style: normal;
font-display: swap;
font-weight: 500;
src: url(fonts/inter-tight-latin-ext-500-normal.woff2) format('woff2');
unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
font-family: 'Inter Tight';
font-style: normal;
font-display: swap;
font-weight: 500;
src: url(fonts/inter-tight-latin-500-normal.woff2) format('woff2');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
font-family: 'Inter Tight';
font-style: normal;
font-display: swap;
font-weight: 600;
src: url(fonts/inter-tight-latin-ext-600-normal.woff2) format('woff2');
unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
font-family: 'Inter Tight';
font-style: normal;
font-display: swap;
font-weight: 600;
src: url(fonts/inter-tight-latin-600-normal.woff2) format('woff2');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+265
View File
@@ -0,0 +1,265 @@
/* Homepage: option 2 sections (3D, neon, fireworks) scoped under .o2.
Panels that act as "screens" (venture stage, fireworks stage) stay dark in
both themes; everything else reads from the shared tokens. */
.o2 {
--o2-text: var(--ink);
--o2-muted: var(--slate);
--o2-faint: var(--smoke);
--neon-off: #222c47;
--neon-on: #e8f9ff;
--neon-glow: 0 0 4px rgba(255,255,255,.9), 0 0 14px #1fb7ff, 0 0 34px #1a7fff, 0 0 72px rgba(26,127,255,.8);
--neon-mid: #9fd8f2;
--stroke-word: rgba(159, 230, 255, .14);
--o2-tile-bd: rgba(159, 230, 255, .3);
--o2-tile-bg: linear-gradient(145deg, rgba(159, 230, 255, .14), rgba(26, 127, 255, .04));
--o2-beam: rgba(31, 183, 255, .30);
}
:root[data-theme="light"] .o2 {
--neon-off: #c3cfe2;
--neon-on: #0a5fd1;
--neon-glow: 0 0 18px rgba(26, 127, 255, .35);
--neon-mid: #6aa6e8;
--stroke-word: rgba(10, 60, 140, .12);
--o2-tile-bd: rgba(26, 127, 255, .25);
--o2-tile-bg: linear-gradient(145deg, rgba(26, 127, 255, .10), rgba(255, 255, 255, .6));
--o2-beam: rgba(31, 140, 255, .16);
}
.o2 > section { position: relative; overflow-x: clip; padding: clamp(5rem, 12vh, 8rem) 0; }
.o2 .lead { color: var(--o2-muted); }
.o2 .grad2 { background: linear-gradient(180deg, #a9dfff 0%, #5aa8ff 60%, #1a7fff); -webkit-background-clip: text; background-clip: text; color: transparent; }
:root[data-theme="light"] .o2 .grad2 { background: linear-gradient(180deg, #1a6fe0, #0b4fb0); -webkit-background-clip: text; background-clip: text; }
/* ---------- hero ---------- */
#hero { min-height: calc(100svh - var(--nav-h)); display: flex; align-items: center; justify-content: center; text-align: center; padding: 3rem 0 6rem; overflow: hidden; }
.beam { position: absolute; left: 50%; top: -10%; width: min(1100px, 140vw); height: 90%; transform: translateX(-50%); pointer-events: none;
background: conic-gradient(from 180deg at 50% 0%, transparent 160deg, transparent 168deg, var(--o2-beam) 180deg, transparent 192deg, transparent 200deg);
filter: blur(26px); opacity: .9; }
.beam::after { content: ""; position: absolute; left: 50%; top: 0; width: 60%; height: 70%; transform: translateX(-50%); background: radial-gradient(closest-side, var(--glow), transparent); }
.floaters .t { position: absolute; display: flex; flex-direction: column; align-items: center; gap: .5rem; top: var(--y); animation: drift 9s ease-in-out infinite; animation-delay: var(--d); }
.floaters .t.l { left: var(--x); } .floaters .t.r { right: var(--x); }
@keyframes drift { 50% { transform: translateY(-16px) rotate(var(--r, 0deg)); } }
.tile { display: grid; place-items: center; width: 60px; height: 60px; border-radius: 18px; border: 1px solid var(--o2-tile-bd); background: var(--o2-tile-bg); color: var(--ice);
box-shadow: 0 0 30px rgba(26, 127, 255, .25), inset 0 0 18px rgba(159, 230, 255, .08); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); transition: box-shadow .3s, border-color .3s; }
.t:hover .tile { border-color: var(--sky); box-shadow: 0 0 44px rgba(31, 183, 255, .5), inset 0 0 18px rgba(159, 230, 255, .15); }
.tile svg { width: 28px; height: 28px; }
.tl { font-size: .74rem; color: var(--o2-muted); white-space: nowrap; }
.icon-row { display: none; grid-template-columns: repeat(3, 1fr); gap: 1.2rem .8rem; max-width: 380px; margin: 2.4rem auto 0; }
.icon-row .t { display: flex; flex-direction: column; align-items: center; gap: .45rem; }
.icon-row .tl { white-space: normal; text-align: center; line-height: 1.2; }
@media (max-width: 1179px) { .floaters { display: none; } .icon-row { display: grid; } }
@media (min-width: 600px) and (max-width: 1179px) { .icon-row { grid-template-columns: repeat(6, 1fr); max-width: 660px; } }
.emblem { position: relative; width: clamp(116px, 12vw, 164px); margin: 0 auto; perspective: 700px; }
.emblem img { display: block; width: 100%; height: auto; transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); transition: transform .3s ease-out; filter: drop-shadow(0 0 30px rgba(26, 127, 255, .65)); animation: float 6s ease-in-out infinite; }
.emblem::after { content: ""; position: absolute; left: 10%; right: 10%; bottom: -14px; height: 18px; background: radial-gradient(closest-side, rgba(31, 183, 255, .55), transparent); filter: blur(6px); }
@keyframes float { 50% { translate: 0 -10px; } }
.hero-in { position: relative; z-index: 2; max-width: 1000px; padding-inline: var(--gutter); display: flex; flex-direction: column; align-items: center; }
.hero-in > * { opacity: 0; transform: translateY(22px); filter: blur(8px); animation: heroIn 1s cubic-bezier(.2, .85, .2, 1) forwards; }
.hero-in > *:nth-child(1) { animation-delay: .05s; } .hero-in > *:nth-child(2) { animation-delay: .25s; } .hero-in > *:nth-child(3) { animation-delay: .45s; }
.hero-in > *:nth-child(4) { animation-delay: .65s; } .hero-in > *:nth-child(5) { animation-delay: .85s; } .hero-in > *:nth-child(6) { animation-delay: 1.05s; }
@keyframes heroIn { to { opacity: 1; transform: none; filter: none; } }
#hero .eyebrow { align-self: center; margin-top: 1rem; }
#hero h1 { margin: 1.2rem auto 1.3rem; font-weight: 500; font-size: clamp(2.4rem, 5.6vw, 5.2rem); line-height: 1; letter-spacing: -0.05em; text-wrap: balance; }
#hero h1 .grad, #hero h1 .grad2 { display: inline; }
#hero .lead { margin: 0 auto 2rem; font-size: clamp(1rem, 1.5vw, 1.18rem); max-width: 62ch; }
#hero .btn-row { justify-content: center; }
.scroll-note { position: absolute; bottom: 1.6rem; left: 50%; transform: translateX(-50%); }
.scroll-note i { display: block; width: 1px; height: 46px; background: linear-gradient(var(--sky), transparent); animation: drip 2s infinite; }
@keyframes drip { 0% { transform: scaleY(0); transform-origin: top; } 50% { transform: scaleY(1); transform-origin: top; } 51% { transform-origin: bottom; } 100% { transform: scaleY(0); transform-origin: bottom; } }
/* ---------- giant words + cube ---------- */
.o2 > .wsec { min-height: min(100svh, 820px); padding: 0; overflow: hidden; }
.wsec-in { position: absolute; inset: 0; perspective: 1200px; }
.cube { --c: min(36vmin, 320px); position: absolute; left: 50%; top: 50%; width: var(--c); height: var(--c); margin: calc(var(--c) / -2) 0 0 calc(var(--c) / -2); transform-style: preserve-3d; animation: spin 26s linear infinite; }
.cube i { position: absolute; inset: 0; border: 1px solid rgba(159, 230, 255, .55); background: rgba(31, 183, 255, .05); box-shadow: inset 0 0 40px rgba(31, 183, 255, .12); }
:root[data-theme="light"] .cube i { border-color: rgba(26, 127, 255, .45); }
.cube i:nth-child(1) { transform: translateZ(calc(var(--c) / 2)); } .cube i:nth-child(2) { transform: rotateY(180deg) translateZ(calc(var(--c) / 2)); }
.cube i:nth-child(3) { transform: rotateY(90deg) translateZ(calc(var(--c) / 2)); } .cube i:nth-child(4) { transform: rotateY(-90deg) translateZ(calc(var(--c) / 2)); }
.cube i:nth-child(5) { transform: rotateX(90deg) translateZ(calc(var(--c) / 2)); } .cube i:nth-child(6) { transform: rotateX(-90deg) translateZ(calc(var(--c) / 2)); }
@keyframes spin { to { transform: rotateX(360deg) rotateY(360deg); } }
.cube-glow { position: absolute; left: 50%; top: 50%; width: 70vmin; height: 70vmin; transform: translate(-50%, -50%); background: radial-gradient(closest-side, rgba(26, 127, 255, .35), transparent); pointer-events: none; }
:root[data-theme="light"] .cube-glow { background: radial-gradient(closest-side, rgba(26, 127, 255, .14), transparent); }
.wmask { position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); }
#wTrack { display: inline-flex; gap: .35em; padding-right: .35em; white-space: nowrap; font-weight: 500; font-size: clamp(4.6rem, 17vw, 16rem); line-height: 1.05; letter-spacing: -0.06em; will-change: transform; }
#wTrack span { background: linear-gradient(180deg, var(--grad-a) 10%, #7fcdff 62%, #1a7fff); -webkit-background-clip: text; background-clip: text; color: transparent; transition: opacity .15s; }
.w-side { position: absolute; right: max(var(--gutter), calc((100vw - var(--page-max)) / 2 + var(--gutter))); bottom: clamp(2rem, 9vh, 5rem); max-width: 320px; display: flex; flex-direction: column; gap: .9rem; align-items: flex-start; z-index: 3; }
.w-side p { color: var(--o2-muted); font-size: .98rem; }
.w-top { position: absolute; left: max(var(--gutter), calc((100vw - var(--page-max)) / 2 + var(--gutter))); top: 2.5rem; z-index: 3; }
@media (max-width: 700px) { .w-side { left: var(--gutter); right: var(--gutter); max-width: none; } }
/* ---------- building: copy beside the portfolio dashboard ---------- */
.bgrid { display: grid; grid-template-columns: 0.75fr 1.25fr; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
.bgrid > * { min-width: 0; }
.bhead { display: flex; flex-direction: column; gap: 1.4rem; align-items: flex-start; }
.cluster-fit { position: relative; width: 100%; }
.cluster-fit .cluster { margin: 0; width: 760px; height: 740px; max-width: none; transform-origin: 0 0; transform: scale(var(--s, 1)); }
.cluster-fit .dash { left: 176px; top: 0; width: 540px; }
.cluster-fit .profile { left: 0; top: 210px; width: 196px; }
.cluster-fit .code { right: 0; top: 420px; width: 320px; }
.cluster-fit .dash-cols { grid-template-columns: 1.25fr 1fr; }
.cluster-fit .row { font-size: 12.5px; }
@media (max-width: 1100px) {
.bgrid { grid-template-columns: 1fr; }
.cluster-fit .cluster { width: auto; height: auto; transform: none; }
.cluster-fit .panel { left: auto; right: auto; top: auto; width: auto; }
.cluster-fit .profile { max-width: 340px; }
}
/* ---------- ventures: tabs + themed 3D object ---------- */
.vhead { text-align: center; display: flex; flex-direction: column; align-items: center; gap: 1.1rem; margin-bottom: 2.4rem; }
.vhead .lead { max-width: 60ch; }
.vhead .eyebrow, .jhead .eyebrow { align-self: center; }
.jhead .eyebrow { align-self: flex-start; }
.vtabs { display: flex; gap: .4rem; padding: .35rem; border: 1px solid var(--bd); border-radius: 14px; background: var(--glass); overflow-x: auto; max-width: 100%; scrollbar-width: none; margin: 0 auto 1.6rem; width: max-content; }
.vtabs::-webkit-scrollbar { display: none; }
.vtabs button { padding: .55rem 1rem; border: 0; border-radius: 10px; background: none; font: 500 .88rem var(--font-sans); color: var(--o2-muted); white-space: nowrap; cursor: pointer; transition: all .25s; }
.vtabs button:hover { color: var(--ink); }
.vtabs button[aria-selected="true"] { background: var(--ink); color: var(--porcelain); }
.vpanel { position: relative; display: grid; grid-template-columns: 1fr 1fr; min-height: 480px; border: 1px solid var(--bd); border-radius: 22px; color: #f3f7ff; overflow: hidden; --ac: #1fb7ff;
--vp-title: #f3f7ff; --vp-text: #b3c0da; --vp-num: #55627f; --vp-lines: rgba(255, 255, 255, .04); --vp-glow: 40%; --vp-arrow-bg: rgba(10, 14, 26, .7); --vp-arrow-fg: #fff; --vp-arrow-bd: rgba(255, 255, 255, .09);
background: linear-gradient(135deg, rgba(255, 255, 255, .04), rgba(255, 255, 255, .01)), #070b16; }
:root[data-theme="light"] .vpanel { --vp-title: #0b1220; --vp-text: #4a5873; --vp-num: #8896b3; --vp-lines: rgba(15, 23, 42, .05); --vp-glow: 28%; --vp-arrow-bg: rgba(255, 255, 255, .85); --vp-arrow-fg: #0b1220; --vp-arrow-bd: rgba(15, 23, 42, .12);
background: linear-gradient(135deg, #ffffff, #eef4fd); box-shadow: 0 30px 60px -40px rgba(15, 23, 42, .35); }
.vpanel::before { content: ""; position: absolute; inset: 0; background: radial-gradient(60% 80% at 75% 50%, color-mix(in srgb, var(--ac) var(--vp-glow), transparent), transparent 70%); transition: background .6s; pointer-events: none; }
.vpanel::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(var(--vp-lines) 1px, transparent 1px) 0 0 / 100% 38px; -webkit-mask-image: linear-gradient(90deg, transparent 30%, #000); mask-image: linear-gradient(90deg, transparent 30%, #000); }
.vinfo { position: relative; z-index: 2; padding: 2.4rem; display: flex; flex-direction: column; justify-content: space-between; gap: 2rem; }
.vnum { font-size: .9rem; color: var(--vp-num); font-variant-numeric: tabular-nums; }
.vinfo h3 { font-weight: 500; font-size: clamp(1.8rem, 3.4vw, 3rem); line-height: 1.05; letter-spacing: -0.045em; margin-bottom: 1rem; color: var(--vp-title); }
.vinfo p { color: var(--vp-text); font-size: 1.08rem; max-width: 34ch; }
.vinfo .vt { transition: opacity .3s, transform .3s; }
.vinfo .vt.out { opacity: 0; transform: translateY(10px); }
#vcanvas { position: relative; z-index: 1; width: 100%; height: 100%; min-height: 340px; cursor: grab; }
.varrows { position: absolute; z-index: 3; right: 1.2rem; bottom: 1.2rem; display: flex; gap: .5rem; }
.varrows button { width: 44px; height: 44px; border: 1px solid var(--vp-arrow-bd); border-radius: 10px; background: var(--vp-arrow-bg); color: var(--vp-arrow-fg); backdrop-filter: blur(8px); display: grid; place-items: center; cursor: pointer; transition: border-color .2s; }
.varrows button:hover { border-color: #1fb7ff; }
.varrows svg { width: 16px; height: 16px; }
@media (max-width: 860px) { .vpanel { grid-template-columns: 1fr; grid-template-rows: 280px auto; } #vcanvas { order: -1; min-height: 280px; } .vinfo { padding: 1.6rem 1.6rem 4.6rem; } }
/* ---------- journey: fireworks + Dylan's stats ---------- */
.jgrid { display: grid; grid-template-columns: 1fr 1.1fr; grid-template-areas: "head stage" "body stage"; gap: 1.6rem clamp(2rem, 5vw, 4rem); align-items: start; }
.jgrid > * { min-width: 0; }
.jhead { grid-area: head; display: flex; flex-direction: column; gap: 1.1rem; align-items: flex-start; }
.jbody { grid-area: body; }
.jstage { grid-area: stage; position: relative; height: clamp(460px, 78vh, 680px); align-self: stretch; overflow: hidden; border-radius: 18px; border: 1px solid var(--bd); color: var(--ink);
--floor-line: rgba(159, 230, 255, .26); --pin: #1fb7ff; --pin-label: #55627f; --pin-done: #fff; --final: #d6e8ff;
background: radial-gradient(70% 55% at 50% 100%, rgba(26, 127, 255, .18), transparent 70%), #060a14; }
:root[data-theme="light"] .jstage { --floor-line: rgba(26, 111, 224, .28); --pin: #1a7fff; --pin-label: #8896b3; --pin-done: #0b1220; --final: #13306b;
background: radial-gradient(70% 55% at 50% 100%, rgba(26, 127, 255, .14), transparent 70%), linear-gradient(180deg, #ffffff, #eef4fd); }
.jlist { display: grid; gap: .7rem; }
.jx { display: flex; gap: 1rem; align-items: flex-start; padding: 1rem 1.2rem; border: 1px solid var(--bd); border-radius: 14px; background: var(--glass); opacity: .4; transition: opacity .6s, border-color .6s, box-shadow .6s, background .6s; }
.jx .ji { flex: none; display: grid; place-items: center; width: 44px; height: 44px; border-radius: 12px; border: 1px solid var(--bd); color: var(--o2-faint); transition: all .6s; }
.jx svg { width: 24px; height: 24px; }
.jx h3 { font-weight: 500; font-size: 1.15rem; letter-spacing: -0.03em; margin-bottom: .15rem; }
.jx p { color: var(--o2-muted); font-size: .92rem; line-height: 1.45; }
.jx.lit { opacity: 1; border-color: rgba(31, 183, 255, .5); background: linear-gradient(150deg, rgba(26, 127, 255, .14), var(--glass)); box-shadow: 0 0 36px rgba(26, 127, 255, .14); }
.jx.lit .ji { color: var(--ice); border-color: rgba(31, 183, 255, .55); box-shadow: 0 0 18px rgba(31, 183, 255, .4); }
.floor { position: absolute; left: -10%; right: -10%; bottom: -6%; height: 46%; transform: perspective(700px) rotateX(64deg); transform-origin: 50% 100%;
background: linear-gradient(var(--floor-line) 1px, transparent 1px) 0 0 / 56px 56px, linear-gradient(90deg, var(--floor-line) 1px, transparent 1px) 0 0 / 56px 56px;
-webkit-mask-image: radial-gradient(closest-side at 50% 70%, #000 35%, transparent); mask-image: radial-gradient(closest-side at 50% 70%, #000 35%, transparent); }
.pins { position: absolute; left: 0; right: 0; bottom: 17%; height: 0; }
.pn { position: absolute; bottom: 0; width: 0; }
.pn i { position: absolute; left: 0; bottom: 0; width: 2px; height: 0; transform: translateX(-1px); background: linear-gradient(var(--pin), transparent); opacity: 0; }
.pn.rise i { height: var(--ph, 100px); opacity: 1; transition: height .8s cubic-bezier(.25, .7, .35, 1), opacity .2s; }
.pn b { position: absolute; left: -8px; bottom: var(--ph, 100px); width: 16px; height: 16px; margin-bottom: -8px; border-radius: 50%; background: #fff; border: 2px solid var(--pin); opacity: 0; transform: scale(.2); transition: opacity .3s, transform .5s cubic-bezier(.3, 1.6, .5, 1); }
.pn.done b { opacity: 1; transform: scale(1); box-shadow: 0 0 24px 6px var(--pin); }
.pn em { position: absolute; top: 20px; left: 0; transform: translateX(-50%); width: 84px; text-align: center; line-height: 1.15; font-style: normal; font-size: .76rem; color: var(--pin-label); transition: color .5s; }
.pn.done em { color: var(--pin-done); }
#fw { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.jfinal { position: absolute; left: 1.2rem; top: 1.2rem; max-width: 240px; font-size: .98rem; line-height: 1.35; color: var(--final); opacity: 0; transform: translateY(8px); transition: opacity .8s, transform .8s; }
.jfinal.show { opacity: 1; transform: none; }
.jreplay { position: absolute; right: 1rem; top: 1rem; opacity: 0; pointer-events: none; transition: opacity .4s; }
.jreplay.show { opacity: 1; pointer-events: auto; }
.o2 .stats b.pop { animation: pop .45s ease; }
@keyframes pop { 40% { transform: scale(1.12); color: var(--sky); } }
.o2 .stats { margin-top: 3.5rem; }
.o2 .stats b { font-weight: 500; letter-spacing: -0.05em; }
@media (max-width: 900px) { .jgrid { grid-template-columns: 1fr; grid-template-areas: "head" "stage" "body"; } .jstage { height: 420px; } }
/* ---------- about: neon words ---------- */
.agrid { display: grid; grid-template-columns: 1.15fr .85fr; gap: clamp(2rem, 5vw, 5rem); align-items: center; }
.agrid > * { min-width: 0; }
.awords span { display: block; padding-bottom: .14em; margin-bottom: -.1em; font-weight: 500; font-size: clamp(2.6rem, 7vw, 6.4rem); line-height: .98; letter-spacing: -0.055em; }
.awords .neon { color: var(--neon-off); }
.awords.is-in .neon { animation: neonOn 2.6s steps(1, end) .7s forwards, neonHum 7s ease-in-out 3.5s infinite; }
.awords.is-in .neon.n2 { animation-delay: 1.5s, 4.3s; }
@keyframes neonOn {
0% { color: var(--neon-off); text-shadow: none; }
10% { color: var(--neon-on); text-shadow: var(--neon-glow); }
14% { color: var(--neon-off); text-shadow: none; }
26% { color: var(--neon-on); text-shadow: var(--neon-glow); }
30% { color: var(--neon-off); text-shadow: none; }
36% { color: var(--neon-mid); text-shadow: 0 0 10px rgba(31, 183, 255, .5); }
40% { color: var(--neon-off); text-shadow: none; }
52% { color: var(--neon-on); text-shadow: var(--neon-glow); }
56% { color: var(--neon-mid); text-shadow: 0 0 8px #1fb7ff, 0 0 22px rgba(26, 127, 255, .7); }
60%, 100% { color: var(--neon-on); text-shadow: var(--neon-glow); }
}
@keyframes neonHum { 0%, 45%, 55%, 100% { opacity: 1; } 48% { opacity: .78; } 50% { opacity: 1; } 52% { opacity: .9; } }
.atext { display: flex; flex-direction: column; gap: 1.1rem; align-items: flex-start; }
.atext .q { font-size: clamp(1.15rem, 1.9vw, 1.5rem); line-height: 1.3; letter-spacing: -0.02em; color: var(--ink); max-width: 36ch; }
@media (max-width: 860px) { .agrid { grid-template-columns: 1fr; } }
/* ---------- consulting cards ---------- */
.chead { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; align-items: end; margin-bottom: 2.6rem; }
.chead > * { min-width: 0; }
.cgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.cc { position: relative; padding: 1.8rem; border: 1px solid var(--bd); border-radius: 16px; background: linear-gradient(160deg, var(--glass), transparent), var(--cloud); overflow: hidden; min-height: 210px; transition: border-color .3s, box-shadow .3s; }
.cc::before { content: ""; position: absolute; right: -40px; top: -40px; width: 200px; height: 200px; background: radial-gradient(closest-side, rgba(26, 127, 255, .28), transparent); transition: opacity .4s; opacity: .6; }
.cc:hover { border-color: rgba(31, 183, 255, .5); box-shadow: 0 0 40px rgba(26, 127, 255, .12); }
.cc .cic { position: relative; display: grid; place-items: center; width: 58px; height: 58px; margin-bottom: 1.6rem; border-radius: 50%; border: 1px solid rgba(31, 183, 255, .4); background: radial-gradient(circle at 35% 30%, #16315f, #08101f); box-shadow: 0 0 24px rgba(26, 127, 255, .4); color: #9fe6ff; }
.cc .cic svg { width: 26px; height: 26px; }
.cc h3 { position: relative; font-weight: 500; font-size: 1.3rem; letter-spacing: -0.03em; margin-bottom: .5rem; }
.cc p { position: relative; color: var(--o2-muted); font-size: .98rem; max-width: 46ch; }
@media (max-width: 860px) { .chead { grid-template-columns: 1fr; } }
@media (max-width: 700px) { .cgrid { grid-template-columns: 1fr; } }
/* ---------- playbook: outline word + our 3D book ---------- */
#playbook { overflow: hidden; background: linear-gradient(transparent, rgba(26, 127, 255, .06), transparent); }
.pbword { position: absolute; left: 0; top: 50%; transform: translateY(-50%); white-space: nowrap; font-weight: 500; font-size: clamp(7rem, 26vw, 24rem); letter-spacing: -0.07em; line-height: 1; color: transparent; -webkit-text-stroke: 1px var(--stroke-word); pointer-events: none; will-change: transform; }
.pbgrid { position: relative; display: grid; grid-template-columns: 1.1fr .9fr; gap: 3rem; align-items: center; }
.pbgrid > * { min-width: 0; }
.pbtext { display: flex; flex-direction: column; gap: 1.2rem; align-items: flex-start; }
.o2 .book-stage .book { box-shadow: -12px 0 0 #091a40, 0 40px 90px rgba(26, 127, 255, .4), inset 0 0 0 1px rgba(255, 255, 255, .12); transform: rotateY(-18deg) rotateX(4deg); cursor: pointer; }
.o2 .book-stage:hover .book, .o2 .book.tap { transform: rotateY(-4deg) rotateX(1deg) translateY(-6px); }
.o2 .book-stage .book .sheen { position: absolute; inset: 0; border-radius: inherit; background: linear-gradient(115deg, transparent 40%, rgba(255, 255, 255, .14) 50%, transparent 60%); pointer-events: none; }
@media (max-width: 860px) { .pbgrid { grid-template-columns: 1fr; } }
/* ---------- press ---------- */
.phead { display: flex; justify-content: space-between; align-items: end; gap: 2rem; flex-wrap: wrap; margin-bottom: 2.2rem; }
.pgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; }
.pt { position: relative; display: flex; flex-direction: column; justify-content: space-between; min-height: 260px; padding: 1.4rem; border: 1px solid var(--bd); border-radius: 16px; background: var(--glass); overflow: hidden; transition: border-color .3s, transform .3s; }
.pt::before { content: ""; position: absolute; inset: 0; background: radial-gradient(80% 60% at 50% 120%, rgba(26, 127, 255, .4), transparent); opacity: .5; transition: opacity .4s; }
.pt:hover { border-color: rgba(31, 183, 255, .5); transform: translateY(-4px); }
.pt:hover::before { opacity: 1; }
.pt h3 { position: relative; font-weight: 500; font-size: 1.5rem; letter-spacing: -0.04em; }
.pt .arrow { position: relative; align-self: flex-end; display: grid; place-items: center; width: 38px; height: 38px; border: 1px solid var(--bd); border-radius: 10px; }
.pt .arrow svg { width: 14px; height: 14px; }
@media (max-width: 960px) { .pgrid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .pgrid { grid-template-columns: 1fr; } .pt { min-height: 150px; } }
/* ---------- poetry ---------- */
.o2 > #poetry { min-height: 100svh; display: flex; align-items: center; justify-content: center; text-align: center; overflow: hidden; }
.arc { position: absolute; left: 50%; bottom: -62vw; width: 130vw; height: 130vw; transform: translateX(-50%); border-radius: 50%; border: 2px solid rgba(31, 183, 255, .65);
box-shadow: 0 0 80px rgba(26, 127, 255, .55), inset 0 0 120px rgba(26, 127, 255, .35), 0 -2px 18px rgba(159, 230, 255, .7); background: radial-gradient(closest-side at 50% 0%, rgba(26, 127, 255, .18), transparent); }
:root[data-theme="light"] .arc { box-shadow: 0 0 60px rgba(26, 127, 255, .25), inset 0 0 120px rgba(26, 127, 255, .15); }
#poetry .in-wrap { position: relative; z-index: 2; max-width: 820px; padding-inline: var(--gutter); display: flex; flex-direction: column; align-items: center; gap: 1.4rem; }
#poetry .eyebrow { align-self: center; }
#poetry .poem-line { color: var(--graphite); font-size: 1.25rem; }
/* ---------- connect ---------- */
.o2 > #connect { padding: clamp(3rem, 8vh, 5rem) 0 0; overflow: hidden; }
.talk { position: relative; width: 100%; height: clamp(14rem, 38vw, 34rem); display: flex; justify-content: center; align-items: center; overflow: hidden; -webkit-mask-image: linear-gradient(transparent, #000 20%, #000 80%, transparent); mask-image: linear-gradient(transparent, #000 20%, #000 80%, transparent); }
.ribbon { position: absolute; left: 50%; top: 50%; width: max(130%, 1500px); max-width: none; height: auto; transform: translate(-50%, -50%); animation: rib 14s ease-in-out infinite alternate; filter: blur(.35px) saturate(1.08); image-rendering: auto; }
@keyframes rib { from { transform: translate(-52%, -48%) rotate(-3deg) scale(1); } to { transform: translate(-48%, -52%) rotate(3deg) scale(1.06); } }
.talk h2 { position: relative; z-index: 2; font-weight: 600; font-size: clamp(5rem, 23vw, 22rem); line-height: .9; letter-spacing: -0.07em; color: #fff; mix-blend-mode: difference; white-space: nowrap; }
:root[data-theme="light"] .talk h2 { mix-blend-mode: normal; color: #0b1220; text-shadow: 0 2px 30px rgba(255, 255, 255, .6); }
.cbody { position: relative; z-index: 2; padding-inline: var(--gutter); display: flex; flex-direction: column; align-items: center; text-align: center; gap: 1.2rem; margin-top: 1rem; }
.cbody .eyebrow { align-self: center; }
.cbody h3 { font-weight: 500; font-size: clamp(1.8rem, 3.6vw, 3rem); letter-spacing: -0.045em; max-width: 22ch; line-height: 1.08; }
.cbody .prose { max-width: 62ch; align-items: center; }
.cbody .prose p { margin-inline: auto; }
@media (prefers-reduced-motion: reduce) {
.hero-in > * { opacity: 1; transform: none; filter: none; animation: none; }
.awords .neon { color: var(--neon-on); text-shadow: var(--neon-glow); }
}
@media (max-width: 640px) { .cluster-fit .dash-cols { grid-template-columns: 1fr; } }
+379
View File
@@ -0,0 +1,379 @@
/* Homepage interactions ported from option 2: floating venture icons, giant
words marquee, scroll-linked type, fireworks journey, themed 3D venture
objects (three.js r128) and the scaled portfolio dashboard. */
(() => {
'use strict';
const $ = (s, c = document) => c.querySelector(s);
const $$ = (s, c = document) => Array.from(c.querySelectorAll(s));
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
const clamp = (v, a, b) => Math.min(b, Math.max(a, v));
const lerp = (a, b, t) => a + (b - a) * t;
const T = (s) => (window.__i18n && window.__i18n[s]) || s;
/* ---------- hero icons: one per venture ---------- */
const svg = (p) => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + p + '</svg>';
const ICONS = [
{ n: 'AI Software', side: 'l', x: '8%', y: '24%', r: '6deg', d: '0s', p: '<rect x="6" y="6" width="12" height="12" rx="2.5"/><path d="M9 3v3M15 3v3M9 18v3M15 18v3M3 9h3M3 15h3M18 9h3M18 15h3"/><path d="M9.6 15v-3.6L12 9.4l2.4 2V15M9.6 13.4h4.8"/>' },
{ n: 'Marketing & AI Search', side: 'l', x: '15%', y: '50%', r: '-7deg', d: '-2s', p: '<circle cx="10.5" cy="10.5" r="6.2"/><path d="m15.2 15.2 5 5"/><path d="m7.6 12.6 2.2-2.4 1.8 1.6 2.6-3"/>' },
{ n: 'Education Technology', side: 'r', x: '8%', y: '30%', r: '-6deg', d: '-1s', p: '<path d="m2.5 9.5 9.5-5 9.5 5-9.5 5z"/><path d="M6.5 12v4.2c0 1.2 2.5 2.8 5.5 2.8s5.5-1.6 5.5-2.8V12"/><path d="M21.5 9.5v6"/>' },
{ n: 'Cybersecurity', side: 'r', x: '12%', y: '62%', r: '7deg', d: '-3s', p: '<path d="M12 3 4.5 6v5.5c0 4.5 3.2 7.8 7.5 9.5 4.3-1.7 7.5-5 7.5-9.5V6z"/><rect x="9" y="10.8" width="6" height="5" rx="1"/><path d="M10.2 10.8V9.4a1.8 1.8 0 0 1 3.6 0v1.4"/>' },
{ n: 'E-Commerce', side: 'l', x: '6%', y: '73%', r: '8deg', d: '-5s', p: '<path d="M5 8h14l-1 12H6z"/><path d="M9 8V7a3 3 0 0 1 6 0v1"/>' },
];
const tileHTML = (i) => '<div class="tile">' + svg(i.p) + '</div><span class="tl">' + T(i.n).replace('&', '&amp;') + '</span>';
const fl = $('#floaters'), ir = $('#iconRow');
if (fl && ir) {
ICONS.forEach((i) => {
const t = document.createElement('div');
t.className = 't ' + i.side;
t.style.cssText = '--x:' + i.x + ';--y:' + i.y + ';--r:' + i.r + ';--d:' + i.d;
t.innerHTML = tileHTML(i);
fl.appendChild(t);
const t2 = document.createElement('div');
t2.className = 't';
t2.innerHTML = tileHTML(i);
ir.appendChild(t2);
});
}
/* emblem follows the cursor a little */
const emblemImg = $('#emblem img');
addEventListener('pointermove', (e) => {
if (!emblemImg || reduce || e.pointerType !== 'mouse') return;
const mx = (e.clientX / innerWidth) * 2 - 1, my = (e.clientY / innerHeight) * 2 - 1;
emblemImg.style.setProperty('--ry', (mx * 16).toFixed(1) + 'deg');
emblemImg.style.setProperty('--rx', (-my * 11).toFixed(1) + 'deg');
}, { passive: true });
/* neon words switch on when they scroll into view */
const awords = $('.awords');
if (awords) new IntersectionObserver((es, o) => es.forEach((e) => { if (e.isIntersecting) { awords.classList.add('is-in'); o.disconnect(); } }), { threshold: .3 }).observe(awords);
/* ---------- portfolio dashboard: scale the fixed-size cluster to its column ---------- */
const fit = $('.cluster-fit'), cl = fit && $('.cluster', fit);
const sizeCluster = () => {
if (!fit || !cl) return;
if (innerWidth <= 1100) { fit.style.height = ''; cl.style.removeProperty('--s'); return; }
const s = Math.min(1, fit.clientWidth / 760);
cl.style.setProperty('--s', s.toFixed(4));
fit.style.height = Math.round(740 * s) + 'px';
};
sizeCluster();
addEventListener('resize', sizeCluster);
/* ---------- scroll-linked playbook word ---------- */
const pbSec = $('#playbook'), pbWord = $('#pbword');
let ticking = false;
function onScroll() {
if (pbSec && pbWord) {
const rp = pbSec.getBoundingClientRect();
const kp = clamp((innerHeight - rp.top) / (innerHeight + rp.height), 0, 1);
pbWord.style.transform = 'translate3d(' + (reduce ? 0 : lerp(-6, -38, kp)).toFixed(1) + '%,-50%,0)';
}
}
addEventListener('scroll', () => { if (!ticking) { ticking = true; requestAnimationFrame(() => { ticking = false; onScroll(); }); } }, { passive: true });
addEventListener('resize', onScroll);
onScroll();
const book = $('#book');
if (book) book.addEventListener('click', () => book.classList.toggle('tap'));
/* ---------- giant words: moves on its own ---------- */
const wTrack = $('#wTrack'), wSpans = wTrack ? $$('span', wTrack) : [], wSec = $('#words');
let wx = 0, wVis = false;
if (wSec) new IntersectionObserver((es) => { wVis = es[0].isIntersecting; }).observe(wSec);
function marquee(dt) {
const half = wTrack.scrollWidth / 2;
if (!half) return;
wx -= (reduce ? 0 : 58) * dt;
if (wx <= -half) wx += half;
wTrack.style.transform = 'translate3d(' + wx.toFixed(1) + 'px,0,0)';
const vw = innerWidth;
wSpans.forEach((s) => {
const r = s.getBoundingClientRect();
const d = Math.abs(r.left + r.width / 2 - vw / 2) / (vw * .55);
s.style.opacity = (.25 + .75 * clamp(1 - d, 0, 1)).toFixed(3);
});
}
/* ---------- journey: fireworks, counters land in Dylan's stats row ---------- */
const jst = $('#jstage'), fw = $('#fw');
const FWOK = !!(jst && fw);
const fx = FWOK ? fw.getContext('2d') : null;
const pinEls = $$('.pn'), jx = $$('.jx'), jfinal = $('#jfinal'), jreplay = $('#jreplay');
const cEx = $('#cEx'), cInd = $('#cInd'), cYr = $('#cYr');
const PINX = [.12, .31, .5, .69, .88], PINF = [.22, .34, .46, .58, .72], LAUNCH = [200, 950, 1700, 2450, 3300];
const COLORS_DARK = ['#ffffff', '#9fe6ff', '#1fb7ff', '#3d8bff', '#d6ecff'];
const COLORS_LIGHT = ['#1a7fff', '#0b5fd0', '#1fb7ff', '#5b6cff', '#0870c4'];
const isLight = () => document.documentElement.getAttribute('data-theme') === 'light';
let FW = { w: 0, h: 0, floor: 0 }, PH = [0, 0, 0, 0, 0], rockets = [], parts = [], extra = [];
let fwState = 'idle', fwStart = 0, fwVis = false, cleared = true, fwLaunched = [false, false, false, false, false], fwPlayed = false;
const countUp = (el, to, suf) => {
if (reduce) { el.textContent = to + (suf || ''); return; }
const t0 = performance.now(), d = 1200;
(function tick(n) {
const k = clamp((n - t0) / d, 0, 1);
el.textContent = Math.round(to * (1 - Math.pow(1 - k, 3))) + (k >= 1 ? (suf || '') : '');
if (k < 1) requestAnimationFrame(tick);
})(t0);
};
const pop = (el) => { el.classList.remove('pop'); void el.offsetWidth; el.classList.add('pop'); };
function sizeFW() {
if (!FWOK) return;
const r = jst.getBoundingClientRect(), dpr = Math.min(devicePixelRatio || 1, 2);
fw.width = Math.round(r.width * dpr); fw.height = Math.round(r.height * dpr);
fx.setTransform(dpr, 0, 0, dpr, 0, 0);
FW.w = r.width; FW.h = r.height; FW.floor = FW.h * .83;
pinEls.forEach((p, i) => { PH[i] = Math.round(FW.h * PINF[i] * .88); p.style.left = (PINX[i] * 100) + '%'; p.style.setProperty('--ph', PH[i] + 'px'); });
}
function burst(x, y, n, spread) {
const COLORS = isLight() ? COLORS_LIGHT : COLORS_DARK;
const base = COLORS[(Math.random() * COLORS.length) | 0], sp = spread || 1;
for (let i = 0; i < n; i++) {
const a = Math.random() * Math.PI * 2, v = (50 + Math.random() * 190) * sp;
parts.push({ x, y, px: x, py: y, vx: Math.cos(a) * v, vy: Math.sin(a) * v, life: 0, max: .85 + Math.random() * .7, c: Math.random() < .65 ? base : COLORS[(Math.random() * COLORS.length) | 0], w: Math.random() < .2 ? 2.2 : 1.3 });
}
}
function launch(i) { pinEls[i].classList.add('rise'); rockets.push({ i, x: PINX[i] * FW.w, y: FW.floor, ty: FW.floor - PH[i], t: 0, dur: .55, trail: [] }); }
function arrive(i) {
const R = { x: PINX[i] * FW.w, y: FW.floor - PH[i] };
pinEls[i].classList.add('done');
if (i < 4) {
jx[i] && jx[i].classList.add('lit');
if (cEx) { cEx.textContent = i + 1; pop(cEx); }
burst(R.x, R.y, 72, 1);
} else {
burst(R.x, R.y, 120, 1.3);
extra = [{ t: .12, x: .3, y: .34 }, { t: .3, x: .68, y: .28 }, { t: .5, x: .5, y: .2 }, { t: .7, x: .15, y: .3 }, { t: .9, x: .85, y: .4 }];
jfinal.classList.add('show');
if (cInd) { countUp(cInd, 6); pop(cInd); }
if (cYr) { countUp(cYr, 8, '+'); pop(cYr); }
fwState = 'done'; fwPlayed = true; jreplay.classList.add('show');
}
}
function resetFW() {
fwState = 'idle'; rockets = []; parts = []; extra = [];
pinEls.forEach((p) => p.classList.remove('rise', 'done')); jx.forEach((x) => x.classList.remove('lit'));
[cEx, cInd, cYr].forEach((el) => el && (el.textContent = '0'));
jfinal.classList.remove('show'); jreplay.classList.remove('show');
fx.clearRect(0, 0, FW.w, FW.h);
}
function startFW() {
if (fwState !== 'idle') return;
sizeFW();
if (reduce) {
pinEls.forEach((p) => p.classList.add('rise', 'done')); jx.forEach((x) => x.classList.add('lit'));
cEx.textContent = '4'; cInd.textContent = '6'; cYr.textContent = '8+'; jfinal.classList.add('show');
fwState = 'done'; fwPlayed = true; return;
}
[cEx, cInd, cYr].forEach((el) => el && (el.textContent = '0'));
fwState = 'play'; fwStart = performance.now(); fwLaunched = [false, false, false, false, false];
}
if (FWOK) {
new IntersectionObserver((es) => {
const e = es[0]; fwVis = e.isIntersecting;
if (e.intersectionRatio >= .45 && fwState === 'idle' && !fwPlayed) startFW();
}, { threshold: [0, .45] }).observe(jst);
jreplay.addEventListener('click', () => { resetFW(); fwPlayed = false; startFW(); });
addEventListener('resize', sizeFW);
sizeFW();
}
function fwFrame(now, dt) {
if (fwState === 'idle' && !parts.length && !rockets.length) { if (!cleared) { fx.clearRect(0, 0, FW.w, FW.h); cleared = true; } return; }
cleared = false;
if (fwState === 'play') { const el = now - fwStart; LAUNCH.forEach((t, i) => { if (!fwLaunched[i] && el >= t) { fwLaunched[i] = true; launch(i); } }); }
if (extra.length) { extra.forEach((e) => { e.t -= dt; }); extra = extra.filter((e) => { if (e.t <= 0) { burst(e.x * FW.w, e.y * FW.h, 70, 1.1); return false; } return true; }); }
const light = isLight();
fx.clearRect(0, 0, FW.w, FW.h); fx.globalCompositeOperation = light ? 'source-over' : 'lighter'; fx.lineCap = 'round';
rockets = rockets.filter((r) => {
r.t += dt; const k = Math.min(1, r.t / r.dur), e = 1 - Math.pow(1 - k, 3); r.y = lerp(FW.floor, r.ty, e);
r.trail.push({ x: r.x + (Math.random() - .5) * 1.5, y: r.y }); if (r.trail.length > 14) r.trail.shift();
for (let j = 1; j < r.trail.length; j++) { const a = j / r.trail.length; fx.strokeStyle = (light ? 'rgba(26,127,255,' : 'rgba(159,230,255,') + (a * .8) + ')'; fx.lineWidth = 2.4 * a; fx.beginPath(); fx.moveTo(r.trail[j - 1].x, r.trail[j - 1].y); fx.lineTo(r.trail[j].x, r.trail[j].y); fx.stroke(); }
fx.fillStyle = light ? '#0b5fd0' : '#fff'; fx.beginPath(); fx.arc(r.x, r.y, 2.6, 0, 6.283); fx.fill();
if (k >= 1) { arrive(r.i); return false; } return true;
});
parts = parts.filter((p) => {
p.life += dt; if (p.life >= p.max) return false;
p.vx *= Math.exp(-1.3 * dt); p.vy = p.vy * Math.exp(-1.3 * dt) + 130 * dt;
p.px = p.x; p.py = p.y; p.x += p.vx * dt; p.y += p.vy * dt;
fx.globalAlpha = 1 - Math.pow(p.life / p.max, 2); fx.strokeStyle = p.c; fx.lineWidth = p.w;
fx.beginPath(); fx.moveTo(p.px, p.py); fx.lineTo(p.x, p.y); fx.stroke();
return true;
});
fx.globalAlpha = 1; fx.globalCompositeOperation = 'source-over';
}
/* ---------- main loop ---------- */
let last = performance.now();
(function loop(now) {
requestAnimationFrame(loop);
const dt = Math.min(.05, (now - last) / 1000); last = now;
if (wVis && wTrack) marquee(dt);
if (FWOK && (fwVis || fwState === 'play' || parts.length || rockets.length)) fwFrame(now, dt);
})(last);
/* ---------- ventures: tabs + a 3D object that matches each business ---------- */
const V = [
{ n: 'MARKETING & AI SEARCH', d: 'Leading teams through a constantly changing search landscape.', c: '#1fb7ff', m: 'magnifier' },
{ n: 'AI SOFTWARE', d: 'Building around new opportunities created by AI.', c: '#7a7bff', m: 'chip' },
{ n: 'EDUCATION TECHNOLOGY', d: 'Creating technology designed for real classrooms.', c: '#27d9ff', m: 'cap' },
{ n: 'CYBERSECURITY', d: 'Solving security challenges for growing businesses.', c: '#8a6bff', m: 'lock' },
{ n: 'E-COMMERCE', d: 'Turning product ideas into businesses built around real customers.', c: '#1a8bff', m: 'bag' },
];
const vtabs = $('#vtabs'), vpanel = $('#vpanel');
if (!vtabs || !vpanel) return;
const vt = $('#vt'), vname = $('#vname'), vdesc = $('#vdesc'), vnum = $('#vnum');
let vi = 0, vSwitch = () => {};
V.forEach((v, i) => {
const b = document.createElement('button');
b.type = 'button'; b.setAttribute('role', 'tab'); b.textContent = T(v.n);
b.addEventListener('click', () => setV(i));
vtabs.appendChild(b);
});
const vbtns = $$('button', vtabs);
function setV(i, first) {
vi = (i + V.length) % V.length;
vbtns.forEach((b, n) => b.setAttribute('aria-selected', String(n === vi)));
const apply = () => { vname.textContent = T(V[vi].n); vdesc.textContent = T(V[vi].d); vnum.textContent = '0' + (vi + 1) + ' / 0' + V.length; vpanel.style.setProperty('--ac', V[vi].c); vt.classList.remove('out'); };
if (first || reduce) apply(); else { vt.classList.add('out'); setTimeout(apply, 200); }
vSwitch(vi);
const sel = vbtns[vi];
if (!first && sel && vtabs.scrollWidth > vtabs.clientWidth) vtabs.scrollTo({ left: sel.offsetLeft - vtabs.clientWidth / 2 + sel.clientWidth / 2, behavior: 'smooth' });
}
$('#vprev').addEventListener('click', () => setV(vi - 1));
$('#vnext').addEventListener('click', () => setV(vi + 1));
vpanel.setAttribute('tabindex', '0');
// In Arabic (right to left) "next" is to the left.
const fwd = document.documentElement.dir === 'rtl' ? -1 : 1;
vpanel.addEventListener('keydown', (e) => { if (e.key === 'ArrowRight') setV(vi + fwd); if (e.key === 'ArrowLeft') setV(vi - fwd); });
let sx0 = null;
vpanel.addEventListener('touchstart', (e) => { sx0 = e.touches[0].clientX; }, { passive: true });
vpanel.addEventListener('touchend', (e) => { if (sx0 === null) return; const dx = e.changedTouches[0].clientX - sx0; if (Math.abs(dx) > 50) setV(vi + (dx < 0 ? fwd : -fwd)); sx0 = null; });
setV(0, true);
if (typeof THREE === 'undefined') return;
(function () {
const canvas = $('#vcanvas');
let r;
try { r = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true }); } catch (e) { return; }
r.setPixelRatio(Math.min(devicePixelRatio || 1, 2));
r.outputEncoding = THREE.sRGBEncoding; r.toneMapping = THREE.ACESFilmicToneMapping; r.toneMappingExposure = 1.2;
// Studio reflections painted on a canvas, as in option 2.
const ec = document.createElement('canvas'); ec.width = 1024; ec.height = 512;
const e = ec.getContext('2d');
const g0 = e.createLinearGradient(0, 0, 0, 512); g0.addColorStop(0, '#1b2f5c'); g0.addColorStop(.5, '#05070f'); g0.addColorStop(1, '#000');
e.fillStyle = g0; e.fillRect(0, 0, 1024, 512);
const box = (x, y, w, h, c, b) => { e.save(); e.shadowColor = c; e.shadowBlur = b; e.fillStyle = c; e.fillRect(x, y, w, h); e.restore(); };
box(120, 50, 300, 36, '#ffffff', 40); box(560, 40, 260, 30, '#d6e8ff', 40); box(250, 150, 26, 240, '#ffffff', 30); box(610, 130, 34, 260, '#2aa8ff', 34); box(860, 140, 30, 240, '#1a7fff', 34); box(40, 160, 26, 220, '#1fb7ff', 30); box(440, 200, 18, 200, '#bfe6ff', 26);
const tex = new THREE.CanvasTexture(ec); tex.mapping = THREE.EquirectangularReflectionMapping; tex.encoding = THREE.sRGBEncoding;
const env = new THREE.PMREMGenerator(r).fromEquirectangular(tex).texture;
const scene = new THREE.Scene(), cam = new THREE.PerspectiveCamera(32, 1, .1, 100);
cam.position.set(0, 0, 9);
const metal = new THREE.MeshStandardMaterial({ color: 0x0b2c6e, metalness: .9, roughness: .22, envMap: env, envMapIntensity: 1.5, emissive: 0x1a7fff, emissiveIntensity: .15 });
const chrome = new THREE.MeshStandardMaterial({ color: 0xdfe8f5, metalness: 1, roughness: .12, envMap: env, envMapIntensity: 1.6 });
const glass = new THREE.MeshPhysicalMaterial({ color: 0x9fdcff, metalness: .1, roughness: .05, transmission: .6, transparent: true, opacity: .55, envMap: env, envMapIntensity: 1.4 });
const glow = new THREE.MeshBasicMaterial({ color: 0x9fe6ff });
const holder = new THREE.Group();
scene.add(holder);
const key = new THREE.PointLight(0x1fb7ff, 60, 30); key.position.set(-4, 3, 6); scene.add(key);
scene.add(new THREE.AmbientLight(0x2a4a99, .8));
const k2 = new THREE.DirectionalLight(0xffffff, 1.1); k2.position.set(3, 4, 5); scene.add(k2);
const M = (geo, mat) => new THREE.Mesh(geo, mat);
const BUILD = {
// Marketing & AI Search: a magnifying glass over rising bars
magnifier() {
const g = new THREE.Group();
const ring = M(new THREE.TorusGeometry(1.05, .17, 24, 64), metal);
const lens = M(new THREE.CircleGeometry(1.0, 48), glass);
const handle = M(new THREE.CylinderGeometry(.16, .2, 1.6, 24), chrome);
handle.position.set(1.15, -1.15, 0); handle.rotation.z = Math.PI / 4;
const lensGroup = new THREE.Group(); lensGroup.add(ring, lens, handle); lensGroup.position.set(.25, .35, .35);
[[-.75, .45], [-.25, .8], [.25, 1.15]].forEach(([x, h], i) => {
const bar = M(new THREE.BoxGeometry(.32, h, .32), i === 2 ? glow : metal);
bar.position.set(x - .1, -1.05 + h / 2, -.6); g.add(bar);
});
g.add(lensGroup);
return g;
},
// AI Software: a processor chip with pins and a glowing core
chip() {
const g = new THREE.Group();
g.add(M(new THREE.BoxGeometry(2.2, 2.2, .32), metal));
const core = M(new THREE.BoxGeometry(1.1, 1.1, .1), glow); core.position.z = .2; g.add(core);
const pinGeo = new THREE.BoxGeometry(.14, .42, .1);
for (let s = 0; s < 4; s++) for (let i = 0; i < 5; i++) {
const p = M(pinGeo, chrome); const off = -0.8 + i * .4;
if (s === 0) p.position.set(off, 1.3, 0);
if (s === 1) p.position.set(off, -1.3, 0);
if (s === 2) { p.position.set(1.3, off, 0); p.rotation.z = Math.PI / 2; }
if (s === 3) { p.position.set(-1.3, off, 0); p.rotation.z = Math.PI / 2; }
g.add(p);
}
g.rotation.x = -.35;
return g;
},
// Education Technology: a graduation cap
cap() {
const g = new THREE.Group();
const board = M(new THREE.BoxGeometry(2.6, .12, 2.6), metal); board.rotation.y = Math.PI / 4; board.position.y = .45; g.add(board);
g.add(M(new THREE.CylinderGeometry(.95, 1.05, .9, 40, 1, true), chrome));
const bottom = M(new THREE.CircleGeometry(.95, 40), metal); bottom.rotation.x = Math.PI / 2; bottom.position.y = -.45; g.add(bottom);
const button = M(new THREE.SphereGeometry(.12, 16, 16), glow); button.position.y = .55; g.add(button);
const cord = M(new THREE.CylinderGeometry(.03, .03, 1.3, 8), glow); cord.position.set(.85, -.1, .85); g.add(cord);
const tassel = M(new THREE.ConeGeometry(.12, .4, 16), glow); tassel.position.set(.85, -.85, .85); g.add(tassel);
g.rotation.x = .35;
return g;
},
// Cybersecurity: a padlock
lock() {
const g = new THREE.Group();
const body = M(new THREE.BoxGeometry(2, 1.6, .7), metal); body.position.y = -.5; g.add(body);
const shackle = M(new THREE.TorusGeometry(.62, .16, 20, 48, Math.PI), chrome); shackle.position.y = .3; g.add(shackle);
[-.62, .62].forEach((x) => { const leg = M(new THREE.CylinderGeometry(.16, .16, .5, 20), chrome); leg.position.set(x, .05, 0); g.add(leg); });
const hole = M(new THREE.CylinderGeometry(.17, .17, .1, 24), glow); hole.rotation.x = Math.PI / 2; hole.position.set(0, -.35, .36); g.add(hole);
const slot = M(new THREE.BoxGeometry(.12, .4, .1), glow); slot.position.set(0, -.62, .36); g.add(slot);
return g;
},
// E-commerce: a shopping bag with handles
bag() {
const g = new THREE.Group();
g.add(M(new THREE.BoxGeometry(2, 2.2, .9), metal));
const band = M(new THREE.BoxGeometry(2.02, .28, .92), glow); band.position.y = .6; g.add(band);
[-.42, .42].forEach((z) => { const h = M(new THREE.TorusGeometry(.5, .09, 16, 40, Math.PI), chrome); h.position.set(0, 1.1, z * .9); g.add(h); });
const tag = M(new THREE.BoxGeometry(.5, .32, .05), chrome); tag.position.set(.55, -.45, .48); tag.rotation.z = -.25; g.add(tag);
g.rotation.x = .1; g.scale.setScalar(.9);
return g;
},
};
let model = null, born = 0;
const dispose = (o) => o.traverse((c) => { if (c.geometry) c.geometry.dispose(); });
vSwitch = (i) => {
if (model) { holder.remove(model); dispose(model); }
model = BUILD[V[i].m]();
holder.add(model);
born = performance.now();
key.color.set(V[i].c); metal.emissive.set(V[i].c); glow.color.set(V[i].c).lerp(new THREE.Color(0xffffff), .45);
};
vSwitch(vi);
let vis = true;
new IntersectionObserver((es) => { vis = es[0].isIntersecting; }).observe(canvas);
const resize = () => { const w = canvas.clientWidth || 1, h = canvas.clientHeight || 1; r.setSize(w, h, false); cam.aspect = w / h; cam.updateProjectionMatrix(); };
addEventListener('resize', resize); resize();
let mx = 0, my = 0;
vpanel.addEventListener('pointermove', (ev) => { const b = vpanel.getBoundingClientRect(); mx = ((ev.clientX - b.left) / b.width - .5) * 2; my = ((ev.clientY - b.top) / b.height - .5) * 2; });
let lt = performance.now();
(function loop(now) {
requestAnimationFrame(loop);
if (!vis) return;
const dt = Math.min(.05, (now - lt) / 1000); lt = now;
const k = reduce ? 1 : clamp((now - born) / 650, 0, 1), sc = reduce ? 1 : (.3 + .7 * (1 - Math.pow(1 - k, 3)));
holder.scale.setScalar(sc);
if (!reduce) { holder.rotation.y += dt * (.45 + (1 - k) * 6); }
holder.rotation.x += ((my * .35) - holder.rotation.x) * .06;
holder.position.x += ((mx * .5) - holder.position.x) * .06;
r.render(scene, cam);
})(lt);
})();
})();
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

+6
View File
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

+77
View File
@@ -0,0 +1,77 @@
/* Arabic: right-to-left layout and type. Loaded only on /ar/ pages, after styles.css. */
:root {
--font-sans: "Noto Sans Arabic", "Inter Tight", "Inter", ui-sans-serif, system-ui, "Geeza Pro", Tahoma, sans-serif;
}
/* Arabic letters join; tracking (positive or negative) breaks them apart. */
body *:not(.brand-word):not(.brand-word *):not(.foot-word) { letter-spacing: 0 !important; }
.h-display-lg, .h-display, .h-lg, .h-md, .rule h3, .book h3, .shout q {
font-family: "Noto Kufi Arabic", var(--font-sans); line-height: 1.35;
}
.statement, .poem, .quote-line { line-height: 1.6; }
.eyebrow, .btn, .text-link, .tag-upper { font-size: 13.5px; }
/* The logo, code samples, URLs and the giant footer word stay left to right. */
.brand, .foot-word, .file, .qlist p, .panel-url, .lang-switch, [dir="ltr"] { direction: ltr; }
/* Arrows */
.flip { transform: scaleX(-1); }
.btn:hover svg.flip, .text-link:hover svg.flip { transform: translateX(-3px) scaleX(-1); }
.btn:hover .go { transform: translateX(-3px); }
.tab-btn .arr { transform: translateX(4px) scaleX(-1); }
.tab-btn[aria-selected="true"] .arr { transform: scaleX(-1); }
#vprev svg, #vnext svg { transform: scaleX(-1); }
/* Physical offsets mirrored */
.skip { left: auto; right: 16px; }
.mobile-menu .sub a { padding-left: 2px; padding-right: 18px; }
.panel-url { margin-left: 0; margin-right: auto; }
.cursor { margin-left: 0; margin-right: 2px; }
.cluster { text-align: right; }
.dash { left: auto; right: 228px; }
.profile { left: auto; right: 0; }
.code { right: auto; left: 0; }
.feature::before { left: auto; right: 0; }
.stat { padding: 28px 0 0 24px; }
.stat b sup { margin-left: 0; margin-right: 2px; }
.quote-line { padding-left: 0; border-left: 0; padding-right: 20px; border-right: 2px solid var(--accent); }
.flow { left: auto; right: -32px; }
.process-step:not(:last-child)::after { right: auto; left: -16px; }
.tab-btn { text-align: right; }
.principle { padding: 32px 0 8px 32px; }
.industries { border-left: 0; border-right: 1px solid var(--mist); }
.industry { border-right: 0; border-left: 1px solid var(--mist); }
.progress { transform-origin: 100% 50%; }
@media (min-width: 641px) {
.stat + .stat { padding-left: 0; border-left: 0; padding-right: 24px; border-right: 1px solid var(--mist); }
}
@media (max-width: 1100px) {
.stat-note { border-right: 0 !important; }
}
@media (min-width: 901px) {
.principle + .principle { padding-left: 0; border-left: 0; padding-right: 32px; border-right: 1px solid var(--mist); }
}
@media (max-width: 900px) {
.flow { right: 16px; }
.process-step:not(:last-child)::after { left: auto; right: 58px; }
}
@media (max-width: 1100px) {
.cluster .panel { right: auto; }
}
/* Homepage */
.cluster-fit .dash { left: auto; right: 176px; }
.cluster-fit .profile { left: auto; right: 0; }
.cluster-fit .code { right: auto; left: 0; }
@media (max-width: 1100px) {
.cluster-fit .panel { right: auto; left: auto; }
}
.w-side { right: auto; left: max(var(--gutter), calc((100vw - var(--page-max)) / 2 + var(--gutter))); }
.w-top { left: auto; right: max(var(--gutter), calc((100vw - var(--page-max)) / 2 + var(--gutter))); }
@media (max-width: 700px) { .w-side { left: var(--gutter); right: var(--gutter); } }
.varrows { right: auto; left: 1.2rem; }
.jfinal { left: auto; right: 1.2rem; }
.jreplay { right: auto; left: 1rem; }
.cc::before { right: auto; left: -40px; }
/* Scroll-driven word tracks move left to right in every language. */
.wmask, .pbword { direction: ltr; }
+173
View File
@@ -0,0 +1,173 @@
(function () {
window.__site = true;
var root = document.documentElement;
// The booking page uses this to keep visitors in the language they filled the form in.
if (!/\/contact\/book\//.test(location.pathname)) { try { sessionStorage.setItem('dm-lang', root.lang || 'en'); } catch (e) {} }
// Translations for the current page's language, written into the page by the build.
var T = function (s) { return (window.__i18n && window.__i18n[s]) || s; };
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// Theme toggle: remembers the visitor's choice; otherwise follows the system.
var themeBtn = document.getElementById('theme-btn');
var current = function () { return root.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'; };
var paint = function () {
if (!themeBtn) return;
var dark = current() === 'dark';
themeBtn.setAttribute('aria-label', T(dark ? 'Switch to light mode' : 'Switch to dark mode'));
themeBtn.setAttribute('aria-pressed', String(dark));
};
var apply = function (t, save) {
root.setAttribute('data-theme', t);
if (save) { try { localStorage.setItem('theme', t); } catch (e) {} }
paint();
};
paint();
if (themeBtn) themeBtn.addEventListener('click', function () { apply(current() === 'dark' ? 'light' : 'dark', true); });
// Nav border on scroll
var nav = document.getElementById('nav');
var progress = document.getElementById('progress');
var onScroll = function () {
nav && nav.classList.toggle('is-scrolled', window.scrollY > 8);
if (progress) {
var max = Math.max(1, document.documentElement.scrollHeight - window.innerHeight);
progress.style.transform = 'scaleX(' + Math.min(1, Math.max(0, window.scrollY / max)) + ')';
}
};
onScroll();
window.addEventListener('scroll', onScroll, { passive: true });
// Mobile menu
var menuBtn = document.getElementById('menu-btn');
var menu = document.getElementById('mobile-menu');
if (menuBtn && menu) {
menuBtn.addEventListener('click', function () {
var open = menu.hidden;
menu.hidden = !open;
menuBtn.setAttribute('aria-expanded', String(open));
menuBtn.setAttribute('aria-label', T(open ? 'Close menu' : 'Open menu'));
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && !menu.hidden) { menu.hidden = true; menuBtn.setAttribute('aria-expanded', 'false'); menuBtn.focus(); }
});
}
// Reveal on scroll
var reveals = document.querySelectorAll('.reveal');
if ('IntersectionObserver' in window && !reduce) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) { en.target.classList.add('is-in'); io.unobserve(en.target); }
});
}, { rootMargin: '0px 0px -8% 0px', threshold: 0.05 });
reveals.forEach(function (el) { io.observe(el); });
} else {
reveals.forEach(function (el) { el.classList.add('is-in'); });
}
// Count-up numbers: <b data-count="8" data-suffix="+">8+</b>
var counters = document.querySelectorAll('[data-count]');
var run = function (el) {
var end = parseInt(el.getAttribute('data-count'), 10);
var suffix = el.getAttribute('data-suffix') || '';
var start = null;
var dur = 1200;
var step = function (t) {
if (!start) start = t;
var p = Math.min((t - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(end * eased) + suffix;
if (p < 1) requestAnimationFrame(step);
};
requestAnimationFrame(step);
};
if ('IntersectionObserver' in window && !reduce) {
var co = new IntersectionObserver(function (entries) {
entries.forEach(function (en) { if (en.isIntersecting) { run(en.target); co.unobserve(en.target); } });
}, { threshold: 0.6 });
counters.forEach(function (el) { co.observe(el); });
}
// Tabs (service lines)
document.querySelectorAll('[data-tabs]').forEach(function (wrap) {
var btns = wrap.querySelectorAll('[role="tab"]');
var panels = wrap.querySelectorAll('[role="tabpanel"]');
var select = function (i, focus) {
btns.forEach(function (b, j) {
var on = i === j;
b.setAttribute('aria-selected', String(on));
b.tabIndex = on ? 0 : -1;
panels[j].hidden = !on;
});
if (focus) btns[i].focus();
};
btns.forEach(function (b, i) {
b.addEventListener('click', function () { select(i); });
b.addEventListener('keydown', function (e) {
var k = e.key;
if (k === 'ArrowDown' || k === 'ArrowRight') { e.preventDefault(); select((i + 1) % btns.length, true); }
if (k === 'ArrowUp' || k === 'ArrowLeft') { e.preventDefault(); select((i - 1 + btns.length) % btns.length, true); }
});
});
var hash = location.hash.replace('#', '');
var start = 0;
panels.forEach(function (p, j) { if (p.id === hash) start = j; });
select(start);
});
// Copy buttons
document.querySelectorAll('[data-copy]').forEach(function (b) {
b.addEventListener('click', function () {
var text = b.getAttribute('data-copy');
var label = b.textContent;
var done = function () { b.textContent = T('Copied'); setTimeout(function () { b.textContent = label; }, 1600); };
try { navigator.clipboard.writeText(text).then(done, function () {}); } catch (e) {}
});
});
// Inquiry form: opens the visitor's email client with the message prefilled.
var form = document.getElementById('inquiry');
if (form) {
form.addEventListener('submit', function (e) {
e.preventDefault();
if (!form.reportValidity()) return;
var d = new FormData(form);
var body = [
'Name: ' + d.get('name'),
'Email: ' + d.get('email'),
d.get('company') ? 'Company: ' + d.get('company') : '',
d.get('topic') ? 'Topic: ' + d.get('topic') : '',
'',
d.get('message')
].filter(function (l, i) { return l !== '' || i === 4; }).join('\n');
var subject = 'Inquiry from ' + d.get('name') + (d.get('topic') ? ' — ' + d.get('topic') : '');
window.location.href = 'mailto:' + form.getAttribute('data-to') + '?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body);
var status = document.getElementById('form-status');
if (status) status.textContent = T('Your email app should open with the message ready to send.');
});
}
// GoHighLevel's form_embed.js hides each embed until the iframe reports it is
// ready. If that message never arrives, show the embed anyway so the form is
// never left invisible.
var reveal = function (f) {
f.style.opacity = '1';
f.style.visibility = 'visible';
f.style.pointerEvents = 'auto';
f.style.position = 'relative';
if (f.style.left === '-9999px') f.style.left = '';
};
var ghl = document.querySelectorAll('.ghl-frame');
if (ghl.length) {
[2500, 6000].forEach(function (ms) {
setTimeout(function () {
ghl.forEach(function (f) {
if (f.closest('[data-off]')) return;
if (getComputedStyle(f).visibility === 'hidden') reveal(f);
});
}, ms);
});
}
root.classList.add('ready');
})();
+1089
View File
File diff suppressed because it is too large Load Diff