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
+18
View File
@@ -0,0 +1,18 @@
import * as React from "react";
/**
* SwipeRow: on phones the items stand side by side and are swiped through,
* with the next one peeking in; from tablet width on they fall back into the
* grid given by `className`. It keeps long lists of equal items from
* stretching the page on a small screen. Styles: `swipe-row` in globals.css.
*/
export const SwipeRow: React.FC<{
className?: string;
children: React.ReactNode;
}> = ({ className = "", children }) => (
<div className={`swipe-row ${className}`}>
{React.Children.map(children, (child) => (
<div className="swipe-slide">{child}</div>
))}
</div>
);
+2 -2
View File
@@ -49,7 +49,7 @@ export const Block: React.FC<BlockProps> = ({
/>
)}
{dark && <BitSeam />}
<div className="relative mx-auto w-full max-w-[1400px] px-5 py-20 md:px-12 md:py-36">
<div className="relative mx-auto w-full max-w-[1400px] px-5 py-14 md:px-12 md:py-36">
<Reveal>
<div className="mb-8 flex items-center gap-4 md:mb-12">
<span
@@ -78,7 +78,7 @@ export const Block: React.FC<BlockProps> = ({
{title}
</h2>
</BitWipe>
<div className="mt-14 md:mt-24">{children}</div>
<div className="mt-10 md:mt-24">{children}</div>
</div>
</section>
);
+1 -1
View File
@@ -26,7 +26,7 @@ export const ClosingCta: React.FC<{ contact: Content["contact"] }> = ({
/>
<div className="bit-glow bit-glow-dots" aria-hidden="true" />
<BitSeam />
<div className="relative mx-auto grid w-full max-w-[1400px] grid-cols-1 gap-14 px-5 py-24 md:px-12 md:py-40 lg:grid-cols-12 lg:items-center lg:gap-12">
<div className="relative mx-auto grid w-full max-w-[1400px] grid-cols-1 gap-12 px-5 py-16 md:px-12 md:py-40 lg:grid-cols-12 lg:items-center lg:gap-12">
<Reveal className="lg:col-span-6">
<BitHeadline
as="h2"
+12 -9
View File
@@ -92,7 +92,7 @@ const PathStory: React.FC<{ path: ServicePath; active: boolean }> = ({
setStep(index);
setAuto(false);
}}
className={`relative flex w-full gap-5 border-t py-6 text-left transition-colors duration-300 md:gap-6 ${
className={`relative flex w-full gap-5 border-t py-4 text-left transition-colors duration-300 md:gap-6 md:py-6 ${
selected ? "border-slate-900" : "border-slate-200"
}`}
>
@@ -122,8 +122,11 @@ const PathStory: React.FC<{ path: ServicePath; active: boolean }> = ({
{item.title}
</span>
<span
className={`mt-1.5 block font-sans text-base leading-relaxed transition-colors duration-300 ${
selected ? "text-slate-600" : "text-slate-400"
className={`mt-1.5 font-sans text-base leading-relaxed transition-colors duration-300 ${
// On phones only the current step is spelled out.
selected
? "block text-slate-600"
: "hidden text-slate-400 md:block"
}`}
>
{item.text}
@@ -153,7 +156,7 @@ export const PathPicker: React.FC = () => {
<div
role="tablist"
aria-label={services.tablistLabel}
className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4 lg:gap-4"
className="grid grid-cols-2 gap-2 md:gap-3 lg:grid-cols-4 lg:gap-4"
>
{paths.map((path, index) => {
const selected = path.id === activeId;
@@ -167,7 +170,7 @@ export const PathPicker: React.FC = () => {
aria-controls={`path-panel-${path.id}`}
onClick={() => setActiveId(path.id)}
data-glow
className={`bit-card group flex h-full flex-col justify-between rounded-3xl border p-5 text-left transition-all duration-300 md:p-7 ${
className={`bit-card group flex h-full flex-col justify-between rounded-3xl border p-4 text-left transition-all duration-300 md:p-7 ${
selected
? "border-slate-900 bg-slate-900 text-white"
: "border-slate-200 bg-white text-slate-900 hover:-translate-y-1 hover:border-slate-400"
@@ -177,11 +180,11 @@ export const PathPicker: React.FC = () => {
<span className="font-mono text-[11px] tracking-[0.2em] text-slate-400">
0{index + 1}
</span>
<span className="mt-3 block font-sans text-xl font-bold leading-tight tracking-tight md:mt-10 md:text-2xl">
<span className="mt-3 block font-sans text-base font-bold leading-tight tracking-tight md:mt-10 md:text-2xl">
{path.need}
</span>
<span
className={`mt-2 block font-sans text-sm leading-relaxed md:mt-4 md:text-base ${selected ? "text-slate-300" : "text-slate-500"}`}
className={`mt-2 hidden font-sans text-sm leading-relaxed md:mt-4 md:block md:text-base ${selected ? "text-slate-300" : "text-slate-500"}`}
>
{path.forWhom}
</span>
@@ -199,7 +202,7 @@ export const PathPicker: React.FC = () => {
id={`path-panel-${path.id}`}
aria-labelledby={`path-tab-${path.id}`}
hidden={!selected}
className="mt-12 md:mt-20"
className="mt-8 md:mt-20"
>
<motion.div
key={selected ? "on" : "off"}
@@ -251,7 +254,7 @@ export const PathPicker: React.FC = () => {
{path.pricingText}
</p>
</div>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 md:col-span-6">
<div className="grid grid-cols-2 gap-6 md:col-span-6 md:gap-8">
{path.prices.map((price) => (
<div key={price.label}>
<p className="m-0 font-sans text-base text-slate-500 md:text-lg">
@@ -1,6 +1,7 @@
"use client";
import { BitAura } from "@/src/components/bits/BitAura";
import { SwipeRow } from "@/src/components/SwipeRow";
import * as React from "react";
import { useNarrow } from "./useNarrow";
import { useContent } from "@/src/components/ContentProvider";
@@ -391,7 +392,7 @@ export const AutomationFlow: React.FC = () => {
</div>
<div className="grid grid-cols-1 border-t border-slate-800 md:grid-cols-12">
<div className="border-b border-slate-800 px-5 py-5 md:col-span-7 md:border-b-0 md:border-r md:px-7">
<div className="px-5 py-5 md:col-span-7 md:border-r md:border-slate-800 md:px-7">
<p className={`${MONO} m-0 text-slate-500`}>{automation.log}</p>
<ol
className="m-0 mt-3 min-h-[7.5rem] list-none p-0 font-mono text-[12px] leading-relaxed text-slate-300 md:text-[13px]"
@@ -413,7 +414,7 @@ export const AutomationFlow: React.FC = () => {
))}
</ol>
</div>
<ul className="m-0 flex list-none flex-col justify-center gap-2 px-5 py-5 md:col-span-5 md:px-7">
<ul className="m-0 hidden list-none flex-col justify-center gap-2 px-5 py-5 md:col-span-5 md:flex md:px-7">
{KINDS.filter((kind) =>
flow.nodes.some((node) => node.kind === kind),
).map((kind) => (
@@ -442,7 +443,7 @@ export const AutomationFlow: React.FC = () => {
</div>
</BitAura>
<div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-2">
<SwipeRow className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-2">
<div className="rounded-3xl border border-slate-200 p-6 md:p-8">
<p className={`${MONO} m-0 text-slate-400`}>{automation.byHand}</p>
<p className="m-0 mt-3 font-sans text-lg leading-relaxed text-slate-500 md:text-xl">
@@ -455,7 +456,7 @@ export const AutomationFlow: React.FC = () => {
{flow.automated}
</p>
</div>
</div>
</SwipeRow>
</div>
);
};
@@ -241,7 +241,10 @@ export const PipelineRun: React.FC = () => {
</p>
<p
className={`m-0 mt-2 font-sans text-base leading-relaxed transition-colors duration-300 ${
active ? "text-slate-600" : "text-slate-400"
// On phones only the current step is spelled out.
active
? "block text-slate-600"
: "hidden text-slate-400 md:block"
}`}
>
{item.text}
@@ -203,7 +203,7 @@ export const WorkShowcase: React.FC = () => {
{copy.text}
</p>
<ul className="m-0 mt-8 list-none p-0">
<ul className="m-0 mt-8 hidden list-none p-0 md:block">
{copy.did.map((line) => (
<li
key={line}