6b509fe052
- 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>
213 lines
8.3 KiB
Plaintext
213 lines
8.3 KiB
Plaintext
---
|
|
import { aiBubbleArticle, aiBubbleBody, aiBubbleImages, aiBubbleSources } from "../../data/ai-bubble";
|
|
import BaseLayout from "../../layouts/BaseLayout.astro";
|
|
import LocaleCopy from "../../components/LocaleCopy.astro";
|
|
|
|
const pageCopy = {
|
|
eyebrow: {
|
|
en: "Article / Business & Technology",
|
|
fr: "Article / Commerce et Technologie",
|
|
nb: "Artikkel / Næringsliv og Teknologi",
|
|
},
|
|
published: { en: "Published", fr: "Publié", nb: "Publisert" },
|
|
sidebarH2: {
|
|
en: "Field Correspondent",
|
|
fr: "Correspondant de terrain",
|
|
nb: "Feltkorrespondent",
|
|
},
|
|
sidebarNote: {
|
|
en: "Dave Gilligan builds AI systems professionally and writes about what that vantage point reveals about an industry in the process of valuing itself.",
|
|
fr: "Dave Gilligan construit des systèmes d'IA à titre professionnel et écrit sur ce que ce point de vue révèle d'une industrie en train de s'évaluer elle-même.",
|
|
nb: "Dave Gilligan bygger AI-systemer profesjonelt og skriver om hva det perspektivet avslører om en industri i ferd med å verdsette seg selv.",
|
|
},
|
|
fieldReport: { en: "Field report", fr: "Reportage de terrain", nb: "Feltrapport" },
|
|
location: { en: "AI & Capital Markets", fr: "IA et marchés financiers", nb: "AI og kapitalmarkeder" },
|
|
creditsTitle: { en: "Source Credits", fr: "Sources", nb: "Kildehenvisninger" },
|
|
creditsMeta: {
|
|
en: "Valuation figures are drawn from publicly reported fundraising rounds and financial press. Image attributions are per Wikimedia Commons licensing terms.",
|
|
fr: "Les chiffres de valorisation sont tirés des tours de financement publiquement rapportés et de la presse financière. Les attributions d'images sont conformes aux termes de licence Wikimedia Commons.",
|
|
nb: "Verdivurderingstall er hentet fra offentlig rapporterte finansieringsrunder og finanspressen. Bildeattribusjon er i henhold til Wikimedia Commons-lisensvilkår.",
|
|
},
|
|
};
|
|
---
|
|
|
|
<BaseLayout
|
|
title={`${aiBubbleArticle.title} | Dave Gilligan`}
|
|
description={aiBubbleArticle.excerpt.en}
|
|
ogType="article"
|
|
ogImage="/images/articles/ai-bubble/data-centre.jpg"
|
|
ogArticlePublishedTime={aiBubbleArticle.publishedAt.replace(' ', 'T') + 'Z'}
|
|
ogArticleSection="Business"
|
|
>
|
|
<main class="jazz-page">
|
|
<section class="container jazz-hero">
|
|
<div class="jazz-hero__copy">
|
|
<span class="eyebrow"><LocaleCopy copy={pageCopy.eyebrow} /></span>
|
|
<h1>{aiBubbleArticle.title}</h1>
|
|
<p class="jazz-hero__lede"><LocaleCopy copy={aiBubbleArticle.subtitle} /></p>
|
|
<p class="jazz-hero__lede"><LocaleCopy copy={aiBubbleArticle.excerpt} /></p>
|
|
</div>
|
|
|
|
<aside class="panel jazz-hero__note">
|
|
<div class="capsule__kicker">
|
|
<span><LocaleCopy copy={pageCopy.published} /></span>
|
|
<span>{aiBubbleArticle.publishedAt.slice(0, 10)}</span>
|
|
</div>
|
|
<h2><LocaleCopy copy={pageCopy.sidebarH2} /></h2>
|
|
<p><LocaleCopy copy={pageCopy.sidebarNote} /></p>
|
|
</aside>
|
|
</section>
|
|
|
|
<section class="container">
|
|
<article class="panel jazz-article article-prose">
|
|
<div class="capsule__kicker">
|
|
<span><LocaleCopy copy={pageCopy.fieldReport} /></span>
|
|
<span><LocaleCopy copy={pageCopy.location} /></span>
|
|
</div>
|
|
|
|
<div class="article-block">
|
|
<figure class="article-fig article-fig--right">
|
|
<img src={aiBubbleImages[0].src} alt={aiBubbleImages[0].alt} loading="lazy" />
|
|
<figcaption>
|
|
<span>{aiBubbleImages[0].credit} / {aiBubbleImages[0].license}</span>
|
|
<a href={aiBubbleImages[0].sourceUrl} target="_blank" rel="noreferrer">{aiBubbleImages[0].sourceLabel}</a>
|
|
</figcaption>
|
|
{aiBubbleImages[0].note && <p class="article-fig__note">{aiBubbleImages[0].note}</p>}
|
|
</figure>
|
|
<p><LocaleCopy copy={{ en: aiBubbleBody.en[0], fr: aiBubbleBody.fr[0], nb: aiBubbleBody.nb[0] }} /></p>
|
|
<p><LocaleCopy copy={{ en: aiBubbleBody.en[1], fr: aiBubbleBody.fr[1], nb: aiBubbleBody.nb[1] }} /></p>
|
|
</div>
|
|
|
|
<div class="article-block">
|
|
<figure class="article-fig article-fig--left">
|
|
<img src={aiBubbleImages[1].src} alt={aiBubbleImages[1].alt} loading="lazy" />
|
|
<figcaption>
|
|
<span>{aiBubbleImages[1].credit} / {aiBubbleImages[1].license}</span>
|
|
<a href={aiBubbleImages[1].sourceUrl} target="_blank" rel="noreferrer">{aiBubbleImages[1].sourceLabel}</a>
|
|
</figcaption>
|
|
{aiBubbleImages[1].note && <p class="article-fig__note">{aiBubbleImages[1].note}</p>}
|
|
</figure>
|
|
<p><LocaleCopy copy={{ en: aiBubbleBody.en[2], fr: aiBubbleBody.fr[2], nb: aiBubbleBody.nb[2] }} /></p>
|
|
<p><LocaleCopy copy={{ en: aiBubbleBody.en[3], fr: aiBubbleBody.fr[3], nb: aiBubbleBody.nb[3] }} /></p>
|
|
</div>
|
|
|
|
<div class="article-block">
|
|
<figure class="article-fig article-fig--right">
|
|
<img src={aiBubbleImages[2].src} alt={aiBubbleImages[2].alt} loading="lazy" />
|
|
<figcaption>
|
|
<span>{aiBubbleImages[2].credit} / {aiBubbleImages[2].license}</span>
|
|
<a href={aiBubbleImages[2].sourceUrl} target="_blank" rel="noreferrer">{aiBubbleImages[2].sourceLabel}</a>
|
|
</figcaption>
|
|
{aiBubbleImages[2].note && <p class="article-fig__note">{aiBubbleImages[2].note}</p>}
|
|
</figure>
|
|
<p><LocaleCopy copy={{ en: aiBubbleBody.en[4], fr: aiBubbleBody.fr[4], nb: aiBubbleBody.nb[4] }} /></p>
|
|
<p><LocaleCopy copy={{ en: aiBubbleBody.en[5], fr: aiBubbleBody.fr[5], nb: aiBubbleBody.nb[5] }} /></p>
|
|
</div>
|
|
|
|
<div class="article-block">
|
|
<figure class="article-fig article-fig--left">
|
|
<img src={aiBubbleImages[3].src} alt={aiBubbleImages[3].alt} loading="lazy" />
|
|
<figcaption>
|
|
<span>{aiBubbleImages[3].credit} / {aiBubbleImages[3].license}</span>
|
|
<a href={aiBubbleImages[3].sourceUrl} target="_blank" rel="noreferrer">{aiBubbleImages[3].sourceLabel}</a>
|
|
</figcaption>
|
|
{aiBubbleImages[3].note && <p class="article-fig__note">{aiBubbleImages[3].note}</p>}
|
|
</figure>
|
|
<p><LocaleCopy copy={{ en: aiBubbleBody.en[6], fr: aiBubbleBody.fr[6], nb: aiBubbleBody.nb[6] }} /></p>
|
|
<p><LocaleCopy copy={{ en: aiBubbleBody.en[7], fr: aiBubbleBody.fr[7], nb: aiBubbleBody.nb[7] }} /></p>
|
|
<p><LocaleCopy copy={{ en: aiBubbleBody.en[8], fr: aiBubbleBody.fr[8], nb: aiBubbleBody.nb[8] }} /></p>
|
|
</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="container source-block">
|
|
<div class="section-header">
|
|
<div class="section-header__title"><LocaleCopy copy={pageCopy.creditsTitle} /></div>
|
|
<div class="section-header__meta"><LocaleCopy copy={pageCopy.creditsMeta} /></div>
|
|
</div>
|
|
|
|
<div class="source-list">
|
|
{aiBubbleSources.map((source) => (
|
|
<article>
|
|
<a href={source.href} target="_blank" rel="noreferrer">{source.label}</a>
|
|
</article>
|
|
))}
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</BaseLayout>
|
|
|
|
<style>
|
|
.article-prose {
|
|
padding: 1.6rem 2rem;
|
|
}
|
|
|
|
.article-block {
|
|
display: flow-root;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.article-fig {
|
|
margin: 0.3rem 0 1rem;
|
|
overflow: hidden;
|
|
border-radius: 1.4rem;
|
|
border: 1px solid var(--line);
|
|
background: rgba(255, 252, 246, 0.84);
|
|
box-shadow: var(--shadow-paper);
|
|
}
|
|
|
|
.article-fig--right {
|
|
float: right;
|
|
width: 44%;
|
|
margin-left: 1.6rem;
|
|
margin-bottom: 0.8rem;
|
|
}
|
|
|
|
.article-fig--left {
|
|
float: left;
|
|
width: 44%;
|
|
margin-right: 1.6rem;
|
|
margin-bottom: 0.8rem;
|
|
}
|
|
|
|
.article-fig img {
|
|
width: 100%;
|
|
height: clamp(13rem, 24vw, 21rem);
|
|
object-fit: cover;
|
|
}
|
|
|
|
.article-fig figcaption {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.4rem 0.8rem;
|
|
padding: 0.65rem 0.9rem 0;
|
|
font-family: var(--font-mono);
|
|
font-size: 0.6rem;
|
|
letter-spacing: 0.12em;
|
|
text-transform: uppercase;
|
|
color: var(--ink-faint);
|
|
}
|
|
|
|
.article-fig figcaption a {
|
|
color: var(--teal);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.article-fig__note {
|
|
margin: 0.35rem 0.9rem 0.9rem;
|
|
color: var(--ink-soft);
|
|
line-height: 1.5;
|
|
font-size: 0.86rem;
|
|
font-style: italic;
|
|
}
|
|
|
|
@media (max-width: 760px) {
|
|
.article-fig--right,
|
|
.article-fig--left {
|
|
float: none;
|
|
width: 100%;
|
|
margin: 1rem 0;
|
|
}
|
|
}
|
|
</style>
|