Skip to content
Merged
1 change: 1 addition & 0 deletions packages/app/public/logos/amd.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/app/public/logos/nvidia.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions packages/app/public/logos/prime-intellect.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions packages/app/src/components/page-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function ChartTabs({ initialTab }: { initialTab: string }) {
return (
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">
{/* Mobile: Dropdown */}
<div className="sm:hidden mb-4">
<div className="lg:hidden mb-4">
<div className="w-full border-t-2 border-secondary dark:border-primary pb-6" />
<Card>
<div className="space-y-2">
Expand Down Expand Up @@ -149,7 +149,7 @@ function ChartTabs({ initialTab }: { initialTab: string }) {
</div>

{/* Desktop: Tabs */}
<TabsList data-testid="chart-section-tabs" className="hidden sm:flex mb-4">
<TabsList data-testid="chart-section-tabs" className="hidden lg:flex mb-4">
<TabsTrigger
data-testid="tab-trigger-inference"
data-ph-capture-attribute-tab="inference"
Expand Down Expand Up @@ -252,7 +252,7 @@ export function PageContent({ initialTab = 'inference' }: { initialTab?: string
<ExportNudge />
<UnofficialRunProvider>
<main className="relative min-h-screen">
<div className="container mx-auto px-4 lg:px-8 flex flex-col gap-16 lg:gap-4">
<div className="container mx-auto px-4 lg:px-8 flex flex-col gap-6 lg:gap-4">
<section>
<Card data-testid="intro-section">
<h2 className="text-lg font-semibold mb-2">
Expand Down
109 changes: 24 additions & 85 deletions packages/app/src/components/quote-carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useCallback, useEffect, useRef, useState } from 'react';

import { track } from '@/lib/analytics';
import { CompanyLogo, highlightBrand } from '@/components/quotes/quote-utils';

export interface CarouselQuote {
text: string;
Expand Down Expand Up @@ -63,48 +64,14 @@ function buildCompanyQuotes(quotes: CarouselQuote[], order?: string[]): CompanyE
return shuffleArray(entries);
}

function CompanyLogo({ quote }: { quote: CarouselQuote }) {
const [failed, setFailed] = useState(false);

if (!quote.logo || failed) {
return (
<div className="h-12 shrink-0 rounded-full bg-muted flex items-center justify-center px-3">
<span className="text-xs font-bold text-muted-foreground">{quote.company[0]}</span>
</div>
);
}

return (
<img
src={`/logos/${quote.logo}`}
alt={quote.company}
className="h-10 min-w-10 max-w-20 shrink-0 object-contain grayscale opacity-70 dark:invert"
onError={() => setFailed(true)}
/>
);
}

function highlightBrand(text: string) {
const parts = text.split(/(InferenceMAX™?|InferenceX™?|InferenceMAX|InferenceX)/gi);
return parts.map((part, i) =>
/^inference(max|x)/i.test(part) ? (
<span key={i} className="text-secondary dark:text-primary font-semibold">
{part}
</span>
) : (
part
),
);
}

function QuoteBlock({ quote }: { quote: CarouselQuote }) {
return (
<blockquote className="w-full">
<p className="text-sm lg:text-base leading-relaxed text-muted-foreground italic">
&ldquo;{highlightBrand(quote.text)}&rdquo;
</p>
<footer className="mt-3 flex items-center gap-3">
<CompanyLogo quote={quote} />
<CompanyLogo company={quote.company} logo={quote.logo} />
<div className="h-12 w-0.5 bg-secondary dark:bg-primary" />
<div className="text-sm">
<span className="font-semibold text-foreground">{quote.name}</span>
Expand All @@ -126,34 +93,13 @@ export function QuoteCarousel({
const [entries, setEntries] = useState<CompanyEntry[]>([]);
const [activeIndex, setActiveIndex] = useState(0);
const [fading, setFading] = useState(false);
const [measuredHeight, setMeasuredHeight] = useState(0);
const measureRef = useRef<HTMLDivElement>(null);
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);

// Build shuffled company order on mount (client only)
useEffect(() => {
setEntries(buildCompanyQuotes(quotes, order));
}, [quotes, order]);

// Measure tallest quote and update on resize
useEffect(() => {
const el = measureRef.current;
if (!el) return;
const measure = () => {
const children = el.children;
let max = 0;
for (let i = 0; i < children.length; i++) {
const h = (children[i] as HTMLElement).offsetHeight;
if (h > max) max = h;
}
setMeasuredHeight(max);
};
measure();
const observer = new ResizeObserver(measure);
observer.observe(el);
return () => observer.disconnect();
}, [entries.length]);

const advance = useCallback(() => {
setFading(true);
setTimeout(() => {
Expand Down Expand Up @@ -187,26 +133,10 @@ export function QuoteCarousel({

if (entries.length === 0) return null;

const current = entries[activeIndex];

return (
<div className="flex flex-col gap-4">
{/* Hidden measurement container — renders all quotes to find tallest */}
<div
ref={measureRef}
aria-hidden
className="absolute left-0 right-0 overflow-hidden pointer-events-none"
style={{ visibility: 'hidden', position: 'absolute', zIndex: -1 }}
>
{entries.map((e) => (
<div key={e.company}>
<QuoteBlock quote={e.quote} />
</div>
))}
</div>

{/* Company logo strip — same shuffled order as cycling */}
<div className="flex flex-wrap items-center justify-between gap-y-2 mx-4">
{/* Company logo strip */}
<div className="flex flex-wrap items-center justify-evenly gap-x-4 gap-y-2 mx-4">
{entries.map((e, i) => (
<button
key={e.company}
Expand All @@ -223,22 +153,31 @@ export function QuoteCarousel({
))}
</div>

{/* Visible quote — fixed height from measurement */}
<div className="relative flex items-start" style={{ minHeight: measuredHeight || undefined }}>
<div
className={`w-full transition-opacity duration-300 ease-in-out ${fading ? 'opacity-0' : 'opacity-100'}`}
>
<QuoteBlock quote={current.quote} />
</div>
{moreHref && (
{/* All quotes stacked in same grid cell — tallest sets height */}
<div className="grid">
{entries.map((e, i) => (
<div
key={e.company}
className={`col-start-1 row-start-1 transition-opacity duration-300 ease-in-out ${
i === activeIndex && !fading ? 'opacity-100' : 'opacity-0'
}`}
aria-hidden={i !== activeIndex}
>
<QuoteBlock quote={e.quote} />
</div>
))}
</div>

{moreHref && (
<div className="flex justify-end">
<a
href={moreHref}
className="absolute right-0 bottom-0 text-xs font-bold text-secondary dark:text-primary hover:underline transition-opacity"
className="text-xs font-bold text-secondary dark:text-primary hover:underline"
>
See more supporters &rarr;
</a>
)}
</div>
</div>
)}
</div>
);
}
37 changes: 37 additions & 0 deletions packages/app/src/components/quotes/quote-utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client';

import { useState } from 'react';

export function CompanyLogo({ company, logo }: { company: string; logo?: string }) {
const [failed, setFailed] = useState(false);

if (!logo || failed) {
return (
<div className="h-12 shrink-0 rounded-full bg-muted flex items-center justify-center px-3">
<span className="text-xs font-bold text-muted-foreground">{company[0]}</span>
</div>
);
}

return (
<img
src={`/logos/${logo}`}
alt={company}
className="h-10 min-w-10 max-w-20 shrink-0 object-contain grayscale opacity-70 dark:invert"
onError={() => setFailed(true)}
/>
);
}

export function highlightBrand(text: string) {
const parts = text.split(/(InferenceMAX™?|InferenceX™?|InferenceMAX|InferenceX)/gi);
return parts.map((part, i) =>
/^inference(max|x)/i.test(part) ? (
<span key={i} className="text-secondary dark:text-primary font-semibold">
{part}
</span>
) : (
part
),
);
}
33 changes: 21 additions & 12 deletions packages/app/src/components/quotes/quotes-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,38 @@

import { Card } from '@/components/ui/card';

import { CompanyLogo, highlightBrand } from './quote-utils';
import { QUOTES } from './quotes-data';

function QuoteCard({
text,
name,
title,
company,
logo,
link,
}: {
text: string;
name: string;
title: string;
company: string;
logo?: string;
link?: string;
}) {
const content = (
<Card className="p-6 lg:p-8">
<blockquote className="space-y-4">
<p className="text-base lg:text-lg leading-relaxed text-foreground/90 italic">
&ldquo;{text}&rdquo;
</p>
<footer className="text-sm text-muted-foreground pl-6">
<span className="font-semibold text-foreground not-italic">&ndash; {name}</span>
<span className="block mt-0.5">{title}</span>
</footer>
</blockquote>
</Card>
<blockquote className="space-y-4">
<p className="text-base lg:text-lg leading-relaxed text-muted-foreground italic">
&ldquo;{highlightBrand(text)}&rdquo;
</p>
<footer className="flex items-center gap-3">
<CompanyLogo company={company} logo={logo} />
<div className="h-12 w-0.5 bg-secondary dark:bg-primary" />
<div className="text-sm">
<span className="font-semibold text-foreground">{name}</span>
<span className="block text-muted-foreground text-xs">{title}</span>
</div>
</footer>
</blockquote>
);

if (link) {
Expand Down Expand Up @@ -61,13 +68,15 @@ export function QuotesContent() {
</p>
</Card>
<Card>
<div className="flex flex-col gap-3 md:pl-6">
<div className="flex flex-col gap-10 md:gap-12 mx-4 md:mx-8">
{QUOTES.map((quote) => (
<QuoteCard
key={quote.name}
text={quote.text}
name={quote.name}
title={quote.title}
company={quote.company}
logo={quote.logo}
link={quote.link}
/>
))}
Expand Down
12 changes: 12 additions & 0 deletions packages/app/src/components/quotes/quotes-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,35 @@ export const QUOTES: Quote[] = [
name: 'Dr. Lisa Su',
title: 'Chair and CEO, AMD',
company: 'AMD',
logo: 'amd.svg',
},
{
text: "Inference demand is growing exponentially, driven by long-context reasoning. NVIDIA Grace Blackwell NVL72 was invented for this new era of thinking AI. NVIDIA is meeting that demand through constant hardware and software innovation to enable what's next in AI. By benchmarking frequently, InferenceMAX\u2122 gives the industry a transparent view of LLM inference performance on real-world workloads. The results are clear: Grace Blackwell NVL72 with TRT-LLM and Dynamo delivers unmatched performance per dollar and per megawatt\u2014powering the most productive and cost-effective AI factories in the world.",
name: 'Jensen Huang',
title: 'Founder & CEO, NVIDIA',
company: 'NVIDIA',
logo: 'nvidia.svg',
},
{
text: "Speed is the moat. InferenceMAX\u2122's nightly benchmarks match the speed of improvement of the AMD software stack. It's fantastic to see AMD's MI300, MI325, and MI355 GPUs performing so well across diverse workloads and interactivity levels.",
name: 'Anush Elangovan',
title: 'VP GPU Software, AMD',
company: 'AMD',
logo: 'amd.svg',
},
{
text: 'InferenceMAX\u2122 highlights workloads that the ML community cares about. At NVIDIA, we welcome these comparisons because they underscore the advantage of our full-stack approach\u2014from GPUs hardware to NVLink networking to NVL72 Rack Scale to Dynamo disaggregated serving that consistently delivers industry-leading inference performance and ROI at scale.',
name: 'Ian Buck',
title: 'VP & GM, Hyperscale, NVIDIA & Inventor of CUDA',
company: 'NVIDIA',
logo: 'nvidia.svg',
},
{
text: "InferenceMAX\u2122's nightly results highlight the rapid pace of progress in the AMD software stack. It's exciting to witness the birth of an open project that provides a tied feedback loop between what the software team works on here at AMD and how it affects specific ML use cases across our MI300, MI325, and MI355 GPUs. I'm looking forward to see what's next for InferenceMAX and to showcase what the AMD platform can do. AMD GPUs will continue to get faster every week.",
name: 'Quentin Colombet',
title: 'Senior Director, AMD, Ex-Brium CEO',
company: 'AMD',
logo: 'amd.svg',
},
{
text: "Our mission at Azure is to give customers the most performant, efficient, and cost-effective cloud for AI. SemiAnalysis InferenceMAX\u2122 supports that mission by providing transparent, reproducible benchmarks that track inference performance across GPUs and software stacks under realistic workloads. This continuous data on throughput, efficiency, and cost per watt strengthens our ability to tune Azure's inference platform for scale, helping customers build with confidence on Microsoft Cloud.",
Expand Down Expand Up @@ -151,4 +156,11 @@ export const QUOTES: Quote[] = [
company: 'Vultr',
logo: 'vultr.svg',
},
{
text: "At Prime Intellect, we're pushing the frontier of AI post-training and open research. InferenceX\u2122 complements that work by providing open, reproducible benchmarks that track real-world inference performance across hardware and software stacks as they evolve. For researchers like us, having transparent, continuously updated data on throughput and efficiency means we can focus on building better models instead of second-guessing infrastructure. This is the kind of community-driven effort that accelerates progress for everyone.",
name: 'Jack Min Ong',
title: 'Researcher, Prime Intellect',
company: 'Prime Intellect',
logo: 'prime-intellect.svg',
},
];