17 min read 3547 words Updated Mar 17, 2026 Created Mar 17, 2026
#ad-automation-platform#ad-creator#advertising#improvement-plan#meta-ads

Ad Creator Improvement Plan

Based on the Meta Ads Audit (Feb 2026), placement performance data, and full codebase review of ad-automation-platform.
All file references are relative to /Users/geoff/code/experiments/ad-automation-platform/.


Current State Summary

The Ad Creator is a 5-step wizard: Setup → Copy & Brand → Generate → Remix → Save.

It uses Gemini for image analysis and generation, produces 4 fixed Meta ad formats (Feed Square 1:1, Feed Portrait 4:5, Story/Reel 9:16, Link Ad 1.91:1), and saves the result as a Creative linked to a Spotlight entity.

What works well

  • Clean step-based UX with reducer state management
  • Entity integration (auto-loads images + business context from entities)
  • Workspace brand settings auto-populate (color, logo)
  • Parallel format generation with progress tracking
  • Remix step with before/after comparison
  • Character count guidance on copy fields

Key gaps identified from the audit data

GapImpactCurrent Behaviour
No placement priorityStories/Reels deliver leads at 46% cheaper CPL — but the tool treats all formats equallyAlways generates all 4 formats with no priority weighting
Generic analysis promptDoesn't incorporate industry knowledge, location strategy, or creative best practicesbuildAnalysisPrompt asks for 3 generic "marketing angles"
Copy system disconnectThe themed copy-generation.service.ts (6 themes, model selection) isn't used by Ad CreatorAd Creator uses its own Gemini analysis → 3 variations
No format selectionCan't choose which formats to generate or prioritiseHardcoded META_AD_FORMATS array, always all 4
No visual style/themeNo way to select twilight, bright & airy, lifestyle, etc.buildGenerationPrompt has optional themeLabel/visualPrompt but UI never sets them
Hardcoded remix suggestionsSame 4 suggestions per format regardless of contextFORMAT_SUGGESTIONS object in StepRemix is static
No creative mix awarenessAudit recommends 5 creatives per ad set with specific format distributionTool produces 1 creative at a time, no batch/set view
Character limits don't match auditHook limit is 60 chars, audit says headline max 40 charsHOOK_LIMIT = 60, SUBHEAD_LIMIT = 125 in StepCopySelect
No campaign goal contextCTAs and messaging should vary by goal (lead gen, awareness, retargeting)CTA list is flat with no goal mapping
No performance contextNo benchmarks, no guidance on what "good" looks likeNo CPL/CTR/CPM reference anywhere in the UI

Proposed Improvements

Phase 1: Strategy-Aware Generation (High Impact, Medium Effort)

These changes make the existing flow smarter without restructuring the UI.

1.1 Enriched Analysis Prompt

Files: src/server/services/ad-prompts.ts

Rewrite buildAnalysisPrompt to incorporate creative strategy knowledge:

  • Add industry context (real estate, e-commerce, etc.) with industry-specific angle suggestions
  • Include location-first messaging guidance when location is provided
  • Request 5 variations instead of 3 (maps to the "5 creatives per ad set" recommendation)
  • Request specific angle types: lifestyle/vibe, location/lifestyle, urgency/scarcity, social proof, benefit-led
  • Add placement awareness: ask the AI to consider which copy works best for Stories vs Feed
  • Include character constraints in the prompt itself (headline max 40 chars, primary text max 125 chars, description max 30 chars)
Current: "Generate 3 distinct high-converting marketing angles"
Proposed: "Generate 5 marketing angles, each optimised for a different placement strategy:
  1. Location-lifestyle (lead with suburb/area appeal) — best for high-performing areas
  2. Lifestyle/aspirational (create desire) — best for Stories/Reels
  3. Urgency/scarcity (time-limited offers) — test tier
  4. Social proof (testimonials, numbers) — test tier
  5. Direct/action (benefit-first, specific detail) — experimental tier"

1.2 Visual Style Selection

Files: src/client/components/ad-creator/StepCopySelect.tsx, src/server/services/ad-prompts.ts, src/client/pages/AdCreator.tsx

Add a "Visual Style" selector to the Copy & Brand step that maps to themeLabel and visualPrompt in the generation prompt:

StylethemeLabelvisualPromptBest For
Twilight Herotwilight"Golden hour/twilight exterior, warm interior glow, blue-hour sky"Exteriors, hero shots
Bright & Airybright-airy"Natural light, clean staging, open spaces, lifestyle warmth"Interiors
Lifestylelifestyle"People enjoying the space, aspirational moments, environmental human scale"Stories/Reels
Detail & Texturedetail"Close-up fixtures, materials, luxury finishes, shallow depth of field"Carousel, Feed
Bold & Minimalbold-minimal"Strong typography, clean negative space, single hero element"Feed, Link ads
Dark & Dramaticdark-dramatic"High contrast, moody lighting, cinematic feel"Reels, Stories

State change:

// Add to AdCreatorState
visualStyle: { label: string; prompt: string } | null;

// Add action
| { type: 'SET_VISUAL_STYLE'; style: { label: string; prompt: string } }

The generation prompt already accepts themeLabel and visualPrompt — just need to wire them through from the UI.

1.3 Campaign Goal Context

Files: src/client/components/ad-creator/StepCopySelect.tsx, src/shared/ad-formats.ts, src/server/services/ad-prompts.ts

Add a campaign goal selector that influences both CTA options and copy generation:

GoalRecommended CTAsCopy Tone
Lead GenerationBook a Viewing, Register Interest, Get OfferSpecific detail, location appeal
AwarenessLearn More, ExploreAspirational, lifestyle
RetargetingSchedule Inspection, Download FloorplanUrgency, social proof, "still thinking about..."
Sales/ConversionApply Now, Reserve Now, Buy NowScarcity, direct value

Filter CTA_OPTIONS by selected goal instead of showing all 10. Pass goal context into the analysis and generation prompts.

1.4 Update Character Limits

Files: src/client/components/ad-creator/StepCopySelect.tsx

Align with the audit data:

const HOOK_LIMIT = 40;      // was 60, audit says max 40
const SUBHEAD_LIMIT = 125;  // already correct
const DESCRIPTION_LIMIT = 30; // new field (currently not in UI)

Add a description field to the copy editor (currently StepSave passes description: '').


Phase 2: Format Flexibility (High Impact, Medium Effort)

2.1 Format Selection & Priority

Files: src/client/components/ad-creator/StepCopySelect.tsx or new sub-step, src/server/services/ad-image-generation.service.ts, src/shared/ad-formats.ts

Replace the fixed 4-format generation with a configurable format picker:

  • Show all available formats as toggleable cards with placement performance data
  • Pre-select formats based on campaign goal (Lead Gen defaults: Story/Reel + Feed Portrait + Feed Square)
  • Show CPL performance indicator per format from the audit data:
    • Story/Reel (9:16): "$3.82 CPL — Best performer"
    • Feed Portrait (4:5): "$7.14 CPL — High volume"
    • Feed Square (1:1): "$7.14 CPL — Universal"
    • Link Ad (1.91:1): "Link previews"
  • Allow custom format addition (e.g., Marketplace 1:1 at a different resolution)
  • Add a "Placement Priority" indicator: Primary / Secondary / Optional

State change:

// Add to AdCreatorState
selectedFormats: { name: string; aspectRatio: string; width: number; height: number; priority: 'primary' | 'secondary' | 'optional' }[];

Backend change: generateAllFormats already takes META_AD_FORMATS — change it to accept a formats array parameter instead of using the hardcoded constant.

2.2 Expand Format Library

Files: src/shared/ad-formats.ts

Add formats the audit data suggests are valuable:

export const ALL_AD_FORMATS = [
  // Current
  { name: 'Feed Square', aspectRatio: '1:1', width: 1080, height: 1080, numericRatio: 1, placement: 'Feed + Marketplace' },
  { name: 'Feed Portrait', aspectRatio: '4:5', width: 1080, height: 1350, numericRatio: 0.8, placement: 'Feed (mobile optimized)' },
  { name: 'Story/Reel', aspectRatio: '9:16', width: 1080, height: 1920, numericRatio: 0.5625, placement: 'Stories, Reels, TikTok' },
  { name: 'Link Ad', aspectRatio: '1.91:1', width: 1200, height: 628, numericRatio: 1.91, placement: 'Link preview ads' },
  // New
  { name: 'Marketplace', aspectRatio: '1:1', width: 1080, height: 1080, numericRatio: 1, placement: 'Facebook Marketplace' },
  { name: 'Carousel Card', aspectRatio: '1:1', width: 1080, height: 1080, numericRatio: 1, placement: 'Carousel ads' },
  { name: 'IG Explore', aspectRatio: '4:5', width: 1080, height: 1350, numericRatio: 0.8, placement: 'Instagram Explore' },
] as const;

