Skip to content

The marketing landing page - #33

Merged
ARCoder181105 merged 9 commits into
mainfrom
landing-page
Aug 28, 2026
Merged

The marketing landing page#33
ARCoder181105 merged 9 commits into
mainfrom
landing-page

Conversation

@ARCoder181105

@ARCoder181105 ARCoder181105 commented Aug 28, 2026

Copy link
Copy Markdown
Owner

docs/UI_GUIDE.md §3.1 has specified a marketing landing page since Phase 3b and named it the
branch after that gate. Phases 4 and 5 went first, so it was overdue rather than upcoming. This is
it, plus the routing it needed and a palette change it turned into.

What is here

Routing. There was no router. / is the landing page and /app is the canvas; anything else
falls to the landing page and there is no 404. lib/router.tsx is about thirty lines over
pushState rather than a router library — two static routes, no parameters, no loaders, and
TanStack Query and Zustand already own all the state a router's data layer would want. The part
that gets hand-rolled routers wrong is Link, so it claims only a plain left click and the test
holds Meta down to prove it.

useSession moved behind /app. That is the point of the split rather than a side effect: the
landing page issues no request to our API and renders with the backend down.
The OAuth callback
now redirects to APP_ROUTE under WEB_APP_URL instead of the bare origin, which was about to
sign people in and then show them the pitch for the product they had just signed in to.

The hero is a live drawing graph, not a screenshot. Plain SVG rather than React Flow, which
wants a measured container and brings the node.width trap from CANVAS_DECISIONS.md §4. The draw
is a mask sweeping across the edge layer — not an animated pathLength, which writes an inline
stroke-dasharray and flattens solid, dashed and dotted into one pattern. HeroGraph.test.tsx
asserts three distinct dash values for exactly that reason. The graph leads with a ghost node: an
unresolved callee at the map's edge, labelled with the name the parser saw. Leading a marketing
hero with what the tool cannot do is deliberate, because that admission is the product.

The page's structural device is the product's notation. Every section rule is a confidence
tier's dash pattern, and each section takes the tier that is true of it: solid over resolution and
the pipeline, dashed over coverage because support past the ECMAScript family genuinely is partial,
dotted over the limits. A reader has met the notation three times before reaching the canvas.
ConfidenceRule draws it and the legend was refactored onto the same component.

One colour rule: no colour appears that does not carry its canvas meaning. No gradient mesh, no
glow, no glass, no bento grid, no sticky bar and no floating pill — §1.3 and §7.

Installed, not written. Three animate-ui components through the shadcn CLI (effects/fade,
texts/sliding-number, components-base-files) and lenis for smooth scrolling, mounted from
Landing alone because at the app root it would take the wheel away from the canvas, where the
wheel means zoom. Every generated file was edited and says so at the top, because add --overwrite
reverts it silently: they ship importing motion/react and @base-ui-components/react, second
copies of the framer-motion and @base-ui/react this repo locks, and the files component's git
status slot carried hardcoded green, amber and red that became a function count instead.

The palette changed, product-wide

Ember/Vellum is gone. It passed both palette rules and read clearly; it goes because cyan on
near-black is the accent every developer tool already uses, which §7 exists to keep us off.

Ultramarine and Letterpress are a two-colour press: two spot inks and a neutral. Ultramarine
is the fact, fired clay is the report, ash is what neither plate covered. The structure is
untouched — three tiers, one accent doing double duty, unresolved quiet and achromatic.
confidence.test.ts is what says the rules still hold, not this description: unresolved is ash at
10–11% saturation nowhere near the red band, and the tightest contrast in either theme is 4.03:1
against a 3:1 floor.

Verified

make test (127 api / 170 web / Go against Postgres), make lint, make typecheck, make go-vet
— all clean. Both taste greps empty: no hex in any .tsx, no blur/gradient/glow in the landing
directory outside comments saying we do not use them.

