feat: a red thread through the homepage, in words and as a line
The rail and the "next, you are wondering" links are gone (owner: flat, and the question stood twice in a row). The page now reads from top to bottom: Every chapter opens with one sentence, and the sentences follow each other as the way of a project: you need someone who builds it, first we find out what you need, sometimes it is less work by hand, sometimes a meeting, then it always runs the same way, in the end something runs, a few questions remain, and it starts with an email. One line runs down the whole page and draws itself as the visitor scrolls. Under each sentence it crosses the page, stands a figure on the line (diamond, hexagon, screen, circle, square, triangle) and continues down the other side; a glowing dot travels at its head and comes to rest in front of the last sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,11 +18,10 @@ import {
|
||||
} from "@/src/components/illustrations/WebPresentation";
|
||||
import { WorkShowcase } from "@/src/components/illustrations/WorkShowcase";
|
||||
import { Reveal } from "@/src/components/Reveal";
|
||||
import { NextChapter } from "@/src/components/story/Chapter";
|
||||
import { StoryGuide } from "@/src/components/story/StoryGuide";
|
||||
import { StoryThread } from "@/src/components/story/StoryThread";
|
||||
import { SwipeRow } from "@/src/components/SwipeRow";
|
||||
import { contentFor } from "@/src/content";
|
||||
import type { ChapterId, SplitTitle } from "@/src/content/types";
|
||||
import type { SplitTitle } from "@/src/content/types";
|
||||
import { DEFAULT_LOCALE, parseLocale } from "@/src/domain/locale";
|
||||
import { languageAlternates, pathFor } from "@/src/domain/routes";
|
||||
|
||||
@@ -70,28 +69,11 @@ export default async function LandingPage({ params }: Props) {
|
||||
faq,
|
||||
} = content;
|
||||
|
||||
/** What makes a section a chapter, and the hand-over to the one after it. */
|
||||
const chapter = (id: ChapterId) => {
|
||||
const at = story.chapters.findIndex((item) => item.id === id);
|
||||
const own = story.chapters[at];
|
||||
const after = story.chapters[at + 1];
|
||||
return {
|
||||
chapter: own && { id: own.id },
|
||||
next: after && {
|
||||
id: after.id,
|
||||
label: story.next,
|
||||
question: after.question,
|
||||
},
|
||||
};
|
||||
};
|
||||
const afterHello = chapter("hello").next;
|
||||
|
||||
return (
|
||||
<div className="relative flex flex-col bg-white">
|
||||
<HomeJsonLd locale={locale} />
|
||||
<HeroStatement hero={content.hero} />
|
||||
<StickyBook />
|
||||
<StoryGuide />
|
||||
|
||||
<PortraitSection
|
||||
id="hello"
|
||||
@@ -99,6 +81,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
label={hello.label}
|
||||
title={hello.title}
|
||||
portraitAlt={hello.portraitAlt}
|
||||
story={story.hello}
|
||||
>
|
||||
<p className="m-0 font-sans text-2xl leading-snug text-white md:text-4xl">
|
||||
{hello.lead}
|
||||
@@ -119,14 +102,13 @@ export default async function LandingPage({ params }: Props) {
|
||||
{hello.careerLabel}
|
||||
</p>
|
||||
<CareerLine stations={hello.career} />
|
||||
{afterHello && <NextChapter {...afterHello} dark />}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Block
|
||||
index="02"
|
||||
label={services.label}
|
||||
{...chapter("services")}
|
||||
chapter={{ line: story.services, figure: "diamond", flip: false }}
|
||||
title={<Title title={services.title} />}
|
||||
>
|
||||
<Reveal>
|
||||
@@ -137,7 +119,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
<Block
|
||||
index="03"
|
||||
label={automation.label}
|
||||
{...chapter("automation")}
|
||||
chapter={{ line: story.automation, figure: "hexagon", flip: true }}
|
||||
title={<Title title={automation.title} />}
|
||||
>
|
||||
<Reveal>
|
||||
@@ -170,7 +152,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
<Block
|
||||
index="04"
|
||||
label={presentations.label}
|
||||
{...chapter("presentations")}
|
||||
chapter={{ line: story.presentations, figure: "screen", flip: false }}
|
||||
tone="dark"
|
||||
title={<Title title={presentations.title} muted="text-slate-500" />}
|
||||
>
|
||||
@@ -243,7 +225,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
<Block
|
||||
index="05"
|
||||
label={working.label}
|
||||
{...chapter("working")}
|
||||
chapter={{ line: story.working, figure: "circle", flip: true }}
|
||||
title={<Title title={working.title} />}
|
||||
>
|
||||
<Reveal>
|
||||
@@ -294,7 +276,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
<Block
|
||||
index="06"
|
||||
label={work.label}
|
||||
{...chapter("work")}
|
||||
chapter={{ line: story.work, figure: "square", flip: false }}
|
||||
tone="dark"
|
||||
title={<Title title={work.title} muted="text-slate-500" />}
|
||||
>
|
||||
@@ -309,7 +291,7 @@ export default async function LandingPage({ params }: Props) {
|
||||
<Block
|
||||
index="07"
|
||||
label={faq.label}
|
||||
{...chapter("faq")}
|
||||
chapter={{ line: story.faq, figure: "triangle", flip: true }}
|
||||
title={<Title title={faq.title} />}
|
||||
>
|
||||
<Reveal>
|
||||
@@ -317,7 +299,8 @@ export default async function LandingPage({ params }: Props) {
|
||||
</Reveal>
|
||||
</Block>
|
||||
|
||||
<ClosingCta contact={content.contact} />
|
||||
<ClosingCta contact={content.contact} story={story.contact} />
|
||||
<StoryThread />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+27
-14
@@ -1339,21 +1339,34 @@ pre:has(code[class*="language-"]) {
|
||||
}
|
||||
}
|
||||
|
||||
/* The story guide: the dot that travels along the chapter rail, and the header line. */
|
||||
.story-dot {
|
||||
/*
|
||||
* The story thread (see StoryThread): one line down the page, indigo on light
|
||||
* sections and light on dark ones, with a glowing dot at its head.
|
||||
*/
|
||||
.story-thread path {
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.story-thread-light {
|
||||
stroke: #4f46e5;
|
||||
}
|
||||
.story-thread-dark {
|
||||
stroke: #c7d2fe;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.story-thread-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin: -6px 0 0 -6px;
|
||||
border-radius: 9999px;
|
||||
background: #fde68a;
|
||||
box-shadow:
|
||||
0 0 0 3px rgba(253, 230, 138, 0.35),
|
||||
0 0 14px 3px rgba(251, 191, 36, 0.8);
|
||||
transition: top 200ms linear;
|
||||
}
|
||||
.story-now {
|
||||
animation: story-now-in 400ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
@keyframes story-now-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(6px);
|
||||
}
|
||||
0 0 0 4px rgba(253, 230, 138, 0.28),
|
||||
0 0 18px 5px rgba(251, 191, 36, 0.85),
|
||||
0 0 44px 14px rgba(129, 140, 248, 0.55);
|
||||
transition: opacity 300ms ease;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user