nxtgauge-frontend-solid/src/routes/about.tsx

597 lines
26 KiB
TypeScript
Raw Normal View History

import { A } from '@solidjs/router';
import { For, Show, createMemo, createSignal, onCleanup, onMount } from 'solid-js';
import PublicBackground from '~/components/PublicBackground';
import PublicFooter from '~/components/PublicFooter';
import PublicHeader from '~/components/PublicHeader';
const chapters = [
{ id: 'chapter-problem', title: '01 The problem' },
{ id: 'chapter-built', title: '02 What we built' },
{ id: 'chapter-trust', title: '03 How trust works' },
{ id: 'chapter-principles', title: '04 Principles' },
{ id: 'chapter-timeline', title: '05 Timeline' },
] as const;
const trustSequence = [
{ num: '01', title: 'Submission', body: 'A user submits profile, job, or requirement details with required context.' },
{ num: '02', title: 'Human Review', body: 'A reviewer checks quality, relevance, and verification requirements.' },
{ num: '03', title: 'Approval', body: 'Validated submissions are approved and moved to publish-ready state.' },
{ num: '04', title: 'Visible to Marketplace', body: 'Approved entities become discoverable and can receive responses.' },
] as const;
const milestones = [
{ title: 'Research', body: 'Mapped trust breakdowns in hiring and service discovery journeys.' },
{ title: 'MVP', body: 'Built a marketplace core with structured onboarding and review signals.' },
{ title: 'Private pilot', body: 'Tested operational workflows with role-specific submissions.' },
{ title: 'Launch', body: 'Released trust-layered marketplace experience to wider users.' },
{ title: 'Next: expanding categories', body: 'Adding more categories while keeping quality controls strong.' },
] as const;
const chapterFourNarrative = [
'We didnt build another marketplace.',
'We built a filter.',
'A review layer.',
'Clarity replaces noise.',
] as const;
const chapterTwoRows = ['Profile status', 'Requirement status', 'Activity transparency'] as const;
export default function AboutPage() {
const [showBackToTop, setShowBackToTop] = createSignal(false);
const [reduceMotion, setReduceMotion] = createSignal(false);
const [activeChapter, setActiveChapter] = createSignal(0);
const [scrollY, setScrollY] = createSignal(0);
// Initialize visible for SSR - all content renders on server, client hydration just updates visibility for animations
const [heroVisible, setHeroVisible] = createSignal(true);
const [problemVisible, setProblemVisible] = createSignal(true);
const [builtVisible, setBuiltVisible] = createSignal(true);
const [trustVisible, setTrustVisible] = createSignal(true);
const [principlesVisible, setPrinciplesVisible] = createSignal(true);
const [timelineVisible, setTimelineVisible] = createSignal(true);
const [closingVisible, setClosingVisible] = createSignal(true);
const [problemProgress, setProblemProgress] = createSignal(0);
const [trustProgress, setTrustProgress] = createSignal(0);
const [principleProgress, setPrincipleProgress] = createSignal(0);
const [builtTilt, setBuiltTilt] = createSignal({ x: 0, y: 0 });
let heroRef: HTMLElement | undefined;
let chapterProblemRef: HTMLElement | undefined;
let chapterBuiltRef: HTMLElement | undefined;
let chapterTrustRef: HTMLElement | undefined;
let chapterPrinciplesRef: HTMLElement | undefined;
let chapterTimelineRef: HTMLElement | undefined;
let closingRef: HTMLElement | undefined;
onMount(() => {
const media = window.matchMedia('(prefers-reduced-motion: reduce)');
const syncMotion = () => setReduceMotion(media.matches);
syncMotion();
const chapterRefs = () => [chapterProblemRef, chapterBuiltRef, chapterTrustRef, chapterPrinciplesRef, chapterTimelineRef];
const revealMap = new Map<HTMLElement, (visible: boolean) => void>();
if (heroRef) revealMap.set(heroRef, setHeroVisible);
if (chapterProblemRef) revealMap.set(chapterProblemRef, setProblemVisible);
if (chapterBuiltRef) revealMap.set(chapterBuiltRef, setBuiltVisible);
if (chapterTrustRef) revealMap.set(chapterTrustRef, setTrustVisible);
if (chapterPrinciplesRef) revealMap.set(chapterPrinciplesRef, setPrinciplesVisible);
if (chapterTimelineRef) revealMap.set(chapterTimelineRef, setTimelineVisible);
if (closingRef) revealMap.set(closingRef, setClosingVisible);
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
const setter = revealMap.get(entry.target as HTMLElement);
if (setter) setter(true);
});
},
{ threshold: 0.12 }
);
revealMap.forEach((_, el) => observer.observe(el));
const onScroll = () => {
setShowBackToTop(window.scrollY > 500);
setScrollY(window.scrollY || 0);
const middle = window.innerHeight * 0.42;
let nextActive = 0;
chapterRefs().forEach((section, index) => {
if (!section) return;
const rect = section.getBoundingClientRect();
if (rect.top <= middle) nextActive = index;
});
setActiveChapter(nextActive);
if (chapterProblemRef && !reduceMotion()) {
const rect = chapterProblemRef.getBoundingClientRect();
const viewport = window.innerHeight;
const start = viewport * 0.9;
const end = viewport * 0.18;
const range = rect.height + (start - end);
const raw = (start - rect.top) / range;
setProblemProgress(Math.max(0, Math.min(1, raw)));
} else if (reduceMotion()) {
setProblemProgress(1);
}
if (chapterTrustRef && !reduceMotion()) {
const rect = chapterTrustRef.getBoundingClientRect();
const viewport = window.innerHeight;
const start = viewport * 0.75;
const end = viewport * 0.18;
const range = rect.height + (start - end);
const raw = (start - rect.top) / range;
setTrustProgress(Math.max(0, Math.min(1, raw)));
} else if (reduceMotion()) {
setTrustProgress(1);
}
if (chapterPrinciplesRef && !reduceMotion()) {
const rect = chapterPrinciplesRef.getBoundingClientRect();
const viewport = window.innerHeight;
const sectionTopAbs = window.scrollY + rect.top;
const start = sectionTopAbs - viewport * 0.9;
const end = sectionTopAbs + rect.height - viewport * 0.52;
const range = Math.max(1, end - start);
const raw = (window.scrollY - start) / range;
setPrincipleProgress(Math.max(0, Math.min(1, raw)));
} else if (reduceMotion()) {
setPrincipleProgress(1);
}
};
onScroll();
window.addEventListener('scroll', onScroll, { passive: true });
media.addEventListener('change', syncMotion);
onCleanup(() => {
window.removeEventListener('scroll', onScroll);
media.removeEventListener('change', syncMotion);
observer.disconnect();
});
});
const progress = createMemo(() => {
if (chapters.length <= 1) return 0;
return (activeChapter() / (chapters.length - 1)) * 100;
});
const progressBetween = (value: number, start: number, end: number) => {
const span = Math.max(0.001, end - start);
return Math.max(0, Math.min(1, (value - start) / span));
};
const effectiveProblemProgress = createMemo(() => (reduceMotion() ? 1 : problemProgress()));
const effectiveTrustProgress = createMemo(() => (reduceMotion() ? 1 : trustProgress()));
const effectivePrincipleProgress = createMemo(() => (reduceMotion() ? 1 : principleProgress()));
const chapterOneHeadlineIn = createMemo(() => progressBetween(effectiveProblemProgress(), 0.12, 0.42));
const chapterOneBodyIn = createMemo(() => progressBetween(effectiveProblemProgress(), 0.24, 0.56));
const chapterOneShapeFade = createMemo(() => (reduceMotion() ? 0 : 0.08 * (1 - effectiveProblemProgress())));
const principleStage = createMemo(() => {
const p = effectivePrincipleProgress();
if (p < 0.25) return 0;
if (p < 0.5) return 1;
if (p < 0.75) return 2;
return 3;
});
const stateTwoUnderline = createMemo(() => progressBetween(effectivePrincipleProgress(), 0.26, 0.46));
const stateThreeLine = createMemo(() => progressBetween(effectivePrincipleProgress(), 0.52, 0.74));
const chapterFourMotion = (idx: number) => {
const p = effectivePrincipleProgress();
const center = (idx + 0.5) / chapterFourNarrative.length;
const preWindow = 0.14;
// Once a line has crossed center, keep it bright and glowing.
if (p >= center) {
return {
opacity: 1,
y: 0,
glow: 52,
};
}
// Before center: approach from dim to bright, but no glow yet.
const distanceToCenter = Math.max(0, center - p);
const t = Math.max(0, Math.min(1, 1 - distanceToCenter / preWindow));
return {
opacity: 0.46 + t * 0.54,
y: 0,
glow: 0,
};
};
const chapterFourBackdropGlow = createMemo(() => {
// Keep chapter backdrop glow once the first narrative line reaches center.
const firstCenter = 0.5 / chapterFourNarrative.length;
return effectivePrincipleProgress() >= firstCenter ? 1 : 0;
});
const scrollToChapter = (chapterId: string) => {
const target = document.getElementById(chapterId);
if (!target) return;
const offset = window.innerWidth >= 1280 ? 180 : 150;
const top = target.getBoundingClientRect().top + window.scrollY - offset;
window.scrollTo({ top: Math.max(0, top), behavior: reduceMotion() ? 'auto' : 'smooth' });
};
return (
<main class="lp-main about-page-root">
<PublicBackground scrollY={scrollY()} reduceMotion={reduceMotion()} meshFactor={0.12} ribbonFactor={0.22} meshCap={40} ribbonCap={55} />
<div class="lp-content about-content about-with-rail">
<PublicHeader />
<aside class="about-chapter-rail">
<div class="about-chapter-track">
<span class="about-chapter-progress" style={{ height: `${progress()}%` }} />
</div>
<ul class="about-chapter-list">
<For each={chapters}>
{(chapter, idx) => (
<li class={idx() === activeChapter() ? 'about-chapter-item-active' : 'about-chapter-item'}>
<a
href={`#${chapter.id}`}
onClick={(event) => {
event.preventDefault();
scrollToChapter(chapter.id);
}}
>
{chapter.title}
</a>
</li>
)}
</For>
</ul>
</aside>
<section ref={heroRef} class="about-hero">
<div class="container">
<div class={`about-hero-inner about-reveal-init ${heroVisible() ? 'about-reveal-show' : ''}`}>
<div>
<p class="about-kicker">Brand Story</p>
<h1 class="about-title">Trust-first hiring and services.</h1>
<p class="about-copy">
Nxtgauge is built to reduce noise, improve quality, and help people connect with confidence.
</p>
<div class="hero-actions">
<A class="lp-primary-btn" href="/contact">Contact us</A>
</div>
</div>
<aside class="about-manifesto-card">
<h2>Our manifesto</h2>
<ul>
<li>Verify what matters</li>
<li>Reduce spam and guesswork</li>
<li>Match people faster</li>
</ul>
<span class="about-sheen-sweep" />
</aside>
</div>
</div>
</section>
<section
id="chapter-problem"
ref={chapterProblemRef}
class="about-section-tight about-chapter-problem-section"
>
<div class="container">
<article class={`about-problem-stage about-reveal-init ${problemVisible() ? 'about-reveal-show' : ''}`}>
<span class="about-problem-halo" style={{ opacity: 0.16 + effectiveProblemProgress() * 0.32 }} />
<span
class="about-problem-shape-a"
style={{
opacity: chapterOneShapeFade(),
transform: `translate3d(${-14 * effectiveProblemProgress()}px, ${10 * effectiveProblemProgress()}px, 0)`,
}}
/>
<span
class="about-problem-shape-b"
style={{
opacity: chapterOneShapeFade(),
transform: `translate3d(${16 * effectiveProblemProgress()}px, ${-8 * effectiveProblemProgress()}px, 0)`,
}}
/>
<span
class="about-problem-shape-c"
style={{
opacity: chapterOneShapeFade(),
transform: `translate3d(${12 * effectiveProblemProgress()}px, ${11 * effectiveProblemProgress()}px, 0)`,
}}
/>
<span
class="about-problem-shape-d"
style={{
opacity: chapterOneShapeFade(),
transform: `translate3d(${-12 * effectiveProblemProgress()}px, ${-10 * effectiveProblemProgress()}px, 0)`,
}}
/>
<p class="about-chapter-label about-chapter-label-light">Chapter 01</p>
<h2 class="about-chapter-title about-chapter-title-dark">The Problem</h2>
<h3
class="about-problem-headline"
style={{
opacity: chapterOneHeadlineIn(),
filter: `blur(${(1 - chapterOneHeadlineIn()) * 5}px)`,
}}
>
The hardest part isnt finding options.
</h3>
<p
class="about-problem-body"
style={{
opacity: chapterOneBodyIn(),
filter: `blur(${(1 - chapterOneBodyIn()) * 3}px)`,
}}
>
Its knowing which one deserves your time.
<br />
Scrolling. Comparing. Second-guessing. Starting over.
<br />
The real cost isnt money.
<br />
Its momentum.
</p>
</article>
</div>
</section>
<section
id="chapter-built"
ref={chapterBuiltRef}
class="about-section-tight"
>
<div class="container">
<article class={`about-glass-light about-chapter-two-shell about-reveal-init ${builtVisible() ? 'about-reveal-show' : ''}`}>
<p class="about-chapter-label about-chapter-label-orange">Chapter 02</p>
<h2 class="about-chapter-title about-chapter-title-light">What We Built</h2>
<div class="about-chapter-two-grid">
<div
class="about-chapter-two-text"
style={{
opacity: builtVisible() ? 1 : 0,
transform: builtVisible() ? 'translate3d(0,0,0)' : 'translate3d(0,8px,0)',
}}
>
<h2 class="about-chapter-two-heading">We wanted to reduce hesitation.</h2>
<p class="about-chapter-two-body">
Not by adding more choices.
<br />
But by designing fewer, stronger ones.
<br />
Nxtgauge is built around one idea:
<br />
Confidence should come before commitment.
</p>
</div>
<aside
class="about-chapter-two-panel"
onMouseMove={(event) => {
if (reduceMotion()) return;
const rect = event.currentTarget.getBoundingClientRect();
const px = (event.clientX - rect.left) / rect.width - 0.5;
const py = (event.clientY - rect.top) / rect.height - 0.5;
setBuiltTilt({ x: -(py * 2), y: px * 2 });
}}
onMouseLeave={() => setBuiltTilt({ x: 0, y: 0 })}
style={{
opacity: builtVisible() ? 1 : 0,
transform: reduceMotion()
? builtVisible()
? 'translate3d(0,0,0)'
: 'translate3d(0,8px,0)'
: builtVisible()
? `perspective(980px) rotateX(${builtTilt().x}deg) rotateY(${builtTilt().y}deg) scale(1)`
: 'perspective(980px) rotateX(0deg) rotateY(0deg) scale(0.95)',
filter: builtVisible() && !reduceMotion() ? 'blur(0px)' : 'blur(3px)',
transitionDelay: builtVisible() ? '150ms' : '0ms',
}}
>
<span class="about-chapter-two-panel-glow" />
<span class="about-chapter-two-reflection" style={{ animation: builtVisible() && !reduceMotion() ? 'chapterTwoSweep 900ms ease-out 1' : 'none' }} />
<div class="about-chapter-two-panel-inner">
<p class="about-chapter-two-panel-label">HOW IT SHOWS UP</p>
<h3 class="about-chapter-two-panel-title">
Before you decide,
<br />
you see signals.
</h3>
<span class="about-chapter-two-divider" />
<div class="about-chapter-two-rows">
<For each={chapterTwoRows}>
{(row, idx) => (
<div
class="about-chapter-two-row"
style={{
opacity: builtVisible() ? 1 : 0,
transform: builtVisible() ? 'translate3d(0,0,0)' : 'translate3d(0,8px,0)',
'transition-delay': builtVisible() ? `${200 + idx() * 100}ms` : '0ms',
}}
>
<span class="about-chapter-two-row-dot" />
{row}
</div>
)}
</For>
</div>
<p class="about-chapter-two-closing">Clarity before commitment.</p>
</div>
</aside>
</div>
</article>
</div>
</section>
<section
id="chapter-trust"
ref={chapterTrustRef}
class="about-section-tight"
>
<div class="container">
<article class={`about-glass-dark about-trust-shell about-reveal-init ${trustVisible() ? 'about-reveal-show' : ''}`}>
<p class="about-chapter-label about-chapter-label-light">Chapter 03</p>
<h2 class="about-chapter-title about-chapter-title-dark">The Trust Layer</h2>
<p class="about-trust-sub">Most reviews complete within 24-48 hours.</p>
<div class="about-trust-sequence">
<div class="about-trust-sequence-list">
<For each={trustSequence}>
{(step, idx) => (
<article
class="about-trust-sequence-card"
style={{
opacity: effectiveTrustProgress() >= (idx() / trustSequence.length) * 0.85 ? 1 : 0.3,
transform: effectiveTrustProgress() >= (idx() / trustSequence.length) * 0.85 ? 'translate3d(0,0,0)' : 'translate3d(0,10px,0)',
}}
>
<div class="about-trust-sequence-row">
<span class="about-trust-sequence-icon">
<span class="about-trust-sequence-dot" />
</span>
<div>
<p class="about-trust-num">{step.num}</p>
<h3>{step.title}</h3>
<p>{step.body}</p>
</div>
</div>
</article>
)}
</For>
</div>
</div>
</article>
</div>
</section>
<section
id="chapter-principles"
ref={chapterPrinciplesRef}
class="about-section-tight about-principles-section"
>
<div class="container">
<article class={`about-glass-dark about-trust-shell about-principle-narrative-section about-reveal-init ${principlesVisible() ? 'about-reveal-show' : ''}`}>
<p class="about-chapter-label about-chapter-label-light">Chapter 04</p>
<h2 class="about-chapter-title about-chapter-title-dark">Principles</h2>
<div class="about-narrative-stage-root">
<Show
when={!reduceMotion()}
fallback={
<div class="about-narrative-stack">
<p class="about-narrative-headline about-narrative-item-active">We didnt build another marketplace.</p>
<div>
<p class="about-narrative-headline about-narrative-item-active">We built a <span class="about-orange-word">filter</span>.</p>
<span class="about-filter-underline-static" />
</div>
<div>
<p class="about-narrative-headline about-narrative-item-active">
A review layer.
<span class="about-principles-subline-inline">Profiles. Jobs. Requirements.</span>
</p>
<span class="about-review-line-static" />
</div>
<p class="about-narrative-headline about-narrative-item-active">Clarity replaces noise.</p>
</div>
}
>
<div class="about-narrative-viewport">
<span
class="about-narrative-glow"
style={{
opacity: chapterFourBackdropGlow() * 0.78,
}}
/>
<div class="about-narrative-stack">
<For each={chapterFourNarrative}>
{(line, idx) => (
<div
class={principleStage() === idx() ? 'about-narrative-item-active' : 'about-narrative-item-inactive'}
style={{
opacity: chapterFourMotion(idx()).opacity,
transform: `translate3d(0, ${chapterFourMotion(idx()).y}px, 0)`,
'text-shadow': chapterFourMotion(idx()).glow > 0
? `0 0 ${chapterFourMotion(idx()).glow}px rgba(253, 98, 22, 0.34)`
: 'none',
}}
>
<p class="about-narrative-headline">
{idx() === 1 ? (
<>
We built a <span class="about-orange-word">filter</span>.
</>
) : (
line
)}
</p>
<Show when={idx() === 1}>
<span class="about-filter-underline" style={{ transform: `scaleX(${stateTwoUnderline()})` }} />
</Show>
<Show when={idx() === 2}>
<>
<span class="about-principles-subline-inline">Profiles. Jobs. Requirements.</span>
<span class="about-review-line" style={{ transform: `scaleX(${stateThreeLine()})` }} />
</>
</Show>
</div>
)}
</For>
</div>
</div>
</Show>
</div>
</article>
</div>
</section>
<section
id="chapter-timeline"
ref={chapterTimelineRef}
class="about-section-tight about-timeline-section-tight"
>
<div class="container">
<div class={`about-glass-light about-timeline-mask-init ${timelineVisible() ? 'about-timeline-mask-show' : ''}`}>
<p class="about-kicker about-kicker-orange">Chapter 05</p>
<h2 class="about-section-title-light">Timeline</h2>
<div class="about-timeline-wrap">
<span class="about-timeline-spine-glow" />
<For each={milestones}>
{(milestone, index) => (
<article
class={`about-timeline-milestone ${timelineVisible() ? 'about-timeline-milestone-visible' : ''}`}
style={{ 'transition-delay': `${index() * 90}ms` }}
>
<span class="about-timeline-index">{index() + 1}</span>
<h3>{milestone.title}</h3>
<p>{milestone.body}</p>
</article>
)}
</For>
</div>
</div>
</div>
</section>
<section ref={closingRef} class="about-section-mid">
<div class="container">
<div class={`about-glass-dark about-closing-card about-reveal-init ${closingVisible() ? 'about-reveal-show' : ''}`}>
<h2>Have a question or want to partner?</h2>
<div class="hero-actions">
<A class="lp-primary-btn" href="/contact">Contact us</A>
<A class="lp-ghost-btn lp-ghost-btn-dark" href="/">Back to home</A>
</div>
</div>
</div>
</section>
<Show when={showBackToTop()}>
<button class="back-top" onClick={() => window.scrollTo({ top: 0, behavior: reduceMotion() ? 'auto' : 'smooth' })}>
</button>
</Show>
<PublicFooter />
</div>
</main>
);
}