Also add safeZones metadata from src/shared/media-formats.ts so the generation prompt can avoid text in unsafe areas (especially Stories top 250px / bottom 340px).

2.3 Placement-Aware Generation Prompts

Files: src/server/services/ad-prompts.ts

Enhance buildGenerationPrompt with placement-specific composition rules:

  • Stories/Reels (9:16): "Full-bleed imagery. Text overlay in centre 60% only — avoid top 250px and bottom 340px for platform UI. Front-load the hook. Swipe-up CTA alignment."
  • Feed (1:1, 4:5): "Thumb-stopping first impression. Headline overlay works well. Property as hero. Max 20% text overlay area."
  • Marketplace (1:1): "Product-listing aesthetic. Property exterior as hero. Price/location overlay. Clean, uncluttered."
  • Link Ad (1.91:1): "Landscape composition. Text-light — the ad platform adds headline below. Focus on a striking visual."

Phase 3: Copy System Integration (Medium Impact, Medium Effort)

3.1 Connect Themed Copy Generation

Files: src/server/trpc/routers/ad-creator.ts, src/client/components/ad-creator/StepCopySelect.tsx

The platform already has a rich copy-generation.service.ts with 6 themes (luxury, urgency, emotional, benefits, professional, minimal) and multi-model support (Claude, GPT-4o, GPT-4o-mini). But the Ad Creator doesn't use it.

Add a "Generate More Copy" feature to StepCopySelect:

  • After the initial Gemini analysis generates 3-5 variations, offer a "Generate themed alternatives" button
  • Let the user pick a theme (luxury, urgency, etc.) and model
  • Call copyGenerationService.generateThemed() with the entity context
  • Merge results into the variation list as additional options
  • Show theme badge on each variation card

New tRPC endpoint:

generateThemedCopy: publicProcedure
  .input(z.object({
    entityId: z.string().optional(),
    theme: z.enum(['luxury', 'urgency', 'emotional', 'benefits', 'professional', 'minimal']),
    model: z.enum(['claude', 'gpt-4o', 'gpt-4o-mini']).optional(),
    variants: z.number().min(1).max(5).optional(),
    guidance: z.string().optional(),
  }))
  .mutation(async ({ input }) => {
    return copyGenerationService.generateThemed(input);
  })

3.2 Copy Performance Tiers

Files: src/client/components/ad-creator/StepCopySelect.tsx

Show copy guidance based on the audit's performance tiers:

  • Proven (CPL <$10): Location-lifestyle led, specificity converts, aspirational but grounded
  • Testing (CPL $10-20): Problem-solution, social proof, scarcity/urgency
  • Experimental: Question-led, benefit-first, number-led

Tag each AI-generated variation with its tier so the user knows which messaging strategy they're selecting. This is purely UI — add a small "Proven" / "Testing" / "Experimental" badge based on the variation's angle matching the tier definitions.

3.3 Smart Remix Suggestions

Files: src/client/components/ad-creator/StepRemix.tsx

Replace the hardcoded FORMAT_SUGGESTIONS with context-aware suggestions:

// Instead of generic "Make text bolder" for every format:
function getSmartSuggestions(format: GeneratedFormat, visualStyle: string, campaignGoal: string): string[] {
  const suggestions: string[] = [];

  if (format.name === 'Story/Reel') {
    suggestions.push('Move text to center 60% (avoid top/bottom safe zones)');
    suggestions.push('Make the visual hook more dramatic for first 3 seconds');
    suggestions.push('Add subtle motion blur to suggest video/animation');
  }

  if (format.name === 'Feed Square' || format.name === 'Feed Portrait') {
    suggestions.push('Reduce text overlay to under 20% of image area');
    suggestions.push('Make the property the dominant element');
  }

  if (visualStyle === 'twilight') {
    suggestions.push('Warmer interior glow through windows');
    suggestions.push('Deepen the blue-hour sky gradient');
  }

  if (campaignGoal === 'retargeting') {
    suggestions.push('Add urgency element (e.g., "Only X remaining")');
    suggestions.push('Include social proof text');
  }

  return suggestions;
}

