# SaaStock Gallery Pages — SEO, AEO & GEO Master Playbook (v2)

Extends the master SaaStock SEO playbook. Covers the full pipeline from raw camera files in Google Drive to live, schema-optimized gallery pages on saastock.com.

---

## Step 0: Image Processing Pipeline

Before any page gets built, raw event photos need to be triaged, classified, renamed, captioned, converted, and organized. This is the step most teams skip — and then every image hits the site as `DSC03753.jpg` with no alt text, no schema, and zero SEO value.

### 0a. Export from Google Drive

Source folders follow the pattern `S23 Photos Shared > Pitch Comp`, `S23 Photos Shared > Expo`, etc. Files are camera defaults (`DSC03753.jpg`, `DSC03771.jpg`).

**Workflow options (fastest to slowest):**

1. **Google Drive desktop app** (recommended) — syncs Drive folders to your local filesystem. The `S23 Photos Shared` folder appears as a regular folder on your Mac. Drag-and-drop from there into Conductor or the processing pipeline. No manual download step.
2. **Drive → zip download** — select all, download as zip, unzip locally. Works but slow for large folders.
3. **Drive API / rclone** — for bulk automation across dozens of event folders. Overkill for a single event, useful when processing 10+ historical galleries at once.

### 0b. Visual classification

Go through each image and assign it to a section. Minimums are listed — there is no hard maximum. If you have 30 good live shots, use 30.

| Section | Minimum | What to look for |
|---------|---------|-----------------|
| `live` | 10+ | Stages with audience visible, expo floor with foot traffic, networking areas with people, speaker on stage mid-talk. **Crowd energy is the priority — never show empty chairs or thin crowds.** Every live photo should make the event look packed. |
| `bts` (behind the scenes) | 10+ | Sponsor booths (before or during the event), rigging, LED wall assembly, empty venue pre-build, production crew at work. This section absorbs both booth setup and build-out photos — there is no separate "setup" section. The more sponsor booths photographed, the better. 15–20 booth photos is normal for a large event. |
| `signage` | 2–4 | Partnership rate boards, wayfinding, next-edition booking boards. |
| `map` | 1 | **The official floor map for the event.** See "Floor Map as Source of Truth" below. |
| `video` | 1+ | Highlight reels, booth pan clips, walkthrough clips. No upper limit — if you have 10 good clips, use 10. |

**Floor Map as Source of Truth**

Every event folder should be scanned for the master floor map — the layout diagram showing booth positions, stage locations, and sponsor names. This image is critical for three reasons:

1. **Sponsor verification.** The floor map lists every company that paid for space. Cross-reference it against the sponsor list in the event's `data.ts`. Any company on the map but missing from the sponsor list gets backfilled immediately.
2. **Tagging.** The floor map image gets tagged with section `map` and a standardized file name: `map-floorplan-[series]-[year].webp` (e.g., `map-floorplan-europe-2023.webp`). This naming convention is non-negotiable — it enables a future `/maps/` rollup page showing floor plans from every event side by side.
3. **Schema.** The floor map `ImageObject` gets additional properties: `"@type": "ImageObject"` with `"about": { "@type": "Event", "@id": "..." }` and `"name": "Official floor plan — SaaStock [Series] [Year]"`. It should also carry `"representativeOfPage": false` (the hero live shot stays representative).

If no floor map is found in the Drive folder, flag it explicitly — don't silently skip it. The map may exist in a different subfolder (look for `Floor Plan`, `Floorplan`, `Map`, `Layout`, `Expo Layout`) or in the event's Dropbox/shared folder outside Drive.

**Selection criteria (keep/kill):**

Keep:
- Shows scale (wide shots with visible crowd density)
- Shows sponsor branding clearly (booth name legible)
- Shows energy (audience engaged, speaker mid-gesture, packed aisle)
- Shows production quality (LED walls, lighting rigs, stage design)
- Named speakers on stage (verifiable from the speaker list)

Kill:
- Blurry, dark, or poorly composed
- Duplicates of the same moment (pick the best one)
- Close-ups of food, lanyards, or swag with no brand context
- **Empty chairs, thin crowds, or sparse aisles** — these undercut the event's credibility. If a photo shows a half-empty room, kill it regardless of other qualities.
- Photos where individuals are identifiable in unflattering or unguarded moments

### 0c. Intelligent renaming

Every file gets renamed before it touches the website. The naming convention encodes section, subject, and is URL-safe.

**Pattern:** `[section]-[subject-slug].[ext]`

**Section prefixes:**

| Section | Prefix examples |
|---------|----------------|
| Live — stages | `stage-bootstrap-audience`, `stage-mainstage-keynote`, `stage-muckrack-overnight-success` |
| Live — panels | `panel-audience-and-stage`, `panel-speaker-mic-closeup` |
| Live — audience | `audience-silent-disco`, `audience-networking-lounge` |
| Live — expo | `expo-overview-from-balcony`, `expo-crowd-foot-traffic` |
| Live — team | `team-founderpath-selfie`, `team-saastock-crew` |
| Behind the scenes — booths | `expo-[company]-booth`, `expo-[company]-booth-wide`, `expo-[company]-booth-overhead` |
| Behind the scenes — build | `mainstage-led-setup`, `mainstage-rigging-build`, `venue-podcast-stage-overview` |
| Signage | `branding-dublin-2024-booking`, `branding-usa-austin-2024` |
| Map | `map-floorplan-europe-2023` |
| Video | `video-expo-hall-walkthrough`, `video-founderpath-booth-pan` |

**Rules:**
- All lowercase, hyphens only, no spaces or underscores
- Company names in booth photos use the company name as-is: `expo-vanta-booth`, `expo-chargebee-booth`
- Speaker names in stage photos use the talk subject, not the speaker name: `stage-muckrack-overnight-success` not `stage-greg-galant`
- Never use camera defaults (`DSC03753`, `IMG_4523`, `MG_0082`)
- Never use sequential numbers (`photo-1`, `photo-2`) — they carry zero semantic value

**Batch renaming:** Create a CSV mapping file first, then apply:

```csv
original,renamed,section,keep
DSC03753.jpg,stage-pitch-comp-speaker-1.jpg,live,yes
DSC03771.jpg,stage-pitch-comp-wide.jpg,live,yes
DSC03778.jpg,stage-pitch-comp-branding.jpg,live,no
DSC03788.jpg,stage-pitch-comp-judges.jpg,live,yes
```

### 0d. Alt text + caption generation

For each kept image, write two distinct pieces of text:

**Alt text** — what the image shows, for screen readers and Google Images indexing.

Pattern: `[Context] at SaaStock [Series] [Year] — [specific visual description]`

Context labels (only two):
- `Live during SaaStock Europe 2023` — for any photo taken while the event was running (audience present, doors open)
- `Behind the scenes at SaaStock Europe 2023` — for everything else: booth setup, rigging, empty venue, production crew, build-out. One label covers all non-live photos.

Rules:
- Never start with "Image of" or "Photo of"
- Include company names visible in frame (e.g., Founderpath, Vanta)
- Include venue name (RDS Simmonscourt) and city (Dublin)
- Keep under 125 characters where possible; go longer for accuracy
- Never duplicate alt text across images on the same page

**Caption** (`<figcaption>`) — editorial context that the alt text can't convey.

Pattern: `[Context label] — [one sentence of editorial insight]`

Rules:
- Start with the same context label prefix as the alt text
- Add information the viewer can't see: "17 years from a 2005 podcast to $50M+ ARR" or "Founderpath's Wall Street Journal feature wall"
- Link speaker names to `/speakers/[slug]/` where profiles exist
- Link company names to sponsor pages or external URLs where relevant
- Never duplicate the alt text verbatim — Google penalizes identical alt + caption

**Example pair:**

```
Alt: "Live during SaaStock Europe 2023 — Muck Rack founder Greg Galant on the Bootstrap Stage with his Overnight Success Story timeline slide"

Caption: "Live during the event — [Greg Galant](/speakers/greg-galant/)'s 'Overnight Success Story' slide: 17 years from a 2005 podcast to $50M+ ARR and a $180M Susquehanna Series A."
```

### 0e. Convert to WebP

All images ship as WebP. No JPEG fallback needed — WebP is supported by every browser since Safari 14 (2020) and every relevant crawler.

**Conversion specs:**

| Output | Width | Quality | Use |
|--------|-------|---------|-----|
| Full-res | 1600px (long edge) | 80 | Lightbox / direct link |
| Thumbnail | 600px (long edge) | 75 | Grid display / srcSet |

**Tools:**
- `cwebp` (Google's CLI): `cwebp -q 80 -resize 1600 0 input.jpg -o output.webp`
- Squoosh CLI: `squoosh-cli --webp '{"quality":80}' --resize '{"width":1600}' input.jpg`
- Sharp (Node.js): for batch processing in a build script

**Video:** Ship `.mp4` (H.264) as the primary format. Add `.webm` (VP9) as the first `<source>` for Chrome/Firefox efficiency. Always generate a poster frame as `.webp`.

### 0f. Directory structure

```
/[series]/[year]/gallery/
├── photos/          ← Full-res WebP (1600px)
│   ├── stage-bootstrap-audience.webp
│   ├── expo-founderpath-booth-wide.webp
│   └── ...
├── thumbs/          ← Thumbnails (600px)
│   ├── stage-bootstrap-audience.webp
│   ├── expo-founderpath-booth-wide.webp
│   └── ...
└── video/
    ├── video-expo-hall-walkthrough.mp4
    ├── video-expo-hall-walkthrough.webm
    ├── video-expo-hall-walkthrough-poster.webp
    └── ...
```

### 0g. Build the photo manifest

As you process each image, log it in a JSON manifest. This manifest drives the HTML generation, JSON-LD schema, image sitemap, and sponsor outreach list.

```json
{
  "event": "SaaStock Europe 2023",
  "series": "europe",
  "year": 2023,
  "venue": "RDS Simmonscourt",
  "city": "Dublin",
  "country": "Ireland",
  "dates": "October 16–18, 2023",
  "stats": { "attendees": 3521, "speakers": 164, "countries": 53 },
  "media": [
    {
      "type": "image",
      "file": "stage-bootstrap-audience",
      "section": "live",
      "width": 1600,
      "height": 2134,
      "dateCreated": "2023-10-17",
      "representativeOfPage": true,
      "name": "Bootstrap Stage at standing-room capacity",
      "alt": "Live during SaaStock Europe 2023 — Bootstrap Stage at RDS Simmonscourt, Dublin, with a packed standing-room crowd",
      "caption": "Live during the event — Bootstrap Stage at standing-room capacity. Dublin 2023's RDS hall amplified the Bootstrap Stage well past its seated capacity.",
      "about": []
    },
    {
      "type": "image",
      "file": "expo-founderpath-booth-wide",
      "section": "bts",
      "width": 1600,
      "height": 1200,
      "dateCreated": "2023-10-15",
      "representativeOfPage": false,
      "name": "Founderpath sponsor booth in the expo hall",
      "alt": "Behind the scenes at SaaStock Europe 2023 — Founderpath sponsor booth with purple branding, stools, monitors, and a Wall Street Journal display",
      "caption": "Behind the scenes — Founderpath's expo booth coming together the day before opening at RDS Simmonscourt. Branded stools, lead-capture USB drives, and a Wall Street Journal feature wall.",
      "about": [
        { "type": "Organization", "name": "Founderpath", "url": "https://founderpath.com" }
      ]
    },
    {
      "type": "video",
      "file": "video-expo-hall-walkthrough",
      "section": "video",
      "duration": "PT28S",
      "dateCreated": "2023-10-17",
      "name": "27-second walkthrough across the expo hall and Mainstage",
      "alt": "Live during SaaStock Europe 2023 — walkthrough clip across the expo hall and Mainstage at RDS Simmonscourt",
      "caption": "Live walkthrough across the SaaStock Europe 2023 Mainstage and expo floor at RDS Simmonscourt."
    }
  ]
}
```

---

## Step 1: Page Structure

### URL pattern

```
saastock.com/europe/2023/gallery
saastock.com/usa/2026/gallery
saastock.com/europe/2024/gallery
```

Always nested under the event, never standalone.

### Required sections (in order)

1. **Hero** — dark background, event name, gallery subtitle, stat bar (X live photos, Y behind the scenes, Z videos)
2. **Section nav** — pill buttons linking to `#live`, `#bts`, `#signage`, `#map`, `#video`. Only show sections that have content.
3. **Video lead** — if a highlight reel exists, lead with it. Video is the highest-engagement format and Google rewards video above the fold.
4. **Live during the event** — photos taken while doors were open. Stages, audience, expo floor, networking. Every photo should show crowd energy.
5. **Behind the scenes** — sponsor booths (setup or live), rigging, build-out, production, empty venue. This is one combined section — no separate "setup" and "build-out."
6. **On-site signage** — partnership boards, wayfinding, next-edition booking boards.
7. **Floor map** — the official event floor plan. Tagged as `map` section, always included when available. See "Floor Map as Source of Truth" above.
8. **More video** — vertical clips, booth pans, backstage. No upper limit on count.
9. **About this event** — 2-paragraph summary with key stats, speaker links, and `Speakable` schema (see below). This is the AEO-critical section.
10. **More galleries nav** — prev/next links to other editions' galleries. Add only when a second gallery exists; don't link to nowhere.

### Section badge labels

| Section | Badge text | Color |
|---------|-----------|-------|
| Live event | `Live · during the event` | Green (#0BA86A) |
| Behind the scenes | `Behind the scenes` | Slate (#3F4861) |
| Signage | `On-site signage` | Pink (#E91E8C) |
| Floor map | `Official floor map` | Navy (#160E41) |

---

## Step 2: Schema (JSON-LD)

### Use `@graph` — not multiple `<script>` blocks

Wrap all schema in a single `<script type="application/ld+json">` using `@graph`. This is cleaner, allows cross-references via `@id`, and is how the Google Structured Data validator prefers to see it.

```json
{
  "@context": "https://schema.org",
  "@graph": [
    { /* BreadcrumbList */ },
    { /* CollectionPage */ },
    { /* ItemList with all ImageObject + VideoObject */ }
  ]
}
```

### Block 1: BreadcrumbList

```json
{
  "@type": "BreadcrumbList",
  "itemListElement": [
    { "@type": "ListItem", "position": 1, "name": "Events", "item": "https://saastock.com/events/" },
    { "@type": "ListItem", "position": 2, "name": "Dublin 2023", "item": "https://saastock.com/europe/2023" },
    { "@type": "ListItem", "position": 3, "name": "Photo gallery", "item": "https://saastock.com/europe/2023/gallery" }
  ]
}
```

### Block 2: CollectionPage

```json
{
  "@type": "CollectionPage",
  "@id": "https://saastock.com/europe/2023/gallery",
  "name": "SaaStock Europe 2023 Photo Gallery",
  "description": "23 photos and 2 videos from SaaStock Europe 2023 at RDS Simmonscourt, Dublin (October 16–18, 2023).",
  "url": "https://saastock.com/europe/2023/gallery",
  "inLanguage": "en",
  "keywords": ["SaaStock", "SaaStock Europe 2023", "B2B SaaS conference", "Dublin", "RDS Simmonscourt", "conference photos"],
  "isPartOf": { "@type": "Event", "@id": "https://saastock.com/europe/2023/#event" },
  "mainEntity": { "@id": "#gallery-list" },
  "speakable": {
    "@type": "SpeakableSpecification",
    "cssSelector": "#about-section"
  }
}
```

The `isPartOf` `@id` must match the `@id` on the `Event` schema in `/europe/2023/page.tsx`. Don't inline the full Event object on every image — reference it by `@id` only. That creates the cross-page entity link.

The `speakable` property targets the "About SaaStock Europe 2023" section so voice assistants (Alexa, Google Assistant, ChatGPT voice) read that block when answering "What was SaaStock Europe 2023 like?"

### Block 3: ItemList

```json
{
  "@type": "ItemList",
  "@id": "#gallery-list",
  "name": "SaaStock Europe 2023 photo gallery",
  "numberOfItems": 25,
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "item": {
        "@type": "ImageObject",
        "contentUrl": "https://saastock.com/europe/2023/gallery/photos/stage-bootstrap-audience.webp",
        "thumbnailUrl": "https://saastock.com/europe/2023/gallery/thumbs/stage-bootstrap-audience.webp",
        "name": "Bootstrap Stage at standing-room capacity — SaaStock Europe 2023",
        "description": "Live during the event — Bootstrap Stage at standing-room capacity.",
        "width": 1600,
        "height": 2134,
        "dateCreated": "2023-10-17",
        "representativeOfPage": true,
        "creator": { "@type": "Organization", "name": "SaaStock", "url": "https://saastock.com" },
        "copyrightHolder": { "@type": "Organization", "name": "Founderpath Inc.", "url": "https://founderpath.com" },
        "license": "https://saastock.com/media-license/",
        "isPartOf": { "@type": "Event", "@id": "https://saastock.com/europe/2023/#event" },
        "contentLocation": {
          "@type": "Place",
          "name": "RDS Simmonscourt",
          "address": { "@type": "PostalAddress", "addressLocality": "Dublin", "addressCountry": "Ireland" }
        }
      }
    },
    {
      "@type": "ListItem",
      "position": 9,
      "item": {
        "@type": "ImageObject",
        "contentUrl": "https://saastock.com/europe/2023/gallery/photos/expo-vanta-booth.webp",
        "name": "Vanta sponsor booth — SaaStock Europe 2023",
        "about": [
          { "@type": "Organization", "name": "Vanta", "url": "https://vanta.com" }
        ]
      }
    }
  ]
}
```

**Key differences from v1:**
- Single `@graph` wrapper instead of 26 separate `<script>` tags
- `isPartOf` references the Event `@id` — doesn't inline the full Event object
- `creator` / `copyrightHolder` / `license` on every image
- `dateCreated` on every image
- `about` → `Organization` on sponsor booth images
- `speakable` on the CollectionPage for voice-assist AEO

---

## Step 3: Sponsor Backfill from Booth Photos

This is a two-way value play: booth photos prove sponsorship, and sponsor credits give context to booth photos.

### The gap (Dublin 2023 example)

The gallery shows 7 sponsor booths: Founderpath, Vanta, Chargebee, Eurekos, AppSumo, Paddle, DealPad. The current sponsor list in `europe/2023/data.ts` has 11 sponsors, but only Chargebee overlaps. **Six sponsors are photographed at the show but not credited anywhere on the site.**

### Fix: Backfill the sponsor list

For every event gallery, audit booth photos against the existing sponsor list. Add missing sponsors with:
- Company name
- Tier (infer from booth size: large custom build with hanging signage or island layout = Diamond/Platinum, standard inline booth with back wall = Partner/Gold, table or pod = Growth/Startup). Use your best judgment — don't leave blanks or mark anything as uncertain.
- URL
- `boothPhotoSlug` linking to the gallery image

### Build: Sponsor ↔ gallery cross-links

Add a `booth` field to the sponsor data model:

```typescript
{
  name: "Vanta",
  tier: "Partner",
  url: "https://vanta.com",
  boothPhotoSlug: "expo-vanta-booth"  // matches gallery image file name
}
```

**On the event page sponsor section:** Render a small clickable thumbnail (80×80) next to sponsors that have booth photos. Clicking links to `/europe/2023/gallery#expo-vanta-booth` — the lightbox auto-opens from the URL hash. Single source of truth, deep-linkable, shareable.

**On the gallery page:** Sponsor booth captions link back to the sponsor section on the event page, e.g., `<a href="/europe/2023#sponsors">Vanta</a>`.

**In the schema:** The `about` → `Organization` on the `ImageObject` completes the structured-data round-trip.

The credibility argument for prospective sponsors: "Vanta sponsored — and here's the actual booth they paid to build." That's miles more convincing than a list of names.

---

## Step 4: Meta Tags

```html
<title>SaaStock [Series] [Year] photo gallery — [City], [Venue] | SaaStock</title>

<meta name="description" content="Photo and video gallery from SaaStock [Series] [Year] — [attendee count] attendees, [speaker count] speakers and [country count] countries across [day count] days at [Venue], [City] ([dates]). [One sentence describing gallery contents].">

<link rel="canonical" href="https://saastock.com/[series]/[year]/gallery">

<meta property="og:title" content="[Same as title]">
<meta property="og:description" content="[Same as meta description]">
<meta property="og:image" content="[URL of the best live-event photo — not a booth, not a build-out]">
<meta property="og:image:alt" content="[Descriptive alt matching the image]">
<meta property="og:type" content="website">
<meta property="og:url" content="[Canonical URL]">

<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@SaaStock">
```

---

## Step 5: Image Sitemap

Extend the main `sitemap.ts` rather than creating a separate `gallery-sitemap.xml`. Add `<image:image>` children to the gallery URL entry:

```xml
<url>
  <loc>https://saastock.com/europe/2023/gallery</loc>
  <image:image>
    <image:loc>https://saastock.com/europe/2023/gallery/photos/stage-bootstrap-audience.webp</image:loc>
    <image:title>Bootstrap Stage at standing-room capacity — SaaStock Europe 2023</image:title>
    <image:caption>Live during the event — Bootstrap Stage at standing-room capacity.</image:caption>
    <image:geo_location>Dublin, Ireland</image:geo_location>
  </image:image>
  <!-- repeat for all images -->
</url>
```

One fewer file to maintain, same crawl result.

---

## Step 6: Internal Links

### From gallery captions → elsewhere on site

- Speaker names → `/speakers/[slug]/` (e.g., "Greg Galant" → `/speakers/greg-galant/`)
- Company names in booth photos → sponsor section on event page or external URL
- Stage names → event page section describing tracks
- "Back to the Dublin 2023 recap" → parent event page

### From event page → gallery

- "View the photo gallery →" link in the hero or after the stats section
- Sponsor booth thumbnails in the sponsor section → gallery with hash deep-link
- "See behind the scenes" in venue/production sections → gallery `#bts`

### From speaker profiles → gallery

- If a speaker appears in a gallery photo, link from their speaker profile: "See [Name] at SaaStock Europe 2023 →"

---

## Step 7: Press & Backlink Generation

### CTA on the gallery page

Add a small card near the top of the gallery:

```
📸 These photos are free to use. No permission needed — just tag @SaaStock.
High-res versions available: email gallery@saastock.com.
```

**Licensing approach:** All SaaStock event photography is released for unrestricted use. No attribution required, though tagging @SaaStock is appreciated. This is intentional — every reuse is free marketing. The `license` field in the schema points to a simple page at `/media-license/` that says:

> All photos and videos in the SaaStock gallery are free to use for any purpose. No permission, attribution, or credit is required. If you'd like to tag us, we're @SaaStock everywhere.

Create this as a minimal static page. It serves two purposes: (1) the `license` URL in the schema resolves to a real page (Google checks), and (2) it signals to AI training pipelines and stock-photo aggregators that the images are free to index and reference.

### Sponsor outreach after publishing

For every sponsor with a booth photo, send:

```
Subject: Your [Company] booth at SaaStock [Year] — photos ready

Hi [name],

We just published the SaaStock [Series] [Year] photo gallery — your [Company] booth
is featured: [direct URL to gallery#expo-company-booth]

Totally free to use — LinkedIn posts, blog recaps, pitch decks, whatever you want.
High-res version available on request, just reply here.

[sign-off]
```

Every sponsor who uses the image in a blog post, LinkedIn update, or recap article creates a backlink. Every speaker who downloads their stage photo and posts it creates a social signal. Don't ask for attribution — the people who link back will do it naturally, and removing the friction increases reuse by 10x.

---

## Step 8: HTML Implementation Details

### Image element pattern

```html
<figure class="group">
  <a href="/europe/2023/gallery/photos/stage-bootstrap-audience.webp"
     id="stage-bootstrap-audience"
     aria-label="[full alt text]">
    <img
      src="/europe/2023/gallery/thumbs/stage-bootstrap-audience.webp"
      srcSet="/europe/2023/gallery/thumbs/stage-bootstrap-audience.webp 600w,
             /europe/2023/gallery/photos/stage-bootstrap-audience.webp 1600w"
      sizes="(min-width: 1180px) 280px, (min-width: 700px) 33vw, 50vw"
      width="1600"
      height="2134"
      loading="lazy"
      decoding="async"
      alt="Live during SaaStock Europe 2023 — Bootstrap Stage at RDS Simmonscourt, Dublin">
    <span class="badge">Live · during the event</span>
  </a>
  <figcaption>Live during the event — Bootstrap Stage at standing-room capacity.</figcaption>
</figure>
```

**Key differences from v1:**
- `id` attribute on the `<a>` enables hash deep-linking (`/gallery#stage-bootstrap-audience`)
- No `title` attribute on `<img>` — it was duplicating alt text for zero SEO benefit (just creating redundant tooltips)
- No `<picture>` with JPEG fallback — WebP coverage is universal in 2026
- First image: `loading="eager"` + `fetchPriority="high"`. All others: `loading="lazy"` + `decoding="async"`.

### Video element pattern

```html
<figure>
  <div style="aspect-ratio: 16/9">
    <video controls playsinline preload="none"
           poster="/europe/2023/gallery/video/video-expo-hall-walkthrough-poster.webp"
           aria-label="[full alt text]">
      <source src="/europe/2023/gallery/video/video-expo-hall-walkthrough.webm" type="video/webm">
      <source src="/europe/2023/gallery/video/video-expo-hall-walkthrough.mp4" type="video/mp4">
    </video>
  </div>
  <figcaption>[Caption text]</figcaption>
</figure>
```

---

## Checklist: Before Publishing Any Gallery Page

### Step 0 — Image processing
- [ ] Photos triaged: no hard cap — use every good photo, but kill empty chairs and thin crowds
- [ ] Each image classified into a section (live/bts/signage/map/video)
- [ ] All files renamed to `[section]-[subject-slug].webp` convention
- [ ] Official floor map located, tagged as `map-floorplan-[series]-[year].webp`
- [ ] Floor map cross-referenced against sponsor list — missing sponsors backfilled
- [ ] If no floor map found in Drive folder, flagged explicitly (check alternate folders)
- [ ] Alt text written for every image (unique, descriptive, includes venue + city)
- [ ] Caption written for every image (editorial context, distinct from alt text)
- [ ] Context labels use only two categories: "Live during" and "Behind the scenes at"
- [ ] Images converted to WebP: 1600px full-res at q80, 600px thumbs at q75
- [ ] Videos: MP4 + WebM sources, poster frame as WebP
- [ ] Photo manifest JSON created with all metadata

### Step 1 — Page structure
- [ ] URL follows `/[series]/[year]/gallery` pattern
- [ ] Hero with event name, stat bar
- [ ] Section nav with anchor links
- [ ] Video lead above the fold (if highlight reel exists)
- [ ] Sections in order: live → bts → signage → map → more video → about
- [ ] "About this event" section with `id="about-section"` for Speakable targeting

### Step 2 — Schema
- [ ] Single `<script type="application/ld+json">` with `@graph` wrapper
- [ ] `BreadcrumbList` with correct event hierarchy
- [ ] `CollectionPage` with `speakable`, `inLanguage`, `keywords`
- [ ] `ItemList` wrapping all `ImageObject` and `VideoObject` entries
- [ ] Every `ImageObject` has: `contentUrl`, `thumbnailUrl`, `name`, `description`, `width`, `height`, `dateCreated`, `creator`, `copyrightHolder`, `license`, `contentLocation`, `isPartOf` (referencing Event `@id`)
- [ ] Sponsor booth images have `about` → `Organization`
- [ ] `VideoObject` includes `duration` in ISO 8601 format

### Step 3 — Sponsors
- [ ] Booth photos audited against sponsor list; missing sponsors backfilled
- [ ] Sponsor data model includes `boothPhotoSlug` where applicable
- [ ] Event page sponsor section shows booth thumbnails with hash deep-links to gallery
- [ ] Gallery booth captions link back to sponsor section on event page

### Step 4 — Meta
- [ ] Title tag follows pattern
- [ ] Meta description includes: event name, attendee count, speaker count, country count, dates, venue, city
- [ ] OG image is a live-event photo (not a booth, not a logo)
- [ ] Canonical URL set
- [ ] No duplicate `title` attributes on `<img>` elements

### Step 5 — Sitemap
- [ ] `<image:image>` entries added to main sitemap for the gallery URL
- [ ] Each image has `image:loc`, `image:title`, `image:caption`, `image:geo_location`

### Step 6 — Links
- [ ] Internal links in captions: speaker names → profiles, company names → sponsor sections
- [ ] Event page links to gallery
- [ ] Gallery links back to event page
- [ ] Prev/next gallery links (add when second gallery exists)

### Step 7 — Outreach
- [ ] Press CTA visible on gallery page (unrestricted use, tag @SaaStock)
- [ ] `/media-license/` static page created and resolving
- [ ] `gallery@saastock.com` mailbox or alias set up
- [ ] Sponsor outreach emails queued with direct links to booth photos

---

## Resolved Decisions

1. **Copyright holder:** Founderpath Inc. holds copyright on all SaaStock event imagery post-acquisition. Use `"copyrightHolder": { "@type": "Organization", "name": "Founderpath Inc.", "url": "https://founderpath.com" }` in all schema.
2. **License:** Unrestricted. All gallery photos and videos are free to use for any purpose, no permission or attribution required. Create a simple static page at `/media-license/` stating this. The `license` URL in the schema must resolve to a real page.
3. **Contact email:** Use `gallery@saastock.com` on the gallery CTA. No press@ address exists. Set up the mailbox or alias before publishing.
4. **Sponsor tiers for backfill:** Use best judgment from booth size and position. Large custom builds with hanging signage = Diamond/Platinum. Standard inline booths = Partner/Gold. Tables or pods = Growth/Startup. Never leave a tier blank or marked as uncertain.