By hand in real Chrome: the whole page in both themes at desktop width, the finished graph with all
three edge styles distinguishable, the index tree, and /app still resolving its session.

Not verified, and I would rather say so

  • The draw animation. requestAnimationFrame ran at about one frame per half-second in the
    browser window available to me, so the sweep and the node stagger never advanced. I inspected the
    finished state by pinning it. The animation needs your eyes.
  • Mobile and tablet. resize_window was ignored by that environment. The breakpoints are
    written and there is no horizontal overflow at the width I could reach, but nobody has seen the
    page at 375px.

Notes

The landing page does not advertise docker compose up, because it does not work yet. That
line ships with NFR-4, which is the next branch: apps/web has no Dockerfile, there is no worker
service so a webhook enqueues a job nothing consumes, the API image runs node dist/index.js
against packages/shared exports pointing at .ts, and the parser service sets both
network_mode: none and depends_on Postgres health.

SessionUnavailable was rewritten for the reader who actually sees it. Once the web app can be
deployed on its own — likely, since the API spawns the Go parser and needs writable disk, so it
cannot go anywhere serverless — a stranger following a link reaches that screen and cannot run
pnpm anything.

Summary by CodeRabbit

  • New Features

    • Added a public landing page with hero graph preview, workflow overview, supported-language details, confidence tiers, repository index, GitHub link, and calls to action.
    • Added client-side navigation between the landing page and application workspace.
    • Added animated accordions, file trees, highlights, fades, and number displays.
    • Added accessible confidence indicators and unresolved-call labeling.
    • Added GitHub star count display and smooth scrolling.
  • Bug Fixes

    • OAuth sign-in now returns directly to the application workspace.
    • Expanded guidance when the API is unavailable.
  • Documentation

    • Updated UI guidance for new routing, palettes, landing page, and mobile behavior.

The app has one surface today and branches on the session to pick it. A
marketing landing page is a second surface that has to be reachable while
signed in, so something has to choose between them.

Two static routes, no parameters, no nesting and no data loading: TanStack
Query already owns everything from the server and Zustand owns the canvas, so
a router library would arrive with a data layer that has nothing to do and a
configuration about as long as this file.

`Link` is the part worth care. A hand-rolled one usually calls preventDefault
unconditionally and quietly breaks modifier-click, so it claims only a plain
left click and leaves the rest to the browser. The test holds Meta down to
pin that, using `userEvent.setup()` -- a bare `userEvent.click` is a fresh
instance that forgets the held key, and the assertion passes without testing
anything.

APP_ROUTE goes in the shared package because the API redirects there too.
The landing page's hero is a call graph drawing itself, not a screenshot of
one (UI_GUIDE §3.1). Plain SVG rather than React Flow: React Flow wants a
measured container and brings pan, zoom and handles a hero has no use for,
along with the node.width trap in docs/CANVAS_DECISIONS.md §4. A fixed
viewBox scales on its own and cannot drop an edge in silence.

The draw is a mask sweeping across the edge layer. Framer's `pathLength`
writes an inline stroke-dasharray, which overwrites the pattern that
distinguishes solid from dashed from dotted -- all three come out identical,
which is the one picture this product must never show (PRD §8). The mask
leaves every path's own attributes alone. It also translates rather than
resizes, so the animated property is a transform.

The graph is laid out by depth and the sweep runs left to right, so the sweep
is the stagger: one animated element for the whole orchestrated moment.

The fixture carries a ghost node -- an unresolved callee at the edge of the
map, dotted, faded and labelled with the name the parser saw. That is
signature 2 from UI_GUIDE §3.2, and leading a hero with what the tool cannot
do is the point rather than an oversight.

`CONFIDENCE` gains `strokeClass` beside `textClass`: a class follows the
theme without the component subscribing to it, and Tailwind cannot see a
class name built by concatenation. `confidenceColor` stays for the canvas,
which needs a raw value for real SVG attributes.
The surface UI_GUIDE §3.1 has specified since Phase 3b and that no phase's
exit test touches. It is the one page that takes the maximal spatial
treatment -- py-24 and above, nested double-bezel cards -- because matching
complexity to the surface is the point, and the canvas stays dense.

