Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,16 @@ Three build shapes from one project (see the header comment in

## Content guardrails (from the brief)

- This is Quentin's **personal** site; it does not speak for any employer. Walmart is
named only as a biographical fact in body copy. No Walmart imagery, internal work,
confidential detail, or implied endorsement; keep case-study specifics generic
("a Fortune 1 retailer"). Do not present him as an official representative in
metadata or structured data: machine-readable employer claims (`worksFor`, an
employer name inside `<head>`) are out. `tools/seo_check.py` fails if a flagged
company name appears in any page `<head>`, so keep such terms in body content only
(never in a `seo.*` field, since those render into `<head>`).
- This is Quentin's **personal** site; it does not speak for any employer. Walmart may
be named in body copy, and public work Quentin himself has posted publicly (e.g. his
own NYC pop-up-shop video) may be shown and named at the owner's discretion. What
stays out regardless: internal, unreleased, or confidential work, and any implied
official endorsement. When describing enterprise engagements in the abstract, still
prefer generic framing ("a Fortune 1 retailer"). Do not present him as an official
representative in metadata or structured data: machine-readable employer claims
(`worksFor`, an employer name inside `<head>`) are out. `tools/seo_check.py` fails if
a flagged company name appears in any page `<head>`, so keep such terms in body
content only (never in a `seo.*` field, since those render into `<head>`).
- "Don't just wear clothes, wear confidence" survives only as a secondary tagline
(the footer), not the headline.

Expand Down
13 changes: 13 additions & 0 deletions content/work.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,19 @@ archive:
capTitle: "Editorial"
capSub: "Color & movement"
footnote: "A selection from a decade of work. Full archive available on request."
video:
eyebrow: "In motion"
heading: "On the floor"
intro: "The work moving, not just the finished frame. Tap to watch."
items:
- poster:
src: "assets/img/work-popup-shop.jpg"
alt: "Quentin Fears styling a New York City pop-up shop"
tag: "Video"
label: "YouTube Short"
caption: "Styling a New York City pop-up shop for Walmart. Brandon Maxwell gives the team a shout-out."
permalink: "https://youtube.com/shorts/PXC-BWog7-Q"
videoSrc: ""
cta:
heading: "I take on a few brand projects at a time."
body: "Creative direction, visual strategy, and campaign work for brands and agencies."
Expand Down
24 changes: 24 additions & 0 deletions keystatic.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,30 @@ export default config({
},
{ label: 'Styling archive' }
),
video: fields.object(
{
eyebrow: fields.text({ label: 'Eyebrow' }),
heading: fields.text({ label: 'Heading' }),
intro: fields.text({ label: 'Intro', multiline: true }),
items: fields.array(
fields.object({
poster: photo('Poster still (vertical, ~1080x1920)'),
caption: fields.text({ label: 'Caption', multiline: true }),
permalink: fields.text({
label: 'Video permalink',
description: 'YouTube or Instagram URL, used as the link when no self-hosted video is set',
}),
videoSrc: fields.text({
label: 'Self-hosted video path (optional)',
description:
'e.g. assets/video/clip-x.mp4 (produced by tools/prepare_reel.py). Blank = the poster links out to the permalink instead.',
}),
}),
{ label: 'Videos', itemLabel: (p) => (p.fields.caption.value || 'Video').slice(0, 48) }
),
},
{ label: 'Video' }
),
cta: cta(),
},
}),
Expand Down
Binary file added public/assets/img/work-popup-shop-1080.avif
Binary file not shown.
Binary file added public/assets/img/work-popup-shop-1080.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/img/work-popup-shop-400.avif
Binary file not shown.
Binary file added public/assets/img/work-popup-shop-400.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/img/work-popup-shop-800.avif
Binary file not shown.
Binary file added public/assets/img/work-popup-shop-800.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/img/work-popup-shop.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/data/img-variants.json
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@
1080
]
},
"assets/img/work-popup-shop.jpg": {
"height": 1920,
"width": 1080,
"widths": [
400,
800,
1080
]
},
"assets/img/work-time.jpg": {
"height": 988,
"width": 791,
Expand Down
38 changes: 38 additions & 0 deletions src/pages/work.astro
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,44 @@ const jsonLd = {
</div>
</section>

<!-- VIDEO: self-hosted vertical video (curated) or a link out to YouTube/Instagram -->
{work.video && work.video.items.length > 0 && (
<Fragment>
<hr class="rule">
<section class="section">
<div class="wrap">
<div class="sec-head reveal">
<p class="eyebrow">{work.video.eyebrow}</p>
<h2>{work.video.heading}</h2>
{work.video.intro && <p>{work.video.intro}</p>}
</div>
<div class="reels">
{work.video.items.map((r) => {
const platform = r.permalink && /youtu\.?be/i.test(r.permalink) ? 'YouTube' : 'Instagram';
return r.videoSrc ? (
<figure class="reel-card reveal" data-video={r.videoSrc} data-poster={r.poster.src} role="button" tabindex="0" aria-label={`Play video: ${r.caption}`}>
<div class="ph reel-card__media" data-tag={r.poster.tag} data-label={r.poster.label}>
<Picture src={r.poster.src} alt={r.poster.alt} sizes="(min-width: 900px) 25vw, (min-width: 640px) 33vw, 46vw" loading="lazy" onerror="this.remove()" />
<span class="reel__play"><span><svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z" fill="#f3efe6"/></svg></span></span>
</div>
<figcaption class="reel-card__cap">{r.caption}</figcaption>
</figure>
) : (
<figure class="reel-card reveal">
<a class="ph reel-card__media" href={r.permalink} target="_blank" rel="noopener" aria-label={`Watch on ${platform}: ${r.caption}`} data-tag={r.poster.tag} data-label={r.poster.label}>
<Picture src={r.poster.src} alt={r.poster.alt} sizes="(min-width: 900px) 25vw, (min-width: 640px) 33vw, 46vw" loading="lazy" onerror="this.remove()" />
<span class="reel-card__ig">Watch on {platform} ↗</span>
</a>
<figcaption class="reel-card__cap">{r.caption}</figcaption>
</figure>
);
})}
</div>
</div>
</section>
</Fragment>
)}

<Cta
heading={work.cta.heading}
body={work.cta.body}
Expand Down
Loading