Skip to content

Service Grid

Overview

The service grid displays the six engagement types as a responsive card grid. Each card has an icon, title, bullet features, and a per-card inquiry CTA.

┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐
│ ◈ KEYNOTE │ │ ◈ EXECUTIVE │ │ ◈ LAW ENFORCEMENT │
│ SPEAKING │ │ WORKSHOPS │ │ TRAINING │
│ │ │ │ │ │
│ RSA · DEF CON │ │ CISO Academy │ │ FBI · USSS · DHS │
│ Black Hat │ │ Board Briefings │ │ Quantico │
│ Fortune 500 │ │ Red Team Stories │ │ State Agencies │
│ │ │ │ │ │
│ [ INQUIRE ] │ │ [ INQUIRE ] │ │ [ INQUIRE ] │
└──────────────────┘ └──────────────────┘ └──────────────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐
│ ◈ THREAT │ │ ◈ MEDIA & │ │ ◈ ENTERPRISE │
│ INTELLIGENCE │ │ DOCUMENTARY │ │ CONSULTING │
│ ... │ │ ... │ │ ... │
└──────────────────┘ └──────────────────┘ └──────────────────────────┘

Implementation

components/ServicesGrid.tsx
const services = [
{
id: 'keynote',
icon: '',
title: 'Keynote Speaking',
features: ['RSA · DEF CON · Black Hat', 'Fortune 500', 'Financial Institutions'],
cta: 'Inquire',
},
// … 5 more
];
export function ServicesGrid() {
return (
<section className="py-20">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
{/* Section header */}
<div className="mb-12">
<span className="text-xs font-mono uppercase tracking-widest text-brand-red">
Services
</span>
<h2 className="mt-2 text-3xl font-bold text-white">
Speaking & Enterprise Services
</h2>
</div>
{/* Grid */}
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{services.map((service) => (
<ServiceCard key={service.id} {...service} />
))}
</div>
</div>
</section>
);
}

Service Card

components/ServiceCard.tsx
interface ServiceCardProps {
icon: string;
title: string;
features: string[];
cta: string;
onInquire?: () => void;
}
export function ServiceCard({ icon, title, features, cta, onInquire }: ServiceCardProps) {
return (
<div className="flex flex-col rounded-sm border border-brand-gray bg-brand-charcoal p-6 gap-4">
{/* Icon */}
<span className="text-2xl text-brand-red font-mono">{icon}</span>
{/* Title */}
<h3 className="text-lg font-semibold uppercase tracking-wide text-white">
{title}
</h3>
{/* Features list */}
<ul className="flex-1 space-y-1">
{features.map((f) => (
<li key={f} className="text-sm text-white/60">{f}</li>
))}
</ul>
{/* CTA */}
<Button variant="ghost" size="sm" onClick={onInquire}>
{cta}
</Button>
</div>
);
}

Inquire CTA Behavior

Each card’s “Inquire” button scrolls to the contact form and pre-fills the Engagement Type select to match the card:

const handleInquire = (engagementType: string) => {
const form = document.getElementById('contact-form');
form?.scrollIntoView({ behavior: 'smooth' });
// Set form field via URL hash or React state
};

Responsive Behavior

ViewportColumnsGap
Mobile (< md)1gap-6 (24px)
Tablet (md)2gap-6
Desktop (lg+)3gap-6

All cards are equal height within each row due to flex flex-col with flex-1 on the features list.