SEO Model Pages (Stripping for Spares Silo)

Overview

The model-specific stripping pages feature expands our SEO hierarchy from 2 levels (Pillar → Make) to 3 levels (Pillar → Make → Model). This creates highly targeted landing pages for specific vehicle models being stripped for spares.

Purpose:

Status: ✅ IMPLEMENTED (December 2024). Dynamic route at src/pages/[make]-[model]-stripping-for-spares.astro (903 lines).

Agent: Use seo-model-pages agent for model page creation, analysis, and optimization. See .claude/agents/seo-model-pages.md.

URL Pattern: /{make}-{model}-stripping-for-spares/

Examples:


URL Hierarchy (3-Level Silo)

/vehicles-stripping-for-spares/              (Level 1: Pillar Page)
├── /bmw-stripping-for-spares/               (Level 2: Make Page - EXISTS)
│   ├── /bmw-3-series-stripping-for-spares/  (Level 3: Model Page - TO BE BUILT)
│   ├── /bmw-1-series-stripping-for-spares/  (Level 3: Model Page - TO BE BUILT)
│   └── /bmw-x5-stripping-for-spares/        (Level 3: Model Page - TO BE BUILT)
├── /mercedes-benz-stripping-for-spares/     (Level 2: Make Page - EXISTS)
│   ├── /mercedes-benz-c-class-stripping-for-spares/  (Level 3: Model Page - TO BE BUILT)
│   └── /mercedes-benz-vito-stripping-for-spares/     (Level 3: Model Page - TO BE BUILT)
└── /renault-stripping-for-spares/           (Level 2: Make Page - EXISTS)
    ├── /renault-sandero-stripping-for-spares/    (Level 3: Model Page - TO BE BUILT)
    ├── /renault-clio-stripping-for-spares/       (Level 3: Model Page - TO BE BUILT)
    └── /renault-kwid-stripping-for-spares/       (Level 3: Model Page - TO BE BUILT)

Existing Pages:

To Create:


Database Analysis (December 2024)

Top Makes by Vehicle Count

Based on query: SELECT make, COUNT(*) FROM vehicles WHERE status IN ('available','active') GROUP BY make ORDER BY COUNT(*) DESC

MakeTotal VehiclesTop Models
Mercedes-Benz201C-Class (37), Vito (13), Sprinter (11)
Renault158Sandero (33), Clio (30), Kwid (24)
Audi103A4 (18), A3 (12), Q5 (9)
BMW903 Series (24), 5 Series (8), X5 (6)
Hyundai81i20 (14), Getz (13), i10 (11)
Volkswagen67Golf (15), Polo (12), Jetta (8)
Kia42Picanto (10), Rio (7), Sportage (5)

High-Volume Models (20+ vehicles)

These models should get dedicated pages immediately (Phase 1):

MakeModelVehicle CountPriorityURL
Mercedes-BenzC-Class371/mercedes-benz-c-class-stripping-for-spares/
RenaultSandero332/renault-sandero-stripping-for-spares/
RenaultClio303/renault-clio-stripping-for-spares/
RenaultKwid244/renault-kwid-stripping-for-spares/
BMW3 Series245/bmw-3-series-stripping-for-spares/

Why 20+ vehicles?

Medium-Volume Models (10-19 vehicles)

Phase 2 expansion candidates:

MakeModelVehicle Count
AudiA418
RenaultMegane18
VolkswagenGolf15
Hyundaii2014
Mercedes-BenzVito13
HyundaiGetz13
RenaultDuster12
VolkswagenPolo12
AudiA312
Hyundaii1011
KiaPicanto10
FordRanger10

Model Naming Issues Found

Problem: Database has variant-specific names that create duplicate pages if not normalized.

Examples:

MakeRaw Database ValuesShould Normalize ToSEO URL
BMWE90, E90 320i, F30 320i, 335i”3 Series”/bmw-3-series-stripping-for-spares/
BMWE60, 520d, 530i”5 Series”/bmw-5-series-stripping-for-spares/
VWGolf 4, Golf 5, Golf 7 GTI”Golf”/volkswagen-golf-stripping-for-spares/
MercedesC200, C220 CDI, C-Class”C-Class”/mercedes-benz-c-class-stripping-for-spares/
AudiA4 B8, S4, A4 Quattro”A4”/audi-a4-stripping-for-spares/

Impact Without Normalization:

Impact With Normalization:

Solution: Use existing src/lib/normalizers/vehicleNormalizer.ts (see Model Normalization section below).


Internal Linking Strategy (Soft Silo)

Modern SEO best practice: Soft silos with contextual cross-linking (not strict siloing).

Where: Breadcrumb + 1-2 mentions in body content

Example:

<!-- Breadcrumb (always present) -->
Home > Vehicles Stripping For Spares > <a href="/bmw-stripping-for-spares/">BMW Spares</a> > BMW 3 Series Spares

<!-- Body paragraph (contextual) -->
<p>Looking for other <a href="/bmw-stripping-for-spares/">BMW models being stripped</a>?
We also stock E90, F30, and G20 variants.</p>

Anchor Text Variety (avoid over-optimization):

Where: Breadcrumb only (don’t force body links)

Example:

Home > <a href="/vehicles-stripping-for-spares/">Vehicles Stripping For Spares</a> > BMW Spares > BMW 3 Series Spares

Rationale: The make page already links to pillar, so model → pillar link via breadcrumb provides the chain.

Where: “Related Models” section (below vehicle grid)

Example:

<section class="related-models">
  <h2>Other Popular BMW Models Being Stripped</h2>
  <ul>
    <li><a href="/bmw-1-series-stripping-for-spares/">BMW 1 Series Stripping</a> (5 vehicles)</li>
    <li><a href="/bmw-x5-stripping-for-spares/">BMW X5 Stripping</a> (3 vehicles)</li>
    <li><a href="/bmw-5-series-stripping-for-spares/">BMW 5 Series Stripping</a> (8 vehicles)</li>
  </ul>
</section>

Anchor Text Pattern: {Make} {Model} Stripping (natural, descriptive)

Display Vehicle Count: Helps users decide which link to click

Where: Contextual mentions in body content (sparingly)

Example:

