Portfolio value: $100,000
Acct: hallowed finance trader
Scroll to draw the curve; the counter climbs with it. Illustrative only.
Portfolio value: $100,000
Acct: hallowed finance trader
The single permitted data moment on the page: a bespoke gold-on-dark alpha curve that draws itself in on scroll, with a HUD-style portfolio counter that counts up in sync. The path draw (stroke-dashoffset) and the counter tween ride the same ScrollTrigger scrub, so the number tracks the line's travel exactly.
Once per page, as a single credibility beat. Never more than one. If you already have a chart, sparkline, or count-up anywhere else, this is not the place for a second.
No real data behind it. No second chart on the same page. No count-up without the draw, no draw without the count-up. No replay button; scroll-scrubbed scenes re-trigger on scroll.
<!-- Requires GSAP: https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/gsap.min.js -->
<style>
:root{
--bg:#0b0a08;--surface:#14110b;--ivory:#efe8d6;--muted:#9c9078;--faint:#5a5446;
--line:#2a2519;--line-faint:rgba(239,232,214,.08);--gold:#c9a24b;--gold-soft:#e7cd8c;
--font-display:'Instrument Serif',ui-serif,Georgia,'Times New Roman',serif;
--font-title:'Libre Caslon Condensed',ui-serif,Georgia,'Times New Roman',serif;
--font-body:'Inter',system-ui,-apple-system,sans-serif;
--font-mono:'JetBrains Mono',ui-monospace,monospace;
--ease:cubic-bezier(.22,1,.36,1);--dur-fast:200ms;--dur-base:400ms;
}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--ivory);font-family:var(--font-body);min-height:100vh;padding:64px 48px}
.intro{max-width:960px;margin:0 auto 48px}
.intro h1{font-family:var(--font-display);font-size:2.5rem;font-weight:400;line-height:1.1;color:var(--ivory)}
.intro p{margin-top:16px;color:var(--muted);font-size:.9375rem;line-height:1.65;max-width:60ch}
.spacer{min-height:28vh}
.stage{max-width:960px;margin:0 auto}
.alpha-fig{
border-radius:1.5rem;
border:1px solid var(--line);
background:rgba(20,17,11,.4);
padding:2.5rem;
overflow:hidden;
}
.alpha-head{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem;flex-wrap:wrap;margin-bottom:1.25rem}
.alpha-head figcaption,
.alpha-head .pv{
font-family:var(--font-mono);font-size:.7rem;text-transform:uppercase;
letter-spacing:.3em;color:var(--faint);
}
.alpha-head .pv{white-space:nowrap}
.alpha-head .pv b{font-weight:500;font-variant-numeric:tabular-nums;letter-spacing:normal;color:var(--gold-soft)}
.alpha-fig svg{width:100%;height:auto;display:block}
.alpha-foot{margin-top:1rem;font-family:var(--font-mono);font-size:.7rem;text-transform:uppercase;letter-spacing:.3em;color:var(--faint)}
</style>
<header class="intro">
<h1>The one allowed data moment.</h1>
<p>Scroll to draw the curve; the counter climbs with it. Illustrative only.</p>
</header>
<div class="spacer" aria-hidden="true"></div>
<main class="stage">
<figure class="alpha-fig">
<div class="alpha-head">
<figcaption>Cumulative alpha · illustrative</figcaption>
<p class="pv">Portfolio value: <b id="alpha-value">$100,000</b></p>
</div>
<svg viewBox="0 0 1200 280" role="img" aria-label="An upward-trending cumulative alpha curve, illustrative">
<defs>
<linearGradient id="hf-alpha-stroke" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#c9a24b" stop-opacity="0.25"></stop>
<stop offset="100%" stop-color="#e7cd8c"></stop>
</linearGradient>
</defs>
<line x1="0" y1="252" x2="1200" y2="252" stroke="#2a2519" stroke-width="1"></line>
<path id="alpha-path" d="M0,250 C150,250 180,212 260,206 C340,200 360,172 460,152 C560,132 580,150 660,122 C760,84 800,112 900,82 C1000,56 1050,42 1200,22" fill="none" stroke="url(#hf-alpha-stroke)" stroke-width="2.5" stroke-linecap="round"></path>
</svg>
<p class="alpha-foot">Acct: hallowed finance trader</p>
</figure>
</main>
<div class="spacer" aria-hidden="true"></div>
<script>
gsap.registerPlugin(ScrollTrigger);
const START_VALUE = 100000, END_VALUE = 1000000;
const formatUsd = v => `$${Math.round(v).toLocaleString('en-US')}`;
const path = document.getElementById('alpha-path');
const valueEl = document.getElementById('alpha-value');
const len = path.getTotalLength();
path.style.strokeDasharray = len;
gsap.matchMedia()
.add('(prefers-reduced-motion: no-preference)', () => {
path.style.strokeDashoffset = len;
valueEl.textContent = formatUsd(START_VALUE);
const counter = { v: START_VALUE };
const tl = gsap.timeline({
defaults: { ease: 'none' },
scrollTrigger: { trigger: path, start: 'top 88%', end: 'bottom 62%', scrub: true },
});
tl.to(path, { strokeDashoffset: 0 }, 0);
tl.to(counter, {
v: END_VALUE,
onUpdate: () => { valueEl.textContent = formatUsd(counter.v); },
}, 0);
return () => tl.scrollTrigger && tl.scrollTrigger.kill();
})
.add('(prefers-reduced-motion: reduce)', () => {
path.style.strokeDashoffset = 0;
valueEl.textContent = formatUsd(END_VALUE);
});
</script>