Merge the product tour into the homepage - #12
Merged
Conversation
The tour was a page of its own that most visitors never reached, so the homepage described the product only in the abstract. Fold it into the root page and retire /product. The section markup moves over unchanged inside a .product-page wrapper: every callout-marker coordinate in style/product.css is a custom property scoped to that class, so the whole stylesheet applies on the homepage untouched. - One prompt iframe per page. src/index.ts binds it by id, so the tour's closing CTA (and its content section) would have been a dead duplicate. - Hero tilt is bound once. Both source pages registered their own mousemove handler for [data-tilt-hero]; merged naively that was two handlers writing conflicting transforms to the same element. - The benefit row is compact: 882px -> 392px at desktop. Tilt and the glare layer are gone - at a quarter size the tilt read as jitter and the glare as a smudge. Scoped as a modifier so showcase.liquid keeps the full-size cards. - Nav scroll-spies the tour instead of matching a page, and takes over its own anchor clicks. "/#product" and "/" are each a different document from "/index.html", so the browser reloaded the whole page before landing. umami.js compounded it: for any <a data-umami-event> it preventDefaults, awaits its tracking request, then assigns location.href itself. Those two links now report their events directly and keep the navigation in-page. - A video slot sits under the hero, currently hidden behind a flag while the video is produced. Attaching a real video turns it on regardless of the flag. - Fixes a pre-existing horizontal scroll: md:w-[1200px] on the manifesto CTA forced 1200px at any viewport >= 768px. Claude-Session: https://claude.ai/code/session_01PJBsFWSPdZvjcWsFwJh587
Two peer-weight buttons under one prompt box split the primary action. The prompt and Generate are the main path; the terminal route is the alternate, so give it the outlined treatment the manifesto link already uses rather than a second primary-cta. The shared box - font, padding, hairline border, radius, centred fit-content, hover brighten - lifts out of .manifesto-cta so both are the same component rather than one copying the other's numbers. What stays specific to the manifesto link is its block display and the padding-left that clears its background-image icon; the launcher gets flex with a 1rem gap, which reproduces the same icon-to-label spacing a different way. Still a <button> with the same id: it opens a dialog rather than navigating, and src/build-with-claude.ts binds it by id, so none of its behaviour moves. The "or" separator only earns its place between two buttons, so it goes. Uses Anthropic's official Spark mark at 24px to match icon-docs.svg, left in its own clay rather than recoloured to the label. Claude-Session: https://claude.ai/code/session_01PJBsFWSPdZvjcWsFwJh587
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 product tour was a page of its own that most visitors never reached, so the homepage described the product only in the abstract. This folds it into the root page and retires
/product.Page order is now: hero → video → prompt CTA → the four tour sections → compact benefits → manifesto → connect.
The tour
Section markup moves over unchanged inside a
.product-pagewrapper. That class is load-bearing: every callout-marker coordinate instyle/product.cssis a custom property scoped to it, so the whole stylesheet applies on the homepage untouched.src/index.tsbinds it by id, so the tour's closing CTA (and its content section inobjects/product.toml) would have been a dead duplicate.mousemovehandler for[data-tilt-hero]; merged naively that was two handlers writing conflicting transforms to the same element./product301s to/#productviapublic/_redirects, so inbound links and the old nav target still land. Cloudflare Pages picks it up the same way it already picks uppublic/_headers.Compact benefit row
All four kept, but 882px → 392px at desktop (1016 → 501 tablet, 1830 → 1023 mobile). Tilt and the glare layer are gone — at a quarter size the tilt read as jitter and the glare as a smudge. Scoped as a modifier because
showcase.liquidstill uses the full-size cards. Columns verified 4 / 2 / 1 at the 1101 / 641 breakpoints.Nav
Producthas no page of its own to match on, so it scroll-spies the tour instead — lit only while the tour crosses the viewport's centre band. Measured: on at scrollY ≈1400, off at ≈4100, exactly one on/off pair across the page.It also takes over its own anchor clicks, as does the logo. Both were doing a full document reload:
/#productand/are each a different document from/index.html, so the browser reloaded before landing.umami.jscompounded it — for any<a data-umami-event>it callspreventDefaultitself, awaits its tracking request, then assignslocation.href, which no handler can cancel. Those two links now report their events directly and keep the navigation in-page. Verified: 0 reloads, movement starts immediately, eased scroll settles ~400ms,prefers-reduced-motionjumps instantly, modified clicks fall through, and 21 other tracked anchors are untouched.Video slot
Sits under the hero, currently hidden behind
show_poster_placeholder = falsewhile the video is produced. Attaching a real video turns the section on regardless of that flag, so nothing needs remembering when it lands.Claude Code CTA
Merged with
origin/main's new in-page flow (Turnstile →claude-cli://→ modal) and restyled the trigger from a secondprimary-ctainto the outlined treatment the manifesto link uses — two peer-weight buttons under one prompt box split the primary action. The shared box lifts onto a shared selector so both are one component; both render at 58px with identical border, radius, padding and font, and the manifesto link's icon and padding are unchanged. Still a<button>with the same id, sobuild-with-claude.tsbinds it exactly as before.Uses Anthropic's official Spark mark at 24px, left in its own clay rather than recoloured to the orange label — worth a second opinion if you'd rather it match.
Also fixed
A pre-existing horizontal scroll on the homepage:
md:w-[1200px]on the manifesto CTA wrapper forced 1200px at any viewport ≥768px. No overflow now at 390/430/500/640/700/768/900/1024/1200/1440.Verification note
Behaviour was verified by driving real Chrome over CDP rather than headless screenshots — headless under
--virtual-time-budgetdoes not deliver scroll-drivenIntersectionObservercallbacks at all, which silently made the scroll-spy look broken when it wasn't.https://claude.ai/code/session_01PJBsFWSPdZvjcWsFwJh587