Phase 3.5: Ad Creator → Campaign Builder Integration (High Impact, Medium Effort)

This is a critical architectural piece. Right now the two tools are disconnected — the Ad Creator produces a Creative, and the CampaignBuilder deploys Creatives. But the handoff doesn't properly use Meta's Asset Customization, which means the 4 format images don't get mapped to the correct placements.

How Meta Ad Formats Actually Work

In Meta's hierarchy: Campaign → Ad Set → Ad. One Ad can have placement-customized assets via the asset_feed_spec. You upload different images for different placements within a single ad — Meta automatically serves the right one:

One Ad = {
  ad_copy: { headline, primary_text, description, cta },
  default_image: feed_square.png,           // fallback
  placement_overrides: {
    instagram_stories: story_reel.png,      // 9:16
    instagram_reels: story_reel.png,        // 9:16
    facebook_reels: story_reel.png,         // 9:16
    facebook_feed: feed_portrait.png,       // 4:5
    instagram_feed: feed_portrait.png,      // 4:5
    audience_network: link_ad.png,          // 1.91:1
  }
}

This is not 4 separate ads. It's 1 ad with 4 placement-specific assets.

3.5.1 Restructure Creative Save Format

Files: src/shared/types/index.ts, src/server/trpc/routers/ad-creator.ts, src/client/components/ad-creator/StepSave.tsx

Currently the Creative type stores formats as a flat array of { format, imageUrl, width, height }. Restructure to include placement mapping:

// Add to Creative type or new PlacementAsset type
interface PlacementAsset {
  name: string;              // "Feed Square", "Story/Reel", etc.
  aspectRatio: string;
  imageUrl: string;
  width: number;
  height: number;
  metaPlacements: string[];  // ["facebook_feed", "instagram_feed", "marketplace"]
  isPrimary: boolean;        // default/fallback image
}

// Creative.images becomes Creative.placementAssets

Each format maps to specific Meta placement keys:

FormatMeta Placements
Feed Square (1:1)facebook_feed, instagram_feed, facebook_marketplace
Feed Portrait (4:5)facebook_feed, instagram_feed (preferred over 1:1 on mobile)
Story/Reel (9:16)instagram_stories, instagram_reels, facebook_reels, facebook_stories
Link Ad (1.91:1)audience_network, facebook_right_column, facebook_search

3.5.2 Campaign Builder Uses Asset Customization

Files: src/server/services/campaign.service.ts, src/server/platforms/meta.ts

When the CampaignBuilder deploys a Creative that has multiple placement assets, it should call createMultiFormatAdCreative() (already exists at meta.ts line ~1803) instead of creating a basic ad creative. This bundles all format images into one ad with the correct placement mapping.

Update createAndDeploy() in campaign.service.ts:

// Current flow:
For each creative → createAdCreative() → createAd()  // one image, all placements

// Proposed flow:
For each creative:
  if creative.placementAssets.length > 1:
    → createMultiFormatAdCreative(placementAssets) → createAd()  // asset customization
  else:
    → createAdCreative() → createAd()  // backwards compatible

3.5.3 Placement-Based Ad Set Strategy (Budget Weighting)

Files: src/server/services/campaign.service.ts, src/client/components/campaign/CampaignBuilder/StepSpotlights.tsx, src/shared/types/index.ts

Critical insight: You cannot weight budget per placement within a single ad. Meta's budget controls exist at the ad set level, not the ad level. Asset customization controls which image shows on each placement, but not how much you spend on each placement.

To push more budget toward Stories/Reels (the $3.82 CPL winner), you need separate ad sets for different placement groups, each with their own budget.

Recommended campaign structure:

Campaign (Lead Gen)
├── Ad Set: Stories + Reels (50% budget — best CPL at $3.82)
│   │  Manual placements: instagram_stories, instagram_reels, facebook_reels, facebook_stories
│   ├── Ad 1: 9:16 hero creative
│   └── Ad 2: 9:16 lifestyle variant
│
├── Ad Set: Feed (35% budget — highest volume, $7.14 CPL)
│   │  Manual placements: facebook_feed, instagram_feed, facebook_marketplace
│   ├── Ad 1: 4:5 + 1:1 asset-customized
│   └── Ad 2: Carousel
│
└── Ad Set: All Placements (15% budget — discovery/testing)
    │  Advantage+ Placements (automatic)
    └── Ad 1: Full asset customization (all 4 format images)

