Skip to content

feat(web): a real landing page, and a shell that isn't the app's - #13

Merged
ralyodio merged 3 commits into
mainfrom
feat/landing-1a-clean
Aug 12, 2026
Merged

ralyodio merged 3 commits into
mainfrom
feat/landing-1a-clean

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Ships direction 1a from the design options. Replaces #12, which was cut from the pre-squash branch head and conflicted.

The structural bug behind "bad UI"

The root layout wrapped every route — including the public landing page — in the signed-in app shell: max-w-2xl (672px) under a fixed bottom tab bar. A stranger's first impression of the product was a narrow strip beneath a mobile app nav. No amount of restyling fixes that; the shell was wrong.

  • Route groups split it: (app) keeps the narrow column and BottomNav; (marketing) is full-bleed with no chrome. URLs are unchanged — route groups don't appear in paths.
  • login and verify move to (marketing), where they always belonged — they had a bottom app-nav before.
  • The root layout now holds only what every route needs.

The page

Leads with the product's actual output rather than a description of it: an approval card with a real quote, a source link, the recommended action and the approve/edit/skip row. Then the evidence trail (identities with confidence, and why name + city never merges), the refusal (409 policy_denied, gate named), and the CTA.

Type is Schibsted Grotesk + IBM Plex Mono via next/font, self-hosted at build time — no blocking request to Google, no layout shift. Deliberately not Inter/Roboto. Palette unchanged from the app's existing tokens so marketing and product don't look like different companies.

One bug worth calling out

The font variables are not named --font-sans / --font-mono. next/font sets its variable on <html>, which is also :root, so reusing the theme's own names makes --font-sans: var(--font-sans), … a self-reference on a single element — an invalid cycle that silently drops the face. Caught by reading the built stylesheet; the build passed either way.

Verification

  • bun run check — format, typecheck, 395 tests, all passing
  • Production build run and requested live: / 200 (26KB), /login 200, /verify 200, /offline 200
  • Confirmed no max-w-2xl and no bottom nav in the marketing HTML
  • Confirmed the font chain resolves --font-schibsted → --font-sans → --default-font-family with no cycle

⚠️ No browser tooling in this session, so this is verified as rendering correct content with correct CSS, not as looking right. Worth an eyeball.

🤖 Generated with Claude Code

ralyodio and others added 3 commits August 12, 2026 01:51
The marketing page rendered inside the signed-in app shell: capped at
max-w-2xl and sitting under the bottom tab bar, so a stranger's first
impression of the product was a 672px strip beneath a mobile nav. Splitting
the shells is most of the fix.

- Route groups: (app) keeps the narrow column and the bottom nav, (marketing)
  gets full bleed and no chrome. URLs are unchanged — route groups do not
  appear in paths — and login and verify move to (marketing), which is where
  they always belonged.
- The root layout now holds only what every route needs.
- The landing page leads with the product's actual output: an approval card
  with a real quote, a source link and the recommended action. Showing the
  artifact argues the case better than describing it. Evidence and refusal
  sections follow, then the CTA.
- Type is Schibsted Grotesk and IBM Plex Mono via next/font, self-hosted at
  build time so there is no blocking request to Google and no layout shift.

The font variables are deliberately not named --font-sans/--font-mono.
next/font sets its variable on <html>, which is also :root, so reusing the
theme's own names makes `--font-sans: var(--font-sans), …` a self-reference
on a single element — an invalid cycle that silently drops the face. Caught
by reading the built stylesheet, not by the build.

Verified by running the production build and requesting the pages: landing,
login, verify and offline all 200 with content, no app chrome on any of them,
and the font chain resolves --font-schibsted -> --font-sans ->
--default-font-family with no cycle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The image job died with a Bun segfault (SIGILL) inside `next build`, while the
same build passed on the host. The difference was `next/font/google`, which
fetches faces over the network during the build.

Local files make the build hermetic, which it should have been anyway: a
release that can fail because fonts.gstatic.com is slow is a release with an
avoidable dependency. 92KB of woff2 — one variable face for Schibsted Grotesk
and three weights of IBM Plex Mono, latin subsets only.

Verified in the built stylesheet: four @font-face rules plus metric-matched
fallbacks, and the variable chain still resolves with no cycle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The smoke test grepped for "warm conversations", which the rewritten page no
longer says, so it failed on a page that had rendered correctly at 26KB. It
now asserts on the headline and on the sample approval card's quote — the
card is the part most likely to silently vanish, since it carries the claim
the page is making.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ralyodio
ralyodio merged commit b579af8 into main Aug 12, 2026
4 checks passed
@ralyodio
ralyodio deleted the feat/landing-1a-clean branch August 16, 2026 17:33
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