the brief
one strategy, three genuinely different directions.
the visitor is an invited, curious friend, or a stranger who saw a poster. the job never changed across the three builds: make them feel what owning a compounding assistant is like, then send them to one place, request access. that was the whole strategy.
from it, three directions were drawn, with no shared components between them, so each could argue for itself on its own terms. each got two critique passes and a live preview route for the verdict.
the sitemap: a single marketing page at /, plus /privacy, /terms, and this /guide. the call to action points at the existing access gate, the one conversion endpoint the whole page bends toward.
the pick
the birth as the landing act. the ledger as the organized scroll.
the verdict was not one of the three. it was a fusion: the birth as the landing act and an evolving background, the ledger as the organized scroll-reveal, fused into one continuous piece. proof was set aside for the marketing page and its rigor kept for the owner console.
so the production page carries one fixed canvas orb as its whole spine. it is born in the hero, settles into the left margin beside the time-rail and dims to a quiet working presence, gains one ring per day-moment as each ledger hour crosses the read-line, then blooms fully alive at the close. no section boundary resets it. one eased state machine, birth to core to rings to skills to alive, runs off a single scroll. the birth animation and the ledger read as one mind living one day.
the techniques
four moves, in their own code.
short excerpts from the live page and console, lightly trimmed. every one of these ships today at genesis.moadh.com.
the scroll-bound orb, as a state machine
from / · one eased state, driven by scroll, drawn on a 2D canvas
// scroll drives one eased state machine: birth -> rings -> skills -> alive
var st = {birth:0.24, rings:0, skills:0, alive:0, core:1, ocx:0, ocy:0};
function readScroll(){
var vh = window.innerHeight;
tgt.birth = clamp(0.24 + window.scrollY/(vh*0.72), 0.24, 1);
// one ring per day-moment whose centre has risen past the read line
var count = 0;
for(var i=0; i<actEls.length; i++){
var r = actEls[i].getBoundingClientRect();
if(r.top + r.height/2 < vh*0.56) count++;
}
tgt.rings = count;
}
// every frame eases the live state toward the target (ease-out, no bounce)
function frame(){
st.birth += (tgt.birth - st.birth)*0.08;
st.rings += (tgt.rings - st.rings)*0.07;
st.alive += (tgt.alive - st.alive)*0.06;
draw(); requestAnimationFrame(frame);
}
the sticky time-rail, relabelling itself
from / · an observer ticks the clock and swaps the rail label per act
function setActive(i){
if(i===active || i<0 || i>=actEls.length) return;
active = i; var a = actEls[i];
rollClock(a.getAttribute('data-time')); // 07:00, 09:30, 13:00 ...
periodEl.textContent = a.getAttribute('data-period');
// the rail relabels itself when the story leaves the day for the week
railLabel.textContent = (i>=4) ? 'the week around it' : 'the day so far';
ticks.forEach(function(tk,idx){
tk.classList.toggle('is-past', idx<i);
tk.classList.toggle('is-now', idx===i);
});
}
one domain, guarded in every page head
in every HTML head · humans move to the canonical host, machine endpoints never emit this script
<!-- in the <head> of every HTML page, never in a machine response -->
<script>if(location.hostname==='mb-genesis.web.app'){
location.replace('https://genesis.moadh.com'
+ location.pathname + location.search + location.hash);
}</script>
<link rel="canonical" href="https://genesis.moadh.com/">
honest accounting, when you forget
from /console.js · the panel only claims to forget what the server actually confirmed
var jobs = ids.map(function(id){
return post(EP.forget, {instanceId:S.id, entryId:id})
.then(function(r){ return {id:id, ok:!!(r && r.ok)}; })
.catch(function(){ return {id:id, ok:false}; });
});
Promise.all(jobs).then(function(results){
var done={}, okN=0, failN=0;
results.forEach(function(res){ if(res.ok){ done[res.id]=1; okN++; } else failN++; });
// only drop what the server actually confirmed forgotten
S.mem = (S.mem||[]).filter(function(x){ return !done[x.id]; });
if(okN && !failN) toast(okN+" forgotten.");
else if(okN && failN) toast(okN+" forgotten, "+failN+" didn't confirm. they remain visible.");
else toast("couldn't forget right now. nothing was removed.");
});
the passes
every pass found something. here is what each one changed.
nothing here is invented. each line is drawn from that pass's own log: the finding, then the fix.
birth · pass 1the spark under-read
the hero orb was too weak, the scroll cue sat below the fold, and memory blocks collided with the nav. changed: raised the birth floor and tightened the core, pulled the hero to one viewport, faded inactive memories. added filmic grain.
birth · pass 2a warm cast at the core
the wordmark had a gap before the comma and the additive core read faintly rose. changed: closed the gap, cooled the two brightest core layers. added synapse spokes so the brain reads as a connected mind, not just orbiting dots.
days · pass 1the rail would not stick
em dashes in the title and meta, trailing null bytes after the closing tag, and a clock that scrolled away instead of pinning. changed: replaced the dashes, stripped the bytes. added auto-numbered ledger folios via a CSS counter.
days · pass 2the real cause of the sticky bug
the first fix was a misdiagnosis. the true cause: the rail cell had no track to travel because it was sized to its content. changed: stretched the cell so the clock pins. added a quiet overnight interlude before the week's edges.
proof · pass 1a dead right half
the hero read like a generic product hero, the briefing bubble reserved full height and sat empty mid-type, and twelve console errors came from font requests failing under a file path. changed: moved the height-lock to the screen container, added an instrument-panel orb, rendered over local http.
proof · pass 2the typer was crawling
the caret read chunky on mobile and a redundant frame wrapper floored the typer at one character per frame. changed: slimmed the caret, dropped the wrapper. added an honest window-footer that tracks the live state while it types.
production · pass 1the hot pinpoint
a warm pinpoint sat at the orb centre and the resting hero spark under-read. changed: cooled the core layers and shrank the pinpoint, lifted the birth floor. added active-ring sync so the lit ring pulses in time with the rail clock.
production · pass 2a pink fringe
the additive stack was clipping to white and throwing a faint pink fringe at the glow centre. changed: lowered the near-white peak so the centre never clips. added a scroll-driven vignette that deepens through the ledger and lifts as the orb blooms.
s9 · directora real 404, and a grey ghost
unknown paths were quietly serving the marketing page with a 200. a scrollcue span collided with the ledger rail class and inflated into a grey box on mobile. the rail label read the same in both acts. changed: removed the catch-all rewrite so a 404 is a real 404, scoped the colliding class, and split the label into the day so far and the week around it. a suspected red pixel at the orb nucleus was scanned twice and ruled a screenshot artifact, not a defect.
s10 · qa gatescontrast, lifted
five gates: anti-slop, accessibility, performance, integrity, content. two dim greys measured 4.20 to 1, below the 4.5 floor. changed: raised a single alpha token and the footer opacity to reach 5.10 to 1, no hue or layout change. everything else passed as shipped: LCP 1.54s, CLS under 0.005, zero em dashes, the real 404 confirmed live.
s12 · consolehonest when it forgets
the owner console converged onto the canon tokens, cyan and Inter replacing the app's older drift, and the memory panel learned to forget honestly. changed: a bulk forget now drops only what the server confirms, and says plainly when some entries remain visible. that is the code shown above.
provenance
nothing on this site was generated.
every mark is procedural or typographic. no stock photography, no generated imagery, no illustration library. the orb is math on a canvas, the rings are arcs, the glows and grain are CSS. this guide holds to the same rule: a radial orb, hairlines, and type.
the orba 2D canvas drawn frame by frame, vanilla requestAnimationFrame, no libraries.
the faviconan inline SVG data-URI: the orb reduced to two circles and a radial fill.
the typeSpace Grotesk and Inter, latin subsets, self-hosted at /fonts/, about 70KB together, preloaded.
the og imagea procedural screenshot of the built hero, quantized to about 96KB. the page photographing itself.
the palettefive values only, tinted neutrals, never pure black or white, cyan kept under a tenth of the surface.
the archive
the three directions are still standing.
each variant lives on at its own preview route, kept out of the index but open to anyone with the link. the roads not taken, still walkable.