Implementation — "Smart Campaign" template in CampaignBuilder:

Add a "Campaign Structure" selector to the CampaignBuilder's Settings step:

StructureDescriptionWhen to use
Placement-Optimised (recommended)Auto-creates 3 ad sets: Stories/Reels (50%), Feed (35%), Discovery (15%)When you have data showing placement CPL variance (like the audit)
Single Ad SetOne ad set with Advantage+ Placements, asset customizationNew campaigns with no performance data yet
ManualUser defines their own ad setsAdvanced users who want full control

When "Placement-Optimised" is selected:

  1. System auto-creates 3 spotlights (ad sets) with pre-configured manual placements
  2. Budget split defaults to 50/35/15 but is adjustable via sliders
  3. Each ad set only accepts creatives in the right format:
    • Stories/Reels ad set → only 9:16 creatives
    • Feed ad set → only 1:1 and 4:5 creatives (asset customized)
    • Discovery ad set → accepts all formats (full asset customization)
  4. The Ad Creator's format picker (Phase 2.1) pre-selects formats based on which ad set the creative is targeting

Mapping from Ad Creator to Campaign ad sets:

Ad Creator generates:          Maps to ad set:
─────────────────────          ────────────────
Story/Reel (9:16)         →   Stories/Reels ad set
Feed Portrait (4:5)       →   Feed ad set (primary)
Feed Square (1:1)         →   Feed ad set (asset customization fallback)
Link Ad (1.91:1)          →   Discovery ad set

This also means the Ad Creator's "Save & Add to Campaign" flow should be smart about which ad set to target based on the format:

  • If saving a 9:16 creative → suggest the Stories/Reels ad set
  • If saving a 4:5 creative → suggest the Feed ad set
  • If saving all formats → suggest the Discovery ad set or split across ad sets

3.5.4 "Deploy to Campaign" from Ad Creator

Files: src/client/components/ad-creator/StepSave.tsx

Add a secondary action on the Save step: "Save & Add to Campaign"

Flow:

  1. Save as Creative (existing)
  2. Show a modal with existing draft/active campaigns for this workspace
  3. If campaign uses Placement-Optimised structure, auto-suggest the right ad set based on format
  4. System calls addCreativeToSpotlight() with asset customization
  5. Creative is live in Meta with correct placement mapping

This bridges the gap between the two tools — the user can go from "image uploaded" to "ad live in Meta" in one flow.

3.5.5 Campaign Goal Propagation

Files: src/client/pages/AdCreator.tsx, src/client/components/ad-creator/StepSave.tsx

When "Save & Add to Campaign" is used, the campaign goal selected in the Ad Creator (Phase 1.3) should:

  • Pre-filter to campaigns with matching objectives
  • Carry through to the Meta ad creative (e.g., lead gen → LEAD_GENERATION objective)
  • Set the correct CTA type via mapToMetaCTA() in meta.ts

Phase 4: Creative Set Management (Medium Impact, Higher Effort)

4.1 Batch Creative Generation

Files: New component, src/client/pages/AdCreator.tsx

The audit recommends 5 creatives per ad set with a specific mix:

SlotFormatPurpose
1Static (9:16)Hero property — Stories/Reels
2Static (4:5 or 1:1)Hero property — Feed
3Video (9:16, 5-15s)Twilight reveal or walkthrough
4Carousel (1:1)Multi-room showcase
5Lifestyle/UGC-styleSocial proof / testimonial

Add a "Creative Set" mode that generates a full ad set in one flow:

  • User picks an entity and uploads images
  • System generates all 5 slots using different copy angles and visual styles
  • Each slot uses the appropriate format and creative direction
  • User can remix individual slots
  • Save as a "Creative Set" (new entity type or grouped creatives)

This is a significant feature but maps directly to what the audit recommends for optimal Meta performance.

4.2 Creative Refresh Tracking

Files: New service + UI component

Track when creatives were generated and flag them for refresh based on the audit's cadence:

  • Static hero shots: refresh every 3-4 weeks
  • Video content: refresh every 4-6 weeks
  • Lifestyle/UGC: refresh every 2-3 weeks

