edrsc.com had no entry point — a visitor could not tell which of the two
apps they wanted. This adds @edr/landing: one static page whose job is to
make that choice obvious, with the two doors above the fold and the rest
kept to supporting context.
Fills in apps/edr-landing/, which until now held an orphaned next-env.d.ts
from an abandoned Next 15 start and was built by nothing.
- Next.js 14 static export. Versions mirror the passenger portal exactly,
so they resolve to what the lockfile already had and add 6 packages.
Depends on no workspace package — @edr/ui-common's Tailwind 4 tokens do
not fit this Tailwind 3 setup.
- Destinations come from NEXT_PUBLIC_PASSENGER_URL / NEXT_PUBLIC_FREIGHT_URL
as origins, with the entry path appended in src/lib/apps.ts. Freight links
to /portal rather than /, which is that app's own marketing landing.
- Design lifted from EDRFreightLandingPage so the two public surfaces read
as one railway. Figures (752 km, ~16 h) are the ones freight already
asserts publicly.
- Fonts load as a stylesheet, not next/font: next/font fetches from
fonts.googleapis.com during `next build` and failed the build outright on
a machine without network. Verified deterministic over repeated builds.
- CountUp renders its final value server-side and the reveal animation's
hidden state is scoped behind html.js, so the page is complete with
JavaScript off instead of blank.
turbo.json: a static export's artifact is out/, which the build task did not
list — a cache hit would have restored a build with the artifact missing.
Also gitignored.
Dockerfile.web: passes the two NEXT_PUBLIC_* build args through (a static
export inlines them at build time, so runtime env does nothing), and exempts
this app from the VITE_* required-check that guards the freight Vite apps and
would otherwise fail its build.
Claude-Session: https://claude.ai/code/session_011ZMMHwK4Ham1Dt19FVZQj3