<!-- On /bmw-3-series-stripping-for-spares/ -->
<p>If you're comparing German luxury sedans, check out our
<a href="/mercedes-benz-c-class-stripping-for-spares/">Mercedes-Benz C-Class inventory</a>
or <a href="/audi-a4-stripping-for-spares/">Audi A4 parts</a>.</p>

Use Cases for Cross-Silo Links:

Important: Only link when contextually relevant. Don’t force cross-links.

Where: Footer section of model pages

Example:

<section class="related-links">
  <h3>Related BMW 3 Series Links</h3>
  <ul>
    <li><a href="/bmw-3-series-engines-for-sale/">BMW 3 Series Engines For Sale</a></li>
    <li><a href="/bmw-scrap-yards/">BMW Scrap Yards Near You</a></li>
    <li><a href="/sell-car-for-scrap/">Sell Your BMW 3 Series</a></li>
  </ul>
</section>

Reuse Existing Component: src/components/MakeModelLinks.astro (already implements this pattern).

Soft Silo Summary

Modern Approach (what we’re doing):

Old Approach (what we’re NOT doing):


Schema Markup

Model pages have 4 breadcrumb levels (vs. 3 for make pages):

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "item": {
        "@id": "https://www.enginefinder.co.za/",
        "name": "Home"
      }
    },
    {
      "@type": "ListItem",
      "position": 2,
      "item": {
        "@id": "https://www.enginefinder.co.za/vehicles-stripping-for-spares/",
        "name": "Vehicles Stripping For Spares"
      }
    },
    {
      "@type": "ListItem",
      "position": 3,
      "item": {
        "@id": "https://www.enginefinder.co.za/bmw-stripping-for-spares/",
        "name": "BMW Spares"
      }
    },
    {
      "@type": "ListItem",
      "position": 4,
      "item": {
        "@id": "https://www.enginefinder.co.za/bmw-3-series-stripping-for-spares/",
        "name": "BMW 3 Series Spares"
      }
    }
  ]
}

Difference from Make Pages: Make pages have 3 levels, model pages have 4 levels.

ItemList Schema (Vehicle Listings)

Purpose: Tells Google this page lists vehicles available for stripping.

{
  "@context": "https://schema.org",
  "@type": "ItemList",
  "name": "BMW 3 Series Vehicles Stripping For Spares",
  "description": "24 BMW 3 Series vehicles available for stripping and spare parts in South Africa",
  "numberOfItems": 24,
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "item": {
        "@type": "Car",
        "name": "2015 BMW 3 Series 320i Stripping For Spares",
        "url": "https://www.enginefinder.co.za/vehicles/bmw-3-series-320i-2015-abc123",
        "vehicleModelDate": "2015",
        "brand": { "@type": "Brand", "name": "BMW" },
        "model": "3 Series",
        "image": "https://example.com/image.jpg",
        "itemCondition": "https://schema.org/UsedCondition",
        "offers": {
          "@type": "Offer",
          "availability": "https://schema.org/InStock"
        }
      }
    }
    // ... more items
  ]
}

Reuse Existing Logic: See src/pages/[make]-stripping-for-spares.astro (lines 393-427) for reference implementation.

Schema Validation Tools

Before Deployment:

Testing Commands:

# Test breadcrumb schema exists
curl https://www.enginefinder.co.za/bmw-3-series-stripping-for-spares/ | grep -o '"@type":"BreadcrumbList"'

# Test item list schema exists
curl https://www.enginefinder.co.za/bmw-3-series-stripping-for-spares/ | grep -o '"@type":"ItemList"'

# Count schema blocks (should be 2+)
curl https://www.enginefinder.co.za/bmw-3-series-stripping-for-spares/ | grep -o 'application/ld+json' | wc -l

Key Files

FilePurposeStatusNotes
src/pages/[make]-[model]-stripping-for-spares.astroModel page templateTO BE CREATEDMain implementation file
src/lib/normalizers/vehicleNormalizer.tsModel name normalizationEXISTSAlready handles BMW, VW, Mercedes, Audi, Toyota, Jeep, Fiat, Nissan
src/pages/[make]-stripping-for-spares.astroMake page templateEXISTSUse as reference (393-427 for ItemList schema)
src/pages/vehicles-stripping-for-spares/index.astroPillar pageEXISTSReference for silo structure
src/components/MakeModelLinks.astroRelated links componentEXISTSReuse for footer cross-links
src/components/Breadcrumb.astroBreadcrumb navigationEXISTSSupports 4-level breadcrumbs
src/components/internal-linking/SchemaMarkupGenerator.tsSchema helperEXISTSOptional utility for generating JSON-LD

Model Normalization

Why Normalization is Needed

Database stores variant-specific names that would create thin-content pages if used directly for SEO URLs.

Problem Without Normalization:

Solution With Normalization:

Existing Normalizers

File: src/lib/normalizers/vehicleNormalizer.ts

Already implemented for:

MakeWhat Gets NormalizedOutputExample
BMWChassis codes (E90, F30, G20)“3 Series”, “5 Series”320i E903 Series
VolkswagenGolf variants (5, 6, 7, GTI)“Golf”Golf 7 GTIGolf
Mercedes-BenzClass variants (C200, C220 CDI)“C-Class”C220 CDIC-Class
AudiSeries variants (A4 B8, S4)“A4”A4 B8 QuattroA4
ToyotaHilux variants (GD-6, D4D, Raider)“Hilux”Hilux D4DHilux
JeepCherokee variants (WJ, WK2)“Grand Cherokee”Grand Cherokee WK2Grand Cherokee
FiatModel familiesBase model500 Pop500
NissanModel familiesBase modelQashqai +2Qashqai

Example: BMW Normalization

// Input: "BMW 320i E90"
normalizeBMWModel("320i E90")
// Output: { baseName: "3 Series", variant: "320i", series: "E90" }

// SEO URL: /bmw-3-series-stripping-for-spares/
// Page Title: "BMW 3 Series Stripping For Spares | 24 Vehicles"
// Heading: "BMW 3 Series (E90, F30, G20) Stripping For Spares"

Adding New Normalizers

If a make needs normalization and doesn’t exist yet, add to vehicleNormalizer.ts:

export function normalizeRenaultModel(model: string): NormalizedModel {
  const modelStr = model.trim().toUpperCase();

  // Example: "SANDERO STEPWAY" → "Sandero"
  if (modelStr.includes('SANDERO')) {
    return {
      baseName: 'Sandero',
      variant: modelStr.replace('SANDERO', '').trim()
    };
  }

  // Example: "CLIO RS" → "Clio"
  if (modelStr.includes('CLIO')) {
    return {
      baseName: 'Clio',
      variant: modelStr.replace('CLIO', '').trim()
    };
  }

  // Default: return as-is
  return { baseName: model };
}

When to Add Normalizers:


Implementation Phases

Phase 1: High-Volume Models (5 Pages)

Target: Models with 20+ vehicles (guaranteed content depth)

Timeline: 1-2 days

MakeModelVehiclesPriorityURL
Mercedes-BenzC-Class371/mercedes-benz-c-class-stripping-for-spares/
RenaultSandero332/renault-sandero-stripping-for-spares/
RenaultClio303/renault-clio-stripping-for-spares/
RenaultKwid244/renault-kwid-stripping-for-spares/
BMW3 Series245/bmw-3-series-stripping-for-spares/

Deliverables:

  1. ✅ Create src/pages/[make]-[model]-stripping-for-spares.astro
  2. ✅ Add 5 model pages to sitemap
  3. ✅ Validate schema markup (Google Rich Results Test)
  4. ✅ Audit internal links (breadcrumbs, related models, cross-links)
  5. ✅ Test on staging environment

Success Criteria:

Phase 2: Medium-Volume Models (7-12 Pages)

Target: Models with 10-19 vehicles

Timeline: 1 day

MakeModelVehicles
AudiA418
RenaultMegane18
VolkswagenGolf15
Hyundaii2014
Mercedes-BenzVito13
HyundaiGetz13
RenaultDuster12
VolkswagenPolo12
AudiA312
Hyundaii1011
KiaPicanto10
FordRanger10

Deliverables:

  1. ✅ Add 7-12 additional model pages
  2. ✅ Update sitemap
  3. ✅ Monitor build time (ensure <5 min)
  4. ✅ Verify no duplicate content issues

Phase 3: Long-Tail Expansion (Ongoing)

Target: Models with 5-9 vehicles (evaluate case-by-case)

Timeline: Ongoing (monthly review)

Evaluation Criteria:

  1. ✅ Search volume (Google Keyword Planner / GSC)
  2. ✅ Low competition (check SERPs)
  3. ✅ Popular models (brand reputation)
  4. ✅ Unique content angle (special features, rarity)

Examples of Good Long-Tail Candidates:

Examples of Poor Long-Tail Candidates:


Thin Content Handling

Problem

Pages with too few vehicles provide:

Solution: Minimum Vehicle Threshold

Rule: Model pages require minimum 5 vehicles to exist.

Implementation:

// In getStaticPaths()
const vehicleCount = await getVehicleCountForModel(make, model);

if (vehicleCount < 5) {
  // Don't generate this page - below threshold
  continue;
}

Why 5?

Alternative: Dynamic Threshold by Make

For premium brands with higher search volume, use lower threshold:

const MIN_VEHICLES_HIGH_VALUE = 3; // BMW, Mercedes, Audi
const MIN_VEHICLES_STANDARD = 5;   // Other makes

const threshold = ['bmw', 'mercedes-benz', 'audi'].includes(make.toLowerCase())
  ? MIN_VEHICLES_HIGH_VALUE
  : MIN_VEHICLES_STANDARD;

if (vehicleCount < threshold) {
  continue; // Skip page generation
}

Rationale: Premium brands have higher search volume, justifying pages with fewer vehicles.

Handling Below-Threshold Models

Option 1: No Page (recommended)

Option 2: Redirect to Make Page

Option 3: Section on Make Page

Recommended: Option 1 (no page). Clean, simple, avoids complexity.


Testing

Build Command

npm run build

Expected Output:

Verify Generated Pages:

# Check if model pages exist
ls dist/bmw-3-series-stripping-for-spares/
ls dist/mercedes-benz-c-class-stripping-for-spares/
ls dist/renault-sandero-stripping-for-spares/

Schema Validation

Tools:

Manual Validation Steps:

  1. Visit test URL: https://search.google.com/test/rich-results
  2. Enter model page URL (e.g., /bmw-3-series-stripping-for-spares/)
  3. Click “Test URL”
  4. Verify BreadcrumbList detected ✅
  5. Verify ItemList detected ✅
  6. Check for errors (should be 0)

Automated Check:

# Extract schema blocks
curl https://www.enginefinder.co.za/bmw-3-series-stripping-for-spares/ \
  | grep -o '<script type="application/ld+json">.*</script>' \
  | sed 's/<[^>]*>//g' \
  | jq '.'

# Expected: 2 JSON-LD blocks (BreadcrumbList + ItemList)

URLs to Test (Phase 1)

After Implementation, test these URLs:

https://www.enginefinder.co.za/mercedes-benz-c-class-stripping-for-spares/
https://www.enginefinder.co.za/renault-sandero-stripping-for-spares/
https://www.enginefinder.co.za/renault-clio-stripping-for-spares/
https://www.enginefinder.co.za/renault-kwid-stripping-for-spares/
https://www.enginefinder.co.za/bmw-3-series-stripping-for-spares/

Checklist per URL:

Manual Check:

  1. Visit model page (e.g., /bmw-3-series-stripping-for-spares/)
  2. Verify breadcrumb has all 4 levels
  3. Check “Related Models” section exists
  4. Verify links to engines/scrap yards pages
  5. Confirm no broken links (404s)
  6. Test cross-silo links (if present)

Automated Check (use SEO crawler):

# Using Screaming Frog or similar
screaming-frog --crawl https://www.enginefinder.co.za/bmw-3-series-stripping-for-spares/
screaming-frog --export internal-links.csv

# Check for:
# - Outbound links to make page (should exist)
# - Outbound links to pillar page (via breadcrumb)
# - Outbound links to sibling models (should exist)
# - No 404s

Performance Testing

Metrics to Check:

Run Lighthouse:

# Install Lighthouse CLI
npm install -g lighthouse

# Run audit
lighthouse https://www.enginefinder.co.za/bmw-3-series-stripping-for-spares/ \
  --only-categories=performance,seo \
  --output=json \
  --output-path=./lighthouse-report.json

Sitemap Updates

Adding Model Pages to Sitemap

File: src/pages/sitemap.xml.ts (or sitemap.xml.astro)

Query Logic:

// Get all make/model combinations with 5+ vehicles
const { data: vehicles } = await supabase
  .from('vehicles')
  .select('make, model')
  .in('status', ['available', 'active'])
  .not('featured_image', 'is', null);

// Group by make + normalized model
const modelCounts = new Map<string, number>();
vehicles.forEach(v => {
  const normalized = normalizeModel(v.make, v.model);
  const key = `${v.make}:${normalized.baseName}`;
  modelCounts.set(key, (modelCounts.get(key) || 0) + 1);
});

// Filter models with 5+ vehicles
const modelUrls = Array.from(modelCounts.entries())
  .filter(([_, count]) => count >= 5)
  .map(([key, _]) => {
    const [make, model] = key.split(':');
    return `/${slugify(make)}-${slugify(model)}-stripping-for-spares/`;
  });

Sitemap XML Format

<url>
  <loc>https://www.enginefinder.co.za/bmw-3-series-stripping-for-spares/</loc>
  <lastmod>2024-12-13</lastmod>
  <changefreq>weekly</changefreq>
  <priority>0.7</priority>
</url>

Priority Values (Hierarchy)

Page TypePriorityRationale
Homepage1.0Top-level entry point
Pillar Page0.9Main category hub
Make Pages0.8Secondary category hubs
Model Pages0.7Tertiary category pages (NEW)
Individual Vehicles0.6Leaf pages
Blog Posts0.5Informational content

Change Frequency

Page TypeChange FrequencyWhy
Model PagesweeklyVehicle inventory changes frequently
Make PagesweeklyNew models added regularly
Pillar PagemonthlyStable overview content

Performance Considerations

Static Generation (SSG)

Model pages should be statically generated at build time (not server-rendered on each request).

Why SSG?

Astro Config:

// astro.config.mjs
export default defineConfig({
  output: 'hybrid', // Default SSR, opt-in to SSG
  adapter: vercel({
    prerender: true // Enable pre-rendering
  })
});

Page-Level Config:

---
// In [make]-[model]-stripping-for-spares.astro
export const prerender = true; // Force SSG for this route

export async function getStaticPaths() {
  // Generate all model page paths at build time
  // ...
}
---

Pagination

For models with 50+ vehicles, implement pagination to avoid performance issues.

Implementation:

const VEHICLES_PER_PAGE = 30;
const totalPages = Math.ceil(vehicleCount / VEHICLES_PER_PAGE);

// URL: /bmw-3-series-stripping-for-spares/?page=2
const currentPage = Astro.url.searchParams.get('page') || 1;
const offset = (currentPage - 1) * VEHICLES_PER_PAGE;

const { data: vehicles } = await supabase
  .from('vehicles')
  .select('*')
  .ilike('make', make)
  .ilike('model', `%${model}%`)
  .range(offset, offset + VEHICLES_PER_PAGE - 1);

Same Pattern Used In: src/pages/[make]-stripping-for-spares.astro (lines 43-45)

SEO Considerations:

Build Time Optimization

Concern: Generating 50+ model pages at build time could slow down deploys.

Solutions:

  1. Incremental Builds (Vercel): Only rebuild changed pages
  2. Parallel Generation: Astro generates pages in parallel by default
  3. Caching: Cache Supabase queries during build
  4. Lazy Loading: Images use lazy loading (reduces initial page weight)

Expected Build Time:

Acceptable: <2 minutes additional build time for Phase 1-3 combined.


Monitoring & Iteration

Metrics to Track

Google Search Console (GSC):

Google Analytics (GA4):

Supabase Analytics:

Success Benchmarks (90 Days)

MetricTargetMeasurement
GSC Impressions500+ per model pageGSC → Performance → Filter by page
GSC CTR5-10%GSC → Performance → Filter by page
Average Position<10 (top 10)GSC → Performance → Filter by page
Pageviews100+ per model pageGA4 → Pages and screens
Bounce Rate<60%GA4 → Pages and screens → Bounce rate
Conversion Rate2-5%GA4 → Events → Quote submissions / Pageviews

Iteration Plan

Month 1:

Month 2:

Month 3:

Ongoing:


Example Implementation (Pseudocode)

File: src/pages/[make]-[model]-stripping-for-spares.astro

---
import Layout from '../layouts/Layout.astro';
import { normalizeModel } from '../lib/normalizers/vehicleNormalizer';
import { slugify } from '../utils/slugs';
import Breadcrumb from '../components/Breadcrumb.astro';
import MakeModelLinks from '../components/MakeModelLinks.astro';
import VehicleCard from '../components/VehicleCard.astro';
import { getAdminSupabase } from '../lib/supabaseAdmin';

export async function getStaticPaths() {
  const supabase = getAdminSupabase();

  // Query database for all make/model combinations
  const { data: vehicles } = await supabase
    .from('vehicles')
    .select('make, model')
    .in('status', ['available', 'active'])
    .not('featured_image', 'is', null);

  // Group by make + normalized model
  const modelCounts = new Map<string, { make: string, model: string, count: number }>();
  vehicles.forEach(v => {
    const normalized = normalizeModel(v.make, v.model);
    const key = `${v.make.toLowerCase()}:${normalized.baseName.toLowerCase()}`;

    if (!modelCounts.has(key)) {
      modelCounts.set(key, { make: v.make, model: normalized.baseName, count: 0 });
    }
    modelCounts.get(key)!.count++;
  });

  // Filter models with 5+ vehicles
  const MIN_VEHICLES = 5;
  const paths = Array.from(modelCounts.values())
    .filter(({ count }) => count >= MIN_VEHICLES)
    .map(({ make, model }) => ({
      params: {
        make: slugify(make),
        model: slugify(model)
      },
      props: {
        makeName: make,
        modelName: model
      }
    }));

  return paths;
}

const { make, model } = Astro.params;
const { makeName, modelName } = Astro.props;

const supabase = getAdminSupabase();

// Get vehicles for this make + model
const { data: vehicles, count } = await supabase
  .from('vehicles')
  .select('*', { count: 'exact' })
  .ilike('make', makeName)
  .ilike('model', `%${modelName}%`)
  .in('status', ['available', 'active'])
  .not('featured_image', 'is', null)
  .order('created_at', { ascending: false });

