Conversation
Three changes to the site-wide navigation, ahead of the cockpit becoming the homepage. Guides is now a split control. The label is a link to the hub and only the arrow opens the list. Compare has no page of its own, so its label can be the opener; Guides does, and making the label open a menu put the hub two clicks from every page on the site. The arrow is a real button with its own accessible name, so the two targets are distinguishable rather than one control that behaves differently depending on where it is clicked. The dropdown's first row was "Start here", pointing at the top of the hub -- which is where the label itself now goes, so the row duplicated its own parent. It is Quickstart instead, going straight to the guide that starts someone off. Glossary moves out of the top level to the foot of that list, and the Entropy Workshop moves the other way, out of the list and onto the bar as "Workshop", because it is a tool people go to directly rather than something to browse for. And the fix underneath: :hover and :focus-within used to open a submenu on their own, alongside .is-open from script. Two sources of truth for one piece of state is what made Guides and Compare overlap -- clicking Guides set .is-open, hovering Compare opened that one too, and the clicked menu then stayed open with the pointer nowhere near it. Only .is-open opens a menu now, and site-refresh.js sets it from pointer, focus and keyboard alike, so whatever opens one closes the others on the way. Hover carries a 160ms close delay, because without it crossing the gap between label and panel closes the menu you are reaching for; touch pointers are excluded, since a tap fires pointerenter and the click would immediately toggle it shut again. A menu also closes when the window loses the pointer and when the layout crosses the desktop breakpoint, which were the other two ways to get one stuck. Measured at 1440: click Guides, hover Compare, and only Compare is open; Escape and an outside click clear both; ArrowDown from the arrow opens the list and moves focus to Quickstart. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The homepage becomes the cockpit: a scene you arrive in, pick a destination from, and fly out of. The rocket launch that introduces it is worth watching once, not on every return, so it is its own route and a session flag decides which of the two a visit lands on. index.html is generated from build/content.mjs like every other root page -- the cockpit container, the arrival frame and the flight video -- and launch.html is registered in the renderer's FILES map so it carries the same generated banner as the rest of the site. It had been a hand-copied shell with a stale nav inside it. assets/js/home-entrance.js is the gate. It is the one script in the head that is neither deferred nor a module, because a redirect issued after the cockpit has painted is a flash of the wrong page. sessionStorage rather than localStorage, so "once" means once per tab: a new window earns the launch again, a reload does not. location.replace rather than assign, so Back from the cockpit leaves the site instead of bouncing through a launch already watched. Reduced motion and a cutscenes-off preference are both checked here, so neither ever loads the launch at all -- no video fetched, nothing to skip. The flag is set either way, so turning cutscenes back on mid-visit does not drop someone into a launch from an ordinary click on Home. Every shipped file now carries a production name. Nothing named review, preview or mockup remains anywhere in docs/, and the superseded first chain -- the older loader, transition script and three entry pages -- is gone rather than left to rot beside its replacement. The arrival is the real guide. docs/guides is deleted and rebuilt from build/guides.mjs on every build, so a preview page could never have survived there; and it did not need to. The galaxy page's article was byte-identical to guides/quickstart.html -- 34,491 characters, the same prose -- so the arrival frame now loads the canonical guide and the journey injects the galaxy skin into it, as it already did for the arrival stylesheet. One copy of the writing, and direct access to the guide keeps working without the flight. Three dependencies came across that no filename search would have found: a stylesheet injected into the arrival frame at runtime, a module reached only through a dynamic import, and two orbit-menu images assembled from a bare filename and a base path. The first pass missed the injected ones because their paths carry a ?v= query inside the string literal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two gaps from the integration. The Quickstart guide now carries the galaxy arrival itself -- the nebula behind the title, the staggered reveal of the heading and opening lines, and a control to watch it again. It is generated onto that one guide by build/render.mjs rather than injected by the journey, for two reasons. The reveal has to survive Cutscenes being switched off, because that switch turns off travel and not the arrival; and the guide has to look the same reached cold from a search result as it does at the end of a flight. The journey still injects its own arrival stylesheet over the top for the blend out of the video, which genuinely does belong to the transition rather than to the page. The paragraph the cloud fade aligns to gets its anchor in build/guides.mjs, where the prose lives. And the banner's Home link now names the cockpit outright. The gate on index.html had only the launch-seen flag to reason from, which is fine for someone who came in through the front door and wrong for everyone else: a reader who followed a search result to a guide has never set that flag, so their first click on Home would have played the launch -- precisely the thing that link is supposed to skip. Home and the brand mark now carry an explicit destination, the gate honours it before it consults anything else, and the fragment is cleared from the address bar afterwards so the cockpit keeps one canonical URL. Verified by routing, with the session cleared between each: a fresh arrival at the root still gets the launch; the same arrival with cutscenes off goes straight to the cockpit and fetches no video; and a reader who lands on a guide first and then clicks Home reaches the cockpit without a launch. Every dependency of the four production routes resolves, including the three kinds that no filename search finds -- injected at runtime, reached by dynamic import, or assembled from a bare filename. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three faults found in the rendered build. The text entrance never played on a direct visit. The reveal only had two triggers -- the Replay button and the journey -- so a reader who reached the Quickstart guide from a search result or the Guides menu got the finished state with no entrance at all. The page starts it itself now, and skips doing so inside the arrival frame, where the parent owns the timing. Deliberately not gated on the cutscenes switch: that switch turns off travel, and the title and text entrance belongs to the guide rather than to the journey. Reduced motion is the one thing that suppresses it. The cutscenes control was missing from the standalone guide, so the preference could only be changed from a page with a flight on it. It ships with the guide now, reading the same stored value, and hides itself inside the arrival frame so there is never a second copy sitting on top of the first. And the flight video -- 15MB -- was fetched the moment the cockpit loaded, before anyone had asked to fly and whether or not the journey was ever going to play it. Someone with reduced motion set, or cutscenes switched off, paid for a download of the thing they had already said they did not want. Nothing is fetched now until travel is both permitted and wanted; flip the switch back on and the fetch happens then. Two things had to move for that to be safe. cockpitReady() waited on a frame traced from the video's first frame, so deferring the fetch would have left the cockpit permanently mid-arrival -- it now treats the cockpit as ready when there is no departure to register. And starting the flight fell through to a twelve-second wait for a video that is no longer coming, so choosing to skip meant staring at "Preparing the sequence"; that early-out covers the switch as well as reduced motion. The preference is read from storage rather than through the helper window.cutscenesEnabled. Both scripts are deferred and the journey is earlier in the document, so the helper does not exist yet when it first runs -- and `undefined !== false` reads as "allowed", which is how a switched-off visit still fetched the video on the first attempt at this. Measured on the running build: cutscenes off, zero .mp4 requests and no source assigned, cockpit still arrives; toggled on mid-visit, the fetch happens and the video attaches; direct guide visit, the reveal runs from ~180ms to ~1430ms and the control is present; inside the arrival frame, one toggle rather than two and no second reveal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A direct visit was running the old replay effect: a black shade wiped off the screen and the whole guide head moved as one block. The approved entrance does neither. It fades the head's rows up in sequence, over clouds and a banner that do not move at all. The difference was that the two effects are driven by different things. The replay is a pair of keyframe animations behind an opaque shade. The arrival is --arrival-title, read by quickstart-galaxy-arrival.css, which offsets each row against the last through --row-start. A direct visit only ever had the first of those stylesheets, so it could only run the first effect. It loads both now and drives the same property over the same 1150ms the journey uses, so the two entrances cannot drift apart -- there is one set of row offsets and one duration, not a copy of each. The replay button runs it too, rather than the wipe it used to. What a direct visit does not take from that stylesheet is the part written for living inside the journey's iframe. There the page is punched transparent and the cloud artwork is suppressed, because the frame behind it already carries clouds and the video supplies the backdrop. Opened on its own this page IS the backdrop, so the charcoal and the clouds come back, and the replay control with them. --arrival-content is pinned at 1 from the start for the same reason: in the flight the banner and body are arriving out of a video, here they are already on screen and stay put. Only the head rows move. Two things found while fixing it. The rows start at zero opacity, so a reveal that never runs leaves the heading and its opening lines invisible -- and it genuinely does not run in a background tab or anywhere the page is not being composited. A timer now acts as a floor, slightly longer than the reveal, so the text ends up on screen whether or not it got to animate on the way. And none of the journey's assets were covered by the version stamp. ASSET_VERSION digests four files, none of them these, and the links added to index.html carried no ?v= at all -- so an edit to the cockpit or the arrival would reach nobody already holding the old copy. It is not hypothetical: a corrected stylesheet sat on disk for several attempts here while the browser went on using its cached one, and the fix read as broken when it was not. Both the digest list and the stamp regex now cover them, kept in step as that file asks. Measured on the running build. Direct visit: shade display:none, clouds visible, banner opacity 1, background #080a0d, replay shown, six rows carrying the flight's own offsets. Inside the arrival frame: no galaxy-direct class, both properties still at 0 awaiting the journey, replay and toggle suppressed, rows holding at 0 -- the flight path untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The guide ships the galaxy arrival stylesheet in its own head, with the build's version on it. The journey then appended a second copy under a hand-written ?v=9 -- the same rules fetched twice, and one of the two pinned to a version that stops moving when the file changes. Edit that stylesheet and the frame could go on being styled by a stale copy the rest of the site had already replaced. It reuses whatever the document already carries now, and only injects when there is nothing to reuse -- the non-galaxy arrival, which has no generated link of its own. The existing link stays where it is rather than being moved last. The order is deliberate: the hero sheet has to follow the arrival sheet because the two collide at equal specificity over the cloud artwork, and on a direct visit the hero's rule is the one that has to win. Appending here would have put the arrival last and hidden the clouds again. One link in the frame carrying the generated version, and the direct visit unchanged: clouds visible, no shade, banner steady. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PR 128 is verified but not yet on main, and the two branches share build/content.mjs, site-refresh.css and site-refresh.js. Merging it here reconciles them now rather than leaving it to be discovered at merge time. All three sources merged without conflict. The only conflicts were generated pages, and only over the asset-version string each branch had stamped into them -- resolved by regenerating from the merged sources, which is the only resolution that can be right for a file nobody edits by hand. What git could not see is that the merge leaves the homepage half of every vortex rule pointing at a hero that is no longer there. That is the next commit. Once 128 lands on main, this branch's diff loses these commits and shows only the journey.
The vortex rules were written as two-page groups when the hero moved to Exchanges, on the understanding that this branch would drop the homepage half once the cockpit replaced that hero. The cockpit has replaced it: index.html renders no .sc-hero at all, so every selector naming the home page beside a hero selector now describes an element that is not there. 37 dead selectors gone, and 17 rules that had nothing left once their home selector was removed. All 37 of the Exchanges vortex rules stay. The risk in this is doing damage to the hero that is still in use, so Exchanges was measured against the figures recorded when that hero was built. Hero box, minimum height, padding, background, figure box and transform, video box and fit, copy box and offset, headline size, action row, the 100px overlap and the media control's position are identical at 1440. At 375 the actions are still stacked at 50px with 30px gaps and no overflow. Nothing that is still rendered changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Everything below 900px shared one composition, built for a phone and
positioned off --cabin-panel: the intro at .1963 of it, the menu at
.4444, the destination 35vw below it. At max(600px,120vw) that panel is
921px on a 768 tablet and 1012px on a phone held sideways, so both got
the phone arrangement scaled by width -- which is why the menu started
at y=467 on the tablet and the destination sat at y=1190, and why
landscape ran to nearly five screens with its menu below the fold.
Tablet portrait binds the panel to the shorter of a cap and the viewport
instead. The artwork keeps its internal relationships and only the
framing tightens: one value moves and the composition follows, rather
than a second set of positions to keep in step with the first.
Short landscape needs an arrangement, not a smaller one. The copy and
menu take the left half, the destination the right, and the two art
panels split one screen between them -- the lower panel's fixed ratio
alone resolved to ~860px, which is what kept the stage three screens
tall even once the content fitted in the first. Keyed on height, because
that is the condition that matters.
Phone portrait keeps its composition; the destination's visible bottom
was landing nine pixels past the fold, close enough to read as cut off
rather than as something to scroll for.
Measured, before and after, at each size:
tablet portrait menu 467 -> 304, destination bottom 1357 -> 943
inside the first screen, stage 1674 -> 1357
phone landscape menu 514 -> 153 and wholly on screen, destination
1170 -> 70-283, scroll 4.7 screens -> exactly one
phone portrait destination bottom 853 -> 794, inside the fold
desktop 1440 cabin, intro, menu, destination and row heights
unchanged; every new block is max-width:900 or
narrower and cannot reach it
The hamburger was a 28px icon and therefore a 28px target. Padding grows
the box to 44 and an equal negative margin returns that growth to the
layout, so the glyph does not move -- including in the open state, where
`top` positions the margin edge and the two cancel. Tablet landscape
gets the desktop bar with its 40-42px rows and no hover to soften them,
so those lift to 44 under (pointer: coarse) -- the actual condition,
rather than a width that a mouse might also match.
Floating controls move from max(16px, env(...)) to env(...) + 16px. On a
device that reports an inset, max() resolves to the inset itself and
puts the control flat against the boundary it was meant to clear.
And the scene's eight stylesheets join the version digest. They carried
hand-written versions -- v=17, v=sun-finisher-2 -- frozen at whatever
each was when last edited by hand, so a change to any of them reached
nobody already holding the old copy. cockpit-scene.html has no shell and
so is not in FILES, which is why nothing was stamping it; it gets a pass
of its own rather than a shell it does not want.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The homepage becomes the cockpit: a scene you arrive in, choose a destination from, and fly out of. The rocket launch that introduces it plays once per tab. The Quickstart flight lands on the real guide.
Routes
index.htmlbuild/content.mjslike every other root pagelaunch.htmlFILESmapcockpit-scene.htmlguides/quickstart.htmlEvery shipped file carries a production name. Nothing called review, preview or mockup remains anywhere in
docs/, and the superseded first chain is deleted rather than left beside its replacement.Entrance
assets/js/home-entrance.jsdecides which front door a visit lands on. It is the one script in the head that is neither deferred nor a module, because a redirect issued after the cockpit has painted is a flash of the wrong page.sessionStorage, so "once" means once per tab — a new window earns the launch, a reload does not.location.replace, so Back from the cockpit leaves the site instead of bouncing through a launch already watched.Home and the brand mark name the cockpit explicitly rather than relying on that flag. Without it the gate had only "have you seen the launch" to reason from, which is right for someone who came through the front door and wrong for everyone else: a reader who followed a search result to a guide has never set it, so their first click on Home would have played the launch — the one thing that link exists to skip.
The arrival is the real guide
docs/guidesis deleted and rebuilt frombuild/guides.mjson every build, so a preview page could never have survived there. It did not need to: the galaxy page's article was byte-identical toguides/quickstart.html— 34,491 characters, the same prose. The frame loads the canonical guide and the galaxy treatment is generated onto it.A direct visit runs the same entrance as the flight, driven by the same
--arrival-titleover the same 1150ms, so the two cannot drift. What it does not borrow is the part of that stylesheet written for living inside the frame, where the page is punched transparent and the clouds suppressed because the video supplies the backdrop. Opened on its own, this page is the backdrop.Preferences
Reduced motion and the cutscenes switch are separate. Cutscenes Off turns off travel; the guide's title and text entrance is part of the guide and survives it. Reduced motion suppresses the entrance and leaves the finished state.
Neither now costs a download. The 15MB flight video was fetched the moment the cockpit loaded, before anyone had asked to fly — so a reader who had already said they did not want to see it paid for it anyway. Nothing is fetched until travel is both permitted and wanted.
Two things had to move for that to be safe:
cockpitReady()waited on a frame traced from the video's first frame, which would have left the cockpit permanently mid-arrival; and starting the flight fell through to a twelve-second wait for a video that is no longer coming.Navigation
Guides is a split control — the label links to the hub, only the arrow opens the list. Compare has no page of its own so its label can be the opener; Guides does, and making the label open a menu put the hub two clicks from every page on the site.
The dropdown fix underneath:
:hoverand:focus-withinused to open a submenu on their own, alongside.is-openfrom script. Two owners for one piece of state is what made Guides and Compare overlap. Only.is-openopens a menu now, set from pointer, focus and keyboard alike.Dependencies
Traced transitively from the four production routes: 244 files, all resolving. Three kinds would not have been found by searching for filenames — a module reached through a dynamic import, four stylesheets injected into the arrival frame at runtime, and two orbit-menu images assembled from a bare filename and a base path. Shipping on names alone would have sent the arrival out unstyled.
Two assets were deliberately excluded: a 9.7MB video reachable only as a default the page always overrides, and the uncompressed 4K source, superseded by the approved
quickstart-runway-moon-v14-4k-compressed-v1.mp4. Video ships at 34.1MiB total, with both 1080p fallbacks retained.ASSET_VERSIONnow covers the journey's own assets. It digested four files, none of them these, so an edit to the cockpit or the arrival would have reached nobody already holding the old copy — which happened during development, with a corrected stylesheet on disk while the browser used its cached one.Verification
npm run buildand all guards pass, a second build introduces no changes, and the committed tree is what the build produces. Site gate run step for step. The Entropy Workshop job scopes in on the asset-version change and passes; the offline artifact and its digest are untouched.Routing, with the session cleared between each: fresh arrival at the root gets the launch; the same arrival with cutscenes off goes straight to the cockpit and fetches no video; a reader who lands on a guide first and then clicks Home reaches the cockpit without a launch.
Reviewed in a rendering browser: rocket to cockpit, flight to guide, corrected Home navigation, mobile 1080p selection, 4K decode-failure fallback, the cloud handoff with no black gap, the staggered text entrance over stationary clouds and banner, and zero MP4 requests under both opt-outs.
Exchanges was measured against the figures recorded when its hero was built, to prove the shared-stylesheet reconciliation did no damage: identical at 1440 and at 375.
Not in scope
No Entropy Workshop or crypto behaviour.
contact.htmlis untouched — it is a deliberate holding page, and the Get Help content proposed for it is a separate task. Preview and archive cleanup is its own PR, to follow this one.🤖 Generated with Claude Code