The page's structural device is the product's own notation. Each section
heading carries a hairline rule whose dash pattern is a confidence tier, and
each section takes the tier that is true of it: the resolution section is
solid, languages is dashed because support past the ECMAScript family
genuinely is partial, and the closing section is dotted because what it
states are the product's limits. A reader has met the notation three times
before ever reaching the canvas.

Nothing here calls our API. `useSession` moves behind the canvas route in the
next commit, so this page renders whether or not the backend is up -- which
is the least a page whose job is to explain the product can do. The one
outbound request is a GitHub star count, which is not required: the control
degrades to a plain link to the repository, and GitHub rate-limits anonymous
callers hard enough that this is the common case.

Colour rule for the page, in one line: no colour appears that does not carry
its canvas meaning. The accent is the hue that means "known" everywhere else,
apricot appears only on a name match, slate only on unresolved. No gradient
mesh, no glow, no glass -- §1.3 bans them and §7 cluster 2 is precisely what
this would otherwise become.

Installed rather than written: `lenis` for smooth scrolling, mounted from
Landing alone because the canvas treats the wheel as zoom; and two animate-ui
primitives via the shadcn CLI, `effects/fade` for the scroll reveals and
`texts/sliding-number` for the star count. They ship importing `motion/react`,
which is a second copy of Framer Motion beside the `framer-motion` this
project locks, so each generated file is retargeted and says so -- `add
--overwrite` reverts it silently.

The legend's tier line and the section rule were the same drawing twice, so
ConfidenceRule is extracted and the legend now uses it.
An atlas has an index and the file tree is it (UI_GUIDE §3.2), so the page
shows one rather than describing it. The counts are the reason it is worth
showing: a file's weight on this canvas is how many functions it holds, and
that is the number the sidebar puts beside every path. The tree is this
repository's own shape, because a made-up src/components/Button.tsx would say
nothing about a tool built to read a polyglot monorepo.

`components-base-files` installed from animate-ui rather than hand-written,
and then edited, which is the model for a generated file. Three edits:

- The upstream right-hand slot is a git status dot in hardcoded green, amber
  and red. There is no diff to report here and no colour outside tokens.ts,
  so the slot became `meta` and carries the function count.
- The imports were `motion/react` and `@base-ui-components/react`, second
  copies of the framer-motion and @base-ui/react this project locks.
- This project's Base UI is a major ahead of the one it was generated
  against: Accordion's value is `AccordionValue<unknown>` rather than
  `string | string[]`, and onValueChange takes an event-details argument. The
  types are taken from the component rather than restated, so the next Base
  UI upgrade surfaces here as a type error and not as a wrong cast. The one
  place an array is assumed is narrowed, not cast.

Each edited file says so at the top, because `add --overwrite` reverts them
silently.
`App` becomes the route switch and the session branching moves into
`AppRoute` unchanged. `useSession` moves with it, which is the point of the
split rather than a side effect: the landing page now issues no request and
renders with the API down, which is the least a page whose job is to explain
the product can do. Anything that is not `/app` is the landing page; there is
no 404 because there is nothing else to be.

The OAuth callback redirected to `env.WEB_APP_URL`, a bare origin. That is
now the marketing page, so it redirects to APP_ROUTE under it -- signing
someone in and then showing them the pitch for the product they just signed
in to is the failure the test asserts against. `.env` is unchanged:
WEB_APP_URL stays an origin and the path is joined here.

App.test.tsx pushes APP_ROUTE before the session cases, which at jsdom's
default `/` would otherwise be asserting against the marketing page.
Four things, all found by looking at it in a browser rather than at the code.