// Breadcrumb (4 levels)
const breadcrumbItems = [
  { name: 'Home', href: '/' },
  { name: 'Vehicles Stripping For Spares', href: '/vehicles-stripping-for-spares/' },
  { name: `${makeName} Spares`, href: `/${make}-stripping-for-spares/` },
  { name: `${makeName} ${modelName} Spares`, href: `/${make}-${model}-stripping-for-spares/` }
];

// BreadcrumbList Schema
const breadcrumbSchema = {
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": breadcrumbItems.map((item, index) => ({
    "@type": "ListItem",
    "position": index + 1,
    "item": {
      "@id": `https://www.enginefinder.co.za${item.href}`,
      "name": item.name
    }
  }))
};

// ItemList Schema
const itemListSchema = {
  "@context": "https://schema.org",
  "@type": "ItemList",
  "name": `${makeName} ${modelName} Vehicles Stripping For Spares`,
  "description": `${count} ${makeName} ${modelName} vehicles available for stripping and spare parts in South Africa`,
  "numberOfItems": count,
  "itemListElement": vehicles.map((vehicle, index) => ({
    "@type": "ListItem",
    "position": index + 1,
    "item": {
      "@type": "Car",
      "name": `${vehicle.year} ${makeName} ${modelName} Stripping For Spares`,
      "url": `https://www.enginefinder.co.za/vehicles/${vehicle.slug}`,
      "vehicleModelDate": vehicle.year,
      "brand": { "@type": "Brand", "name": makeName },
      "model": modelName,
      "image": vehicle.featured_image,
      "itemCondition": "https://schema.org/UsedCondition",
      "offers": {
        "@type": "Offer",
        "availability": "https://schema.org/InStock"
      }
    }
  }))
};

// Get related models (same make, different model)
const { data: relatedModels } = await supabase
  .rpc('get_related_models', {
    make_param: makeName,
    exclude_model: modelName
  })
  .limit(5);
---

<Layout
  title={`${makeName} ${modelName} Stripping for Spares | ${count} Vehicles`}
  description={`Browse ${count} ${makeName} ${modelName} vehicles being stripped for spare parts in South Africa. Quality used parts from trusted scrapyards.`}
>
  <!-- Schema Markup -->
  <script type="application/ld+json" set:html={JSON.stringify(breadcrumbSchema)} />
  <script type="application/ld+json" set:html={JSON.stringify(itemListSchema)} />

  <!-- Hero Section -->
  <div class="hero bg-gradient-to-r from-blue-600 to-blue-800 text-white py-12">
    <div class="container mx-auto px-4">
      <Breadcrumb items={breadcrumbItems} theme="light" />
      <h1 class="text-4xl font-bold mt-4">{makeName} {modelName} Stripping for Spares</h1>
      <p class="text-xl mt-2">
        {count} {makeName} {modelName} vehicles currently being stripped for parts
      </p>
    </div>
  </div>

  <!-- Vehicle Grid -->
  <div class="container mx-auto px-4 py-8">
    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
      {vehicles.map(vehicle => (
        <VehicleCard vehicle={vehicle} />
      ))}
    </div>
  </div>

  <!-- Related Models (Horizontal Links) -->
  {relatedModels && relatedModels.length > 0 && (
    <section class="container mx-auto px-4 py-8 border-t">
      <h2 class="text-2xl font-bold mb-4">Other Popular {makeName} Models Being Stripped</h2>
      <ul class="grid grid-cols-1 md:grid-cols-3 gap-4">
        {relatedModels.map(rm => (
          <li>
            <a
              href={`/${slugify(rm.make)}-${slugify(rm.model)}-stripping-for-spares/`}
              class="text-blue-600 hover:underline"
            >
              {makeName} {rm.model} Stripping
            </a>
            <span class="text-gray-500 ml-2">({rm.count} vehicles)</span>
          </li>
        ))}
      </ul>
    </section>
  )}

  <!-- Cross-Links (Footer) -->
  <MakeModelLinks make={makeName} model={modelName} />
</Layout>

Database Schema (No Changes Needed)

Existing vehicles table already supports model pages:

ColumnTypeUsed For
maketextFiltering by make
modeltextFiltering by model (requires normalization)
yeartextDisplay + filtering on model page
transmissiontextFiltering on model page
engine_sizetextFiltering on model page
fuel_typetextFiltering on model page
featured_imagetextDisplay in grid (required)
statustextFilter active/available only
created_attimestampOrdering (newest first)
slugtextVehicle detail page URL

No migrations required. All necessary columns exist.



Questions & Decisions

Q1: Should we create model pages for makes with <5 total vehicles?

Decision: No. If a make has <5 vehicles total, stick with make page only.

Rationale: Thin content risk outweighs SEO benefit. Focus on makes with sufficient inventory depth.

Q2: Should we normalize all makes or only high-volume ones?

Decision: Start with high-volume (BMW, Mercedes, VW, Renault), expand as needed.

Rationale: Focus effort where impact is highest (80/20 rule). Add normalizers incrementally as lower-volume makes grow.

Q3: Should we use hyphens (-) or underscores (_) in URLs?

Decision: Use hyphens (-) for SEO best practice.

Example: /bmw-3-series-stripping-for-spares/ ✅ (not bmw_3_series ❌)

Rationale: Google treats hyphens as word separators, underscores as word joiners. Hyphens = better SEO.

Q3.5: Should we use nested URLs (e.g., /bmw-stripping/3-series/) or flat URLs?

Decision: Use flat URLs with breadcrumb schema for hierarchy signals.

Current (KEEP): /bmw-3-series-stripping-for-spares/NOT recommended: /bmw-stripping-for-spares/3-series/

Rationale:

  1. Modern SEO: Google’s John Mueller confirms URL structure matters less than content, schema, and internal linking
  2. Breadcrumb Schema: Already signals 4-level hierarchy to Google (Home → Pillar → Make → Model)
  3. Industry Standard: Amazon, eBay, and major e-commerce sites use flat URLs with breadcrumb schemas
  4. Implementation Simplicity: Astro’s file-based routing with [make]-[model]-stripping-for-spares.astro is clean
  5. Flexibility: Geographic pages will use city-prefix (/cape-town/bmw-3-series-stripping-for-spares/), not nested

For Geographic Pages (Future):

/{city}/{make}-{model}-stripping-for-spares/
/cape-town/bmw-3-series-stripping-for-spares/

City-prefix is preferred because:

Q4: Should model pages include quote form?

Decision: Yes, reuse QuickQuoteForm component (prefill make + model).

Rationale:

Q5: What if a model drops below 5 vehicles?

Decision: Keep page live until 0 vehicles, then:

Rationale: Preserve SEO equity from existing rankings. Users can still submit quotes for wanted parts.

Q6: Should we show sold vehicles or only available?

Decision: Only available (status IN ('available', 'active')).

Rationale: Sold vehicles mislead users. Better UX to show only in-stock inventory.


Summary

Goal: Create model-specific stripping pages that capture long-tail searches and improve user experience.

Approach: 3-level silo structure (Pillar → Make → Model) with soft internal linking.

Priority: High-volume models first (20+ vehicles), expand to medium/long-tail models.

Key Success Factors:

  1. ✅ Proper model normalization (reuse existing vehicleNormalizer.ts)
  2. ✅ Strong breadcrumb + body links (upward in silo)
  3. ✅ Horizontal links to sibling models (within make)
  4. ✅ Contextual cross-silo links (when relevant, not forced)
  5. ✅ Complete schema markup (BreadcrumbList + ItemList)
  6. ✅ Minimum 5 vehicles per page (avoid thin content)
  7. ✅ Static generation (SSG) for performance
  8. ✅ Monitoring via GSC + GA4 (iterate based on data)

Next Steps:

  1. Create src/pages/[make]-[model]-stripping-for-spares.astro (DONE)
  2. Test with 1 model page locally (DONE)
  3. Validate schema markup (DONE)
  4. Audit internal links (DONE)
  5. 📊 Monitor GSC + GA4 for performance optimization
  6. 🔧 Implement GSC-driven page prioritization
  7. 🔧 Add FAQ schema to high-traffic pages
  8. 🔧 Consider geographic variations if demand exists

GSC Integration (NEW - December 2024)

Overview

Model pages can now be prioritized and optimized using Google Search Console data. Use the seo-model-pages agent in coordination with the seo-redirect-manager agent for data-driven SEO.

Priority Scoring Formula

Priority Score = (impressions × 0.4) + (clicks × 0.3) + (vehicle_count × 0.3)
PriorityCriteriaAction
HIGH>500 impressions OR >50 clicks AND >10 vehiclesCreate/optimize page immediately
MEDIUM>100 impressions OR >10 clicks AND >5 vehiclesQueue for review
LOW<100 impressions AND <10 clicksSkip or consolidate with make page

GSC Data Sources

SourcePathData Available
GSC Dashboard/admin/search-console/dashboardImpressions, clicks, CTR, position
URL Mapping/admin/search-console/url-mappingRedirect suggestions with priority
Gap Analyzersrc/lib/seo-agent/gapAnalyzer.tsContent opportunities, missing pages

Integration Workflow

1. Authenticate via /admin/search-console/sites (OAuth2)
2. Fetch GSC metrics from /admin/search-console/dashboard
3. Cross-reference with vehicle counts from database
4. Calculate priority score for each make/model combination
5. Create pages for HIGH priority opportunities first
6. Monitor CTR improvements in GSC

High-Impression/Low-CTR Optimization

Pages with high impressions but low CTR (<2%) are optimization targets:

Symptoms:

Solutions:

  1. Improve title tag (add vehicle count, “South Africa”)
  2. Enhance meta description (add call-to-action)
  3. Add FAQ schema for rich snippets
  4. Improve page content depth

Content Depth Requirements (NEW)

Minimum Content Checklist

Every model page MUST include:

SectionRequiredImplementationStatus
Hero + Vehicle Count✅ YesLines 391-427✅ Done
Vehicle Grid (5+ vehicles)✅ YesLines 541-614✅ Done
Filter Bar✅ YesLines 441-539✅ Done
Related Models (3+)✅ YesLines 335-367✅ Done
Supplier Sidebar (1+)✅ YesLines 320-333✅ Done
SEO Content Block✅ YesLines 729-782✅ Done
FAQ Section (3+ questions)⏳ RecommendedNot implementedTODO
Buying Tips⏳ RecommendedNot implementedTODO

Unique Content Requirements

To avoid template duplication penalties:

  1. Unique Intro Paragraph: Each model page should have a unique intro mentioning:

  2. Model-Specific Parts List: Include commonly requested parts:

    BMW 3 Series: turbos, DME modules, N52/N54 engines, headlights
    Mercedes C-Class: W204/W205 parts, OM651 engines, LED headlights
    VW Golf: GTI parts, DSG gearboxes, TSI engines
    
  3. Generation References: Mention model generations where applicable:

    "Browse E90, F30, and G20 BMW 3 Series vehicles..."
    

FAQ Schema Template

Add to high-traffic model pages for rich snippets:

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What parts are available from BMW 3 Series vehicles being stripped?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Common parts include engines (N52, N54, B48), gearboxes, turbos, DME modules..."
      }
    },
    {
      "@type": "Question",
      "name": "How do I get a quote for BMW 3 Series parts?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use our quote form to describe the part you need..."
      }
    }
  ]
}

Geographic Variations (Future)

Current State

All model pages serve nationwide (South Africa). No city-specific variations exist.

When to Create Geographic Pages

Create city-specific pages ONLY when:

  1. Model has 10+ vehicles in specific metro area
  2. GSC shows location-specific search demand
  3. Unique content can be provided (local supplier info)

Proposed URL Pattern

/{city}/{make}-{model}-stripping-for-spares/
/cape-town/bmw-3-series-stripping-for-spares/
/johannesburg/mercedes-benz-c-class-stripping-for-spares/

Target Metro Areas

CitySlugMin VehiclesNotes
Johannesburgjohannesburg10Largest market
Cape Towncape-town10Second largest
Durbandurban8KZN hub
Pretoriapretoria8Gauteng secondary
Port Elizabethport-elizabeth5Eastern Cape

Implementation Requirements

  1. Location Data: Need supplier.city or vehicle location field
  2. Unique Content: Include local supplier names, contact details
  3. No Thin Content: Only create if 10+ vehicles in area
  4. Internal Linking: Link between city pages + national page

Cannibalization Prevention

To avoid city pages competing with national page:


Duplicate/Cannibalization Detection (NEW)

The Problem

Without normalization, the database creates duplicate pages:

Cannibalization Rules Matrix

