You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by we...@apache.org on 2026/06/22 06:23:36 UTC
(apisix-website) 01/01: feat(seo): rewrite homepage for CTR + funnel to docs/pillars/plugins
This is an automated email from the ASF dual-hosted git repository.
moonming pushed a commit to branch feat/seo-homepage-rewrite
in repository https://gitbox.apache.org/repos/asf/apisix-website.git
commit 3a7f7dd6a3a74ff756b38a21223ad4552f3b69ce
Author: Ming Wen <mo...@gmail.com>
AuthorDate: Mon Jun 22 14:23:24 2026 +0800
feat(seo): rewrite homepage for CTR + funnel to docs/pillars/plugins
- Add 'Trusted by teams everywhere' auto-scrolling logo marquee (pure-CSS
transform, hover-pause, reduced-motion safe; logos pluggable via CDN).
- Add 'Integrates with your stack' grid reusing the real plugin-sprite icons
(Prometheus, Datadog, SkyWalking, Zipkin, Kafka, gRPC, OIDC, Keycloak...).
- Add intent 'Pathways' cards routing to docs, learning center, and plugin hub.
- Slim the hero: drop the WebGL HeroCanvas (load-speed win), add eyebrow,
second CTA, and a trust-stats row; center the text layout.
- Compose: hero -> trusted-by -> pathways -> product -> integrations -> ...
---
website/src/components/sections/HeroSection.tsx | 70 ++++++-------
website/src/components/sections/Integrations.tsx | 95 +++++++++++++++++
website/src/components/sections/Pathways.tsx | 67 ++++++++++++
website/src/components/sections/TrustedBy.tsx | 80 +++++++++++++++
website/src/css/landing-sections/hero.scss | 114 ++++++++++++++-------
website/src/css/landing-sections/integrations.scss | 97 ++++++++++++++++++
website/src/css/landing-sections/pathways.scss | 62 +++++++++++
website/src/css/landing-sections/trusted-by.scss | 89 ++++++++++++++++
website/src/pages/index.tsx | 6 ++
9 files changed, 603 insertions(+), 77 deletions(-)
diff --git a/website/src/components/sections/HeroSection.tsx b/website/src/components/sections/HeroSection.tsx
index f30f108b146..3dab2641758 100644
--- a/website/src/components/sections/HeroSection.tsx
+++ b/website/src/components/sections/HeroSection.tsx
@@ -2,61 +2,49 @@ import type { FC } from 'react';
import React from 'react';
import Link from '@docusaurus/Link';
import Translate from '@docusaurus/Translate';
-
-import BrowserOnly from '@docusaurus/BrowserOnly';
-import useWindowType from '@theme/hooks/useWindowSize';
import useBaseUrl from '@docusaurus/useBaseUrl';
-import ArrowAnim from '../ArrowAnim';
import '../../css/landing-sections/hero.scss';
-const LazyLoadHeroCanvas = () => {
- const windowType = useWindowType();
- if (windowType === 'mobile') return null;
-
- return (
- <BrowserOnly>
- {() => {
- // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
- const HeroCanvas = require('../HeroCanvas').default;
- return <HeroCanvas />;
- }}
- </BrowserOnly>
- );
-};
+const STATS: { value: string; label: JSX.Element }[] = [
+ { value: '100+', label: <Translate id="hero.stats.plugins">plugins</Translate> },
+ { value: '~18k', label: <Translate id="hero.stats.qps">QPS / core</Translate> },
+ { value: '0.2 ms', label: <Translate id="hero.stats.latency">added latency</Translate> },
+ { value: 'Apache 2.0', label: <Translate id="hero.stats.license">licensed</Translate> },
+];
const HeroSection: FC = () => (
- <div className="hero-sec-wrap" style={{ width: '100%' }}>
+ <header className="hero-sec-wrap">
<div className="hero-text">
+ <span className="hero-eyebrow">
+ <Translate id="hero.component.eyebrow">Apache Software Foundation top-level project</Translate>
+ </span>
<h1 className="hero-title">
- <Translate id="hero.component.title.fragment1">API Gateway & AI Gateway for APIs and AI Agents</Translate>
+ <Translate id="hero.component.title.main">The open-source API Gateway & AI Gateway</Translate>
</h1>
- <h2
- className="hero-subtitle"
- style={{
- color: '#E8433E', fontSize: 32, fontWeight: 700, lineHeight: 1.2,
- }}
- >
- <Translate id="hero.component.title.fragment2">
- Open-Source, Community-Driven, Future-Ready
- </Translate>
- </h2>
- <h3 className="hero-subtitle">
- <Translate id="hero.component.subtitle.content">
- APISIX API Gateway provides rich traffic management features like load balancing, dynamic
- upstream, canary release, circuit breaking, auth, and observability.
+ <p className="hero-subtitle">
+ <Translate id="hero.component.subtitle.main">
+ High-performance traffic management for APIs, microservices, and LLM workloads —
+ dynamic routing, load balancing, authentication, observability, and 100+ plugins.
</Translate>
- </h3>
+ </p>
<div className="hero-ctas">
<Link target="_parent" to={useBaseUrl('docs/apisix/getting-started')} className="btn btn-download">
- <Translate id="hero.component.download.btn">Getting Started</Translate>
+ <Translate id="hero.component.download.btn">Get started</Translate>
+ </Link>
+ <Link target="_parent" to={useBaseUrl('plugins/')} className="btn btn-secondary">
+ <Translate id="hero.component.plugins.btn">Browse 100+ plugins</Translate>
</Link>
- <ArrowAnim />
</div>
+ <ul className="hero-stats">
+ {STATS.map((stat) => (
+ <li className="hero-stats__item" key={stat.value}>
+ <span className="hero-stats__value">{stat.value}</span>
+ <span className="hero-stats__label">{stat.label}</span>
+ </li>
+ ))}
+ </ul>
</div>
- <div className="add-margin">
- <LazyLoadHeroCanvas />
- </div>
- </div>
+ </header>
);
export default HeroSection;
diff --git a/website/src/components/sections/Integrations.tsx b/website/src/components/sections/Integrations.tsx
new file mode 100644
index 00000000000..65254a2c673
--- /dev/null
+++ b/website/src/components/sections/Integrations.tsx
@@ -0,0 +1,95 @@
+import type { FC } from 'react';
+import React from 'react';
+import Head from '@docusaurus/Head';
+import Link from '@docusaurus/Link';
+import Translate from '@docusaurus/Translate';
+
+import '../../css/landing-sections/integrations.scss';
+
+interface Integration {
+ name: string;
+ /** Plugin name whose brand-colored icon lives in the shared plugin sprite. */
+ icon?: string;
+ /**
+ * Optional logo URL, for ecosystem logos that are not plugin icons (e.g.
+ * Kubernetes, OpenTelemetry, LLM providers). Host official colored SVGs on
+ * the API7 CDN and set this; it takes precedence over `icon`.
+ */
+ logo?: string;
+}
+
+const GROUPS: { title: string; items: Integration[] }[] = [
+ {
+ title: 'Observability & tracing',
+ items: [
+ { name: 'Prometheus', icon: 'prometheus' },
+ { name: 'Datadog', icon: 'datadog' },
+ { name: 'Apache SkyWalking', icon: 'skywalking' },
+ { name: 'Zipkin', icon: 'zipkin' },
+ { name: 'Google Cloud', icon: 'google-cloud-logging' },
+ ],
+ },
+ {
+ title: 'Authentication & security',
+ items: [
+ { name: 'OpenID Connect', icon: 'openid-connect' },
+ { name: 'Keycloak', icon: 'authz-keycloak' },
+ { name: 'Casbin', icon: 'authz-casbin' },
+ ],
+ },
+ {
+ title: 'Streaming & protocols',
+ items: [
+ { name: 'Apache Kafka', icon: 'kafka-logger' },
+ { name: 'gRPC', icon: 'grpc-transcode' },
+ ],
+ },
+];
+
+const IntegrationTile: FC<{ item: Integration }> = ({ item }) => (
+ <div className="integrations__tile" title={item.name}>
+ {item.logo ? (
+ <img className="integrations__img" src={item.logo} alt={item.name} loading="lazy" width={40} height={40} />
+ ) : (
+ <svg className="integrations__icon" aria-hidden="true">
+ <use xlinkHref={`#icon${item.icon}`} />
+ </svg>
+ )}
+ <span className="integrations__name">{item.name}</span>
+ </div>
+);
+
+const Integrations: FC = () => (
+ <section className="integrations" aria-labelledby="integrations-heading">
+ <Head>
+ {/* Injects the shared plugin icon sprite used by #icon<name> references. */}
+ <script src="/js/plugin-icon.js" defer />
+ </Head>
+ <h2 id="integrations-heading" className="integrations__heading">
+ <Translate id="home.integrations.title">Integrates with your stack</Translate>
+ </h2>
+ <p className="integrations__subtitle">
+ <Translate id="home.integrations.subtitle">
+ Connect APISIX to your observability, security, and streaming tools through 100+ plugins.
+ </Translate>
+ </p>
+ <div className="integrations__groups">
+ {GROUPS.map((group) => (
+ <div className="integrations__group" key={group.title}>
+ <div className="integrations__group-title">{group.title}</div>
+ <div className="integrations__tiles">
+ {group.items.map((item) => (
+ <IntegrationTile item={item} key={item.name} />
+ ))}
+ </div>
+ </div>
+ ))}
+ </div>
+ <Link className="integrations__cta" to="/plugins/">
+ <Translate id="home.integrations.cta">Explore all 100+ plugins</Translate>
+ {' →'}
+ </Link>
+ </section>
+);
+
+export default Integrations;
diff --git a/website/src/components/sections/Pathways.tsx b/website/src/components/sections/Pathways.tsx
new file mode 100644
index 00000000000..76105343df6
--- /dev/null
+++ b/website/src/components/sections/Pathways.tsx
@@ -0,0 +1,67 @@
+import type { FC } from 'react';
+import React from 'react';
+import Link from '@docusaurus/Link';
+import Translate from '@docusaurus/Translate';
+
+import '../../css/landing-sections/pathways.scss';
+
+interface Pathway {
+ id: string;
+ title: JSX.Element;
+ description: JSX.Element;
+ cta: JSX.Element;
+ to: string;
+}
+
+const PATHWAYS: Pathway[] = [
+ {
+ id: 'build',
+ to: '/docs/apisix/getting-started/',
+ title: <Translate id="home.pathways.build.title">Get started</Translate>,
+ description: (
+ <Translate id="home.pathways.build.desc">
+ Install APISIX and configure your first route in minutes.
+ </Translate>
+ ),
+ cta: <Translate id="home.pathways.build.cta">Read the docs</Translate>,
+ },
+ {
+ id: 'learn',
+ to: '/learning-center/',
+ title: <Translate id="home.pathways.learn.title">Learn the concepts</Translate>,
+ description: (
+ <Translate id="home.pathways.learn.desc">
+ API gateway, AI gateway, rate limiting, mTLS, Kubernetes and more.
+ </Translate>
+ ),
+ cta: <Translate id="home.pathways.learn.cta">Explore the learning center</Translate>,
+ },
+ {
+ id: 'plugins',
+ to: '/plugins/',
+ title: <Translate id="home.pathways.plugins.title">Browse plugins</Translate>,
+ description: (
+ <Translate id="home.pathways.plugins.desc">
+ 100+ plugins for auth, security, traffic control, observability and AI.
+ </Translate>
+ ),
+ cta: <Translate id="home.pathways.plugins.cta">Open the plugin hub</Translate>,
+ },
+];
+
+const Pathways: FC = () => (
+ <section className="pathways" aria-label="Get started with Apache APISIX">
+ {PATHWAYS.map((pathway) => (
+ <Link className="pathways__card" to={pathway.to} key={pathway.id}>
+ <h3 className="pathways__title">{pathway.title}</h3>
+ <p className="pathways__desc">{pathway.description}</p>
+ <span className="pathways__cta">
+ {pathway.cta}
+ {' →'}
+ </span>
+ </Link>
+ ))}
+ </section>
+);
+
+export default Pathways;
diff --git a/website/src/components/sections/TrustedBy.tsx b/website/src/components/sections/TrustedBy.tsx
new file mode 100644
index 00000000000..59f0cefb7d4
--- /dev/null
+++ b/website/src/components/sections/TrustedBy.tsx
@@ -0,0 +1,80 @@
+import type { FC } from 'react';
+import React, { useState } from 'react';
+import Link from '@docusaurus/Link';
+import Translate, { translate } from '@docusaurus/Translate';
+
+import '../../css/landing-sections/trusted-by.scss';
+
+interface TrustedUser {
+ name: string;
+ link: string;
+ /**
+ * Optional logo URL. When omitted, a clean text wordmark is shown instead, so
+ * the section renders correctly before official logos are available.
+ * Host official customer logos on the API7 CDN (where the showcase logos
+ * already live) and set this to e.g.
+ * `https://static.apiseven.com/uploads/users/nasa.svg`.
+ */
+ logo?: string;
+}
+
+const USERS: TrustedUser[] = [
+ { name: 'NASA', link: 'https://www.jpl.nasa.gov/' },
+ { name: 'Zoom', link: 'https://zoom.us/' },
+ { name: 'HPE', link: 'https://www.hpe.com/' },
+ { name: 'Tencent', link: 'https://www.tencent.com/en-us/' },
+ { name: 'Hisense', link: 'https://global.hisense.com/' },
+ { name: 'k6', link: 'https://k6.io/' },
+ { name: 'Citi', link: 'https://www.citigroup.com/' },
+ { name: 'API7.ai', link: 'https://api7.ai/' },
+ { name: "McDonald's", link: 'https://www.mcdonalds.com/' },
+ { name: 'KFC', link: 'https://global.kfc.com/' },
+];
+
+const UserLogo: FC<{ user: TrustedUser }> = ({ user }) => {
+ const [failed, setFailed] = useState(!user.logo);
+ return (
+ <Link
+ className="trusted-by__item"
+ to={user.link}
+ target="_blank"
+ rel="noopener noreferrer"
+ aria-label={user.name}
+ >
+ {failed ? (
+ <span className="trusted-by__wordmark">{user.name}</span>
+ ) : (
+ <img
+ className="trusted-by__logo"
+ src={user.logo}
+ alt={user.name}
+ loading="lazy"
+ width={132}
+ height={36}
+ onError={() => setFailed(true)}
+ />
+ )}
+ </Link>
+ );
+};
+
+const TrustedBy: FC = () => (
+ <section
+ className="trusted-by"
+ aria-label={translate({ id: 'home.trustedBy.label', message: 'Organizations using Apache APISIX' })}
+ >
+ <div className="trusted-by__eyebrow">
+ <Translate id="home.trustedBy.title">Trusted by teams everywhere</Translate>
+ </div>
+ <div className="trusted-by__viewport">
+ {/* Duplicated once so the CSS marquee loops seamlessly at translateX(-50%). */}
+ <div className="trusted-by__track">
+ {[...USERS, ...USERS].map((user, index) => (
+ <UserLogo key={`${user.name}-${index}`} user={user} />
+ ))}
+ </div>
+ </div>
+ </section>
+);
+
+export default TrustedBy;
diff --git a/website/src/css/landing-sections/hero.scss b/website/src/css/landing-sections/hero.scss
index 61bdaa5005f..29fecfbbe81 100644
--- a/website/src/css/landing-sections/hero.scss
+++ b/website/src/css/landing-sections/hero.scss
@@ -23,57 +23,49 @@
.hero-subtitle {
z-index: 100;
- font-size: 1.1rem;
+ font-size: 1.15rem;
font-family: MaisonNeue-Light, sans-serif;
position: relative;
- color: #615d5d;
- line-height: 30px;
+ color: #5f5d5d;
+ line-height: 1.6;
letter-spacing: 0.2px;
- margin: 25px 0;
- padding-right: 20px;
- animation-delay: 0.25s;
+ max-width: 620px;
+ margin: 1.25rem 0 0;
+ animation-delay: 0.2s;
}
.hero-ctas {
display: flex;
align-items: center;
- animation-delay: 0.5s;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 12px;
+ margin-top: 1.75rem;
+ animation-delay: 0.4s;
}
.hero-sec-wrap {
display: flex;
- background: #f4f4f4ad;
- height: 100vh;
-}
-
-.homeCanvas-overlay {
- position: absolute;
- width: 50vw;
- height: 100vh;
- background: #fff;
- top: -1px;
- right: 0;
-}
-
-.homeCanvas {
- width: 50vw;
- height: 100vh;
+ justify-content: center;
+ background: #f7f7f8;
+ padding: clamp(3rem, 9vh, 7rem) 24px clamp(2.5rem, 7vh, 5rem);
}
.hero-text {
- height: 100%;
+ width: 100%;
+ max-width: 780px;
display: flex;
- flex-wrap: wrap;
- align-content: center;
- width: 50%;
- padding: 0 0 0 6vw;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
}
.hero-title {
font-family: MaisonNeue-Bold, sans-serif;
color: #121212;
- width: 42vw;
- font-size: 4.2rem;
+ max-width: 780px;
+ font-size: clamp(2.4rem, 1rem + 4.2vw, 4rem);
+ line-height: 1.08;
letter-spacing: 0.2px;
margin: 0;
}
@@ -150,12 +142,7 @@
@include respond-between(md, lg) {
.hero-title {
- width: 40vw;
- font-size: 5.2rem;
- }
-
- .hero-subtitle {
- width: 40vw;
+ font-size: clamp(3rem, 1rem + 5vw, 4.5rem);
}
}
@@ -205,3 +192,58 @@
animation: none;
}
}
+
+.hero-eyebrow {
+ display: inline-block;
+ font-size: 0.82rem;
+ font-weight: 600;
+ color: #a32d2d;
+ background: #fcebeb;
+ padding: 5px 14px;
+ border-radius: 20px;
+ margin-bottom: 1.25rem;
+}
+
+.btn-secondary {
+ background: #fff;
+ color: #1c1c1c;
+ border: 1px solid #d6d6d6;
+}
+
+.btn-secondary:hover {
+ background: #f1f1f1;
+ color: #1c1c1c;
+ border-color: #c0c0c0;
+}
+
+.hero-stats {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 1.5rem 2.5rem;
+ list-style: none;
+ width: 100%;
+ max-width: 640px;
+ margin: 2.25rem 0 0;
+ padding: 1.5rem 0 0;
+ border-top: 1px solid #e6e6e6;
+}
+
+.hero-stats__item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.hero-stats__value {
+ font-size: 1.5rem;
+ font-weight: 700;
+ color: #e8433e;
+ line-height: 1.1;
+}
+
+.hero-stats__label {
+ font-size: 0.85rem;
+ color: #777;
+ margin-top: 2px;
+}
diff --git a/website/src/css/landing-sections/integrations.scss b/website/src/css/landing-sections/integrations.scss
new file mode 100644
index 00000000000..4fb36e7b630
--- /dev/null
+++ b/website/src/css/landing-sections/integrations.scss
@@ -0,0 +1,97 @@
+.integrations {
+ max-width: 960px;
+ margin: 0 auto;
+ padding: 3.5rem var(--ifm-spacing-horizontal) 1rem;
+ text-align: center;
+}
+
+.integrations__heading {
+ font-size: clamp(1.6rem, 1rem + 2vw, 2.25rem);
+ font-weight: 700;
+ margin-bottom: 0.5rem;
+}
+
+.integrations__subtitle {
+ color: #6b6b6b;
+ max-width: 620px;
+ margin: 0 auto 2.25rem;
+ font-size: 1rem;
+ line-height: 1.6;
+}
+
+.integrations__groups {
+ display: flex;
+ flex-direction: column;
+ gap: 1.75rem;
+}
+
+.integrations__group-title {
+ font-size: 0.78rem;
+ letter-spacing: 0.07em;
+ text-transform: uppercase;
+ color: #9a9a9a;
+ margin-bottom: 0.85rem;
+}
+
+.integrations__tiles {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 0.75rem;
+}
+
+.integrations__tile {
+ display: flex;
+ align-items: center;
+ gap: 0.6rem;
+ padding: 0.65rem 1.1rem;
+ border: 1px solid #ececec;
+ border-radius: 0.75rem;
+ background: #fff;
+ transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
+
+ &:hover {
+ transform: translateY(-3px);
+ border-color: #f3b6b3;
+ box-shadow: 0 6px 18px rgba(232, 67, 62, 0.08);
+ }
+}
+
+.integrations__icon {
+ width: 30px;
+ height: 30px;
+ flex-shrink: 0;
+}
+
+.integrations__img {
+ width: 30px;
+ height: 30px;
+ object-fit: contain;
+ flex-shrink: 0;
+}
+
+.integrations__name {
+ font-size: 0.95rem;
+ font-weight: 500;
+ color: #2c2c2a;
+ white-space: nowrap;
+}
+
+.integrations__cta {
+ display: inline-block;
+ margin-top: 2rem;
+ font-size: 1rem;
+ font-weight: 600;
+ color: #e8433e;
+
+ &:hover {
+ color: #c7352f;
+ text-decoration: none;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .integrations__tile:hover {
+ transform: none;
+ }
+}
diff --git a/website/src/css/landing-sections/pathways.scss b/website/src/css/landing-sections/pathways.scss
new file mode 100644
index 00000000000..01312e5d666
--- /dev/null
+++ b/website/src/css/landing-sections/pathways.scss
@@ -0,0 +1,62 @@
+.pathways {
+ max-width: 1000px;
+ margin: 0 auto;
+ padding: 1rem var(--ifm-spacing-horizontal) 1.5rem;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1.25rem;
+}
+
+@media (max-width: 820px) {
+ .pathways {
+ grid-template-columns: 1fr;
+ }
+}
+
+.pathways__card {
+ display: flex;
+ flex-direction: column;
+ background: #fff;
+ border: 1px solid #ececec;
+ border-top: 3px solid #e8433e;
+ border-radius: 0.75rem;
+ padding: 1.6rem 1.4rem;
+ color: inherit;
+ transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
+
+ &:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 10px 28px rgba(0, 0, 0, 0.07);
+ border-color: #f3b6b3;
+ border-top-color: #e8433e;
+ color: inherit;
+ text-decoration: none;
+ }
+}
+
+.pathways__title {
+ font-size: 1.25rem;
+ font-weight: 700;
+ margin: 0 0 0.5rem;
+ color: #1c1c1c;
+}
+
+.pathways__desc {
+ font-size: 0.95rem;
+ line-height: 1.55;
+ color: #6b6b6b;
+ margin: 0 0 1rem;
+ flex: 1;
+}
+
+.pathways__cta {
+ font-size: 0.95rem;
+ font-weight: 600;
+ color: #e8433e;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .pathways__card:hover {
+ transform: none;
+ }
+}
diff --git a/website/src/css/landing-sections/trusted-by.scss b/website/src/css/landing-sections/trusted-by.scss
new file mode 100644
index 00000000000..fc2a5c28004
--- /dev/null
+++ b/website/src/css/landing-sections/trusted-by.scss
@@ -0,0 +1,89 @@
+.trusted-by {
+ padding: 3rem 0 2.5rem;
+ text-align: center;
+}
+
+.trusted-by__eyebrow {
+ font-size: 0.8rem;
+ letter-spacing: 0.09em;
+ text-transform: uppercase;
+ color: #8a8a8a;
+ margin-bottom: 1.5rem;
+}
+
+.trusted-by__viewport {
+ overflow: hidden;
+ // Fade the logos in/out at both edges instead of cutting them off hard.
+ -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
+ mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
+}
+
+.trusted-by__track {
+ display: flex;
+ width: max-content;
+ align-items: center;
+ gap: 3.5rem;
+ // Compositor-only animation (transform). The track holds two identical
+ // copies of the list, so translateX(-50%) lands exactly on the loop seam.
+ animation: trusted-by-marquee 45s linear infinite;
+ will-change: transform;
+}
+
+.trusted-by:hover .trusted-by__track {
+ animation-play-state: paused;
+}
+
+.trusted-by__item {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &:hover {
+ text-decoration: none;
+ }
+}
+
+.trusted-by__logo {
+ height: 34px;
+ width: auto;
+ object-fit: contain;
+ filter: grayscale(1);
+ opacity: 0.65;
+ transition: filter 0.2s ease, opacity 0.2s ease;
+
+ .trusted-by__item:hover & {
+ filter: grayscale(0);
+ opacity: 1;
+ }
+}
+
+.trusted-by__wordmark {
+ font-size: 1.15rem;
+ font-weight: 700;
+ white-space: nowrap;
+ color: #7a7975;
+ transition: color 0.2s ease;
+
+ .trusted-by__item:hover & {
+ color: #e8433e;
+ }
+}
+
+@keyframes trusted-by-marquee {
+ from {
+ transform: translateX(0);
+ }
+ to {
+ transform: translateX(-50%);
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .trusted-by__track {
+ animation: none;
+ width: auto;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 2rem 3rem;
+ }
+}
diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx
index a57c738c3c3..672772387c0 100644
--- a/website/src/pages/index.tsx
+++ b/website/src/pages/index.tsx
@@ -13,6 +13,9 @@ import Benefits from '../components/sections/Benefits';
import Comparison from '../components/sections/Comparison';
import OpensourcePromo from '../components/sections/OpensourcePromo';
import EndCTA from '../components/sections/Endcta';
+import TrustedBy from '../components/sections/TrustedBy';
+import Pathways from '../components/sections/Pathways';
+import Integrations from '../components/sections/Integrations';
// Structured data for the homepage. Organization + WebSite are already injected
// globally by config/schema-org.js; these add product-level (SoftwareApplication)
@@ -136,8 +139,11 @@ const Index: FC = () => (
<script type="application/ld+json">{JSON.stringify(HOMEPAGE_FAQ_SCHEMA)}</script>
</Head>
<HeroSection />
+ <TrustedBy />
+ <Pathways />
<Architecture />
<Features />
+ <Integrations />
<Benefits />
<Comparison />
<OpensourcePromo />