feat(portfolio): add operator profile with banner, publication, and honors
This commit is contained in:
143
components/portfolio/OperatorProfile.tsx
Normal file
143
components/portfolio/OperatorProfile.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import { honors, identity, publication, skillsMatrix } from "@/lib/portfolio";
|
||||
|
||||
export function OperatorProfile() {
|
||||
return (
|
||||
<section
|
||||
id="operator"
|
||||
className="bg-blacksite-bg px-4 py-20 text-blacksite-text sm:px-6 lg:px-8"
|
||||
>
|
||||
<div className="mx-auto max-w-7xl">
|
||||
<div className="section-label mb-4">OPERATOR PROFILE /.06</div>
|
||||
|
||||
<div className="overflow-hidden rounded-2xl border border-blacksite-line bg-blacksite-surface shadow-[0_24px_90px_rgba(0,0,0,0.35)]">
|
||||
<div className="relative h-48 overflow-hidden border-b border-blacksite-line bg-blacksite-bg2 sm:h-56 lg:h-64">
|
||||
<img
|
||||
src="/banner.jpg"
|
||||
alt=""
|
||||
className="h-full w-full object-cover opacity-45 saturate-50"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/85" aria-hidden="true" />
|
||||
<div
|
||||
className="absolute inset-0 bg-[linear-gradient(90deg,rgba(0,240,255,0.12),transparent_34%,rgba(255,90,31,0.08))]"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-px bg-blacksite-line lg:grid-cols-[1fr_1.15fr]">
|
||||
<div className="bg-blacksite-surface p-6 sm:p-8 lg:p-10">
|
||||
<p className="section-label mb-4">IDENTITY</p>
|
||||
<h2 className="font-display text-4xl leading-tight tracking-tight text-blacksite-text sm:text-5xl">
|
||||
{identity.name}
|
||||
</h2>
|
||||
<dl className="mt-8 grid gap-5 text-sm text-blacksite-muted sm:text-base">
|
||||
<div>
|
||||
<dt className="section-label mb-2">ROLE</dt>
|
||||
<dd className="text-blacksite-text">{identity.roleLine}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="section-label mb-2">EDUCATION</dt>
|
||||
<dd>{identity.education}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="section-label mb-2">SITE</dt>
|
||||
<dd>{identity.site}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-px bg-blacksite-line">
|
||||
<article className="bg-blacksite-surface2 p-6 sm:p-8 lg:p-10">
|
||||
<p className="section-label mb-4">PUBLICATION</p>
|
||||
<h3 className="font-display text-2xl leading-tight text-blacksite-text">
|
||||
{publication.title}
|
||||
</h3>
|
||||
<p className="mt-4 text-sm text-blacksite-muted sm:text-base">
|
||||
{publication.venue} · {publication.year} · {publication.authorship}
|
||||
</p>
|
||||
<a
|
||||
href={publication.href}
|
||||
className="section-label mt-6 inline-flex text-signal-cyan underline decoration-signal-cyan/35 underline-offset-4 transition-colors hover:text-blacksite-text focus-visible:outline-signal-cyan"
|
||||
>
|
||||
DOI: {publication.doi}
|
||||
</a>
|
||||
</article>
|
||||
|
||||
<div className="bg-blacksite-surface p-6 sm:p-8 lg:p-10">
|
||||
<p className="section-label mb-5">HONORS</p>
|
||||
<ul className="grid gap-4">
|
||||
{honors.map((honor) => (
|
||||
<li
|
||||
key={`${honor.title}-${honor.year}`}
|
||||
className="grid gap-2 border-l border-signal-orange/50 pl-4 sm:grid-cols-[auto_1fr] sm:items-baseline sm:gap-4"
|
||||
>
|
||||
<span className="section-label text-signal-orange">
|
||||
{honor.year || "RECOGNITION"}
|
||||
</span>
|
||||
<span>
|
||||
<span className="text-blacksite-text">{honor.title}</span>
|
||||
{honor.detail ? (
|
||||
<span className="text-blacksite-muted"> — {honor.detail}</span>
|
||||
) : null}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-blacksite-line bg-blacksite-bg2 p-6 sm:p-8 lg:p-10">
|
||||
<div className="mb-6 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div>
|
||||
<p className="section-label mb-3">SKILLS MATRIX</p>
|
||||
<h3 className="font-display text-2xl text-blacksite-text">
|
||||
Domain → Capabilities
|
||||
</h3>
|
||||
</div>
|
||||
<p className="section-label text-blacksite-faint">OPERATING SURFACE</p>
|
||||
</div>
|
||||
|
||||
<div className="overflow-hidden rounded-xl border border-blacksite-line">
|
||||
<table className="w-full border-collapse text-left text-sm sm:text-base">
|
||||
<thead className="bg-blacksite-surface2">
|
||||
<tr>
|
||||
<th scope="col" className="section-label px-4 py-3 sm:px-5">
|
||||
Domain
|
||||
</th>
|
||||
<th scope="col" className="section-label px-4 py-3 sm:px-5">
|
||||
Capabilities
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-blacksite-line bg-blacksite-surface">
|
||||
{skillsMatrix.map((row) => (
|
||||
<tr key={row.category}>
|
||||
<th
|
||||
scope="row"
|
||||
className="w-56 px-4 py-4 align-top font-mono text-xs uppercase tracking-[0.16em] text-blacksite-text sm:px-5"
|
||||
>
|
||||
{row.category}
|
||||
</th>
|
||||
<td className="px-4 py-4 text-blacksite-muted sm:px-5">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{row.skills.map((skill) => (
|
||||
<span
|
||||
key={skill}
|
||||
className="rounded-full border border-blacksite-line bg-blacksite-surface2 px-3 py-1 text-xs text-blacksite-text sm:text-sm"
|
||||
>
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user