Add a "Freshness" indicator to the Creatives list page. When a creative is stale, show a "Refresh" action that opens the Ad Creator pre-populated with the same entity and settings.


Phase 5: Performance Integration (Lower Priority, Future)

5.1 Performance Benchmarks in UI

Show audit benchmarks contextually:

  • In StepGenerate: "Stories/Reels: targeting $3.82 CPL (best performing placement)"
  • In StepCopySelect: "Location-led copy in high-performing areas can achieve <$8 CPL"
  • In StepSave: "This creative targets: Feed + Stories. Expected CPL range: $4-8"

5.2 Kill/Scale Rules Display

When viewing saved creatives, show threshold guidance:

  • "If this creative gets 0 leads after $24.70 spend → Pause"
  • "If CPL exceeds $19.38 → Review"
  • "If CPL stays below $8 with 10+ leads → Scale"

5.3 Retargeting Creative Templates

Add a "Retargeting" campaign goal option that generates different creative:

  • Different messaging: "Still thinking about [Property]?"
  • Social proof heavy
  • Property-specific details
  • Expected CPL target: $3-5 (40-60% lower than prospecting)

Implementation Priority

PhaseEffortImpactRecommendation
1. Strategy-Aware Generation2-3 daysHighDo first — biggest bang for buck, improves output quality immediately
2. Format Flexibility2-3 daysHighDo second — lets users focus spend on best-performing placements
3. Copy System Integration2-3 daysMediumDo third — unlocks the existing themed copy system
3.5. Campaign Builder Integration3-4 daysHighDo alongside or right after Phase 3 — connects Ad Creator output to live Meta deployment with proper asset customization
4. Creative Set Management4-5 daysMediumDo fourth — moves toward the full "5 per ad set" recommendation
5. Performance Integration2-3 daysLowerDo last — nice to have, makes the tool educational

Files Modified Per Phase

Phase 1

  • src/server/services/ad-prompts.ts — rewrite buildAnalysisPrompt
  • src/client/components/ad-creator/StepCopySelect.tsx — visual style selector, campaign goal, updated char limits, description field
  • src/client/pages/AdCreator.tsx — new state fields (visualStyle, campaignGoal), new actions
  • src/shared/ad-formats.ts — CTA options grouped by goal

Phase 2

  • src/shared/ad-formats.ts — expanded format library with placement metadata
  • src/client/components/ad-creator/StepCopySelect.tsx or new StepFormats.tsx — format picker UI
  • src/server/services/ad-image-generation.service.ts — accept formats parameter instead of hardcoded
  • src/server/services/ad-prompts.ts — placement-specific composition rules
  • src/server/trpc/routers/ad-creator.ts — pass selected formats through

Phase 3

  • src/server/trpc/routers/ad-creator.ts — new generateThemedCopy endpoint
  • src/client/components/ad-creator/StepCopySelect.tsx — themed generation UI, performance tier badges
  • src/client/components/ad-creator/StepRemix.tsx — smart suggestions

Phase 3.5

  • src/shared/types/index.tsPlacementAsset type, restructure Creative images
  • src/server/trpc/routers/ad-creator.ts — save with placement mapping
  • src/client/components/ad-creator/StepSave.tsx — "Save & Add to Campaign" action
  • src/server/services/campaign.service.ts — use createMultiFormatAdCreative for multi-asset creatives, placement-optimised ad set creation
  • src/server/platforms/meta.ts — ensure createMultiFormatAdCreative maps placement keys correctly
  • src/client/components/campaign/CampaignBuilder/StepSettings.tsx — campaign structure selector (Placement-Optimised / Single / Manual)
  • src/client/components/campaign/CampaignBuilder/StepSpotlights.tsx — auto-create placement-group ad sets with budget split sliders
  • src/client/components/campaign/CampaignBuilder/types.ts — campaign structure type, placement group configs

Phase 4

  • New: src/client/components/ad-creator/CreativeSetBuilder.tsx
  • New: src/server/services/creative-set.service.ts
  • src/shared/types/index.ts — creative set types
  • src/server/trpc/routers/creatives.ts — set CRUD

Phase 5

  • src/client/components/ad-creator/StepGenerate.tsx — benchmark callouts
  • src/client/components/ad-creator/StepSave.tsx — expected performance summary
  • Creatives list page — freshness indicators, kill/scale guidance