ScenarioRuleExample
Same model, different trimConsolidate to series page320i, 330i, 340i → 3 Series
Same model, different genSingle page with year filtersE90, F30, G20 → 3 Series
Performance variantConsolidate unless >20 vehiclesM3 → 3 Series (unless high volume)
Coupe/Sedan variantsConsolidate3 Series Coupe → 3 Series
Electric variantSeparate page if >10 vehiclesi3 gets own page

Detection Query

-- Find potential cannibalization (multiple variants per model)
SELECT
  make,
  normalized_model,
  COUNT(DISTINCT model) as variant_count,
  SUM(vehicle_count) as total_vehicles,
  array_agg(DISTINCT model) as raw_variants
FROM (
  SELECT
    make,
    model,
    normalizeModelForSeo(make, model) as normalized_model,
    COUNT(*) as vehicle_count
  FROM vehicles
  WHERE status IN ('available', 'active')
  GROUP BY make, model
) subquery
GROUP BY make, normalized_model
HAVING COUNT(DISTINCT model) > 3
ORDER BY total_vehicles DESC;

Resolution Actions

  1. Merge: Variants already consolidated via modelSeoNormalizer.ts
  2. Redirect: Create 301s from old variant URLs (if any exist)
  3. Keep Separate: Only if variant has independent search volume (>500 impressions in GSC)

Model Normalizer Coverage

File: src/lib/normalizers/modelSeoNormalizer.ts (1,082 lines)

MakeVariants CoveredCanonical Models
BMW6711 (3 Series, 5 Series, X5, etc.)
Mercedes-Benz7118 (C-Class, E-Class, Vito, etc.)
Audi4518 (A4, A3, Q5, etc.)
VW3010 (Golf, Polo, Jetta, etc.)
Renault2014 (Sandero, Clio, Kwid, etc.)

Missing Variant Detection

Run periodically to find unmapped variants:

// Check for variants not in MODEL_MAPPINGS
const unmappedVariants = vehicles
  .filter(v => {
    const result = normalizeModelForSeo(v.make, v.model);
    return result.slug === slugify(v.model); // Returns original = unmapped
  })
  .map(v => `${v.make}:${v.model}`);

console.log('Unmapped variants:', unmappedVariants);

Logging & Audit Trail (NEW)

Proposed Change Log Table

CREATE TABLE seo_model_page_changes (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  action TEXT NOT NULL, -- 'created', 'updated', 'deleted', 'redirected'
  page_slug TEXT NOT NULL,
  make TEXT,
  model TEXT,
  old_state JSONB, -- Previous page config (for rollback)
  new_state JSONB, -- New page config
  reason TEXT, -- "Thin content", "GSC demand", "Manual request"
  gsc_data JSONB, -- Snapshot of GSC metrics at time of change
  vehicle_count INT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  created_by TEXT DEFAULT 'seo-model-pages-agent'
);

-- Index for recent changes lookup
CREATE INDEX idx_seo_changes_created ON seo_model_page_changes(created_at DESC);

Rollback Workflow

  1. Identify Problem: Traffic dropped after change
  2. Query Recent Changes:
    SELECT * FROM seo_model_page_changes
    WHERE created_at > NOW() - INTERVAL '7 days'
    ORDER BY created_at DESC;
    
  3. Restore from old_state: Use stored JSON to recreate previous config
  4. Log Restoration: Create new entry with action = ‘rollback’

Audit Report Template

📊 SEO Model Pages Audit - Last 30 Days

Pages Created: 12
Pages Updated: 5
Pages Redirected: 3
Pages Deleted: 0

Top Performers (GSC):
1. /bmw-3-series-stripping-for-spares/ - +45% CTR, 1,200 clicks
2. /mercedes-benz-c-class-stripping-for-spares/ - +32% CTR, 890 clicks

Underperformers (need attention):
1. /chery-qq-stripping-for-spares/ - 0 clicks, position 45
   → Recommendation: Redirect to /chery-stripping-for-spares/

Rollback Candidates:
- None (all changes showing positive impact)

Make Page Enhancements (December 2024)

Overview

The make pages (src/pages/[make]-stripping-for-spares.astro) have been enhanced with two new sections to improve user engagement, lead generation, and internal SEO linking.

Location: Lines 1180-1250

Previous State: Plain text list of part categories with inline links to engines page

New Design:

Parts Included (12 total):

PartImageCategory
Complete Enginecomplete-engine.webpEngine
Gearboxgearbox.webpTransmission
Turbochargerturbocharger.webpEngine
Cylinder Headcylinder-head.webpEngine
Alternatoralternator.webpElectrical
Starter Motorstarter-motor.webpElectrical
Radiatorradiator.webpCooling
ECU / Computerecu-computer.webpElectrical
Fuel Pumpfuel-pump.webpFuel
Power Steeringpower-steering-pump.webpSteering
Air Conditioningac-compressor.webpClimate
Suspensionsuspension.webpSuspension

Image Location: /public/images/parts/ (50 WebP files available)

WhatsApp Number: 27723375272 (Engine Finder automated flow)

Code Pattern:

<a
  href="https://wa.me/27723375272"
  target="_blank"
  rel="noopener noreferrer"
  class="group relative bg-white rounded-xl border hover:shadow-lg hover:border-[#E41F29] transition-all duration-300 transform hover:-translate-y-1 active:scale-95"
>
  <div class="aspect-square bg-gray-50 relative overflow-hidden">
    <img src={`/images/parts/${part.image}`} class="w-full h-full object-contain p-3 group-hover:scale-110 transition-transform duration-300" />
    <!-- Category badge & WhatsApp indicator -->
  </div>
  <div class="p-3 text-center border-t">
    <span class="text-sm font-medium">{part.name}</span>
  </div>
</a>

2. Browse Models Section (NEW)

Location: Lines 1266-1290

Purpose: Contextual internal links to model pages for SEO (soft silo)

Why This Approach:

Implementation:

<!-- Query models with 5+ vehicles -->
const MIN_VEHICLES_FOR_MODEL_PAGE = 5;
const { data: allModelsData } = await supabase
  .from('vehicles')
  .select('model, suppliers!inner(status)')
  .eq('suppliers.status', 'active')
  .ilike('make', makeNameForDB)
  .not('featured_image', 'is', null);

