Files
greywall-landing-page/components/comparison.tsx
2026-03-19 15:31:15 -04:00

238 lines
7.6 KiB
TypeScript

'use client'
import { ArrowRight, Check, X, Minus } from 'lucide-react'
type CellValue = 'yes' | 'no' | 'partial' | string
interface Row {
feature: string
greywall: CellValue
safehouse: CellValue
claudeSandbox: CellValue
containers: CellValue
}
const rows: Row[] = [
{
feature: 'Linux support',
greywall: 'yes',
safehouse: 'no',
claudeSandbox: 'yes',
containers: 'yes',
},
{
feature: 'macOS support',
greywall: 'yes',
safehouse: 'yes',
claudeSandbox: 'yes',
containers: 'partial',
},
{
feature: 'Filesystem isolation',
greywall: 'yes',
safehouse: 'yes',
claudeSandbox: 'yes',
containers: 'yes',
},
{
feature: 'Network isolation',
greywall: 'yes',
safehouse: 'no',
claudeSandbox: 'yes',
containers: 'yes',
},
{
feature: 'Command blocking',
greywall: 'yes',
safehouse: 'no',
claudeSandbox: 'no',
containers: 'no',
},
{
feature: 'Real-time violation monitoring',
greywall: 'yes',
safehouse: 'no',
claudeSandbox: 'no',
containers: 'no',
},
{
feature: 'Learning mode',
greywall: 'yes',
safehouse: 'no',
claudeSandbox: 'no',
containers: 'no',
},
{
feature: 'Works with any agent',
greywall: 'yes',
safehouse: 'yes',
claudeSandbox: 'no',
containers: 'yes',
},
{
feature: 'Sandboxes entire process',
greywall: 'yes',
safehouse: 'yes',
claudeSandbox: 'partial',
containers: 'yes',
},
{
feature: 'Native tool access',
greywall: 'yes',
safehouse: 'yes',
claudeSandbox: 'yes',
containers: 'no',
},
{
feature: 'No image rebuilds on changes',
greywall: 'yes',
safehouse: 'yes',
claudeSandbox: 'yes',
containers: 'no',
},
{
feature: 'Syscall filtering',
greywall: 'yes',
safehouse: 'no',
claudeSandbox: 'no',
containers: 'partial',
},
{
feature: 'Dynamic allow/deny',
greywall: 'yes',
safehouse: 'no',
claudeSandbox: 'partial',
containers: 'no',
},
]
function CellIcon({ value }: { value: CellValue }) {
if (value === 'yes') {
return (
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-400/10" aria-label="Supported">
<Check className="h-3 w-3 text-green-400" aria-hidden="true" />
</span>
)
}
if (value === 'no') {
return (
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-red-400/10" aria-label="Not supported">
<X className="h-3 w-3 text-red-400/70" aria-hidden="true" />
</span>
)
}
if (value === 'partial') {
return (
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-yellow-400/10" aria-label="Partial support">
<Minus className="h-3 w-3 text-yellow-400/70" aria-hidden="true" />
</span>
)
}
return (
<span className="text-xs font-mono text-muted-foreground">{value}</span>
)
}
export function Comparison() {
return (
<section id="comparison" className="py-24 px-6 border-t border-border/30">
<div className="mx-auto max-w-5xl">
<div className="max-w-2xl mb-12">
<div className="flex items-center gap-2 mb-4">
<ArrowRight className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
How it compares
</span>
</div>
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
Not all sandboxes are equal.
</h2>
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
Greywall combines filesystem isolation, network control, syscall filtering,
and real-time monitoring in a single tool. Here&apos;s how it stacks up.
</p>
</div>
{/* Table — scrollable on mobile */}
<div className="rounded-lg border border-border/40 overflow-x-auto scrollbar-hide">
<table className="w-full text-sm min-w-[540px]">
<thead>
<tr className="border-b border-border/40 bg-card/40">
<th className="text-left py-3 sm:py-3.5 px-3 sm:px-4 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
Feature
</th>
<th className="text-center py-3 sm:py-3.5 px-2 sm:px-3 font-sans font-semibold text-xs uppercase tracking-wider text-primary">
Greywall
</th>
<th className="text-center py-3 sm:py-3.5 px-2 sm:px-3 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
Safehouse
</th>
<th className="text-center py-3 sm:py-3.5 px-2 sm:px-3 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
Claude
</th>
<th className="text-center py-3 sm:py-3.5 px-2 sm:px-3 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
Containers
</th>
</tr>
</thead>
<tbody>
{rows.map((row, i) => (
<tr
key={row.feature}
className={`border-b border-border/20 ${i % 2 === 0 ? 'bg-card/10' : ''}`}
>
<td className="py-2.5 sm:py-3 px-3 sm:px-4 font-serif text-sm text-foreground whitespace-nowrap">
{row.feature}
</td>
<td className="py-2.5 sm:py-3 px-2 sm:px-3 text-center">
<div className="flex justify-center">
<CellIcon value={row.greywall} />
</div>
</td>
<td className="py-2.5 sm:py-3 px-2 sm:px-3 text-center">
<div className="flex justify-center">
<CellIcon value={row.safehouse} />
</div>
</td>
<td className="py-2.5 sm:py-3 px-2 sm:px-3 text-center">
<div className="flex justify-center">
<CellIcon value={row.claudeSandbox} />
</div>
</td>
<td className="py-2.5 sm:py-3 px-2 sm:px-3 text-center">
<div className="flex justify-center">
<CellIcon value={row.containers} />
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Legend */}
<div className="mt-6 flex flex-wrap items-center gap-5 text-xs font-sans text-muted-foreground">
<div className="flex items-center gap-1.5">
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-400/10">
<Check className="h-3 w-3 text-green-400" aria-hidden="true" />
</span>
Supported
</div>
<div className="flex items-center gap-1.5">
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-yellow-400/10">
<Minus className="h-3 w-3 text-yellow-400/70" aria-hidden="true" />
</span>
Partial
</div>
<div className="flex items-center gap-1.5">
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-red-400/10">
<X className="h-3 w-3 text-red-400/70" aria-hidden="true" />
</span>
Not supported
</div>
</div>
</div>
</section>
)
}