feat: a shorter page on phones

On a phone the homepage was about 29 screens long; it is now about 22.
The desktop layout is unchanged to the pixel.

Lists of equal items (automation notes, project fit, principles, reasons for
a web presentation, by hand vs. automated) are swiped through side by side
(SwipeRow) instead of stacked. The four service cards form a compact 2x2
grid, step lists spell out only the current step, the workflow legend and
the per-project task lists are left out, and sections use less padding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 15:50:26 +02:00
co-authored by Claude Opus 5.5
parent 5b037d7e0c
commit ca0eddfde8
9 changed files with 87 additions and 29 deletions
+12 -11
View File
@@ -19,6 +19,7 @@ import {
} from "@/src/components/illustrations/WebPresentation";
import { WorkShowcase } from "@/src/components/illustrations/WorkShowcase";
import { Reveal } from "@/src/components/Reveal";
import { SwipeRow } from "@/src/components/SwipeRow";
import { contentFor } from "@/src/content";
import type { SplitTitle } from "@/src/content/types";
import { DEFAULT_LOCALE, parseLocale } from "@/src/domain/locale";
@@ -122,7 +123,7 @@ export default async function LandingPage({ params }: Props) {
<Reveal>
<AutomationFlow />
</Reveal>
<div className="mt-16 grid grid-cols-1 gap-12 md:mt-24 md:grid-cols-3 md:gap-10">
<SwipeRow className="mt-12 grid grid-cols-1 gap-12 md:mt-24 md:grid-cols-3 md:gap-10">
{automation.notes.map((group, i) => (
<Reveal key={group.title} delay={0.1 * i}>
<div className="border-t border-slate-900 pt-6">
@@ -140,7 +141,7 @@ export default async function LandingPage({ params }: Props) {
</div>
</Reveal>
))}
</div>
</SwipeRow>
</Block>
<Block
@@ -162,7 +163,7 @@ export default async function LandingPage({ params }: Props) {
</BitAura>
</Reveal>
<div className="mt-16 grid grid-cols-1 gap-x-10 gap-y-14 sm:grid-cols-2 md:mt-28 lg:grid-cols-4">
<SwipeRow className="mt-12 grid grid-cols-1 gap-x-10 gap-y-14 md:mt-28 md:grid-cols-2 lg:grid-cols-4">
{presentations.reasons.map((reason, i) => (
<Reveal key={reason.title} delay={0.08 * i}>
<div className="rounded-2xl border border-slate-800 bg-slate-950/60 p-4">
@@ -176,7 +177,7 @@ export default async function LandingPage({ params }: Props) {
</p>
</Reveal>
))}
</div>
</SwipeRow>
<div className="mt-16 grid grid-cols-1 gap-12 md:mt-28 md:grid-cols-12 md:gap-12">
<Reveal className="md:col-span-6">
@@ -220,7 +221,7 @@ export default async function LandingPage({ params }: Props) {
label={technology.label}
title={<Title title={technology.title} />}
>
<div className="grid grid-cols-1 gap-12 md:grid-cols-3 md:gap-10">
<SwipeRow className="grid grid-cols-1 gap-12 md:grid-cols-3 md:gap-10">
{technology.fit.map((group, i) => (
<Reveal key={group.title} delay={0.1 * i}>
<div
@@ -242,12 +243,12 @@ export default async function LandingPage({ params }: Props) {
</div>
</Reveal>
))}
</div>
</SwipeRow>
<Reveal>
<div className="mt-16 rounded-3xl bg-slate-50 p-6 md:mt-24 md:p-10">
<div className="mt-12 rounded-3xl bg-slate-50 p-6 md:mt-24 md:p-10">
<p className={`${MONO} m-0`}>{technology.techLabel}</p>
<div className="mt-8 grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-4">
<div className="mt-6 grid grid-cols-1 gap-7 sm:grid-cols-2 md:mt-8 md:gap-10 lg:grid-cols-4">
{technology.tech.map((group) => (
<div key={group.area}>
<p className="m-0 font-sans text-xl font-bold tracking-tight text-slate-900 md:text-2xl">
@@ -269,7 +270,7 @@ export default async function LandingPage({ params }: Props) {
</div>
</Reveal>
<div className="mt-16 md:mt-24">
<div className="mt-12 md:mt-24">
<Reveal>
<h3 className="m-0 font-sans text-3xl font-bold tracking-tighter text-slate-900 md:text-5xl">
{technology.pipelineTitle}
@@ -296,7 +297,7 @@ export default async function LandingPage({ params }: Props) {
</div>
</BitAura>
</Reveal>
<div className="grid grid-cols-1 gap-x-12 gap-y-12 md:grid-cols-2 md:gap-y-16">
<SwipeRow className="grid grid-cols-1 gap-x-12 gap-y-12 md:grid-cols-2 md:gap-y-16">
{working.principles.map((item, i) => (
<Reveal key={item.title} delay={0.08 * i}>
<div className="border-t border-slate-900 pt-6">
@@ -307,7 +308,7 @@ export default async function LandingPage({ params }: Props) {
</div>
</Reveal>
))}
</div>
</SwipeRow>
</Block>
<Block
+32
View File
@@ -1352,6 +1352,38 @@ pre:has(code[class*="language-"]) {
}
}
/*
* SwipeRow: on phones equal items stand side by side and are swiped through,
* with the next one peeking in. From tablet width on the slide wrappers
* dissolve and the items sit in the grid the row was given.
*/
@media (max-width: 767px) {
.swipe-row {
display: flex;
gap: 1rem;
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
/* Bleed to the screen edges, but start in line with the text. */
margin-inline: -1.25rem;
padding-inline: 1.25rem;
scroll-padding-inline: 1.25rem;
scrollbar-width: none;
}
.swipe-row::-webkit-scrollbar {
display: none;
}
.swipe-slide {
flex: 0 0 82%;
scroll-snap-align: start;
}
}
@media (min-width: 768px) {
.swipe-slide {
display: contents;
}
}
/*
* Native feel: the page should behave like a well-made app on every input type.
*/