Files
davegilligan-new/src/pages/cv.astro
T
daveadmin 6b509fe052 Add complete Open Graph metadata layer across all 20 pages
- Add og:title, og:description, og:image, og:url, og:type, og:site_name to BaseLayout
- Add og:locale (en_GB) + og:locale:alternate (fr_FR, nb_NO) multilingual signals
- Add article:published_time, article:section, article:author on all 5 article pages
- Add Twitter/X summary_large_image card and canonical link on every page
- Generate 13 Jazz Noir branded 1200x630 PNG OG images (satori + resvg-js)
- Add scripts/generate-og-images.mjs + Special Elite font for future regeneration
- Add public/images/articles/ and public/assets/ which were previously untracked

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 01:38:51 +02:00

180 lines
6.1 KiB
Plaintext

---
import { schoolDossiers } from "../data/site";
import {
cvHero,
cvHighlights,
cvMandates,
cvSkillTracks,
cvSourceNotes,
cvTimeline,
} from "../data/cv";
import BaseLayout from "../layouts/BaseLayout.astro";
import LocaleCopy from "../components/LocaleCopy.astro";
const currentMandates = cvMandates.slice(0, 3);
const educationSlice = schoolDossiers.slice(0, 5);
---
<BaseLayout
title="CV | Dave Gilligan"
description="A custom CV desk covering Dave Gilligan's current ventures, earlier finance and reinsurance work, education route, and the operating themes tying them together."
ogImage="/images/og/cv.png"
>
<main class="cv-page">
<section class="container cv-hero">
<div class="cv-hero__copy">
<span class="eyebrow"><LocaleCopy copy={cvHero.eyebrow} /></span>
<h1><LocaleCopy copy={cvHero.title} /></h1>
<p class="cv-hero__lede"><LocaleCopy copy={cvHero.lede} /></p>
<div class="cv-hero__actions">
<a class="button button--dark" href="#career-record">Read the chronology</a>
<a class="button button--soft" href="/education">Open the education issue</a>
</div>
</div>
<aside class="panel cv-poster">
<div class="capsule__kicker">
<span>Editorial note</span>
<span>Public record</span>
</div>
<p class="cv-poster__eyebrow">Bridgework</p>
<h2>Finance, systems, language, and advocacy on one line.</h2>
<p><LocaleCopy copy={cvHero.note} /></p>
<div class="cv-poster__grid">
{cvHighlights.en.map((_, i) => (
<article>
<span>Signal</span>
<p><LocaleCopy copy={{ en: cvHighlights.en[i], fr: cvHighlights.fr[i], nb: cvHighlights.nb[i] }} /></p>
</article>
))}
</div>
</aside>
</section>
<section class="container cv-current">
<div class="section-header">
<div class="section-header__title">Current Mandates</div>
<div class="section-header__meta">
The live front of the work: one civic desk, one Norwegian ENK, one AI infrastructure house.
</div>
</div>
<div class="cv-current__grid">
{currentMandates.map((mandate) => (
<article class="panel cv-mandate">
<div class="cv-mandate__meta">
<span>{mandate.years}</span>
<span>{mandate.role}</span>
<span>{mandate.location}</span>
</div>
<h2>{mandate.org}</h2>
<p class="cv-mandate__summary"><LocaleCopy copy={mandate.summary} /></p>
<p><LocaleCopy copy={mandate.detail} /></p>
<a href={mandate.sourceUrl} target="_blank" rel="noreferrer">
Source: {mandate.sourceLabel}
</a>
</article>
))}
</div>
</section>
<section id="career-record" class="container cv-record">
<div class="section-header">
<div class="section-header__title">Career Record</div>
<div class="section-header__meta">
Earlier chapters are kept compact here so the timeline stays readable without losing the hard details.
</div>
</div>
<div class="cv-record__layout">
<div class="cv-record__timeline">
{cvTimeline.map((role) => (
<article class="cv-role">
<div class="cv-role__year">{role.years}</div>
<div class="panel cv-role__body">
<div class="cv-role__heading">
<div>
<p>{role.location}</p>
<h3>{role.org}</h3>
</div>
<strong>{role.role}</strong>
</div>
<p class="cv-role__summary"><LocaleCopy copy={role.summary} /></p>
<ul>
{role.bullets.en.map((_, i) => (
<li><LocaleCopy copy={{ en: role.bullets.en[i], fr: role.bullets.fr[i], nb: role.bullets.nb[i] }} /></li>
))}
</ul>
</div>
</article>
))}
</div>
<aside class="cv-side">
<article class="panel cv-tracklist">
<div class="capsule__kicker">
<span>Operating tracks</span>
<span>Portable assets</span>
</div>
{cvSkillTracks.map((track) => (
<section>
<h3><LocaleCopy copy={track.label} /></h3>
<ul>
{track.items.map((item) => (
<li>{item}</li>
))}
</ul>
</section>
))}
</article>
<article class="panel cv-education-note">
<div class="capsule__kicker">
<span>Study route</span>
<span>Five schools</span>
</div>
<p>
The education record has its own issue because it reads more like a migration map than
a list of institutions.
</p>
<div class="cv-education-note__list">
{educationSlice.map((school) => (
<article>
<strong>{school.institution}</strong>
<span>{school.years}</span>
<p>{school.program}</p>
</article>
))}
</div>
<a class="button button--dark" href="/education">Read the school dossier</a>
</article>
</aside>
</div>
</section>
<section class="container source-block">
<div class="section-header">
<div class="section-header__title">Source Notes</div>
<div class="section-header__meta">
Current roles are paraphrased from official sites. Earlier employment history is grounded in the standing public profile and your supplied LinkedIn text.
</div>
</div>
<div class="source-list">
{cvSourceNotes.map((source) => (
<article>
<a href={source.url} target="_blank" rel="noreferrer">{source.label}</a>
<p>{source.note}</p>
</article>
))}
</div>
</section>
</main>
</BaseLayout>