// Group by normalized SEO slug
const seoModelCounts = new Map<string, number>();
allModelsData?.forEach(v => {
  const seoModel = getCanonicalModelObject(makeName, v.model);
  seoModelCounts.set(seoModel.slug, (seoModelCounts.get(seoModel.slug) || 0) + 1);
});

// Top 12 models sorted by count
const browseModels = Array.from(seoModelCounts.entries())
  .filter(([_, count]) => count >= MIN_VEHICLES_FOR_MODEL_PAGE)
  .sort((a, b) => b[1] - a[1])
  .slice(0, 12);

Display:

Anchor Text Strategy:

Example Output (BMW):

Browse BMW Models
┌─────────────────┬──────────────────┐
│ 3 Series   [49] │ 1 Series    [13] │
├─────────────────┼──────────────────┤
│ X5          [8] │ 5 Series     [7] │
└─────────────────┴──────────────────┘

SEO Impact

Before Enhancement:

After Enhancement:

Key Files Changed:

Mobile UX Considerations


Model Page Fixes (December 2024)

1. Pagination Bug Fix

Problem: Model pages were showing thin content errors for valid models (e.g., BMW 1 Series with 13 vehicles).

Root Cause: The database query applied .range(0, 29) pagination BEFORE filtering by model. This meant:

  1. Query fetched first 30 vehicles by MAKE only
  2. Then filtered by MODEL client-side
  3. If model’s vehicles weren’t in first 30 results, page showed <5 vehicles → thin content redirect

Why 3 Series Worked: With 49 vehicles, more likely to appear in first 30 newest BMW vehicles. Why 1 Series Failed: With 13 vehicles, may not be in newest 30 BMW vehicles.

Solution: Remove .range() from initial query, filter all vehicles first, then apply pagination with .slice().

Before (Lines 140-186):

const { data: vehicles } = await supabase
  .from('vehicles')
  .select('*')
  .ilike('make', makeNameForDB)
  .eq('suppliers.status', 'active')
  .not('featured_image', 'is', null)
  .range(offset, offset + VEHICLES_PER_PAGE - 1); // ❌ Pagination BEFORE model filter

After:

const { data: allVehicles } = await supabase
  .from('vehicles')
  .select('*')
  .ilike('make', makeNameForDB)
  .eq('suppliers.status', 'active')
  .not('featured_image', 'is', null); // ✅ No pagination - get all

// Filter by model using SEO slug comparison
const allMatchingVehicles = allVehicles?.filter(v => {
  const vehicleSlug = getCanonicalModelObject(v.make, v.model).slug;
  const expectedSlug = `${makeSlug}-${modelSlug}`;
  return vehicleSlug === expectedSlug;
}) || [];

// Apply pagination AFTER filtering
const vehicles = allMatchingVehicles.slice(offset, offset + VEHICLES_PER_PAGE);

Key Insight: getCanonicalModelObject() returns full slug (e.g., “bmw-3-series”), not just model portion.

Problem: Sidebar had long single-column list requiring excessive scrolling.

Solution: Compact 2-column grid layout with WhatsApp hover overlay.

New Design Features:

Categories (5 total):

CategoryParts
Engine & MechanicalComplete Engine, Cylinder Head, Turbocharger, Gearbox
ElectricalAlternator, Starter Motor, ECU, Wiring Loom
Body & ExteriorBumpers, Doors, Headlights, Tail Lights
Cooling & DrivetrainRadiator, Differential, Steering Rack, Alloy Wheels
InteriorDashboard, Seats

Code Pattern:

<div class="grid grid-cols-2 gap-2 mt-2">
  <a href={whatsappUrl} class="group relative bg-gray-50 rounded-lg p-2 text-center hover:bg-green-50 transition-colors">
    <!-- WhatsApp overlay -->
    <div class="absolute inset-0 bg-green-500 rounded-lg flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
      <svg class="w-8 h-8 text-white"><!-- WhatsApp icon --></svg>
    </div>
    <span class="relative z-10 text-sm group-hover:text-white transition-colors">Part Name</span>
  </a>
</div>

WhatsApp Message Format:

Hi, I'm looking for a Complete Engine for a {makeName} {modelName}. Do you have any available?

File Changed: src/pages/[make]-[model]-stripping-for-spares.astro (lines 640-900)


Agent Coordination

SEO Model Pages Agent

File: .claude/agents/seo-model-pages.md

Use for:

SEO Redirect Manager Agent

File: .claude/agents/seo-redirect-manager.md

Use for:

Coordination Workflow

1. SEO Model Pages: Identify high-volume models from DB
2. → SEO Redirect Manager: Fetch GSC data for demand validation
3. SEO Model Pages: Calculate priority score (DB + GSC)
4. SEO Model Pages: Create pages for HIGH priority opportunities
5. → SEO Redirect Manager: Create redirects from old variant URLs
6. Monitor GSC for 30 days
7. Iterate based on CTR/position data

Example Agent Prompts

Analyze models with GSC data:

Analyze BMW models in the database. Cross-reference with GSC impressions.
Show vehicle counts, GSC metrics, and recommend which models need dedicated pages.

Detect cannibalization:

Scan all stripping-for-spares pages for potential cannibalization.
Identify models with multiple URL variants competing for same keywords.
Recommend consolidation strategy with 301 redirects.

Create optimized page:

Create a model page for Mercedes-Benz C-Class with:
- Proper breadcrumb schema (4 levels)
- Unique intro mentioning W204/W205 parts
- FAQ schema for rich snippets
- Internal links to related models and engines page

🤖 Subagent Rule

Before modifying SEO model pages:

  1. Spawn a subagent with subagent_type: "seo-model-pages" for model page work
  2. Or use subagent_type: "seo-redirect-manager" for redirect work
  3. Include this CLAUDE.md path in the prompt: src/pages/seo-model-pages/CLAUDE.md
  4. After work is complete, update this CLAUDE.md with changes

Example agent prompts:

Analyze BMW models in the database. Cross-reference with GSC impressions.
Show vehicle counts, GSC metrics, and recommend which models need dedicated pages.

Scan all stripping-for-spares pages for potential cannibalization.
Identify models with multiple URL variants competing for same keywords.
Recommend consolidation strategy with 301 redirects.

Create a model page for Mercedes-Benz C-Class with proper breadcrumb schema,
unique intro mentioning W204/W205 parts, FAQ schema, and internal links.