The viewBox was 560x360 for a drawing 268 units tall, and an SVG scales to fit
its own empty space, so the graph came out small and floated in the middle of
its bezel. The height is now measured off the lowest baseline plus the ghost's
caption plus a margin.

The four surfaces each carried their own max width -- the title block and the
sections at 64rem, the hero at 72rem -- so the wordmark did not line up with
the headline under it, and the whole page read as a column floating in a field
of ground. One shared LANDING_SHELL at 104rem, and the prose inside caps
itself instead.

The headline was sized to wrap to five lines in the column it ended up with.
It lands on three now.

The em dash is gone from the visible copy, here and in the sections.
The scale is a two-colour press: two spot inks and a neutral. A printer with
two plates and paper has exactly three things to say, which is exactly how
many answers resolution has. Ultramarine is the fact, fired clay is the
report, and the ash is what neither plate covered. Read as ink weight rather
than as temperature, so it survives being drawn as a hairline on a canvas the
reader is zoomed out of.

Nothing was wrong with Ember/Vellum. It passed both palette rules, it read
clearly, and it shipped through Phases 3b to 5. It goes because cyan on
near-black is the accent every developer tool already uses, and UI_GUIDE §7
exists to keep this project off the look its whole category has converged on.
The structure it established is untouched: three tiers, one accent doing
double duty as focus and links, and `unresolved` quiet and achromatic.

Both palette rules still hold with margin, and confidence.test.ts is what says
so rather than this message. Unresolved is ash at 10-11% saturation in the
ground's own hue family, nowhere near the red band. The tightest contrast in
either theme is unresolved on light paper at 4.03:1, against a 3:1 floor.

UI_GUIDE §1.1 is rewritten to the new tables and §7.1 records Ember/Vellum as
a direction that shipped and was replaced, not as a mistake.
"Start it with pnpm dev" assumes the only person who reaches this screen is
running the stack and has forgotten a process. Once the web app can be
deployed on its own -- which is a likely outcome, since the API spawns the Go
parser and needs writable disk, so it cannot go anywhere serverless -- the
other reader is a stranger who followed a link, clicked through from the
landing page, and cannot run pnpm anything.

Names what is missing, keeps the local instruction, and offers a way onward in
both directions: the source for someone who wants to run it, and the landing
page for someone who just wants to know what it is.
UI_GUIDE §3.1 said the page was unopened; it has shipped. §3.1a now carries
what it is and what it deliberately is not, including the two traps a later
change would otherwise rediscover: `pathLength` flattening the three tiers,
and Lenis taking the wheel away from the canvas if it is ever mounted at the
app root.

TASKLIST records what was verified in a browser and, separately, what was not:
requestAnimationFrame ran at roughly one frame per half-second in the
available window and resize_window was ignored, so the draw animation and the
mobile breakpoints are unverified rather than working.

NFR-4 is written down as next, with the four faults named.
@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 510b374d-ed22-471c-9a0f-1dd41dfcf8ba

📥 Commits

