---
name: visual-competitive-analysis
description: Create visual competitive analysis pages that break down competitor brands by color palette, typography, layout patterns, positioning, and strategic threat level. Use when asked to analyze a competitor's visual identity, build a competitive landscape page, create brand DNA comparisons, or research competitor design systems. Triggers on "competitive analysis," "competitor research," "brand analysis," "visual research," "brand DNA," "competitive landscape," or "competition page." Produces CE-styleguide-compliant HTML pages with structured sections.
---

# Visual Competitive Analysis

Build structured, CE-styleguide-compliant competitive analysis pages that dissect competitor brands across visual identity, positioning, and strategic dimensions.

## Output Types

1. **Single brand deep-dive** — Full-page analysis of one competitor
2. **Index page** — Card grid showing all competitors at a glance, linking to individual pages

## Page Structure — Single Brand

Every brand page follows this structure. Sections 1-10 are always present. Image sections (A, B, C) are inserted when agency source images are available — they act as visual anchors, not galleries.

⚠️ **Before building: every section in the table below has a required HTML `id`. Use them exactly — the sticky nav scrollspy depends on them. Section IDs are canonical: `quick-facts`, `positioning`, `brand-evolution`, `color`, `typography`, `layout`, `assessment`, `verdict`, `personality`, `vs-client`. Color (#4) and Typography (#5) are always separate sections — never merge them.**

| # | Section | Component | Content |
|---|---------|-----------|---------|
| **A** | **Hero Image** | `image-hero` | **One single large image** — full container width (1100px), 480px height, object-fit cover. Must be reflective of the brand identity (not a campaign photo). Use one large image, NOT a mosaic or collage. This is the first thing the viewer sees — it must immediately say "this is [Brand]." Choose the most brand-representative image available: the signature color, the product, the identity system in context. |
| 1 | Quick Facts | `facts-grid` (4-col, 4-8 cells) | Revenue, margin, users, AUM, avg balance, growth, founded |
| 2 | Positioning | Narrative + `callout` + `tags` | Market position, mission, recent pivots, key exec quotes |
| 3 | Brand Evolution | `timeline` | Chronological brand moments: founding → rebrands → crises → current |
| 4 | Color Palette | `palette-grid` (5-col) + `image-alongside` | 3-6 swatches: hex, role name, extracted from live CSS. One agency image alongside showing palette in actual use. **MUST be its own `<section id="color">` — never merged with Typography.** |
| 5 | Typography | `type-specimen` + `type-details` + `image-alongside` | Font family, weights, character. One agency image showing the headline typeface in context. **MUST be its own `<section id="typography">` — never merged with Color.** |
| **B** | **Brand in Practice** | `image-grid` (3-col) | 6 agency images in a 3-col grid. No captions. Placed after Typography, before Layout. Shows the identity system in real-world application. |
| 6 | Layout & UX | `two-col` (2 rows) | Density, theme posture, grid structure, interaction patterns |
| 7 | Assessment | `table` (accordion) | What works / what doesn't. **Accordion — summary row always visible, click to expand full table.** |
| 8 | Strategic Verdict | `verdict-grid` (4-col) | Borrow / Reject / Watch / Threat Level |
| 9 | Brand Personality | `three-col` (accordion) | Archetype, voice, target audience signal. **Accordion — collapsed by default.** |
| 10 | vs Client | `table` + `callout` (accordion) | Head-to-head comparison + strategic implication. **Accordion — collapsed by default.** |
| **C** | **More from Agency** | `image-strip` (horizontal scroll) | 4-6 remaining agency images in a horizontal scroll strip. Source credit: "Via [Agency Name]" in `--light` color. |
| 11 | Social Strategy | `platform-grid` + `content-pillars` | Platform presence (followers, frequency, tone per channel: IG/TikTok/X/YouTube/LinkedIn). Content pillar breakdown with % split. Top-performing formats. Paid vs organic signals. **Instagram grid: match native format — posts = 1:1 square containers, stories/reels = 9:16 portrait containers. Never force one format into the other.** **Accordion — open by default.** |
| 12 | Content & Campaign | `campaign-card` + `timeline-row` | Hero campaigns (name, agency, year, media mix, notable talent). OOH, TV, digital, creator collabs. Campaign visual language assessment. **Accordion — collapsed by default.** |
| 13 | Audience & Community | `three-col` + `callout` | Who engages (demographics, psychographics). Sentiment signals. UGC behavior. Community management style — how they reply, handle complaints, reward advocates. **Accordion — collapsed by default.** |

## Required Section IDs — Non-negotiable

Every single-brand page MUST have these 10 `<section>` elements with EXACTLY these IDs. This is the contract between the page structure and the sticky nav scrollspy.

```html
<section id="quick-facts">    <!-- §1 Quick Facts -->
<section id="positioning">    <!-- §2 Positioning -->
<section id="brand-evolution"><!-- §3 Brand Evolution -->
<section id="color">          <!-- §4 Color Palette — SEPARATE from Typography -->
<section id="typography">     <!-- §5 Typography — SEPARATE from Color -->
<section id="layout">         <!-- §6 Layout & UX -->
<section id="assessment">     <!-- §7 Assessment -->
<section id="verdict">        <!-- §8 Strategic Verdict -->
<section id="personality">    <!-- §9 Brand Personality -->
<section id="vs-client">      <!-- §10 vs Client -->
```

⛔ **Do NOT merge Color and Typography into a single `#brand-book` section.** They are separate sections. The nav links to `#color` and `#typography` independently. If you merge them, the scrollspy breaks and the user cannot jump to Color or Type directly.

⛔ **Do NOT rename these IDs.** `#brand-book`, `#identity`, `#colors` are all wrong. The IDs above are canonical.

## Page UX — Sticky Nav + Accordions

Every single-brand page MUST include:

### Sticky Section Nav
A fixed bar at the top (appears after scrolling past the page header) with pill-links to each section. Active section highlights in `--black`. Implementation:

```html
<nav class="section-nav" id="section-nav">
  <div class="container">
    <div class="nav-pills">
      <a href="#quick-facts" class="nav-pill active">Facts</a>
      <a href="#positioning" class="nav-pill">Positioning</a>
      <a href="#brand-evolution" class="nav-pill">Evolution</a>
      <a href="#color" class="nav-pill">Color</a>
      <a href="#typography" class="nav-pill">Type</a>
      <a href="#brand-in-practice" class="nav-pill">In Practice</a>
      <a href="#layout" class="nav-pill">Layout</a>
      <a href="#assessment" class="nav-pill">Assessment</a>
      <a href="#verdict" class="nav-pill">Verdict</a>
      <a href="#social" class="nav-pill">Social</a>
      <a href="#campaigns" class="nav-pill">Campaigns</a>
      <a href="#audience" class="nav-pill">Audience</a>
      <a href="#vs-client" class="nav-pill">vs eToro</a>
    </div>
  </div>
</nav>
```

CSS: `position: sticky; top: 0; z-index: 100; background: #fff; border-bottom: 1px solid #eee; padding: 12px 0;`
Active detection: IntersectionObserver on each section, update `.active` class on corresponding pill.

### Accordions (3 sections)
Assessment, Brand Personality, and vs Client sections use accordion pattern:

```html
<div class="accordion-section" data-section="assessment">
  <div class="accordion-header" onclick="toggleAccordion(this)">
    <span class="accordion-summary">7 dimensions assessed — 4 strong, 3 weak</span>
    <span class="accordion-icon">+</span>
  </div>
  <div class="accordion-body">
    <!-- full table content -->
  </div>
</div>
```

JS: toggle `open` class, animate `max-height` from 0 to content height.
Default state: Assessment = **open** (most used), Personality + vs Client = **closed**.

## Image Integration Patterns

Agency images are **design elements**, not a gallery. Three placement modes:

### A — Hero Image (full-width anchor)
```html
<div class="image-hero">
  <img src="[agency-img-01.jpg]" alt="[Brand] — [Agency] 2024" loading="lazy">
</div>
```
CSS: `width: 100%; height: 480px; object-fit: cover; object-position: center;`
Place: immediately after `<header>`, before section 1.
Use: the most compositionally strong image — usually the identity system overview shot.

### B — Image Alongside (paired with section content)
```html
<div class="section-with-image">
  <div class="section-content"><!-- palette or type specimen --></div>
  <div class="section-image">
    <img src="[agency-img]" alt="" loading="lazy">
  </div>
</div>
```
CSS: `display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: start;`
Use in: Color Palette section (shows palette in use), Typography section (shows typeface in context).

### C — Brand in Practice Grid (3-col, 6 images)
```html
<section id="brand-in-practice">
  <p class="section-label">Brand in Practice</p>
  <p class="agency-credit">Via Porto Rocha, 2024</p>
  <div class="image-grid">
    <img src="img-02.jpg" loading="lazy">
    <img src="img-03.jpg" loading="lazy">
    <img src="img-04.jpg" loading="lazy">
    <img src="img-05.jpg" loading="lazy">
    <img src="img-06.jpg" loading="lazy">
    <img src="img-07.jpg" loading="lazy">
  </div>
</section>
```
CSS: `display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px;` — no padding, images flush to each other. Each image: `aspect-ratio: 16/9; object-fit: cover; width: 100%;`

### D — Horizontal Image Strip (end of page)
```html
<section class="image-strip-section">
  <p class="section-label">More from Porto Rocha</p>
  <div class="image-strip">
    <img src="img-08.jpg" loading="lazy">
    <img src="img-09.jpg" loading="lazy">
    <!-- ... -->
  </div>
</section>
```
CSS: `display: flex; gap: 2px; overflow-x: auto; scroll-snap-type: x mandatory;` Each image: `flex: 0 0 380px; height: 240px; object-fit: cover; scroll-snap-align: start;`

**Rules for all image placements:**
- `alt` text: `"[Brand] — [Agency] [Year]"` or empty for purely decorative
- No captions below images — let visuals speak
- No borders, shadows, border-radius on images
- Agency credit in `--light` color, 10px, uppercase
- Images from `/assets/[brand]/` folder (copy from agency-images dir after download)

## CE Style Guide Compliance

Every page must pass the CE quality gate. Non-negotiable rules:

- **Background:** white (`#fff`) — never dark
- **Fonts:** Larken (display/headings), Inter (body), JetBrains Mono (data/code)
- **Larken:** Load via Typekit `https://use.typekit.net/ffj8sbd.css`
- **Colors:** Only 6 values — `--red: #cc0000`, `--black: #1a1a1a`, `--grey: #666`, `--light: #999`, `--border: #eee`, `--bg: #fff`
- **Container:** `max-width: 1100px` (index pages may use 1400px for card grids)
- **Section labels:** 10px, uppercase, 0.1em tracking, `--red`
- **Headings:** Larken, 400 weight, -0.02em letter-spacing
- **Max text width:** 680px for body paragraphs
- **Borders:** 1px solid `#eee` only — no thicker, no `#e5e5e5`
- **No:** gradients, box-shadows, bold (700), border-radius > 4px, emojis, stock photos

## Template

⛔ **Do NOT open the template until research (Step 0 + Phase 3b image review) is complete.** The template is the output container — fill it with sourced data, not the other way around.

Start from `assets/brand-page-template.html`. It contains all CSS and component markup patterns. Replace `{{PLACEHOLDERS}}` with real content.

## Research Process

⛔ **EXECUTION ORDER IS MANDATORY.** Do NOT generate any HTML, do NOT open the live site, do NOT write any copy until Steps 0 and Phase 3b are complete and images are reviewed. Research runs first. HTML generation runs second. No exceptions.

Read `references/research-methodology.md` for the full data collection checklist per brand. Summary:

### Step 0 — Agency Source Extraction (ALWAYS first — complete before any other step)

Before touching the live site, identify who did the brand work and extract primary visual assets directly from their case study.

**How to identify the agency:**
```bash
web_search "[Brand Name] rebrand agency case study"
# Common outputs: Porto Rocha, Collins, Koto, Wolff Olins, DixonBaxi, Pentagram, Character
```

**Extract images from the agency case study:**

| Agency CMS | Extraction method |
|---|---|
| **Prismic** (Porto Rocha, many studios) | Hit `https://[studio].cdn.prismic.io/api/v2` → get master ref → query `case_study` type by uid |
| **Sanity CDN** (Koto, Wolff Olins) | `extract-images.sh` handles automatically |
| **Mux video** (Collins, W+K) | `extract-images.sh` handles automatically — pulls `.webp` thumbnails |
| **Cloudinary / imgix** | `extract-images.sh` handles automatically |
| **Standard HTML** | `extract-images.sh "https://agency.com/case-study/brand" ./output 15` |

**Prismic API pattern (Porto Rocha example):**
```bash
# 1. Get master ref
MASTER_REF=$(curl -sL "https://[studio].cdn.prismic.io/api/v2" | python3 -c "import json,sys; d=json.load(sys.stdin); print([r['ref'] for r in d['refs'] if r.get('isMasterRef')][0])")

# 2. Get all case studies, find the brand
curl -sL "https://[studio].cdn.prismic.io/api/v2/documents/search?ref=${MASTER_REF}&q=%5B%5Bat(document.type%2C%22case_study%22)%5D%5D&pageSize=100"

# 3. Pull images for specific uid
curl -sL "https://[studio].cdn.prismic.io/api/v2/documents/search?ref=${MASTER_REF}&q=%5B%5Bat(my.case_study.uid%2C%22[brand-uid]%22)%5D%5D" | python3 -c "
import json,sys
data=json.load(sys.stdin)
def find_imgs(obj,depth=0,imgs=[]):
    if depth>15: return imgs
    if isinstance(obj,dict):
        url=obj.get('url','')
        if url and 'prismic' in url and any(e in url.lower() for e in ['.jpg','.png','.webp']):
            imgs.append(url)
        [find_imgs(v,depth+1,imgs) for v in obj.values()]
    elif isinstance(obj,list):
        [find_imgs(i,depth+1,imgs) for i in obj]
    return imgs
[print(i) for i in find_imgs(data.get('results',[{}])[0].get('data',{}))]
"
```

**Download at 1400px:**
```bash
# Add &w=1400 to any Prismic URL
curl -sL "https://images.prismic.io/[studio]/[hash]_[name].jpg?auto=format,compress&w=1400" -o brand-01.jpg
```

**Output:** 10-20 production-quality images saved to `./agency-images/[brand]/`

---

### Phase 3b — Visual Image Review ⚠️ MANDATORY — runs immediately after Step 0

**Before any image goes into the page, every captured image MUST pass a 3-check quality gate using the `image` tool (vision model).**

For every image captured in Step 0 (or from any source), run:

```
image(
  image="path/to/image.jpg",
  prompt="Score this image on 3 binary checks:
  1. BRAND VISIBLE: Is the brand's core visual identity clearly present — logo, signature color, typeface, or distinctive design element? (YES/NO)
  2. CLEAN SHOT: Is this free of cookie banners, loading states, nav-only crops, error pages, or empty whitespace dominating the frame? (YES/NO)
  3. COMPOSITIONALLY STRONG: Does this image represent how the brand wants to be seen — professional quality, intentional composition, not a candid, screenshot artifact, or partial crop? (YES/NO)
  Output exactly: BRAND_VISIBLE=[YES/NO] CLEAN_SHOT=[YES/NO] COMP_STRONG=[YES/NO] VERDICT=[PASS/FAIL] REASON=[one sentence]"
)
```

**Decision rules:**
- All 3 = YES → **PASS** — include the image
- Any = NO → **FAIL** — do NOT include the image
- For every FAIL: move to the next source in the Image Source Hierarchy and re-capture
- If no replacement is available: use a brand-representative screenshot from the live site (Section C fallback below)
- Log each FAIL in a comment: `<!-- REJECTED: [filename] — reason -->`

**Image Source Hierarchy (try in order when a slot needs filling):**
1. Agency case study (Step 0 — primary source)
2. Agency portfolio / Behance / Dribbble (same campaign, different view)
3. Press coverage with editorial photography (It's Nice That, The Brand Identity, Campaign)
4. Brand's own press kit / media assets (brand.com/newsroom or media kit)
5. Live site screenshot (homepage, key landing page, product page) — labeled "via brand.com"

**Minimum image requirements after review:**
- Hero (Section A): 1 PASS image required
- Color section alongside: 1 PASS image required
- Typography section alongside: 1 PASS image required  
- Brand in Practice grid (Section B): minimum 4 PASS images (fill remaining slots with Source Hierarchy tier 4-5)
- Image strip (Section C): minimum 3 PASS images

If after exhausting all 5 tiers a slot still cannot be filled with a PASS image, leave that slot empty — never include a FAIL image.

---

### Step 1 — Existing project data
Check the project's `brand/strategy/` folder and any prior research files before searching externally.

### Step 2 — Live site inspection
Extract actual colors, fonts, copy from the live site CSS:
```bash
nohup pinchtab --headless > /tmp/pinchtab.log 2>&1 &
sleep 3
pinchtab nav "https://brand.com" && sleep 5
pinchtab text > /tmp/brand-text.txt
```
For CSS values: use `scrapling` or `curl` + grep for hex values, font-family declarations.

### Step 3 — Financial data
Public filings, earnings, investor materials.

### Step 4 — Recent brand moves
`web_search` for rebrands, acquisitions, pivots in last 90 days.

### Step 5 — Compile into sections
Fill each of the 10 sections + image sections with sourced data.

## Index Page

When building the index/overview page with all competitors, read `references/index-page-structure.md` for card grid format and tier organization.

## Deployment

Place completed pages in the project's `visual-research/` folder:
```
visual-research/
├── index.html          ← All-brands index
├── robinhood/index.html
├── revolut/index.html
├── coinbase/index.html
└── ...
```

For staging deployment: copy to `public/{{project}}-competition/{{brand}}/index.html`, commit, push.

## Quality Checklist

Before delivering any page, verify:

- [ ] Typekit loaded (`ffj8sbd`)
- [ ] Only Larken / Inter / JetBrains Mono
- [ ] Only 6 CE colors used
- [ ] `grep -i "gradient"` → 0 results
- [ ] `grep "box-shadow"` → 0 results
- [ ] `grep "font-weight.*[6-9]00"` → 0 results
- [ ] Container max-width correct
- [ ] All 10 sections present with correct IDs: `#quick-facts`, `#positioning`, `#brand-evolution`, `#color`, `#typography`, `#layout`, `#assessment`, `#verdict`, `#personality`, `#vs-client`
- [ ] Color (`#color`) and Typography (`#typography`) are SEPARATE sections — NOT merged into `#brand-book` or any other combined section
- [ ] Responsive at 600px and 900px breakpoints
- [ ] Data sourced and current (note dates on financial figures)
- [ ] Sticky section nav present and scrollspy working
- [ ] Assessment accordion open by default; Personality + vs Client closed
- [ ] Agency source images present (Step 0 was run)
- [ ] Hero image placed (Section A) — ONE large brand-representative image, NOT a mosaic/collage
- [ ] Brand in Practice grid present (Section B) with agency credit
- [ ] Horizontal image strip present (Section C)
- [ ] Image assets copied to `assets/[brand]/` folder (not hotlinked from tmp)

## Visual QA Gate — MANDATORY

**No page deploys without passing visual QA.** This is a hard gate, not optional.

### Process
1. Deploy page to staging
2. Run `python3 scripts/visual-qa/section-audit.py <staging-url>`
   - Playwright scrolls full page (triggers lazy-load), captures screenshot
   - ImageMagick slices into 900px sections
   - Vision model audits each section against 7 criteria
3. **All FAILs must be fixed** before reporting complete
4. WARNs are flagged but don't block deploy
5. Re-run audit after fixes to confirm resolution

### 7 Criteria (locked)
1. **Image fit** — no stretching, no overflow, no cropping that loses subject
2. **Image visibility** — actually loads, no broken refs, no alt-text showing
3. **Aspect ratio** — natural for subject, consistent within grids
4. **Content relevance** — right image in right section context
5. **Spacing & alignment** — no dead space, balanced columns, no mismatched heights
6. **Text readability** — sufficient contrast, no clipping, no orphan lines
7. **Visual hierarchy** — clear section flow, hero prominence, proper label/heading/body order

### Sub-agent briefs
Every build brief MUST include: "Run visual QA (`scripts/visual-qa/section-audit.py`) before reporting done. All FAILs must be resolved."
