Merge option 2 homepage and visual language into the site

Rebuild the homepage on option 2's sections and effects (3D DNA, spinning
cube, giant words, fireworks journey, neon type, chrome ribbon) with copy
kept verbatim from the content document. Each venture now has its own 3D
object, the portfolio dashboard sits beside the DNA, the journey uses the
original stats row and the playbook keeps our book. Inner pages adopt the
same navy, glass and gradient skin, with dark as the default theme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Alejandro Veras
2026-10-01 12:47:20 -04:00
parent e6279c2e91
commit d0357f1c52
27 changed files with 5120 additions and 219 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ await cp(join(root, 'static'), out, { recursive: true });
// Fingerprint CSS/JS so a new deploy never reuses a stale cached copy. // Fingerprint CSS/JS so a new deploy never reuses a stale cached copy.
const hashOf = async (f) => createHash('sha256').update(await readFile(join(root, 'static', f))).digest('hex').slice(0, 10); const hashOf = async (f) => createHash('sha256').update(await readFile(join(root, 'static', f))).digest('hex').slice(0, 10);
const assetVersions = { '/assets/styles.css': await hashOf('assets/styles.css'), '/assets/site.js': await hashOf('assets/site.js') }; const assetVersions = Object.fromEntries(await Promise.all(['styles.css', 'site.js', 'home.css', 'home.js', 'fonts.css'].map(async (f) => ['/assets/' + f, await hashOf('assets/' + f)])));
const fingerprint = (html) => Object.entries(assetVersions).reduce((h, [u, v]) => h.replaceAll(`"${u}"`, `"${u}?v=${v}"`), html); const fingerprint = (html) => Object.entries(assetVersions).reduce((h, [u, v]) => h.replaceAll(`"${u}"`, `"${u}?v=${v}"`), html);
const urls = []; const urls = [];
+14 -9
View File
@@ -61,13 +61,13 @@ export function icon(name, cls = '') {
// Dylan Mazzei monogram: italic "D" (inherits text color) and the sky-blue blade. // Dylan Mazzei monogram: italic "D" (inherits text color) and the sky-blue blade.
export const mark = (cls = 'logo-mark') => `<svg class="${cls}" viewBox="0 0 104 60" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M9 4h55c10 0 13 7 9 14L58 46c-3 6-8 8-15 8.5L3 57zM21.5 15.5 18.3 45.8 40 44.5c4-.3 6-1.5 7.5-4.3l12-21.4c1.2-2.2.3-3.3-2-3.3z"/><path fill="#33b9ff" d="M24 59 102 1 91 52 72 54.5 79 33z"/><path fill="#8fdcff" d="M24 59 102 1 79 33z"/></svg>`; export const mark = (cls = 'logo-mark') => `<svg class="${cls}" viewBox="0 0 104 60" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M9 4h55c10 0 13 7 9 14L58 46c-3 6-8 8-15 8.5L3 57zM21.5 15.5 18.3 45.8 40 44.5c4-.3 6-1.5 7.5-4.3l12-21.4c1.2-2.2.3-3.3-2-3.3z"/><path fill="#33b9ff" d="M24 59 102 1 91 52 72 54.5 79 33z"/><path fill="#8fdcff" d="M24 59 102 1 79 33z"/></svg>`;
export const logo = () => `${mark()}<span class="brand-word">Dylan <b>Mazzei</b></span>`; export const logo = () => `<img class="logo-img" src="/assets/o2/logo.png" alt="" width="640" height="121">${mark()}<span class="brand-word">Dylan <b>Mazzei</b></span>`;
export const eyebrow = (t) => `<span class="eyebrow">${t}</span>`; export const eyebrow = (t) => `<span class="eyebrow">${t}</span>`;
export function btn(text, href, variant = 'primary', extra = '') { export function btn(text, href, variant = 'primary', extra = '') {
const ext = /^https?:/.test(href) ? ' target="_blank" rel="noopener"' : ''; const ext = /^https?:/.test(href) ? ' target="_blank" rel="noopener"' : '';
return `<a class="btn btn-${variant}${extra ? ' ' + extra : ''}" href="${href}"${ext}>${text} ${icon('arrow')}</a>`; return `<a class="btn btn-${variant}${extra ? ' ' + extra : ''}" href="${href}"${ext}>${text}<span class="go">${icon('arrow')}</span></a>`;
} }
export function textLink(text, href) { export function textLink(text, href) {
@@ -126,13 +126,13 @@ function nav(path) {
<nav aria-label="Primary"><ul class="nav-links">${desktop}</ul></nav> <nav aria-label="Primary"><ul class="nav-links">${desktop}</ul></nav>
<div class="nav-cta"> <div class="nav-cta">
<button class="theme-btn" id="theme-btn" type="button" aria-label="Switch to dark mode">${icon('moon', 'moon')}${icon('sun', 'sun')}</button> <button class="theme-btn" id="theme-btn" type="button" aria-label="Switch to dark mode">${icon('moon', 'moon')}${icon('sun', 'sun')}</button>
<a class="btn btn-primary btn-sm" href="/contact/">START A CONVERSATION</a> <a class="btn btn-primary btn-sm" href="/contact/">START A CONVERSATION<span class="go">${icon('arrow')}</span></a>
<button class="menu-btn" id="menu-btn" type="button" aria-expanded="false" aria-controls="mobile-menu" aria-label="Open menu">${icon('menu')}</button> <button class="menu-btn" id="menu-btn" type="button" aria-expanded="false" aria-controls="mobile-menu" aria-label="Open menu">${icon('menu')}</button>
</div> </div>
</div> </div>
<div class="wrap mobile-menu" id="mobile-menu" hidden> <div class="wrap mobile-menu" id="mobile-menu" hidden>
<ul>${mobile}</ul> <ul>${mobile}</ul>
<a class="btn btn-primary" href="/contact/">START A CONVERSATION ${icon('arrow')}</a> <a class="btn btn-primary" href="/contact/">START A CONVERSATION<span class="go">${icon('arrow')}</span></a>
</div> </div>
</header>`; </header>`;
} }
@@ -184,7 +184,7 @@ function footer() {
</footer>`; </footer>`;
} }
export function page({ path, title, description, body, schema = [] }) { export function page({ path, title, description, body, schema = [], head = '', scripts = '' }) {
const canonical = SITE.url + path; const canonical = SITE.url + path;
const person = { const person = {
'@context': 'https://schema.org', '@context': 'https://schema.org',
@@ -206,8 +206,7 @@ export function page({ path, title, description, body, schema = [] }) {
<title>${title}</title> <title>${title}</title>
<meta name="description" content="${description}"> <meta name="description" content="${description}">
<link rel="canonical" href="${canonical}"> <link rel="canonical" href="${canonical}">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)"> <meta name="theme-color" content="#04060c">
<meta name="theme-color" content="#0b0b0d" media="(prefers-color-scheme: dark)">
<meta name="color-scheme" content="light dark"> <meta name="color-scheme" content="light dark">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<meta property="og:site_name" content="Dylan Mazzei"> <meta property="og:site_name" content="Dylan Mazzei">
@@ -222,18 +221,24 @@ export function page({ path, title, description, body, schema = [] }) {
<link rel="icon" href="/favicon.svg" type="image/svg+xml"> <link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@500;700&family=JetBrains+Mono:wght@500&family=Unbounded:wght@700&display=swap"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@500&family=Unbounded:wght@700&display=swap">
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/inter-tight-latin-500-normal.woff2">
<link rel="stylesheet" href="/assets/fonts.css">
<link rel="stylesheet" href="/assets/styles.css"> <link rel="stylesheet" href="/assets/styles.css">
<script>(function(){var r=document.documentElement;try{var t=localStorage.getItem('theme');if(t==='dark'||t==='light')r.setAttribute('data-theme',t)}catch(e){}if(!r.hasAttribute('data-theme')&&window.matchMedia&&matchMedia('(prefers-color-scheme: dark)').matches)r.setAttribute('data-theme','dark')})();document.documentElement.classList.add('js');setTimeout(function(){if(!window.__site)document.documentElement.classList.remove('js')},2500)</script> ${head}
<script>(function(){var r=document.documentElement;try{var t=localStorage.getItem('theme');if(t==='dark'||t==='light')r.setAttribute('data-theme',t)}catch(e){}if(!r.hasAttribute('data-theme'))r.setAttribute('data-theme','dark')})();document.documentElement.classList.add('js');setTimeout(function(){if(!window.__site)document.documentElement.classList.remove('js')},2500)</script>
${ld} ${ld}
</head> </head>
<body> <body>
<div class="bgfx" aria-hidden="true"></div>
<div class="progress" id="progress" aria-hidden="true"></div>
${nav(path)} ${nav(path)}
<main id="main"> <main id="main">
${body} ${body}
</main> </main>
${footer()} ${footer()}
<script src="/assets/site.js" defer></script> <script src="/assets/site.js" defer></script>
${scripts}
</body> </body>
</html> </html>
`; `;
+1 -1
View File
@@ -50,7 +50,7 @@ export default {
</div> </div>
<div class="field field-full"><label for="f-message">Message</label><textarea id="f-message" name="message" required></textarea></div> <div class="field field-full"><label for="f-message">Message</label><textarea id="f-message" name="message" required></textarea></div>
</div> </div>
<button class="btn btn-primary btn-block" type="submit">SEND AN INQUIRY ${icon('arrow')}</button> <button class="btn btn-primary btn-block" type="submit">SEND AN INQUIRY<span class="go">${icon('arrow')}</span></button>
<p class="form-status" id="form-status" role="status" aria-live="polite"></p> <p class="form-status" id="form-status" role="status" aria-live="polite"></p>
</form> </form>
+180 -126
View File
@@ -1,42 +1,88 @@
import { icon, eyebrow, btn, textLink, mark } from '../layout.mjs'; import { icon, eyebrow, btn, textLink, mark } from '../layout.mjs';
const ventures = [ const ventures = [
['search', 'MARKETING &amp; AI SEARCH', 'Leading teams through a constantly changing search landscape.'], ['search', 'MARKETING &amp; AI SEARCH'],
['chip', 'AI SOFTWARE', 'Building around new opportunities created by AI.'], ['chip', 'AI SOFTWARE'],
['bottle', 'SPIRITS', 'Taking a consumer brand from idea to market.'], ['bottle', 'SPIRITS'],
['edu', 'EDUCATION TECHNOLOGY', 'Creating technology designed for real classrooms.'], ['edu', 'EDUCATION TECHNOLOGY'],
['shield', 'CYBERSECURITY', 'Solving security challenges for growing businesses.'], ['shield', 'CYBERSECURITY'],
['bag', 'E-COMMERCE', 'Turning product ideas into businesses built around real customers.'], ['bag', 'E-COMMERCE'],
]; ];
const exits = [ const exits = [
['umbrella', 'Insurance', 'I grew an insurance business around strong client relationships, a clear understanding of the market, and service people could trust.'], ['Insurance', 'I grew an insurance business around strong client relationships, a clear understanding of the market, and service people could trust.', '<path d="M24 5 8 11v11c0 10 7 17 16 21 9-4 16-11 16-21V11z"/><path d="m17 24 5 5 9-10"/>'],
['megaphone', 'Marketing', 'Led an agency focused on helping businesses reach the right audiences, strengthen their presence, and turn attention into growth.'], ['Marketing', 'Led an agency focused on helping businesses reach the right audiences, strengthen their presence, and turn attention into growth.', '<path d="M8 20v8h6l14 8V12l-14 8z"/><path d="M34 18c3 2 3 10 0 12M14 28l2 9h5l-2-9"/>'],
['heart', 'Home Healthcare', 'Grew a people-first business in an industry where trust, quality of care, and responsible leadership mattered every day.'], ['Home Healthcare', 'Grew a people-first business in an industry where trust, quality of care, and responsible leadership mattered every day.', '<path d="M6 22 24 7l18 15M10 20v21h28V20"/><path d="M24 35c-6-4-8-7-8-10a4.2 4.2 0 0 1 8-1.5A4.2 4.2 0 0 1 32 25c0 3-2 6-8 10z"/>'],
['shirt', 'Apparel', 'Took a consumer concept into the market, shaping the product, brand, and customer experience from the ground up.'], ['Apparel', 'Took a consumer concept into the market, shaping the product, brand, and customer experience from the ground up.', '<path d="M17 6 6 12l4 9 5-2v23h18V19l5 2 4-9-11-6c-1 3-4 5-7 5s-6-2-7-5z"/>'],
];
const consult = [
['<circle cx="11" cy="11" r="6.5"/><path d="m16 16 4.5 4.5"/>', 'We look closely at the business', ''],
['<path d="M4 5h16v11H9l-5 4z"/>', 'Talk through what is getting in the way', ''],
['<circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="3.2"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3"/>', 'Figure out where your attention can make the biggest difference', ''],
['<path d="M12 3 4 6v6c0 4.5 3.4 7.6 8 9 4.6-1.4 8-4.5 8-9V6z"/><path d="m8.5 12 2.5 2.5 4.5-5"/>', 'Every engagement starts with a diagnostic.', 'Not because I already know the answer, but because your business deserves one that actually fits.'],
]; ];
const ventureRows = ventures const ventureRows = ventures
.map(([ic, name]) => `<div class="row"><span class="ico">${icon(ic)}</span><span class="name">${name.replace('&amp;', '&amp;')}</span><span class="status"><span class="dot"></span>Active</span></div>`) .map(([ic, name]) => `<div class="row"><span class="ico">${icon(ic)}</span><span class="name">${name}</span><span class="status"><span class="dot"></span>Active</span></div>`)
.join(''); .join('');
const exitRows = exits const exitRows = exits
.map(([, name]) => `<div class="row">${icon('check', 'check')}<span class="name">${name}</span><span class="status">Exited</span></div>`) .map(([name]) => `<div class="row">${icon('check', 'check')}<span class="name">${name}</span><span class="status">Exited</span></div>`)
.join(''); .join('');
const exitWords = ['Insurance.', 'Marketing.', 'Home&nbsp;Healthcare.', 'Apparel.'];
const arrowUR = '<svg viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 10 10 4M5 4h5v5"/></svg>';
export default { export default {
path: '/', path: '/',
title: 'Dylan Mazzei | Founder, Operator &amp; Entrepreneur', title: 'Dylan Mazzei | Founder, Operator &amp; Entrepreneur',
description: 'Meet Dylan Mazzei, a founder and operator leading ventures across AI, software, spirits, education, cybersecurity, e-commerce, and more.', description: 'Meet Dylan Mazzei, a founder and operator leading ventures across AI, software, spirits, education, cybersecurity, e-commerce, and more.',
head: '<link rel="stylesheet" href="/assets/home.css">',
scripts: '<script src="/assets/o2/three.min.js" defer></script>\n<script src="/assets/o2/dna-hero.min.js" defer></script>\n<script src="/assets/home.js" defer></script>',
body: ` body: `
<section class="hero" aria-labelledby="hero-title"> <div class="o2">
<div class="wrap">
<div class="hero-copy anim">
<span class="tag tag-upper tag-white" style="border:1px solid var(--mist)"><span class="dot"></span>DYLAN MAZZEI | FOUNDER &amp; OPERATOR</span>
<h1 id="hero-title" class="h-display-lg">One Good Idea Has Never Been Enough for Me.</h1>
<p class="lead">I’ve spent the last eight years turning ideas into companies across marketing, AI, software, spirits, education, cybersecurity, e-commerce, and more. Four have been sold. Others are growing. And I’m nowhere near finished building.</p>
<div class="btn-row">${btn('MY STORY', '/about/')}</div>
</div>
<section id="hero" aria-labelledby="hero-title">
<div class="beam" aria-hidden="true"></div>
<div class="floaters" id="floaters" aria-hidden="true"></div>
<div class="hero-in">
<div class="emblem" id="emblem"><img alt="Dylan Mazzei monogram" src="/assets/o2/dylan-monogram.webp" width="720" height="480"></div>
${eyebrow('DYLAN MAZZEI | FOUNDER &amp; OPERATOR')}
<h1 id="hero-title"><span class="grad">One Good Idea Has Never</span> <span class="grad2">Been Enough for Me.</span></h1>
<p class="lead">I’ve spent the last eight years turning ideas into companies across marketing, AI, software, spirits, education, cybersecurity, e-commerce, and more. Four have been sold. Others are growing. And I’m nowhere near finished building.</p>
<div class="btn-row">${btn('MY STORY', '/about/')}${btn('EXPLORE MY VENTURES', '#ventures', 'ghost')}</div>
<div class="icon-row" id="iconRow" aria-hidden="true"></div>
</div>
<div class="scroll-note" aria-hidden="true"><i></i></div>
</section>
<section class="wsec" id="words" aria-label="Four exits">
<div class="wsec-in">
<div class="cube-glow" aria-hidden="true"></div>
<div class="cube" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i></div>
<div class="w-top">${eyebrow('4 EXITS')}</div>
<div class="wmask" aria-hidden="true"><div id="wTrack">${[...exitWords, ...exitWords].map((w) => `<span>${w}</span>`).join('')}</div></div>
<p class="sr-only">Insurance. Marketing. Home Healthcare. Apparel.</p>
<div class="w-side"><p>Four have been sold. Others are growing. And I’m nowhere near finished building.</p>${btn('FOLLOW THE JOURNEY', '#journey', 'ghost', 'btn-sm')}</div>
</div>
</section>
<section id="building" aria-labelledby="building-title">
<div class="wrap">
<div class="bhead reveal">
<div class="stack-sm">${eyebrow('CURRENT VENTURES')}<h2 id="building-title" class="h-display">The Companies I’m Leading Today</h2></div>
<div class="stack-sm">
<p class="lead">I like being close to the decisions that shape a company, especially when there is something new to figure out.</p>
<div>${btn('EXPLORE MY VENTURES', '/portfolio/')}</div>
</div>
</div>
<div class="bgrid">
<div class="bstack" id="bstack">
${'<span class="r" aria-hidden="true">Still building</span>'.repeat(6)}
<div class="bglow" aria-hidden="true"></div>
<dna-hero id="dna" class="dna3d" transparent interactive duration="20" aria-label="3D chrome DNA helix. Drag to rotate."></dna-hero>
<img class="helix" id="dnaFallback" hidden alt="" src="/assets/o2/dna-fallback.webp">
</div>
<div class="cluster-fit reveal">
<div class="cluster" aria-hidden="true"> <div class="cluster" aria-hidden="true">
<div class="panel dash"> <div class="panel dash">
<div class="panel-bar"><span class="lights"><i></i><i></i><i></i></span><span>Portfolio</span><span class="panel-url">dylanmazzei.com/portfolio</span></div> <div class="panel-bar"><span class="lights"><i></i><i></i><i></i></span><span>Portfolio</span><span class="panel-url">dylanmazzei.com/portfolio</span></div>
@@ -60,7 +106,6 @@ export default {
<strong>Dylan Mazzei</strong> <strong>Dylan Mazzei</strong>
<span class="prole">FOUNDER &amp; OPERATOR</span> <span class="prole">FOUNDER &amp; OPERATOR</span>
<div class="chips"><span class="tag">Texas</span><span class="tag">Florida</span><span class="tag">New York</span><span class="tag">Dubai</span></div> <div class="chips"><span class="tag">Texas</span><span class="tag">Florida</span><span class="tag">New York</span><span class="tag">Dubai</span></div>
<span class="btn btn-primary btn-sm btn-block">START A CONVERSATION</span>
</div> </div>
</div> </div>
<div class="panel panel-dark code"> <div class="panel panel-dark code">
@@ -69,41 +114,68 @@ export default {
</div> </div>
</div> </div>
</div> </div>
</section>
<section class="section" id="ventures" aria-labelledby="ventures-title">
<div class="wrap">
<div class="sec-head reveal">
<div class="stack-sm">${eyebrow('CURRENT VENTURES')}<h2 id="ventures-title" class="h-display">The Companies I’m Leading Today</h2></div>
<div class="prose">
<p>I like being close to the decisions that shape a company, especially when there is something new to figure out.</p>
<p>Today, I lead businesses across AI, marketing, software, spirits, education technology, cybersecurity, and e-commerce. They operate in very different worlds, which keeps me curious.</p>
</div> </div>
</div> </div>
<div class="features reveal">
${ventures.map(([ic, h, p]) => `<article class="feature"><span class="ic">${icon(ic)}</span><h3>${h}</h3><p>${p}</p></article>`).join('')}
</div>
<div class="sec-foot reveal" style="justify-content:flex-end">${btn('EXPLORE MY VENTURES', '/portfolio/')}</div>
</div>
</section> </section>
<section class="section" id="journey" aria-labelledby="journey-title"> <section id="ventures" aria-labelledby="ventures-title">
<div class="wrap"> <div class="wrap">
<div class="sec-head reveal"> <div class="vhead reveal">
<div class="stack-sm">${eyebrow('THE JOURNEY SO FAR')}<h2 id="journey-title" class="h-display">Good Opportunities Rarely Look the Same.</h2></div> ${eyebrow('CURRENT VENTURES')}
<p class="body">I’ve never looked at opportunity through the lens of one industry. Over the years, that curiosity has taken me into very different businesses, including four that grew into successful exits.</p> <h2 id="ventures-title" class="sr-only">Current ventures</h2>
<p class="lead">Today, I lead businesses across AI, marketing, software, spirits, education technology, cybersecurity, and e-commerce. They operate in very different worlds, which keeps me curious.</p>
</div> </div>
<div class="cards cards-4 exits reveal"> <ul class="sr-only">
${exits <li>MARKETING &amp; AI SEARCH Leading teams through a constantly changing search landscape.</li>
.map( <li>AI SOFTWARE Building around new opportunities created by AI.</li>
([ic, h, p]) => `<article class="card exit"><div class="card-top"><span class="ic">${icon(ic)}</span><span class="tag tag-white">${icon('check', 'check')}Exit</span></div><h3 class="h-md">${h}</h3><p>${p}</p></article>` <li>SPIRITS Taking a consumer brand from idea to market.</li>
) <li>EDUCATION TECHNOLOGY Creating technology designed for real classrooms.</li>
.join('')} <li>CYBERSECURITY Solving security challenges for growing businesses.</li>
<li>E-COMMERCE Turning product ideas into businesses built around real customers.</li>
</ul>
<div class="vtabs reveal" id="vtabs" role="tablist" aria-label="Ventures"></div>
<div class="vpanel reveal" id="vpanel">
<div class="vinfo">
<div><span class="vnum" id="vnum">01 / 06</span></div>
<div class="vt" id="vt" aria-live="polite"><h3 id="vname">MARKETING &amp; AI SEARCH</h3><p id="vdesc">Leading teams through a constantly changing search landscape.</p></div>
<div class="btn-row">${btn('EXPLORE MY VENTURES', '/portfolio/', 'primary', 'btn-sm')}</div>
</div> </div>
<div class="stats reveal"> <canvas id="vcanvas" aria-hidden="true"></canvas>
<div class="stat"><span>EXITS</span><b data-count="4">4</b></div> <div class="varrows">
<div class="stat"><span>INDUSTRIES</span><b data-count="6">6</b></div> <button id="vprev" type="button" aria-label="Previous venture"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M13 8H3M7 4 3 8l4 4"/></svg></button>
<div class="stat"><span>YEARS OPERATING</span><b data-count="8" data-suffix="+">8+</b></div> <button id="vnext" type="button" aria-label="Next venture"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8h10M9 4l4 4-4 4"/></svg></button>
</div>
</div>
</div>
</section>
<section id="journey" aria-labelledby="journey-title">
<div class="wrap">
<div class="jgrid">
<div class="jhead reveal">
${eyebrow('THE JOURNEY SO FAR')}
<h2 id="journey-title" class="h-display">Good Opportunities Rarely Look the Same.</h2>
<p class="lead">I’ve never looked at opportunity through the lens of one industry. Over the years, that curiosity has taken me into very different businesses, including four that grew into successful exits.</p>
</div>
<div class="jstage" id="jstage">
<div class="floor" aria-hidden="true"></div>
<div class="pins" aria-hidden="true">
${['Insurance', 'Marketing', 'Home Healthcare', 'Apparel', 'Today'].map((p) => `<div class="pn"><i></i><b></b><em>${p}</em></div>`).join('')}
</div>
<canvas id="fw" aria-hidden="true"></canvas>
<p class="jfinal" id="jfinal">Others are growing. And I’m nowhere near finished building.</p>
<button class="btn btn-ghost-light btn-sm jreplay" id="jreplay" type="button">Replay</button>
</div>
<div class="jbody">
<ul class="jlist">
${exits.map(([h, p, path]) => `<li class="jx"><div class="ji"><svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${path}</svg></div><div><h3>${h}</h3><p>${p}</p></div></li>`).join('')}
</ul>
</div>
</div>
<div class="stats">
<div class="stat"><span>EXITS</span><b id="cEx">4</b></div>
<div class="stat"><span>INDUSTRIES</span><b id="cInd">6</b></div>
<div class="stat"><span>YEARS OPERATING</span><b id="cYr">8+</b></div>
<div class="stat stat-note"> <div class="stat stat-note">
<p>Different businesses have called for different decisions, teams, and ways of thinking. That range still shapes how I approach every opportunity today.</p> <p>Different businesses have called for different decisions, teams, and ways of thinking. That range still shapes how I approach every opportunity today.</p>
${textLink('FOLLOW THE JOURNEY', '/about/')} ${textLink('FOLLOW THE JOURNEY', '/about/')}
@@ -112,116 +184,98 @@ export default {
</div> </div>
</section> </section>
<section class="section" id="about" aria-labelledby="about-title"> <section id="about" aria-labelledby="about-title">
<div class="wrap"> <div class="wrap agrid">
<div class="showcase reveal">
<div class="stack"> <div class="stack">
${eyebrow('ABOUT DYLAN')} ${eyebrow('ABOUT DYLAN')}
<h2 id="about-title" class="h-display">Curiosity Got Me Started. Ambition Kept Me Moving.</h2> <h2 id="about-title" class="awords"><span class="grad">Curiosity</span><span class="grad">Got Me Started.</span><span class="neon">Ambition</span><span class="neon n2">Kept Me Moving.</span></h2>
<p class="quote-line">I have always been more interested in what could be possible than what already exists.</p>
<div class="prose">
<p>That curiosity has taken me into industries I never expected to enter, across markets, teams, and ideas that looked nothing alike from the outside. What connects them is the part I enjoy most: seeing potential early, bringing the right people around it, and giving an idea the chance to become something real.</p>
<p>Business is a big part of my story, but it is not the whole story.</p>
</div>
<div class="btn-row">${btn('GET TO KNOW DYLAN', '/about/')}</div>
</div>
<div class="visual portrait">
<img src="/assets/dylan-portrait.jpg" alt="Dylan Mazzei" width="900" height="900" loading="lazy">
<div class="flow" aria-hidden="true">
<small>What connects them is the part I enjoy most:</small>
<ol><li>seeing potential early</li><li>bringing the right people around it</li><li>giving an idea the chance to become something real</li></ol>
</div>
</div> </div>
<div class="atext reveal">
<p class="q">I have always been more interested in what could be possible than what already exists.</p>
<p class="lead">That curiosity has taken me into industries I never expected to enter, across markets, teams, and ideas that looked nothing alike from the outside. What connects them is the part I enjoy most: seeing potential early, bringing the right people around it, and giving an idea the chance to become something real.</p>
<p class="lead">Business is a big part of my story, but it is not the whole story.</p>
${btn('GET TO KNOW DYLAN', '/about/')}
</div> </div>
</div> </div>
</section> </section>
<section class="section" id="consulting" aria-labelledby="consult-title"> <section id="consult" aria-labelledby="consult-title">
<div class="wrap"> <div class="wrap">
<div class="showcase reverse reveal"> <div class="chead">
<div class="visual panel steps-panel" aria-hidden="true"> <div class="stack-sm reveal">${eyebrow('CONSULT DYLAN')}<h2 id="consult-title" class="h-display">Sometimes You Need Another Founder in the Room.</h2></div>
<div class="panel-bar"><span class="lights"><i></i><i></i><i></i></span><strong>Diagnosis Before Prescription.</strong></div> <div class="stack-sm reveal">
<ol> <p class="lead" style="color:var(--ink)">I know what it is like to make decisions when the answer isn't obvious, and the outcome belongs to you.</p>
<li class="sp-step"><span class="num">1</span><h4>Business Diagnostic</h4></li> <p class="lead">That is the perspective I bring when I work with founders. We look closely at the business, talk through what is getting in the way, and figure out where your attention can make the biggest difference.</p>
<li class="sp-step"><span class="num">2</span><h4>Roadmap Delivery</h4></li> <div>${btn('WORK WITH DYLAN', '/consulting/')}</div>
<li class="sp-step"><span class="num">3</span><h4>Execution Sprint or Advisory</h4></li>
</ol>
</div> </div>
<div class="stack">
${eyebrow('CONSULT DYLAN')}
<h2 id="consult-title" class="h-display">Sometimes You Need Another Founder in the Room.</h2>
<p class="quote-line">I know what it is like to make decisions when the answer isn't obvious, and the outcome belongs to you.</p>
<div class="prose">
<p>That is the perspective I bring when I work with founders. We look closely at the business, talk through what is getting in the way, and figure out where your attention can make the biggest difference.</p>
<p>Every engagement starts with a diagnostic. Not because I already know the answer, but because your business deserves one that actually fits.</p>
</div>
<div class="btn-row">${btn('WORK WITH DYLAN', '/consulting/')}</div>
</div> </div>
<div class="cgrid">
${consult.map(([path, h, p]) => `<article class="cc reveal"><div class="cic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${path}</svg></div><h3>${h}</h3>${p ? `<p>${p}</p>` : ''}</article>`).join('')}
</div> </div>
</div> </div>
</section> </section>
<section class="section" id="playbook" aria-labelledby="playbook-title"> <section id="playbook" aria-labelledby="playbook-title">
<div class="wrap"> <div class="pbword" id="pbword" aria-hidden="true">Playbook Playbook</div>
<div class="showcase reveal"> <div class="wrap pbgrid">
<div class="stack"> <div class="pbtext reveal">
${eyebrow('FROM THE NOTEBOOK')} ${eyebrow('FROM THE NOTEBOOK')}
<h2 id="playbook-title" class="h-display">Different Businesses. Different Beginnings. One Playbook.</h2> <h2 id="playbook-title" class="h-display">Different Businesses. Different Beginnings. One Playbook.</h2>
<p class="body">I’ve started enough businesses to know the beginning matters.</p> <p class="lead">I’ve started enough businesses to know the beginning matters.</p>
<ul class="beats"> <p class="lead">The early calls. The first customers. The moments when an idea has to become more than an idea.</p>
<li>${icon('arrow')}The early calls.</li> <p class="lead">The Business Launch Playbook puts my approach to those moments on the page, shaped by years of starting, operating, and growing businesses across very different industries.</p>
<li>${icon('arrow')}The first customers.</li>
<li>${icon('arrow')}The moments when an idea has to become more than an idea.</li>
</ul>
<p class="body">The Business Launch Playbook puts my approach to those moments on the page, shaped by years of starting, operating, and growing businesses across very different industries.</p>
</div> </div>
<div class="visual book-stage"> <div class="book-stage reveal">
<div class="book" role="img" aria-label="The Business Launch Playbook by Dylan Mazzei"> <div class="book" id="book" role="img" aria-label="The Business Launch Playbook by Dylan Mazzei">
${mark()} ${mark()}
<h3>The Business Launch Playbook</h3> <h3>The Business Launch Playbook</h3>
<div class="by">Dylan Mazzei</div> <div class="by">Dylan Mazzei</div>
</div> <span class="sheen" aria-hidden="true"></span>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<section class="section" id="press" aria-label="Press and poetry"> <section id="press" aria-labelledby="press-title">
<div class="wrap duo reveal">
<article class="card card-lg">
${eyebrow('PRESS &amp; MEDIA')}
<h2 class="h-display">The Work Spoke. Then I Did.</h2>
<div class="prose">
<p>About the businesses. The exits. The ideas. What worked. What didn’t. And what I’m thinking about next.</p>
<p>Some of those conversations made it into interviews, profiles, podcasts, and features.</p>
</div>
<div class="spacer"></div>
<div>${btn('SEE THE FEATURES', '/press/')}</div>
</article>
<article class="card card-lg card-dark" id="poetry">
${eyebrow('A DIFFERENT SIDE')}
<h2 class="h-display">And Then There’s Poetry.</h2>
<p class="poem"><span>Not every idea of mine becomes a company.</span><span>Some become words.</span></p>
<p class="body">Poetry gives me somewhere to put thoughts that don't belong in a business plan, a strategy session, or a boardroom.</p>
</article>
</div>
</section>
<section class="section" id="connect" aria-labelledby="connect-title">
<div class="wrap"> <div class="wrap">
<div class="closing reveal"> <div class="phead">
<div class="closing-inner"> <div class="stack-sm reveal">${eyebrow('PRESS &amp; MEDIA')}<h2 id="press-title" class="h-display">The Work Spoke. Then I Did.</h2></div>
<div class="stack-sm reveal" style="max-width:48ch">
<p class="lead">About the businesses. The exits. The ideas. What worked. What didn’t. And what I’m thinking about next.</p>
<p class="lead">Some of those conversations made it into interviews, profiles, podcasts, and features.</p>
</div>
</div>
<div class="pgrid">
${['Interviews', 'Profiles', 'Podcasts', 'Features'].map((t) => `<a class="pt reveal" href="/press/"><h3>${t}</h3><span class="arrow">${arrowUR}</span></a>`).join('')}
</div>
<div class="btn-row reveal" style="margin-top:2rem">${btn('SEE THE FEATURES', '/press/', 'ghost')}</div>
</div>
</section>
<section id="poetry" aria-labelledby="poetry-title">
<div class="arc" aria-hidden="true"></div>
<div class="in-wrap reveal">
${eyebrow('A DIFFERENT SIDE')}
<h2 id="poetry-title" class="h-display">And Then There’s Poetry.</h2>
<p class="poem-line">Not every idea of mine becomes a company.<br>Some become words.</p>
<p class="lead">Poetry gives me somewhere to put thoughts that don't belong in a business plan, a strategy session, or a boardroom.</p>
</div>
</section>
<section id="connect" aria-labelledby="connect-title">
<div class="talk" aria-hidden="true"><img class="ribbon" alt="" src="/assets/o2/ribbon.webp" width="1600" height="900" loading="lazy"><h2>Let’s talk</h2></div>
<div class="cbody">
${eyebrow('LET’S CONNECT')} ${eyebrow('LET’S CONNECT')}
<h2 id="connect-title" class="h-display-lg">Interesting Things Usually Start With a Conversation.</h2> <h3 id="connect-title" class="grad">Interesting Things Usually Start With a Conversation.</h3>
<div class="prose"> <div class="prose">
<p>Maybe you have an idea taking shape. A business at an interesting crossroads. A partnership worth exploring. Or simply a question you think I might have a perspective on.</p> <p>Maybe you have an idea taking shape. A business at an interesting crossroads. A partnership worth exploring. Or simply a question you think I might have a perspective on.</p>
<p>I’ve learned to pay attention when an interesting conversation begins. Some of the best opportunities in my life have started exactly that way.</p> <p>I’ve learned to pay attention when an interesting conversation begins. Some of the best opportunities in my life have started exactly that way.</p>
<p class="body-ink">If there’s something worth talking about, I’m listening.</p> <p style="color:var(--ink)">If there’s something worth talking about, I’m listening.</p>
</div> </div>
<div class="btn-row">${btn('START A CONVERSATION', '/contact/')}</div> <div class="btn-row">${btn('START A CONVERSATION', '/contact/')}</div>
</div> </div>
</div>
</div>
</section> </section>
</div>
`, `,
}; };
+1 -1
View File
@@ -34,7 +34,7 @@ export default {
<div class="profile-meta"> <div class="profile-meta">
<strong>Dylan Mazzei</strong> <strong>Dylan Mazzei</strong>
<span class="prole">FOUNDER &amp; OPERATOR</span> <span class="prole">FOUNDER &amp; OPERATOR</span>
<a class="btn btn-primary btn-sm btn-block" href="mailto:${SITE.pressEmail}">${SITE.pressEmail}</a> <a class="btn btn-primary btn-sm btn-block" href="mailto:${SITE.pressEmail}" style="justify-content:space-between">${SITE.pressEmail}<span class="go">${icon("mail")}</span></a>
</div> </div>
</div> </div>
</div> </div>
+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;
}
+269
View File
@@ -0,0 +1,269 @@
/* 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: #d3dceb;
--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, DNA and the portfolio dashboard side by side ---------- */
.bhead { display: grid; grid-template-columns: 1.1fr 1fr; gap: 3rem; align-items: end; margin-bottom: 3rem; }
.bhead > * { min-width: 0; }
.bgrid { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: 2rem; align-items: center; }
.bgrid > * { min-width: 0; }
.bstack { position: relative; overflow: visible; height: min(78vh, 680px); display: flex; flex-direction: column; justify-content: center; }
.bstack .r { display: block; white-space: nowrap; font-weight: 500; font-size: clamp(2.6rem, 5.6vw, 6.2rem); line-height: .98; letter-spacing: -0.055em;
background: linear-gradient(180deg, var(--grad-a), #5fb4ff 70%, #1a7fff); -webkit-background-clip: text; background-clip: text; color: transparent; will-change: transform; }
.dna3d { position: absolute; left: 50%; top: 50%; height: 104%; width: auto; aspect-ratio: 2 / 3; transform: translate(-50%, -50%); z-index: 2; }
.helix { position: absolute; left: 50%; top: 50%; height: 104%; width: auto; pointer-events: none; transform: translate(-50%, -50%); filter: drop-shadow(0 0 40px rgba(26, 127, 255, .5)); }
.helix[hidden], .dna3d[hidden] { display: none; }
.bglow { position: absolute; left: 50%; top: 50%; width: 80%; height: 90%; transform: translate(-50%, -50%); background: radial-gradient(closest-side, rgba(26, 127, 255, .28), transparent); pointer-events: none; }
.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) {
.bhead { grid-template-columns: 1fr; gap: 1.5rem; }
.bgrid { grid-template-columns: 1fr; }
.bstack { height: 62vh; }
.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 rgba(255, 255, 255, .09); border-radius: 22px; color: #f3f7ff;
background: linear-gradient(135deg, rgba(255, 255, 255, .04), rgba(255, 255, 255, .01)), #070b16; overflow: hidden; --ac: #1fb7ff; }
.vpanel::before { content: ""; position: absolute; inset: 0; background: radial-gradient(60% 80% at 75% 50%, color-mix(in srgb, var(--ac) 40%, transparent), transparent 70%); transition: background .6s; pointer-events: none; }
.vpanel::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(rgba(255, 255, 255, .04) 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: #55627f; 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: #f3f7ff; }
.vinfo p { color: #b3c0da; font-size: 1.08rem; max-width: 34ch; }
.vinfo .vt { transition: opacity .3s, transform .3s; }
.vinfo .vt.out { opacity: 0; transform: translateY(10px); }
.vinfo .btn-primary { background: #fff; color: #06101f; }
#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 rgba(255, 255, 255, .09); border-radius: 10px; background: rgba(10, 14, 26, .7); color: #fff; 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 rgba(255, 255, 255, .09); color: #f3f7ff;
background: radial-gradient(70% 55% at 50% 100%, rgba(26, 127, 255, .18), transparent 70%), #060a14; }
.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(rgba(159, 230, 255, .26) 1px, transparent 1px) 0 0 / 56px 56px, linear-gradient(90deg, rgba(159, 230, 255, .26) 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(#1fb7ff, 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 #1fb7ff; 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 #1fb7ff; }
.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: #55627f; transition: color .5s; }
.pn.done em { color: #fff; }
#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: #d6e8ff; 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(165%, 1900px); max-width: none; height: auto; transform: translate(-50%, -50%); animation: rib 14s ease-in-out infinite alternate; }
@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; }
.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; } }
+402
View File
@@ -0,0 +1,402 @@
/* 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;
/* ---------- 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: 'Spirits', side: 'l', x: '6%', y: '73%', r: '8deg', d: '-4s', p: '<path d="M10 2.5h4M10.5 2.5v4.2L8.6 9.4a3 3 0 0 0-.6 1.8V20a1.5 1.5 0 0 0 1.5 1.5h5A1.5 1.5 0 0 0 16 20v-8.8a3 3 0 0 0-.6-1.8l-1.9-2.7V2.5"/><path d="M8 13.5h8v4H8z"/>' },
{ n: 'Education Technology', side: 'r', x: '8%', y: '22%', 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: '15%', y: '50%', 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: 'r', 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">' + 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: "still building" rows + playbook word ---------- */
const bst = $('#bstack'), rows = bst ? $$('.r', bst) : [], pbSec = $('#playbook'), pbWord = $('#pbword');
let ticking = false;
function onScroll() {
if (bst) {
const r = bst.getBoundingClientRect();
const k = clamp((innerHeight - r.top) / (innerHeight + r.height), 0, 1) - .5;
rows.forEach((el, i) => {
const dir = i % 2 ? 1 : -1;
const op = 1 - Math.abs(i - 2.5) / 3.2;
el.style.opacity = clamp(op, .18, 1).toFixed(2);
el.style.transform = reduce ? '' : 'translate3d(' + (k * dir * (120 + i * 14)).toFixed(1) + 'px,0,0)';
});
}
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 = ['#ffffff', '#9fe6ff', '#1fb7ff', '#3d8bff', '#d6ecff'];
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 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; }); }
fx.clearRect(0, 0, FW.w, FW.h); fx.globalCompositeOperation = '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 = '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 = '#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';
}
/* ---------- 3D DNA: fall back to the still image without WebGL ---------- */
setTimeout(() => {
const dna = $('#dna'), fb = $('#dnaFallback');
const ok = dna && dna.shadowRoot && dna.shadowRoot.querySelector('canvas');
if (!ok && fb) { if (dna) dna.hidden = true; fb.hidden = false; }
}, 1500);
/* ---------- 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: 'SPIRITS', d: 'Taking a consumer brand from idea to market.', c: '#3fa9ff', m: 'bottle' },
{ 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 = 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 = V[vi].n; vdesc.textContent = V[vi].d; vnum.textContent = '0' + (vi + 1) + ' / 06'; 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');
vpanel.addEventListener('keydown', (e) => { if (e.key === 'ArrowRight') setV(vi + 1); if (e.key === 'ArrowLeft') setV(vi - 1); });
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 ? 1 : -1)); 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;
},
// Spirits: a bottle (lathe profile) with a chrome cap
bottle() {
const g = new THREE.Group();
const pts = [[0, -1.7], [.78, -1.7], [.86, -1.55], [.86, .35], [.7, .7], [.32, 1.05], [.27, 1.55], [0, 1.55]].map(([x, y]) => new THREE.Vector2(x, y));
g.add(M(new THREE.LatheGeometry(pts, 48), glass));
const liquid = M(new THREE.CylinderGeometry(.78, .78, 1.6, 48), metal); liquid.position.y = -.85; g.add(liquid);
const label = M(new THREE.CylinderGeometry(.875, .875, .55, 48, 1, true), glow); label.position.y = -.35; g.add(label);
const cap = M(new THREE.CylinderGeometry(.3, .3, .4, 32), chrome); cap.position.y = 1.72; g.add(cap);
g.scale.setScalar(.85);
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: 170 KiB

+3851
View File
File diff suppressed because one or more lines are too long
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: 123 KiB

+6
View File
File diff suppressed because one or more lines are too long
+8 -9
View File
@@ -5,7 +5,6 @@
// Theme toggle: remembers the visitor's choice; otherwise follows the system. // Theme toggle: remembers the visitor's choice; otherwise follows the system.
var themeBtn = document.getElementById('theme-btn'); var themeBtn = document.getElementById('theme-btn');
var mq = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
var current = function () { return root.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'; }; var current = function () { return root.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'; };
var paint = function () { var paint = function () {
if (!themeBtn) return; if (!themeBtn) return;
@@ -20,17 +19,17 @@
}; };
paint(); paint();
if (themeBtn) themeBtn.addEventListener('click', function () { apply(current() === 'dark' ? 'light' : 'dark', true); }); if (themeBtn) themeBtn.addEventListener('click', function () { apply(current() === 'dark' ? 'light' : 'dark', true); });
if (mq && mq.addEventListener) {
mq.addEventListener('change', function (e) {
var saved = null;
try { saved = localStorage.getItem('theme'); } catch (err) {}
if (!saved) apply(e.matches ? 'dark' : 'light', false);
});
}
// Nav border on scroll // Nav border on scroll
var nav = document.getElementById('nav'); var nav = document.getElementById('nav');
var onScroll = function () { nav && nav.classList.toggle('is-scrolled', window.scrollY > 8); }; 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(); onScroll();
window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('scroll', onScroll, { passive: true });
+150 -35
View File
@@ -33,8 +33,14 @@
--code-num: #ffb38a; --code-num: #ffb38a;
--code-dim: #8a8a8a; --code-dim: #8a8a8a;
--shadow-color: rgba(0, 0, 0, 0.12); --shadow-color: rgba(0, 0, 0, 0.12);
--bd: rgba(15, 23, 42, 0.08);
--glass: rgba(255, 255, 255, 0.7);
--glow: rgba(26, 127, 255, 0.10);
--grid-line: rgba(15, 23, 42, 0.035);
--grad-a: #0b1220; --grad-b: #13306b; --grad-c: #1a6fe0;
--ice: #0870c4; --sky: #1a7fff;
--font-sans: "Inter", "DM Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; --font-sans: "Inter Tight", "Inter", "DM Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace; --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
--text-caption: 14px; --text-caption: 14px;
@@ -65,48 +71,60 @@
/* Dark theme: follows the system unless the visitor picks one with the toggle. */ /* Dark theme: follows the system unless the visitor picks one with the toggle. */
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) { :root:not([data-theme="light"]) {
--accent: #33b9ff; --accent: #1fb7ff;
--accent-press: #66cbff; --accent-press: #5cc9ff;
--accent-soft: rgba(51, 185, 255, 0.14); --accent-soft: rgba(31, 183, 255, 0.14);
--accent-glow: rgba(51, 185, 255, 0.35); --accent-glow: rgba(31, 183, 255, 0.35);
--on-accent: #04121c; --on-accent: #04101c;
--logo-blue: #33b9ff; --logo-blue: #33b9ff;
--ink: #f2f2f3; --ink: #f3f7ff;
--graphite: #d6d6d9; --graphite: #c3cde2;
--slate: #a4a4aa; --slate: #8896b3;
--smoke: #85858c; --smoke: #6c7894;
--ash: #9a9aa1; --ash: #8896b3;
--mist: #26262b; --mist: rgba(255, 255, 255, 0.09);
--cloud: #141417; --cloud: rgba(255, 255, 255, 0.035);
--porcelain: #0b0b0d; --porcelain: #04060c;
--surface-dark: #18181c; --surface-dark: #0b1120;
--surface-dark-2: #101013; --surface-dark-2: #070b16;
--dark-line: #2c2c32; --dark-line: rgba(255, 255, 255, 0.1);
--nav-bg: rgba(11, 11, 13, 0.82); --nav-bg: rgba(4, 6, 12, 0.66);
--shadow-color: rgba(0, 0, 0, 0.6); --shadow-color: rgba(0, 0, 0, 0.6);
--bd: rgba(255, 255, 255, 0.09);
--glass: rgba(255, 255, 255, 0.035);
--glow: rgba(26, 127, 255, 0.10);
--grid-line: rgba(255, 255, 255, 0.028);
--grad-a: #ffffff; --grad-b: #a9dfff; --grad-c: #5aa8ff;
--ice: #9fe6ff; --sky: #1fb7ff;
color-scheme: dark; color-scheme: dark;
} }
} }
:root[data-theme="dark"] { :root[data-theme="dark"] {
--accent: #33b9ff; --accent: #1fb7ff;
--accent-press: #66cbff; --accent-press: #5cc9ff;
--accent-soft: rgba(51, 185, 255, 0.14); --accent-soft: rgba(31, 183, 255, 0.14);
--accent-glow: rgba(51, 185, 255, 0.35); --accent-glow: rgba(31, 183, 255, 0.35);
--on-accent: #04121c; --on-accent: #04101c;
--logo-blue: #33b9ff; --logo-blue: #33b9ff;
--ink: #f2f2f3; --ink: #f3f7ff;
--graphite: #d6d6d9; --graphite: #c3cde2;
--slate: #a4a4aa; --slate: #8896b3;
--smoke: #85858c; --smoke: #6c7894;
--ash: #9a9aa1; --ash: #8896b3;
--mist: #26262b; --mist: rgba(255, 255, 255, 0.09);
--cloud: #141417; --cloud: rgba(255, 255, 255, 0.035);
--porcelain: #0b0b0d; --porcelain: #04060c;
--surface-dark: #18181c; --surface-dark: #0b1120;
--surface-dark-2: #101013; --surface-dark-2: #070b16;
--dark-line: #2c2c32; --dark-line: rgba(255, 255, 255, 0.1);
--nav-bg: rgba(11, 11, 13, 0.82); --nav-bg: rgba(4, 6, 12, 0.66);
--shadow-color: rgba(0, 0, 0, 0.6); --shadow-color: rgba(0, 0, 0, 0.6);
--bd: rgba(255, 255, 255, 0.09);
--glass: rgba(255, 255, 255, 0.035);
--glow: rgba(26, 127, 255, 0.10);
--grid-line: rgba(255, 255, 255, 0.028);
--grad-a: #ffffff; --grad-b: #a9dfff; --grad-c: #5aa8ff;
--ice: #9fe6ff; --sky: #1fb7ff;
color-scheme: dark; color-scheme: dark;
} }
@@ -779,6 +797,99 @@ html:not(.js) .tabs { grid-template-columns: 1fr; }
.book { transform: none; } .book { transform: none; }
} }
/* =====================================================================
Option 2 skin: navy canvas, neon-blue glow, glass cards, gradient type.
Everything reads from tokens so the light theme keeps working.
===================================================================== */
.bgfx {
position: fixed; inset: 0; z-index: 0; pointer-events: none;
background:
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px) 0 0 / 8.3333vw 100%,
radial-gradient(60% 40% at 50% 0%, var(--glow), transparent 70%);
}
main, .footer { position: relative; z-index: 2; }
.progress { position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 95; transform-origin: 0 50%; transform: scaleX(0); background: linear-gradient(90deg, #1a7fff, #9fe6ff); }
h1, h2, h3, h4 { font-weight: 600; }
.h-display-lg, .h-display, .grad {
font-weight: 500; letter-spacing: -0.045em; padding-bottom: 0.08em;
background: linear-gradient(180deg, var(--grad-a) 20%, var(--grad-b) 75%, var(--grad-c));
-webkit-background-clip: text; background-clip: text; color: transparent;
}
.h-display-lg { letter-spacing: -0.05em; line-height: 1.02; }
.h-lg, .h-md, .h-sm { font-weight: 500; letter-spacing: -0.035em; }
.band, .card-dark, .closing-dark, .plan-featured, .feature-press, .cal-card {
--grad-a: #ffffff; --grad-b: #a9dfff; --grad-c: #5aa8ff; --bd: rgba(255, 255, 255, 0.1);
}
/* Eyebrow becomes the glass pill */
.eyebrow {
align-self: flex-start; gap: 8px; padding: 6px 14px; border: 1px solid var(--bd); border-radius: 99px;
background: var(--glass); color: var(--ice); font-size: 11.5px; letter-spacing: 0.08em;
-webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.eyebrow::before { width: 6px; height: 6px; border-radius: 50%; background: var(--sky); box-shadow: 0 0 10px var(--sky); }
.center .eyebrow, .sec-head-center .eyebrow, .closing-inner .eyebrow { align-self: center; }
.band .eyebrow, .card-dark .eyebrow, .closing-dark .eyebrow { color: #9fe6ff; background: rgba(255, 255, 255, .04); }
/* Buttons: white key with a blue arrow tile, glass outline variant */
.btn { border-radius: 12px; min-height: 48px; padding: 8px 8px 8px 20px; gap: 14px; font-weight: 600; font-size: 12.5px; letter-spacing: 0.06em; }
.btn .go { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 8px; background: #1a7fff; color: #fff; flex: none; transition: transform .25s ease; }
.btn .go svg { width: 14px; height: 14px; }
.btn:hover .go { transform: translateX(3px); }
.btn:hover svg { transform: none; }
.btn-primary { background: var(--ink); color: var(--porcelain); }
.btn-primary:hover { background: var(--ink); box-shadow: 0 0 36px rgba(159, 230, 255, .35); }
.btn-ghost, .btn-ghost-light { background: var(--glass); color: var(--ink); border-color: var(--bd); padding: 8px 20px; }
.btn-ghost .go, .btn-ghost-light .go { display: none; }
.btn-ghost:hover, .btn-ghost-light:hover { background: var(--glass); border-color: var(--sky); }
.btn-ghost-light { color: #fff; border-color: rgba(255, 255, 255, .2); background: rgba(255, 255, 255, .03); }
.btn-light { background: #fff; color: #06101f; }
.btn-light:hover { background: #fff; box-shadow: 0 0 36px rgba(159, 230, 255, .35); }
.btn-sm { min-height: 40px; padding: 5px 5px 5px 16px; font-size: 11.5px; }
.btn-sm .go { width: 28px; height: 28px; }
.text-link { color: var(--sky); }
/* Glass cards */
.card, .showcase, .closing, .process-step, .plan, .venture, .tab-panel, .faq details, .cover, .topic, .signal, .industries, .direct { border: 1px solid var(--bd); }
.card, .showcase, .closing, .process-step, .plan:not(.plan-featured), .venture, .tab-panel, .cover, .topic {
background: linear-gradient(160deg, var(--glass), transparent 120%), var(--cloud);
}
.card, .process-step, .venture, .cover, .topic { overflow: hidden; }
.card::after, .process-step::after, .cover::after, .topic::after {
content: ""; position: absolute; right: -60px; top: -60px; width: 220px; height: 220px; pointer-events: none;
background: radial-gradient(closest-side, var(--glow), transparent); opacity: 0; transition: opacity .4s ease;
}
.card:hover::after, .process-step:hover::after, .cover:hover::after, .topic:hover::after { opacity: 1; }
.card:hover, .process-step:hover, .cover:hover, .topic:hover { border-color: rgba(31, 183, 255, .45); }
.ic { border: 1px solid rgba(31, 183, 255, .35); background: radial-gradient(circle at 35% 30%, var(--accent-soft), transparent); box-shadow: 0 0 22px var(--glow); }
.card .ic { background: radial-gradient(circle at 35% 30%, var(--accent-soft), transparent); }
/* Navigation: glass bar with a pill group, like option 2 */
.nav { background: transparent; border-bottom: 0; -webkit-backdrop-filter: none; backdrop-filter: none; }
.nav::before {
content: ""; position: absolute; inset: 0; z-index: -1; background: var(--nav-bg);
-webkit-backdrop-filter: blur(20px) saturate(1.5); backdrop-filter: blur(20px) saturate(1.5);
border-bottom: 1px solid var(--bd);
}
.nav-links { gap: 2px; padding: 4px; border: 1px solid var(--bd); border-radius: 12px; background: var(--glass); }
.nav-links a, .nav-links button { border-radius: 8px; padding: 8px 14px; font-size: 13.5px; color: var(--slate); }
.nav-links a:hover, .nav-links button:hover, .nav-links li:focus-within > button { background: transparent; color: var(--ink); }
.nav-links a[aria-current="page"], .nav-links .is-active > button { color: var(--ink); background: rgba(26, 127, 255, .22); }
.dropdown { background: var(--nav-bg); -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px); border-color: var(--bd); }
:root[data-theme="dark"] .dropdown { background: rgba(10, 14, 26, .96); }
.theme-btn, .menu-btn { border-radius: 10px; border-color: var(--bd); background: var(--glass); }
/* Real logo on dark, vector recreation on light */
.logo-img { display: none; height: 30px; width: auto; }
:root[data-theme="dark"] .brand .logo-img { display: block; }
:root[data-theme="dark"] .brand .logo-mark, :root[data-theme="dark"] .brand .brand-word { display: none; }
.footer .brand .logo-img { display: block; }
.footer .brand .logo-mark, .footer .brand .brand-word { display: none; }
.footer { border-top: 1px solid var(--bd); }
/* ---------- Responsive ---------- */ /* ---------- Responsive ---------- */
@media (max-width: 1100px) { @media (max-width: 1100px) {
.nav-links { display: none; } .nav-links { display: none; }
@@ -873,3 +984,7 @@ html:not(.js) .tabs { grid-template-columns: 1fr; }
.foot-col:last-child { grid-column: 1 / -1; } .foot-col:last-child { grid-column: 1 / -1; }
.btn { white-space: normal; text-align: center; } .btn { white-space: normal; text-align: center; }
} }
/* Option 2 skin: small fixes */
.band .showcase { border: 0; background: transparent; }
.plan-featured { outline: 1px solid rgba(31, 183, 255, .45); box-shadow: 0 0 50px rgba(26, 127, 255, .18); }
+93
View File
@@ -0,0 +1,93 @@
Copyright 2022 The Inter Project Authors (https://github.com/rsms/inter-tight) InterTight-Italic[wght].ttf: Copyright 2022 The Inter Project Authors (https://github.com/rsms/inter-tight)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+21
View File
@@ -0,0 +1,21 @@
The MIT License
Copyright © 2010-2021 three.js authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,21 @@
The MIT License
Copyright © 2010-2025 three.js authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.