Reviewing files that changed from the base of the PR and between 4e240ed and d7eb1d0.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (50)
  • TASKLIST.md
  • apps/api/src/auth/routes.test.ts
  • apps/api/src/auth/routes.ts
  • apps/web/components.json
  • apps/web/package.json
  • apps/web/src/App.test.tsx
  • apps/web/src/App.tsx
  • apps/web/src/components/ConfidenceLegend.tsx
  • apps/web/src/components/ConfidenceRule.tsx
  • apps/web/src/components/animate-ui/components/base/files.tsx
  • apps/web/src/components/animate-ui/primitives/animate/slot.tsx
  • apps/web/src/components/animate-ui/primitives/base/accordion.tsx
  • apps/web/src/components/animate-ui/primitives/base/files.tsx
  • apps/web/src/components/animate-ui/primitives/effects/fade.tsx
  • apps/web/src/components/animate-ui/primitives/effects/highlight.tsx
  • apps/web/src/components/animate-ui/primitives/texts/sliding-number.tsx
  • apps/web/src/components/landing/Bezel.tsx
  • apps/web/src/components/landing/ClosingCta.tsx
  • apps/web/src/components/landing/GitHubStars.tsx
  • apps/web/src/components/landing/Hero.tsx
  • apps/web/src/components/landing/HeroGraph.test.tsx
  • apps/web/src/components/landing/HeroGraph.tsx
  • apps/web/src/components/landing/HowItWorks.tsx
  • apps/web/src/components/landing/Index.tsx
  • apps/web/src/components/landing/Landing.test.tsx
  • apps/web/src/components/landing/Landing.tsx
  • apps/web/src/components/landing/LandingFooter.tsx
  • apps/web/src/components/landing/LandingHeader.tsx
  • apps/web/src/components/landing/Languages.tsx
  • apps/web/src/components/landing/OpenAtlas.tsx
  • apps/web/src/components/landing/Reveal.tsx
  • apps/web/src/components/landing/Section.tsx
  • apps/web/src/components/landing/Tiers.tsx
  • apps/web/src/components/landing/shell.ts
  • apps/web/src/hooks/use-controlled-state.tsx
  • apps/web/src/hooks/use-is-in-view.tsx
  • apps/web/src/lib/confidence.ts
  • apps/web/src/lib/constants.ts
  • apps/web/src/lib/get-strict-context.tsx
  • apps/web/src/lib/github.ts
  • apps/web/src/lib/hero-graph.test.ts
  • apps/web/src/lib/hero-graph.ts
  • apps/web/src/lib/motion.ts
  • apps/web/src/lib/router.test.tsx
  • apps/web/src/lib/router.tsx
  • apps/web/src/lib/tokens.ts
  • apps/web/src/lib/useSmoothScroll.ts
  • apps/web/src/test-setup.ts
  • docs/UI_GUIDE.md
  • packages/shared/src/constants.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Changes

The web app now serves a landing page at / and the application at /app. It adds animated landing-page sections, repository graph and file-tree previews, confidence visuals, GitHub star retrieval, updated palettes, and expanded routing and component tests. OAuth callbacks now redirect to /app.

Landing page composition
apps/web/src/components/landing/* | The new landing page combines hero, workflow, index, language, confidence, CTA, header, and footer sections.

Routing and OAuth integration
apps/web/src/App.tsx, apps/web/src/lib/router.tsx, apps/api/src/auth/routes.ts, packages/shared/src/constants.ts | The shared APP_ROUTE constant drives client navigation, application rendering, and OAuth redirects.

Animated component foundation
apps/web/src/components/animate-ui/*, apps/web/src/hooks/* | The web app adds animated slots, fades, highlights, accordions, file trees, sliding numbers, and supporting hooks.

Validation and design updates
apps/web/src/**/*.test.*, apps/web/src/lib/tokens.ts, docs/UI_GUIDE.md, TASKLIST.md | Tests cover routing, landing content, graph rendering, and navigation. Palette and project documentation now describe the shipped landing page.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to d7eb1

This PR adds the marketing landing page, separates / from /app, and updates successful sign-ins to reach the application route; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant Visitor
  participant App
  participant Landing
  participant Router
  participant AppRoute

  Visitor->>App: Open /
  App->>Landing: Render landing page
  Landing->>Landing: Render sections and animations
  Visitor->>Router: Activate OpenAtlas
  Router->>App: Navigate to /app
  App->>AppRoute: Render application route
Loading

Warning

Some tools did not complete. Review the errors below.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/api/src/auth/routes.test.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

apps/api/src/auth/routes.ts

ESLint skipped: the matched ESLint configuration already failed (missing-dependency).

apps/web/package.json

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

  • 44 others

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/settings/billing.

Comment @coderabbitai help to get the list of available commands.

@ARCoder181105
ARCoder181105 merged commit 28e8363 into main Aug 28, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant