# Media Gallery → Public Render Audit

**Date:** 2026-09-25  
**Goal:** Admin media gallery selections (and text) must appear on every public page after save.

---

## 1. Problem statement

| Symptom | Observed |
|---------|----------|
| Admin media picker | Selects and previews correctly |
| Admin save | Succeeds (“saved successfully”) |
| Public site | Old / default images still shown |

---

## 2. Root cause (confirmed)

### A. `x-ui.optimized-img` ignores custom `$src` when `base` exists
Home hero stores:

```
https://apex.ronnieridge.com/storage/media/pages/{uuid}.jpg
```

But the Blade view passes a hardcoded `base="images/hero/optimized/assistant-woman"`.  
The component prefers local optimized WebP/JPG under `base` whenever those files exist, so the gallery URL never renders.

### B. Detail pages hardcode `PageImages::*`
About, Services, Hire, Industries, Case Studies, Locations call `PageImages::forX()` and ignore:

- `featured_image_id` / featured media relation  
- SEO `og_image` from admin  
- Any section image fields saved in meta

### C. ID-mode pickers need a public URL resolver
Blog / services / industries store `featured_image_id`. Public views must resolve Media → URL (not assume a path string).

---

## 3. Fix strategy

1. **Core component** — `optimized-img`: if `$src` is a media/storage (or other non-static) URL, skip `base` and render `$src`.
2. **Helper** — small `MediaUrl` / extend `PageImages` to resolve:
   - absolute URL  
   - relative `/images/...`  
   - `featured_image_id` → Media model URL  
   - fallback static path
3. **Page-by-page** — wire admin fields → public views; verify save → public HTML.
4. **Text** — confirm HomePageBuilder / content builders already merge stored meta (they do); re-verify after image fixes.

---

## 4. Page audit order

| # | Public URL | Admin source | Image fields |
|---|------------|--------------|--------------|
| 1 | `/` Home | Home Page CMS | hero ×3, features, talent, process, industries, book_cta |
| 2 | `/about` | Other pages / About | featured + section images if any |
| 3 | `/services`, `/services/{slug}` | Services CRUD | featured_image_id, OG |
| 4 | `/hire`, `/hire/{slug}` | Hire roles | featured / OG / section |
| 5 | `/industries`, `/industries/{slug}` | Industries | featured_image_id, OG |
| 6 | `/case-studies`, detail | Case studies | featured_image_id, OG |
| 7 | `/blog`, `/blog/{slug}` | Blog posts | featured_image_id, OG |
| 8 | `/locations` + nested | Locations | OG / PageImages fallback |
| 9 | `/contact`, `/book`, legal | Contact / settings | logos, OG, any pickers |
| 10 | Global | Settings branding | logo, favicon, default OG |

For each page:

1. Inventory admin media-picker fields  
2. Confirm save persists (DB / meta)  
3. Confirm public template reads that value  
4. Confirm HTML `src` matches gallery URL  
5. Confirm text fields update  
6. Mark pass/fail in checklist below  

---

## 5. Verification checklist

- [x] Home hero main/secondary/tertiary show gallery picks  
- [x] Home features / process / talent / CTA images show gallery picks  
- [x] Home text (title, lead, FAQ) updates publicly  
- [x] About images + copy (featured image + excerpt/content)  
- [x] Service detail featured image  
- [x] Hire role image (linked service featured + SEO OG)  
- [x] Industry featured image  
- [x] Case study featured image  
- [x] Blog post featured + OG  
- [x] Locations pages (SEO OG drives body + social when set)  
- [x] Contact / Book (no media gallery body fields — HTTP 200 OK)  
- [x] Settings logo / default OG (picker wired; uses SEO resolver fallbacks)

### Root cause fixes applied
1. `x-ui.optimized-img` — custom `/storage/` & media URLs win over hardcoded `base` optimized assets  
2. `PageImages::preferMedia()` / `preferUrl()` — detail pages prefer admin featured / OG over static defaults  
3. Controllers load `featuredImage` / `seoMeta` for public show actions  
4. `SeoMetaResolver` falls back to `featuredImageUrl()` for OG tags  
5. Media picker hidden inputs keep server-rendered `value` so selections survive Alpine edge cases  
6. About builder uses admin `content` for intro when present  

### Live regression (2026-09-25)
All media-bearing public pages returned the assigned gallery URL after save.

---

## 6. Execution phases

| Phase | Work |
|-------|------|
| 0 | This plan document |
| 1 | Fix `optimized-img` + shared media URL helper |
| 2 | Home page wire + live verify |
| 3 | About |
| 4 | Services |
| 5 | Hire |
| 6 | Industries |
| 7 | Case studies |
| 8 | Blog |
| 9 | Locations / Contact / Settings |
| 10 | Full regression pass |

---

*Execute phases in order. Do not mark a page complete until public HTML shows the saved media URL.*
