Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,407 changes: 3,407 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-intersection-observer": "^9.16.0",
"recharts": "^3.2.0",
"rehype-pretty-code": "^0.14.1",
"shiki": "^3.2.1",
"sonner": "^1.5.0",
Expand Down
2 changes: 1 addition & 1 deletion src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
:root {
--primary: black;
--secondary: #666;
--background: #f5f0ec;
--background: #fff;
--negative: #222;
--subtle: #0000001a; /* black/10 */
--danger: red;
Expand Down
47 changes: 30 additions & 17 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
'use client'

import { useFold } from '~/lib/hooks/use-fold'
// import { useFold } from '~/lib/hooks/use-fold'
import { cn } from '~/lib/utils/cn'
// import { Button } from '~/ui/button'
import { CTA } from '~/ui/cta'
import { Chat } from '~/ui/demo/chat'
import { Footer } from '~/ui/footer'
// import { Arrow } from '~/ui/icons/arrow'
import { Partners } from '~/ui/partners'
import { ScrollButton } from '~/ui/scroll-button'
import { Testimonials } from '~/ui/testimonials'
import { TrustedBy } from '~/ui/trusted-by'
import { Video } from '~/ui/video/video'

// import { VimeoVideo } from '~/ui/video/vimeo-video'

// import { Video } from '~/ui/video/video'

const Section = ({ children, className }: { children: React.ReactNode; className?: string }) => (
<div
className={cn(
Expand All @@ -22,25 +28,26 @@ const Section = ({ children, className }: { children: React.ReactNode; className
)

const Hero = () => {
const { isBelowFold } = useFold()
// const { isBelowFold } = useFold()
return (
<Section className="relative h-screen">
<div className="flex h-full w-full flex-col items-center justify-center">
<div className="w-fit max-w-5xl">
<h2 className="mb-4 text-2xl">
We&apos;re an applied AI Lab helping companies get intelligence to production.
</h2>
<Video
hlsUrl="https://d2os0zhpsj02b0.cloudfront.net/hero/hls/master.m3u8"
mp4Url="https://d2os0zhpsj02b0.cloudfront.net/hero/preview.mp4"
posterUrl="/images/video-thumbnail.jpg"
transcriptionUrl="/transcripts/hero.vtt"
/>
<div className="flex w-fit max-w-5xl flex-col items-center gap-24">
<div className="flex flex-col items-center gap-4">
<h1 className="text-2xl">Rubric Agency</h1>
<h1 className="mb-4 text-4xl">
We build{' '}
<span className="bg-gradient-to-r from-gray-800 to-blue-600 bg-clip-text text-transparent dark:from-gray-200 dark:to-blue-600">
powerful
</span>{' '}
AI systems.
</h1>
</div>

<Chat />
</div>
</div>
<ScrollButton
className={cn('absolute bottom-6 transition-opacity', { 'opacity-0': isBelowFold })}
/>
<TrustedBy />
</Section>
)
}
Expand All @@ -50,10 +57,16 @@ export default function Page() {
<div className="flex flex-col items-center">
<Hero />
<Section className="space-y-40">
<TrustedBy />
<Testimonials />
<Partners />
<CTA />
{/* <VimeoVideo /> */}
<Video
hlsUrl="https://d2os0zhpsj02b0.cloudfront.net/hero/hls/master.m3u8"
mp4Url="https://d2os0zhpsj02b0.cloudfront.net/hero/preview.mp4"
posterUrl="/images/video-thumbnail.jpg"
transcriptionUrl="/transcripts/hero.vtt"
/>
</Section>
<Footer />
</div>
Expand Down
1 change: 1 addition & 0 deletions src/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { cn } from '~/lib/utils/cn'

const variants = {
brand: 'bg-black dark:bg-white text-white dark:text-black rounded-full hover:',
default: 'bg-subtle dark:enabled:hover:bg-white/20 enabled:hover:bg-black/20 rounded-full',
ghost: 'dark:enabled:hover:bg-white/20 enabled:hover:bg-black/20 rounded-full',
icon:
Expand Down
6 changes: 4 additions & 2 deletions src/ui/cta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Button } from './button'
import { Arrow } from './icons/arrow'

export const CTA = () => {
const hook = "We don't have a sales team. Let's talk."
const hook = 'Production AI builds in 90 days.'
const posthog = usePostHog()

return (
Expand All @@ -24,7 +24,9 @@ export const CTA = () => {
})
}
>
<Button className="w-full sm:w-fit">Get in touch</Button>
<Button variant="brand" className="w-full sm:w-fit">
Get in touch
</Button>
</Link>
<Link
href="/blog/introducing-rubric-labs"
Expand Down
49 changes: 49 additions & 0 deletions src/ui/demo/blocks/chart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useMemo } from 'react'
import { Area, Line, LineChart, ReferenceDot, ResponsiveContainer } from 'recharts'

export type ChartData = { name: string; value: number }[]

export function Chart({ data }: { data: ChartData }) {
const nf = new Intl.NumberFormat()
const maxIndex = useMemo(() => {
if (!data || data.length === 0) return -1
let idx = 0
let max = data[0]?.value ?? 0
for (let i = 1; i < data.length; i++) {
const v = data[i]?.value ?? 0
if (v > max) {
max = v
idx = i
}
}
return idx
}, [data])
const peak = maxIndex >= 0 ? data[maxIndex] : undefined

return (
<div className="w-full rounded-md border-1 border-gray-200 bg-white p-2 shadow-sm dark:border-gray-800 dark:bg-transparent">
<div className="mb-1 flex items-center justify-between">
<div className="text-gray-500 text-xs dark:text-gray-400">Visitors</div>
{peak ? (
<div className="rounded-full border-1 border-gray-200 bg-white px-1.5 py-0.5 text-[10px] text-gray-600 dark:border-gray-800 dark:bg-transparent dark:text-gray-400">
Peak {nf.format(peak.value)} at {peak.name}
</div>
) : null}
</div>
<div className="h-20">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={data}>
<Area type="monotone" dataKey="value" fill="#E5E7EB" stroke="none" />
<Line type="monotone" dataKey="value" stroke="#3B82F6" strokeWidth={2} dot={false} />
{peak ? (
<>
<ReferenceDot x={peak.name} y={peak.value} r={6} fill="#3B82F6" fillOpacity={0.15} />
<ReferenceDot x={peak.name} y={peak.value} r={3.5} fill="#3B82F6" />
</>
) : null}
</LineChart>
</ResponsiveContainer>
</div>
</div>
)
}
71 changes: 71 additions & 0 deletions src/ui/demo/blocks/stats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use client'

import { useEffect, useMemo, useState } from 'react'

export type StatsData = { active: number; total: number }

export function Stats({ data }: { data: StatsData }) {
const nf = useMemo(() => new Intl.NumberFormat(), [])

// delta % vs a naive baseline (total - active), clamp to [-100, 100]
const baseline = Math.max(1, data.total - data.active)
const deltaPct = Math.max(
-100,
Math.min(100, Math.round(((data.total - baseline) / baseline) * 100))
)
const deltaSign = deltaPct >= 0 ? '+' : ''

// Live counter simulation based on provided active (gentle jitter)
const [live, setLive] = useState<number>(() => Math.max(0, Math.round(data.active)))
useEffect(() => {
setLive(Math.max(0, Math.round(data.active)))
}, [data.active])
useEffect(() => {
const id = window.setInterval(() => {
setLive(prev => {
const jitter = Math.floor(Math.random() * 3) - 1 // -1..+1
const next = Math.max(0, prev + jitter)
return next
})
}, 2400)
return () => window.clearInterval(id)
}, [])

return (
<div className="grid w-full grid-cols-2 gap-2">
{/* 24h total */}
<div className="w-full rounded-lg border-1 border-gray-200 bg-white p-2 shadow-sm dark:border-gray-800 dark:bg-transparent">
<div className="flex items-baseline justify-between">
<div className="text-gray-500 text-xs dark:text-gray-400">Page views</div>
<div className="flex items-center gap-1 rounded-full border-1 border-green-500 bg-white px-1.5 py-0.5 text-[10px] text-green-600 dark:border-green-900/50 dark:bg-transparent dark:text-green-400">
<span>
{deltaSign}
{deltaPct}%
</span>
</div>
</div>
<div className="mt-1 flex items-end justify-between">
<div className="font-semibold text-gray-900 text-xl dark:text-gray-100">
{nf.format(data.total)}
</div>
<div className="text-gray-500 text-xs dark:text-gray-400">today</div>
</div>
</div>

{/* Live now */}
<div className="w-full rounded-lg border-1 border-gray-200 bg-white p-2 shadow-sm dark:border-gray-800 dark:bg-transparent">
<div className="flex items-baseline justify-between">
<div className="text-gray-500 text-xs dark:text-gray-400">Live page views</div>
<div className="flex items-center gap-1 rounded-full border-1 border-red-500 bg-white px-1.5 py-0.5 text-[10px] text-red-600 dark:border-red-900/50 dark:bg-transparent dark:text-red-400">
<span className="inline-block h-1.5 w-1.5 animate-pulse rounded-full bg-red-500" />
LIVE
</div>
</div>
<div className="mt-1 flex items-end justify-between">
<div className="font-semibold text-gray-900 text-xl dark:text-gray-100">{nf.format(live)}</div>
<div className="text-gray-500 text-xs dark:text-gray-400">right now</div>
</div>
</div>
</div>
)
}
9 changes: 9 additions & 0 deletions src/ui/demo/chat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Messages } from './messages'

export function Chat() {
return (
<div className="flex h-[420px] w-[500px] flex-col flex-col justify-between gap-4 rounded-2xl border-1 border-gray-100 p-8 shadow-lg md:w-[700px] dark:border-gray-900">
<Messages />
</div>
)
}
Loading