---
name: fluent-mystique-preview
description: "Mock, modify, and preview Mystique UI components in the live Fluent Commerce app before building or deploying. Three modes: ADD (inject new), MODIFY (alter existing), REMOVE (preview without). Highlights all changes with color-coded borders and badges. Supports iterative refinement. Triggers on \"preview UI\", \"mock component\", \"preview manifest\", \"how will it look\", \"show me the UI\", \"preview in OMS\", \"preview in store\", \"visual preview\", \"UI mockup\", \"what would it look like if\", \"preview change\", \"modify the page\"."
user-invocable: true
read-only: true
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
argument-hint: "[--mode add|modify|remove] [--app oms|store|servicepoint|adminconsole] [--route /orders] [--manifest <path>] [--component <alias>] [--feature <slug>]"
cursor-tier: manual
---

# Mystique UI Preview & Iteration

Preview how a Mystique component or manifest change will look in the live Fluent Commerce app — **before writing any code or deploying anything.** Works for both **new components** and **modifications to existing pages.** All changes are highlighted with color-coded borders and badges so the user sees exactly what changed. Supports an interactive refinement loop until the user approves the visual design.

This skill acts as a **visual check gate** in the feature lifecycle — slotting between plan approval and actual build.

**Token optimization:** This skill uses the `__fcTest` helper library from `fluent-ui-test/BROWSER_OPTIMIZATIONS.md` for quick page verification (`pageSummary()` ~500 tokens) and component discovery (`scanComponents()` ~400 tokens) before falling back to `take_snapshot` (~30K tokens) when full DOM structure is needed. The preview-specific `__fcPreview` runtime library (see `PREVIEW_RUNTIME.md`) handles all ADD/MODIFY/REMOVE visual feedback via `applyAll()`, `clear()`, and `showSummary()` — inject it once alongside `__fcTest` via `initScript`.

## Pre-flight: Feedback Check

Before starting, check for past learnings from this skill:

1. Check if `accounts/<PROFILE>/feedback/fluent-mystique-preview.jsonl` exists
2. If yes, read last 20 records
3. Filter to: FAILURE or USER_CORRECTED outcomes, confidence = CONFIRMED or PROMOTED
4. Extract top 3 relevant learnings — use as internal guidance during execution
5. Do NOT show raw feedback to the user — silently adjust approach based on past issues

## Ownership Boundary

This skill owns **pre-build visual preview and iterative mock refinement** in live Fluent UX apps.

Other skills own:
- Building real manifest JSON: `/fluent-mystique-builder`
- Building real SDK components: `/fluent-mystique-scaffold`, `/fluent-mystique-component`
- Validating manifest structure: `/fluent-mystique-validate`
- Post-deploy browser verification: `/fluent-ui-test`
- Recording before/after evidence of real changes: `/fluent-ui-record`
- Manifest complexity analysis: `/fluent-mystique-analyze`

**Key distinction:** This skill injects **mock React components** (using the app's global React 17 + MaterialUI 4.12) to preview what doesn't exist yet, OR modifies existing DOM to preview changes. Injected components are visually identical to real Mystique components — same MUI theme, fonts, spacing. Falls back to HTML strings for simple indicators. `/fluent-ui-test` verifies what's already deployed. `/fluent-ui-record` captures evidence of real changes.

## Feature Lifecycle Position

```
plan APPROVED
    |
    v
/fluent-mystique-preview    <-- THIS SKILL (visual check gate)
    |
    +-- iterate until "looks good"
    |
    v
VISUAL APPROVED (status.json: preview = "APPROVED")
    |
    v
/fluent-mystique-builder    (build real manifest matching approved mock)
    |
    v
/fluent-mystique-validate   (structural validation)
    |
    v
deploy + /fluent-ui-test    (verify real matches preview)
```

When a feature has `preview: "APPROVED"` in status.json, `/fluent-mystique-builder` can reference the approved preview report as the visual target.

## Preview Modes

Three distinct modes — auto-detected from user intent or set explicitly with `--mode`:

### Mode: ADD (new component)

Inject entirely new components that don't exist yet. The injected HTML gets a pulsing green `ADDED` badge and dashed green border.

**Triggers:** "add a card", "new section", "preview what X would look like", "inject a table"

### Mode: MODIFY (change existing)

Alter an existing deployed page element — add a column to a list, change card layout, rearrange sections, restyle fields, swap component types. The modified element gets a yellow `MODIFIED` badge and yellow border. New sub-elements within it get green `ADDED` highlights.

**Triggers:** "add a column to the orders list", "change the card to show X", "move the sidebar", "rearrange the tabs", "make the table wider", "what if we changed..."

### Mode: REMOVE (preview without)

Hide an existing element to preview the page without it. The removed element gets a red `REMOVED` badge with strikethrough overlay and 30% opacity, so the user sees what's being removed without it disappearing entirely.

**Triggers:** "what if we removed the chart", "preview without the sidebar", "hide the status column", "remove the filter panel"

### Auto-detection

```
1. Parse user intent from natural language
2. IF describes something that doesn't exist on the page → MODE: ADD
3. IF describes changing/moving/resizing something visible → MODE: MODIFY
4. IF describes removing/hiding something visible → MODE: REMOVE
5. Mixed operations are supported — a single preview can ADD + MODIFY + REMOVE different elements
6. Present detected mode to user for confirmation before proceeding
```

## Change Highlighting System

**Every change is visually highlighted in the screenshot** so the user sees exactly what's different from the baseline. This is the core differentiator — not just "here's the page", but "here's what changed and where."

### Highlight styles (injected via evaluate_script)

```javascript
// Injected once as <style id="__fc-preview-styles">
(function() {
  if (document.getElementById('__fc-preview-styles')) return;
  var s = document.createElement('style');
  s.id = '__fc-preview-styles';
  s.textContent = `
    /* Glow pulse — animated box-shadow for visual glow effect */
    @keyframes __fc-glow-green {
      0%,100% { box-shadow: 0 0 12px 4px rgba(76,175,80,0.4), 0 0 24px 8px rgba(76,175,80,0.15); }
      50% { box-shadow: 0 0 20px 8px rgba(76,175,80,0.6), 0 0 40px 16px rgba(76,175,80,0.25); }
    }
    @keyframes __fc-glow-yellow {
      0%,100% { box-shadow: 0 0 12px 4px rgba(255,152,0,0.4), 0 0 24px 8px rgba(255,152,0,0.15); }
      50% { box-shadow: 0 0 20px 8px rgba(255,152,0,0.6), 0 0 40px 16px rgba(255,152,0,0.25); }
    }
    @keyframes __fc-glow-red {
      0%,100% { box-shadow: 0 0 10px 3px rgba(244,67,54,0.3); }
      50% { box-shadow: 0 0 16px 6px rgba(244,67,54,0.5); }
    }
    @keyframes __fc-badge-pulse { 0%,100%{opacity:1} 50%{opacity:0.7} }

    /* ADDED — green dashed border + green badge */
    .__fc-highlight-added {
      outline: 3px dashed #4caf50 !important;
      outline-offset: 4px;
      animation: __fc-glow-green 2s ease-in-out infinite !important;
      position: relative;
    }

    /* MODIFIED — yellow solid border + yellow badge */
    .__fc-highlight-modified {
      outline: 3px solid #ff9800 !important;
      outline-offset: 4px;
      animation: __fc-glow-yellow 2s ease-in-out infinite !important;
      position: relative;
    }

    /* REMOVED — red border on wrapper, element dimmed inside */
    .__fc-removed-wrapper {
      position: relative !important;
      outline: 3px solid #f44336 !important;
      outline-offset: 4px;
      animation: __fc-glow-red 2s ease-in-out infinite !important;
    }
    .__fc-highlight-removed {
      filter: grayscale(0.8) brightness(0.5) !important;
    }
    /* Strikethrough overlay */
    .__fc-removed-strike {
      position: absolute;
      top: 50%;
      left: 0;
      right: 0;
      height: 3px;
      background: #f44336;
      z-index: 10001;
      pointer-events: none;
    }
    /* Badge base */
    .__fc-change-badge {
      position: absolute;
      top: 4px;
      right: 8px;
      z-index: 10002;
      color: #fff;
      padding: 2px 10px;
      border-radius: 12px;
      font: 700 11px/1.6 sans-serif;
      letter-spacing: 0.5px;
      animation: __fc-badge-pulse 2s ease-in-out infinite;
      pointer-events: none;
    }
    .__fc-badge-added {
      background: linear-gradient(135deg, #4caf50, #2e7d32);
      box-shadow: 0 2px 8px rgba(76,175,80,0.4);
    }
    .__fc-badge-modified {
      background: linear-gradient(135deg, #ff9800, #e65100);
      box-shadow: 0 2px 8px rgba(255,152,0,0.4);
    }
    .__fc-badge-removed {
      background: linear-gradient(135deg, #f44336, #c62828);
      box-shadow: 0 2px 8px rgba(244,67,54,0.4);
    }
  `;
  document.head.appendChild(s);
})();
```

### Applying highlights

The `__fcPreview` runtime (see `PREVIEW_RUNTIME.md`) wraps these into `applyAll()`, `add()`, `modify()`, `remove()`. You do NOT need to call these directly — use `__fcPreview.applyAll()` in Phase 4.

<details>
<summary>Internal: standalone highlight functions (used by __fcPreview internally)</summary>

```javascript
// Attach highlight functions to window for cross-evaluate_script access
(function() {
  // ADD highlight — green dashed border + badge on the element
  window.__fcHighlightAdded = function(el, label) {
    el.classList.add('__fc-highlight-added');
    el.style.position = 'relative';
    var badge = document.createElement('div');
    badge.className = '__fc-change-badge __fc-badge-added';
    badge.textContent = label || 'ADDED';
    el.appendChild(badge);
  };

  // MODIFY highlight — yellow border + badge on the element
  window.__fcHighlightModified = function(el, label) {
    el.classList.add('__fc-highlight-modified');
    el.style.position = 'relative';
    var badge = document.createElement('div');
    badge.className = '__fc-change-badge __fc-badge-modified';
    badge.textContent = label || 'MODIFIED';
    el.appendChild(badge);
  };

  // REMOVE highlight — wraps element so badge + strikethrough avoid opacity inheritance
  window.__fcHighlightRemoved = function(el, label) {
    // Wrap target in a container to isolate opacity
    var wrapper = document.createElement('div');
    wrapper.className = '__fc-removed-wrapper __fc-preview-element';
    el.parentNode.insertBefore(wrapper, el);
    wrapper.appendChild(el);

    // Dim the original element (badge is on wrapper, so it stays fully visible)
    el.classList.add('__fc-highlight-removed');

    // Badge on wrapper (NOT on the dimmed element)
    var badge = document.createElement('div');
    badge.className = '__fc-change-badge __fc-badge-removed';
    badge.textContent = label || 'REMOVED';
    wrapper.appendChild(badge);

    // Strikethrough overlay on wrapper (NOT inside the dimmed element)
    var strike = document.createElement('div');
    strike.className = '__fc-removed-strike';
    wrapper.appendChild(strike);
  };
})();
```

</details>

### Change summary overlay

`__fcPreview.applyAll()` auto-injects the summary panel (top-right corner showing counts with color-coded dots). `__fcPreview.showSummary(added, modified, removed)` can be called separately if needed. `__fcPreview.clear()` removes it.

### Clearing highlights

Before each new iteration, clear all previous highlights using the runtime library:

```javascript
window.__fcPreview.clear()
// Unwraps REMOVED elements, removes ADD containers + added columns,
// clears MODIFY highlights, removes all badges and summary panel.
// Returns { ok: true }
```

For iteration (clear + re-apply in one call):
```javascript
window.__fcPreview.clear(); JSON.stringify(window.__fcPreview.applyAll(NEW_OPS))
```

## Prerequisites

### Required
1. **Chrome DevTools MCP configured** in `.mcp.json` with credentials:
   ```json
   {
     "chrome-devtools": {
       "command": "npx",
       "args": ["-y", "chrome-devtools-mcp@latest"],
       "env": {
         "FLUENT_UI_BASE_URL": "https://<account>.<env>.apps.engineering.fluentcommerce.com",
         "FLUENT_UI_USERNAME": "<username>",
         "FLUENT_UI_PASSWORD": "<password>"
       }
     }
   }
   ```
   Credentials are resolved automatically from `FLUENT_PROFILE` (already in `.mcp.json`). Override with `FLUENT_UI_USERNAME`/`FLUENT_UI_PASSWORD` or `FLUENT_UI_TOKEN` for different users. See `/fluent-ui-test` Credential Resolution for the full chain.
2. **Chrome DevTools MCP connected** — `navigate_page`, `take_snapshot`, `take_screenshot`, `evaluate_script` tools must be available.
3. **Valid Fluent credentials** — resolved automatically from the profile configured in `.mcp.json` (zero extra config). Override options: explicit creds in `.mcp.json`, role file (`.fluent-ui-credentials.json` + `--role`), or token injection. See `/fluent-ui-test` Credential Resolution.
4. **A target to preview** — either:
   - A manifest fragment JSON (local file or plan description)
   - A plan.md with Section 10 (UI / Manifest Artifacts)
   - A component alias and props description
   - A natural language description ("add a status column to the orders list")

### Optional
- **Feature context** — `accounts/<PROFILE>/features/<slug>/status.json` for lifecycle tracking
- **Existing manifest** — if modifying an existing page, the current deployed manifest helps identify what to change

## Phase 0: Prerequisite Check

```
1. Check Chrome DevTools MCP tools (navigate_page, evaluate_script, take_screenshot)
   -> BLOCKED if not available: "Chrome DevTools MCP is not running. To configure it, run
      `node bin/cli.mjs install --profile <PROFILE>` (adds chrome-devtools to .mcp.json),
      then restart your IDE. This is a standard workspace prerequisite installed by fluent-ai-skills."
2. Resolve FLUENT_UI_BASE_URL from .mcp.json
3. Resolve credentials via enhanced chain (see /fluent-ui-test Credential Resolution):
   a. FLUENT_PROFILE from .mcp.json → read profile files (DEFAULT — zero config)
   b. FLUENT_UI_USERNAME + FLUENT_UI_PASSWORD in .mcp.json (override)
   c. .fluent-ui-credentials.json in workspace root (--role support)
   d. FLUENT_UI_TOKEN in .mcp.json (token injection, skip login)
   e. Shell env FLUENT_USERNAME + FLUENT_PASSWORD (legacy)
   f. Ask user
4. Create output directory:
   - With feature: accounts/<PROFILE>/features/<feature-slug>/assets/ (durable — linked from spec/plan)
   - Without feature: .tmp/ui-preview/<YYYY-MM-DD_HHmmss>/ (ephemeral)
5. Read feature plan if --feature provided (extract Section 10 for manifest artifacts)
```

### Phase 0a: Manifest Selector Cache (once per session, before browser)

Pre-analyze manifests to get the exact labels for all UI elements. This eliminates "guess the label" misses and lets you know injection point selectors before touching the browser.

```bash
# Check if cache exists
ls accounts/<PROFILE>/manifests/selector-cache-slim.json 2>/dev/null

# If missing, generate it (~2 seconds, reads local manifest JSONs)
node fluent-ai-skills/tools/manifest-selector-map.mjs --profile <PROFILE>
```

Then read `selector-cache-slim.json` once (~15K tokens) for exact navigation labels, button names, card headings, and column headers. See `fluent-ui-test/BROWSER_OPTIMIZATIONS.md` "Layer 4: Manifest Selector Cache" for details.

**Skip if:** Manifest files don't exist under `accounts/<PROFILE>/manifests/`, or this is an ad-hoc preview without existing manifests.

### Phase 0b: Inject Helper Libraries (MANDATORY — use initScript)

**Use `initScript` on `navigate_page`** to auto-inject BOTH `__fcTest` AND `__fcPreview` during navigation — eliminates separate round trips.

```
navigate_page:
  url: <target URL>
  initScript: <__fcTest IIFE from BROWSER_OPTIMIZATIONS.md> + <__fcPreview IIFE from PREVIEW_RUNTIME.md>
```

Pass `initScript` on **every `navigate_page` call** (both are idempotent — check `if (window.__fcTest) return` / `if (window.__fcPreview) return`). Combine queries with `batch()` — target **max 2-3 MCP calls per page** (navigate + combined evaluate + optional screenshot).

```javascript
// ONE evaluate_script call replaces 3-4 separate calls:
() => JSON.stringify(window.__fcTest.batch([
  { action: 'pageSummary' },
  { action: 'scanComponents' },
  { action: 'findCard', args: ['Target Card Title'] }
]))
```

**Fallback:** If `initScript` doesn't work, inject via `evaluate_script`. Verify: `window.__fcTest ? 'ready' : 'not_injected'`.

**MUI-aware finders** (use with labels from selector-cache-slim.json):
- `findCard(title)`, `findButton(label)`, `findTab(label)`, `findNav(label)`, `findInput(label)`
- `findTableColumn(header)`, `findTableRow(col, val)`
- `clickNav(l)`, `clickButton(l)`, `clickTab(l)`, `clickTableRow(c,v)`, `fillInput(l,v)`
- `batch([ops])` — multiple operations in one `evaluate_script` call
- `pageState()` — ultra-compact page summary (~150 tokens vs ~30K snapshot)
- `diffState(before, after)` — compare two `pageState()` snapshots, returns only changes (~50 tokens)
- `login(user, pass)` — handle login form without take_snapshot (~200 tokens vs ~30K)
- `installGuard()` — SPA navigation guard, preserves timeline across reloads

**Core helpers:**
- `pageSummary()` — compact page state (~500 tokens)
- `scanComponents()` — detect MUI + custom SDK components (~400 tokens)
- `verifyComponent(alias)` — check if specific component rendered (~100 tokens)
- `find(text, role)` — locate elements without snapshots (~100 tokens)
- `highlight(selectorOrText, label)` — click telemetry (~100 tokens)
- `annotate(text, type)` — step annotation banner (~100 tokens)

**For preview injection:** Use `findCard(title)` to locate the target card for ADD/MODIFY operations. Use `scanComponents()` to inventory existing cards and find injection points. Use `batch()` to apply multiple DOM changes in one `evaluate_script` call.

## Phase 1: Parse Preview Target

Determine what to change, where, and which mode to use.

### Input sources (priority order)

| Source | What to extract |
|--------|----------------|
| `--manifest <path>` | Read manifest JSON, diff against deployed manifest to find changes |
| `--feature <slug>` | Read `plan.md` Section 10, extract planned UI structure |
| `--component <alias>` | Single component preview with props |
| Natural language | "Add a 3-column card", "Remove the chart", "Add a Priority column to the list" |

### Parse steps

1. **Detect mode** — ADD / MODIFY / REMOVE (auto-detect from intent or `--mode` flag)
2. **Identify the target app** — OMS App or Store App (from `--app` or infer from route)
3. **Identify the target route** — which page to navigate to
4. **Identify the target element(s)** — what to add/change/remove
5. **Generate the change plan** — list of operations with selectors and HTML specs

Present the preview plan to the user:
```
Preview Plan:
  App: OMS App
  Route: /oms/orders/456
  Changes:
    [ADD]    New "Appeasement" card after "Order Details" — fc.card.attribute with 4 fields
    [MODIFY] "Order Items" list — add Priority column (3rd position)
    [REMOVE] "Shipping Estimate" chart — hide from page
  Highlights: 1 green (added), 1 yellow (modified), 1 red (removed)
```

## Phase 2: Navigate to Target Page

Follow the same login + navigation protocol as `/fluent-ui-test`:

```
1. navigate_page to about:blank (clear SPA state)
2. IF token injection (FLUENT_UI_TOKEN resolved in Phase 0):
   a. navigate_page to FLUENT_UI_BASE_URL (app domain — needed for localStorage scope)
   b. evaluate_script: localStorage.setItem('token', '<TOKEN>'); localStorage.setItem('accessToken', '<TOKEN>');
   c. navigate_page to FLUENT_UI_BASE_URL + target route
   d. IF login page still appears → token expired, fall through to form login using remaining credential chain
3. ELSE (form login):
   a. navigate_page to FLUENT_UI_BASE_URL + target route
   b. Check if login page → if so, use `__fcTest.login(user, pass)` (no take_snapshot needed for login)
      Fallback: fill username + password fields manually via fill_form if login() unavailable
4. wait_for page to settle (3 seconds)
5. Inject __fcTest helper library (see Phase 0b — skip if already injected this session)
6. evaluate_script: JSON.stringify(window.__fcTest.pageSummary())
   → Quick verification (~500 tokens): check title, tabs, cards, buttons match expected page
   → IF page looks wrong (missing expected elements, loading spinner, error alerts):
     ESCALATE to take_snapshot (~30K tokens) for full DOM analysis
   → IF page looks correct: proceed without take_snapshot
7. Install SPA guard: evaluate_script: window.__fcTest.installGuard()
   → Tracks hash-change navigation, preserves timeline across reloads
```

**Role-based preview:** If user says "preview as admin" or uses `--role admin`, resolve credentials from `.fluent-ui-credentials.json` → `roles.admin` entry. This enables previewing the same page as different users to verify role-based UI visibility.

**Navigation — NEVER hardcode URLs.** Mystique routes are hash-based (`#/orders/{id}/{retailerId}/{ref}`) and defined in manifest fragments. Custom code can change paths entirely. To navigate to entity detail pages:
1. Navigate to the list page (use route from manifest or nav links in `take_snapshot`)
2. Use `take_snapshot` to discover link hrefs in the accessibility tree (e.g., `uid=X link "ORDER-001" url="...#/orders/123/2/ORDER-001"`)
3. Click the link via its `uid` — this respects whatever routing the manifest defines
4. Never construct detail page URLs by string concatenation

## Phase 3: Capture Baseline

Capture the current state of the page before any changes.

```
1. take_screenshot → save as baseline.png (clean, unmodified page)
2. evaluate_script: JSON.stringify(window.__fcTest.scanComponents())
   → Quick component inventory (~400 tokens): cards, tables, tabs, custom data-testid elements
   → Helps identify injection points and existing structure without full DOM snapshot
3. Use MUI-aware finders to locate injection points:
   → findCard("Order Details") to find the target card for ADD mode
   → findTableColumn("Status") to find column positions for MODIFY mode
   → Use selector-cache-slim.json labels (from Phase 0a) for exact heading text
   IF MUI finders + scanComponents() are insufficient:
   take_snapshot → save DOM structure to baseline-snapshot.txt (~30K tokens)
   ELSE: skip take_snapshot, use MUI finders for precise injection point selection
4. __fcPreview is already injected (via initScript in Phase 2 navigate_page — alongside __fcTest)
   Verify: evaluate_script: window.__fcPreview ? 'ready' : 'not_injected'
```

**Note:** Baseline screenshot is captured BEFORE any style/script injection to represent the true unmodified state.

## Phase 4: Apply Changes & Highlight

Apply all planned changes, then highlight every modification so the user sees exactly what's different.

**MANDATORY: Use `__fcPreview.applyAll()` from the runtime library (see `PREVIEW_RUNTIME.md`).** This batches all DOM mutations into ONE `evaluate_script` call, auto-sorts operations (REMOVE → MODIFY → ADD), applies highlights, and shows the summary panel.

### Step 4a: Show Loading Indicators (RECOMMENDED)

Before injecting actual components, drop pulsing placeholder slots so the user sees immediate visual feedback. This is especially valuable when the AI needs time to build complex HTML:

```javascript
// evaluate_script — instant visual feedback:
JSON.stringify(window.__fcPreview.prepareSlot(
  INJECTION_SELECTOR,
  { label: 'Building preview…' }
))
// Returns: { ok: true, slotId: '__fc-slot-1' }
```

For multiple changes, prepare multiple slots in one call:
```javascript
JSON.stringify([
  window.__fcPreview.prepareSlot(SEL_1, { label: 'Card A…' }),
  window.__fcPreview.prepareSlot(SEL_2, { label: 'Column B…' })
])
```

### Step 4b-pre: Style Scraping (MANDATORY)

**NEVER hardcode MUI variants, heading levels, elevation, or spacing.** Always scrape the live page first and match what exists.

Use the runtime's `scrapeStyles()` — one call extracts heading tag/class/variant/textTransform/fontSize, label styles, value styles, card elevation/padding, and grid spacing from the live DOM:

```javascript
// One evaluate_script call — scrapes all style info from existing page elements:
JSON.stringify(window.__fcPreview.scrapeStyles())
// Returns: { ok: true, heading: { tag, className, variant, textTransform, fontSize, fontWeight, color },
//            label: { fontSize, fontWeight, color, className },
//            value: { fontSize, fontWeight, color },
//            card: { elevation, borderRadius, background, contentPadding },
//            grid: { spacing } }
```

The returned `styles` object is passed directly to `buildCard()`, `buildTable()`, and `buildGrid()` — they produce components that match the page exactly. Common patterns observed:

| Page type | Heading | Elevation | Grid |
|-----------|---------|-----------|------|
| Entity detail (order, fulfilment) | `h3`, `text-transform: uppercase`, 48px | 1 | `spacing={2}`, half-width `xs={12} md={6}` |
| List page | `h5` or `h6` | 1 | Full-width |
| Modal/dialog | `h6` | 0-2 | Varies |

**If the page has no cards** (e.g., empty state), `scrapeStyles()` returns safe defaults (`h3` + elevation 1) automatically.

### Step 4b-pre2: Multi-Component Scoping

When the plan involves multiple components:

- **Same page:** Preview ALL components that appear on the current page in one batch. Use `prepareSlot` for each, then fill in sequence.
- **Different pages:** Preview one page at a time. Complete the iteration loop (Phase 5) for page A, then navigate to page B and preview there.
- **Single component focus:** If the user asked to preview a specific component, only inject that one — even if the plan has more.

### Step 4b: Apply Changes (replace loading slots with actual content)

**DEFAULT: Use builder functions** — `buildCard()`, `buildTable()`, `buildGrid()` produce style-matched React components using the app's global `React` (17.0.2), `ReactDOM`, and `MaterialUI` (4.12). Output is visually identical to native Mystique components. Fall back to manual `React.createElement` for custom layouts, or HTML strings for simple indicators.

```javascript
// DEFAULT: Scrape + Build + Mount (see PREVIEW_RUNTIME.md Pattern 1)
var styles = window.__fcPreview.scrapeStyles();
var card = window.__fcPreview.buildCard(styles, {
  title: 'Card Title',              // from plan/spec — never invented
  attributes: [
    { label: 'Reference', value: 'REF-001' },
    { label: 'Status', value: 'PENDING', chip: true, chipBg: '#fff3e0', chipColor: '#e65100' },
    { label: 'Amount', value: '$45.00' }
  ]
});
JSON.stringify(window.__fcPreview.injectGridItem(card, 'ADDED: Card Title', { width: 6 }))

// MULTI-COMPONENT: Build a grid of cards + tables (Pattern 1b)
var grid = window.__fcPreview.buildGrid(styles, [
  { width: 6, element: window.__fcPreview.buildCard(styles, { title: 'Summary', attributes: [...] }) },
  { width: 6, element: window.__fcPreview.buildCard(styles, { title: 'Details', attributes: [...] }) },
  { width: 12, element: window.__fcPreview.buildTable(styles, { title: 'Items', columns: [...], rows: [...] }) }
]);
JSON.stringify(window.__fcPreview.mountReact('.MuiGrid-container', grid, 'ADDED: Section'))
// NOTE: For individual cards in a grid, prefer injectGridItem() over mountReact() — see Pattern 1 above

// CUSTOM LAYOUT: When builders don't fit, use React.createElement directly (Pattern 7 in PREVIEW_RUNTIME.md)
// Always pass scrapeStyles() output to match heading/label/value styles

// FALLBACK: HTML strings (simple indicators only)
JSON.stringify(window.__fcPreview.fillSlot('__fc-slot-1', CARD_HTML, 'ADDED: New Card'))

// BATCH MIXED OPS: One-shot applyAll for MODIFY + REMOVE (builders handle ADD via injectGridItem/mountReact)
JSON.stringify(window.__fcPreview.applyAll([
  { mode: 'modify', selector: '.MuiTable-root', header: 'Priority', cellValues: ['High','Low','Med'] },
  { mode: 'modify', selector: '.MuiCard-root', label: 'MODIFIED: Card Title', fn: function() { /* DOM changes */ } },
  { mode: 'remove', selector: '[data-testid="shipping-chart"]', label: 'REMOVED: Chart' }
]))
// Returns: { added: N, modified: N, removed: N, errors: [] }
```

After the `evaluate_script`: `take_screenshot` → present to user. **Typical Phase 4 = 3-4 calls** (prepareSlot + fill/applyAll + screenshot).

**Without loading indicators (still supported):** Skip step 4a and call `applyAll()` directly for a 2-call Phase 4.

### Mode: ADD — Inject new component

```javascript
// PREFERRED FOR GRID CONTEXTS (detail pages): injectGridItem — copies sibling MUI classes
var styles = window.__fcPreview.scrapeStyles();
var card = window.__fcPreview.buildCard(styles, { title: 'New Card', attributes: [...] });
JSON.stringify(window.__fcPreview.injectGridItem(card, 'ADDED: New Card', { width: 6 }))
// width: 6 = half, 12 = full. Options: { container, after, width }

// PREFERRED FOR NON-GRID CONTEXTS: mountReact — generic positioning
window.__fcPreview.mountReact(INJECTION_SELECTOR, card, 'ADDED: New Card', 'after');

// FALLBACK: HTML via applyAll()
{ mode: 'add', selector: 'INJECTION_SELECTOR', html: COMPONENT_HTML,
  label: 'ADDED: New Card', position: 'after' }
// position: 'before' | 'after' | 'inside-first' | 'inside-last' (default: 'after')
```

**Grid contexts (detail pages):** Use `injectGridItem` — it copies exact sibling MUI Grid classes for perfect alignment and anchors to a sibling card for tab-switch cleanup. **NEVER use `mountReact` with `inside-last` on a grid container** — the wrapper div breaks MUI Grid flex layout.

**Non-grid contexts:** `mountReact` creates a `__fc-preview-element` container, renders the React element, inserts relative to the target, and applies a green dashed border + `ADDED` badge. `inject` does the same with raw HTML.

### Mode: MODIFY — Alter existing element

```javascript
// Via __fcPreview.applyAll():
// Option A: Add a column to a table
{ mode: 'modify', selector: '.MuiTable-root', label: 'MODIFIED: Orders List',
  header: 'Priority', cellValues: ['High', 'Low', 'Med'] }

// Option B: Custom DOM changes via fn callback
{ mode: 'modify', selector: 'EXISTING_SELECTOR', label: 'MODIFIED: Card Title',
  fn: function(el) {
    el.querySelector('.MuiCardHeader-title').textContent = 'New Title';
  }
}
```

The runtime finds the element, applies any `fn` callback or column addition, then adds a yellow solid border + `MODIFIED` badge. New sub-elements (added `<th>/<td>` cells) get individual green `ADDED` highlights so the user sees both "this table was changed" AND "these specific cells are new."

### Mode: REMOVE — Preview without element

```javascript
// Via __fcPreview.applyAll():
{ mode: 'remove', selector: 'REMOVE_SELECTOR', label: 'REMOVED: Shipping Chart' }
```

The runtime wraps the element in a container, dims it to 30% opacity with grayscale, adds a strikethrough overlay and red border + `REMOVED` badge. The element stays visible so the user sees what's being removed and how surrounding layout reflows.

**Why not delete?** Completely removing a DOM element can cause React/MUI layout shifts that don't reflect real behavior (the manifest change would trigger a clean re-render). Dimming + strikethrough shows the intent without breaking the page layout.

### Mixed operations

A single preview can combine all three modes using `batch()` for efficiency. Apply in order: REMOVE first (dims elements), then MODIFY (alters in-place), then ADD (injects new). This prevents selector conflicts.

**Batch all DOM mutations** in a single `evaluate_script` call using `__fcPreview.applyAll()`:
```javascript
// ONE call — auto-sorts REMOVE → MODIFY → ADD, applies highlights + summary:
JSON.stringify(window.__fcPreview.applyAll([
  { mode: 'remove', selector: 'REMOVE_SEL', label: 'REMOVED: Chart' },
  { mode: 'modify', selector: 'MODIFY_SEL', label: 'MODIFIED: List' },
  { mode: 'add', selector: 'INJECTION_POINT', html: CARD_HTML, label: 'ADDED: Card' }
]))
```

### Injection Point Discovery

**Preferred:** Use `scanComponents()` (~400 tokens) to identify page structure and potential injection points. **Fallback:** Use `take_snapshot` (~30K tokens) when `scanComponents()` doesn't provide enough selector detail:

| Target description | How to find selector |
|---|---|
| After a specific card | Look for `.MuiCard-root` with matching header text |
| In a tab panel | Find `.MuiTab-root` labels, identify target panel container |
| Specific list/table | Find `.MuiTable-root` parent, match by column headers |
| New section in page | Find the page's main content area (`.MuiGrid-container` or role-based) |
| Sidebar element | Find the existing sidebar column |
| Any named element | Search DOM snapshot for text content to locate the component |

**Fallback:** If the exact selector can't be determined from the snapshot, ask the user to describe the element more specifically, or inject at the end of the main content area.

### SPA re-render resilience

React/MUI re-renders (tab switches, data loads, accordions expanding) can wipe injected DOM. Before each screenshot or user presentation:

```
1. evaluate_script: window.__fcPreview.isAlive()
   → returns true if preview elements still in DOM, false if SPA re-rendered
2. IF false: re-apply via __fcPreview.applyAll(previousOps)
3. IF true: proceed normally
```

When warning the user about re-render risk: "SPA navigation or tab switches may reset the preview. I'll re-apply automatically if that happens."

### Post-change

```
1. (Summary overlay is injected automatically by __fcPreview.applyAll())
2. evaluate_script: await new Promise(r => setTimeout(r, 500))  // let MUI animations settle
3. take_screenshot → save as preview-iteration-0.png
4. Present screenshot to user:
   "Here's the preview with 1 addition (green), 1 modification (yellow), and 1 removal (red).
    The change summary panel in the top-right shows the counts."
```

## Phase 5: Iteration Loop

Use `diffState()` to verify each iteration changed only what was intended:

```javascript
// Before applying iteration changes:
var stateBeforeIteration = window.__fcTest.pageState();

// After applying changes:
var stateAfterIteration = window.__fcTest.pageState();
var iterationDiff = window.__fcTest.diffState(stateBeforeIteration, stateAfterIteration);
// Present diff to user: "This iteration added 1 card, removed 0 elements"
```

The user reviews the highlighted preview and gives natural language feedback.

### Loop protocol

```
REPEAT:
  1. Present current screenshot to user
  2. Ask: "How does this look? Describe changes or say 'looks good' to approve."
  3. IF user says "looks good" / "approved" / "ship it" / "done":
       -> EXIT loop, proceed to Phase 6
  4. ELSE parse user feedback:
       a. Clear previous highlights (remove badges + outline classes)
       b. Interpret and apply the requested change
       c. Re-apply highlights (new highlights reflecting current state vs baseline)
       d. Update change summary overlay
       e. take_screenshot → save as preview-iteration-<N>.png
       f. Log change: { iteration: N, feedback: "user text", change: "what was modified", mode: "add|modify|remove" }
       g. CONTINUE loop
```

### Supported iteration commands

| User says | Action | Mode |
|---|---|---|
| "Move it above the card" | Re-order DOM: `insertBefore` | MODIFY |
| "Make the font bigger" | Adjust font-size on target elements | MODIFY |
| "Add a column for status" | Insert new `<th>/<td>` in table | ADD within MODIFY |
| "Remove the icon" | Apply REMOVED highlight to icon element | REMOVE |
| "Change the color to blue" | Update background/color CSS | MODIFY |
| "Make it a table instead of cards" | Replace injected HTML with table variant | ADD (replace) |
| "Wider / narrower" | Adjust width styles or grid column span | MODIFY |
| "Add padding / spacing" | Adjust margin/padding CSS | MODIFY |
| "Actually, put that column back" | Remove REMOVED highlight, restore visibility | Undo REMOVE |
| "Show it with sample data" | Populate with realistic placeholder data | MODIFY |
| "Try a different layout" | Replace entire injection with alternative HTML | ADD (replace) |
| "What if we also removed X" | Apply REMOVED highlight to X | REMOVE |

### Undo support

Users can undo individual changes during iteration:
- "Put that back" / "Undo the removal" → clear REMOVED highlight, restore opacity
- "Remove the new card" → delete the ADD container, remove ADDED highlight
- "Revert the column change" → restore original table structure, clear MODIFIED highlight

### Iteration limits

- **Soft limit:** After 5 iterations, suggest: "We've done 5 rounds. Want to approve the current version, or keep refining?"
- **Hard limit:** After 10 iterations, strongly recommend approval or starting over with a different approach.
- All iterations are captured — the final report shows the full evolution.

## Phase 5.5: Capture Final Screenshots

After the user approves, capture both highlighted and clean final states.

```
1. take_screenshot → save as final-highlighted.png (with all highlight borders/badges visible)
2. evaluate_script: window.__fcPreview.clear() (removes all highlights, badges, wrappers, summary panel)
3. take_screenshot → save as final-clean.png (true preview — what the page would look like after real implementation)
4. Write iterations.json — array of { iteration, feedback, change, mode } for each refinement step
5. Write changes.json — structured manifest of all ADD/MODIFY/REMOVE operations with selectors
6. Generate GIFs (if ffmpeg available):
   a. before-after.gif — 2-frame toggle (baseline + final-highlighted)
   b. before-after-3frame.gif — 3-frame (baseline + final-highlighted + final-clean)
   c. evolution.gif — all iteration screenshots in sequence (only if >2 iterations)
   If ffmpeg is not found OR exits non-zero (Windows note: concat/palette filter exits -22
   "Invalid argument" — treat as unavailable):
   -> Tell the user: "GIF generation skipped — ffmpeg not available or failed on this platform.
      Before + after PNGs saved. GIF is optional and does not block preview approval.
      The HTML report includes a CSS flip widget instead."
```

Both screenshots are used in the report: `final-highlighted.png` shows what changed, `final-clean.png` shows the end result.

## Phase 6: Generate Preview Report

Compose a self-contained HTML evidence report showing the preview evolution with visual diff.

### Report structure

```
Header:
  - App name, route, profile, date
  - Feature slug (if applicable)
  - Total iterations
  - Approval status
  - Change summary: N added, N modified, N removed

Visual Diff Section:
  - Side-by-side: baseline (left) vs final approved (right)
  - Changes legend: green=added, yellow=modified, red=removed
  - Color-coded highlight badges on each changed element

Baseline Section:
  - "Before" screenshot (original page)
  - DOM snapshot summary

Preview Evolution:
  - For each iteration:
    - Screenshot (base64 embedded)
    - User feedback that triggered this iteration
    - What was changed and which mode (ADD/MODIFY/REMOVE)
  - Final approved screenshot prominently displayed

Change Manifest:
  - Table of all changes with columns:
    | # | Mode | Target Element | Description | Selector |
  - This table provides LLM context for /fluent-mystique-builder (convention-based handoff, not machine integration)

Component Specification:
  - Component alias(es) used
  - Props/configuration
  - Approximate HTML that was injected/modified
  - MUI classes used

Handoff Notes (manifest-level translation of DOM operations):
  - For each ADD: Mystique component alias, route path, fragment name, parent descendant
  - For each MODIFY: which fragment, which descendant to change, new props/children
  - For each REMOVE: which fragment, which descendant to remove
  - Data queries needed (GraphQL fields observed in sample data)
  - Suggested route structure
```

### Report output

```bash
# Encode screenshots to base64 (Node.js — guaranteed available, works on Windows/macOS/Linux)
node -e "process.stdout.write(require('fs').readFileSync('baseline.png').toString('base64'))" > baseline.b64
# Repeat for each iteration screenshot
```

Write self-contained HTML report (same pattern as `/fluent-ui-record`):
- All images as base64 data URIs
- CSS-in-head with dark gradient header
- Side-by-side baseline vs final comparison with change highlights
- Change summary with color-coded legend
- Collapsible iteration history
- No external dependencies

Output path:
- With feature: `accounts/<PROFILE>/features/<feature-slug>/assets/<timestamp>/report.html`
- Without feature: `.tmp/ui-preview/<timestamp>/report.html`

### Animated before/after (GIF + CSS flip)

Generate an animated comparison that shows the transition from baseline to final state. Two output formats — use whichever fits the embedding target.

#### Format 1: Animated GIF (for plan.md, dashboards, external docs)

Combine baseline + final screenshots into a looping GIF. Requires `ffmpeg` (often already installed for Chrome DevTools MCP screencast).

```bash
# Check ffmpeg availability
which ffmpeg 2>/dev/null && echo "AVAILABLE" || echo "NOT_FOUND"

# Generate animated GIF from baseline + final screenshots
# - 1.5 second per frame, loop forever, optimized palette
ffmpeg -y \
  -framerate 1 -t 1 -i baseline.png \
  -framerate 1 -t 1 -i final-highlighted.png \
  -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0,split[s0][s1];[s0]palettegen=max_colors=128[p];[s1][p]paletteuse=dither=bayer" \
  -loop 0 \
  before-after.gif

# Also generate a 3-frame version: baseline → highlighted → clean
ffmpeg -y \
  -framerate 1 -t 1 -i baseline.png \
  -framerate 1 -t 1 -i final-highlighted.png \
  -framerate 1 -t 1 -i final-clean.png \
  -filter_complex "[0:v][1:v][2:v]concat=n=3:v=1:a=0,split[s0][s1];[s0]palettegen=max_colors=128[p];[s1][p]paletteuse=dither=bayer" \
  -loop 0 \
  before-after-3frame.gif
```

```bash
# Evolution GIF (all iterations in sequence, only if >2 iterations)
# Concatenate all iteration screenshots into a single animation
ffmpeg -y \
  -framerate 1 -t 1 -i baseline.png \
  -framerate 1 -t 1 -i preview-iteration-0.png \
  -framerate 1 -t 1 -i preview-iteration-1.png \
  -framerate 1 -t 1 -i final-highlighted.png \
  -filter_complex "[0:v][1:v][2:v][3:v]concat=n=4:v=1:a=0,split[s0][s1];[s0]palettegen=max_colors=128[p];[s1][p]paletteuse=dither=bayer" \
  -loop 0 \
  evolution.gif
# Adjust input count to match actual iteration count
```

**Fallback (no ffmpeg):** GIF generation requires ffmpeg. If unavailable OR if ffmpeg exits with a non-zero code (on Windows, the concat/palette filter fails with exit code -22 "Invalid argument" — treat this as unavailable), skip GIF output and tell the user clearly rather than surfacing a confusing shell error. Use the CSS flip widget (Format 2) as the primary animated comparison. The HTML report always includes the CSS flip widget regardless of ffmpeg availability.

#### Format 2: CSS flip animation (for HTML reports, self-contained dashboards)

Embed both images with a CSS crossfade toggle — zero dependencies, smaller than GIF, interactive.

```html
<!-- Before/After Flip Widget — self-contained HTML -->
<div class="__fc-preview-flip" style="position:relative;display:inline-block;cursor:pointer">
  <img class="__fc-flip-before" src="data:image/png;base64,BASELINE_B64"
       style="display:block;max-width:100%;border-radius:8px" />
  <img class="__fc-flip-after" src="data:image/png;base64,FINAL_B64"
       style="display:none;max-width:100%;border-radius:8px" />
  <div class="__fc-flip-label" style="position:absolute;top:12px;left:12px;
       background:rgba(0,0,0,0.75);color:#fff;padding:4px 12px;border-radius:16px;
       font:700 12px/1.5 sans-serif;letter-spacing:0.5px;pointer-events:none;
       transition:all 0.3s">BEFORE</div>
  <div class="__fc-flip-hint" style="position:absolute;bottom:8px;right:8px;
       font:11px/1.4 sans-serif;color:rgba(255,255,255,0.6);
       background:rgba(0,0,0,0.5);padding:2px 8px;border-radius:8px">
       Click to toggle / Hover to compare</div>
</div>

<script>
// Toggle on click, auto-flip on hover
document.querySelectorAll('.__fc-preview-flip').forEach(function(w) {
  var before = w.querySelector('.__fc-flip-before');
  var after = w.querySelector('.__fc-flip-after');
  var label = w.querySelector('.__fc-flip-label');
  var showing = 'before';

  function show(which) {
    if (which === 'after') {
      before.style.display = 'none';
      after.style.display = 'block';
      label.textContent = 'AFTER';
      label.style.background = 'rgba(76,175,80,0.85)';
    } else {
      after.style.display = 'none';
      before.style.display = 'block';
      label.textContent = 'BEFORE';
      label.style.background = 'rgba(0,0,0,0.75)';
    }
    showing = which;
  }

  w.addEventListener('click', function() { show(showing === 'before' ? 'after' : 'before'); });
  w.addEventListener('mouseenter', function() { show('after'); });
  w.addEventListener('mouseleave', function() { show('before'); });
});
</script>
```

**Enhanced: Auto-play mode** — auto-flips every 2 seconds with a smooth crossfade:

```css
/* Auto-play: "before" stays in normal flow (sets container height), "after" overlays it */
.__fc-preview-flip.auto-play { position: relative; }
.__fc-preview-flip.auto-play .__fc-flip-before {
  display: block; /* normal flow — defines container dimensions */
  transition: opacity 0.5s ease-in-out;
  opacity: 1;
}
.__fc-preview-flip.auto-play .__fc-flip-after {
  position: absolute; top: 0; left: 0; width: 100%;
  display: block; /* visible but overlaid, NOT display:none */
  transition: opacity 0.5s ease-in-out;
  opacity: 0;
}
.__fc-preview-flip.auto-play.show-after .__fc-flip-before { opacity: 0; }
.__fc-preview-flip.auto-play.show-after .__fc-flip-after { opacity: 1; }
```

```javascript
// Auto-play toggle every 2 seconds (store interval ID for cleanup)
document.querySelectorAll('.__fc-preview-flip.auto-play').forEach(function(w) {
  var id = setInterval(function() { w.classList.toggle('show-after'); }, 2000);
  w.dataset.autoPlayId = id; // allows cleanup: clearInterval(w.dataset.autoPlayId)
});
```

#### Which format to use where

| Target | Format | Why |
|---|---|---|
| Preview report (report.html) | CSS flip (auto-play) | Interactive, zero dependencies, user can pause/hover |
| Feature dashboard | CSS flip or GIF (base64 `<img>`) | Dashboard is already HTML, both work |
| plan.md | GIF file reference `![](path/to/before-after.gif)` | Markdown renders GIFs in VS Code/GitHub preview |
| Slack / email / Jira | GIF attachment | Universal format, auto-plays everywhere |
| Presentation / PDF | GIF or side-by-side PNG | GIF auto-plays in Google Slides; PDF needs static |

#### Embedding in spec.md (MANDATORY)

After producing preview screenshots, **always embed them in the feature's spec.md** (section 9a, Visual Mocks subsection). Screenshots saved to `features/<slug>/assets/` use simple relative links. The dashboard generator base64-encodes relative image paths at build time, so images render inline in the self-contained HTML dashboard.

```markdown
### Visual Mocks (Live Preview)

**Before** — Current page layout:

![Before](assets/preview-before.png)

**After** — With proposed changes:

![After](assets/preview-after.png)

> Preview produced via `/fluent-mystique-preview` on <date>.
```

If a GIF was generated, embed that instead (auto-plays in VS Code/GitHub preview).

#### Embedding in plan.md

When a preview is approved, add a visual reference to the feature plan:

```markdown
## §10 UI / Manifest Artifacts

### Visual Preview (approved YYYY-MM-DD)

![Before/After Preview](assets/<ts>/before-after.gif)

> Preview approved on <date>. See [full report](assets/<ts>/report.html) for iteration history.
```

#### Embedding in feature dashboard

The dashboard generator (`fluent-ai-skills/tools/generate-feature-dashboard.mjs`) can detect preview reports and embed the flip widget:

```javascript
// In buildDetailPanel, check for preview report
if (feat.status.preview === 'APPROVED' && feat.status.previewReport) {
  // Option A: Embed GIF directly (simplest)
  if (feat.status.previewGif) {
    html += `<img src="${feat.status.previewGif}" alt="Before/After Preview" style="max-width:100%;border-radius:8px" />`;
  }
  // Option B: Read baseline + final images, embed as CSS flip widget (interactive)
}
```

## Phase 7: Gate & Handoff

### Update status.json (if feature context exists)

```json
{
  "preview": "APPROVED",
  "previewReport": "assets/<timestamp>/report.html",
  "previewGif": "assets/<timestamp>/before-after.gif",
  "previewDate": "YYYY-MM-DD",
  "previewChanges": {
    "added": 1,
    "modified": 1,
    "removed": 0
  }
}
```

### Handoff signal

```
-> READY: Visual preview approved (1 added, 1 modified, 0 removed)
-> NEXT: /fluent-mystique-builder (build manifest matching approved preview)
   Reference: <report-path>
   Change manifest: <N> operations to implement
```

If the user says "rejected" or "start over" instead of approving:

```
-> SKIP: Preview not approved — user requested restart
   Preserved: <report-path> (all iterations saved for reference)
```

## Component-to-HTML Mapping

The core mapping layer that translates Mystique component aliases to Material UI HTML. The host Fluent app already loads Material UI v4.12 — injected HTML inherits these styles automatically via MUI class names.

### Page-level components

| Alias | MUI HTML approximation |
|---|---|
| `fc.page` | `<div class="MuiContainer-root">` with page header |
| `fc.page.section` | `<div class="MuiPaper-root MuiPaper-elevation1">` with optional header |
| `fc.page.section.column` | `<div class="MuiGrid-item">` with width class |
| `fc.page.section.header` | `<div class="MuiTypography-h6">` text |

### Content components

| Alias | MUI HTML approximation |
|---|---|
| `fc.list` | `<table class="MuiTable-root"><thead><tr>` + column headers + `</tr></thead><tbody>` + sample rows + `</tbody></table>` wrapped in `<div class="MuiPaper-root">` |
| `fc.card.attribute` | `<div class="MuiCard-root MuiCard-elevation1"><div class="MuiCardHeader-root">` + title + `<div class="MuiCardContent-root">` + key-value grid |
| `fc.card.image` | `<div class="MuiCard-root">` with `<img>` placeholder |
| `fc.tabs` | `<div class="MuiTabs-root"><button class="MuiTab-root">` per tab + `<div role="tabpanel">` content |
| `fc.stepper` | `<div class="MuiStepper-root"><div class="MuiStep-root">` per step |
| `fc.tile.metric` | `<div class="MuiCard-root">` with large number + label |
| `fc.accordion` | `<div class="MuiAccordion-root"><div class="MuiAccordionSummary-root">` + `<div class="MuiAccordionDetails-root">` |

### Chart components

| Alias | MUI HTML approximation |
|---|---|
| `fc.chart.line` | `<div class="MuiPaper-root">` with SVG placeholder showing line chart shape |
| `fc.chart.bar` | `<div class="MuiPaper-root">` with SVG placeholder showing bar chart shape |
| `fc.chart.gauge` | `<div class="MuiPaper-root">` with SVG arc placeholder |

### Action components

| Alias | MUI HTML approximation |
|---|---|
| `fc.button.bar` | `<div style="display:flex;gap:8px">` + `<button class="MuiButton-root MuiButton-contained">` per action |
| `fc.action.inline` | `<button class="MuiButton-root MuiButton-text MuiButton-textPrimary">` |
| `fc.modal.button` | `<button class="MuiButton-root MuiButton-outlined">` (modal not rendered in preview) |

### Form/field components

| Alias | MUI HTML approximation |
|---|---|
| `select` | `<div class="MuiFormControl-root"><label class="MuiInputLabel-root">` + `<select class="MuiSelect-root">` |
| `fc.field.daterange` | Two `<input type="date" class="MuiInput-root">` with "to" label |
| `fc.field.multistring` | `<div class="MuiChip-root">` tag display |

### Layout components

| Alias | MUI HTML approximation |
|---|---|
| `fc.conditional` | Renders children directly (no visual wrapper) |
| `fc.repeater` | Renders children N times with sample data |
| `fc.provider.graphql` | Invisible data provider (renders children with placeholder data) |
| `fc.filterPanel` | `<div class="MuiPaper-root">` with filter field inputs in a row |

### Sample data generation

When injecting components that display data, generate realistic placeholder values:

| Data type | Sample values |
|---|---|
| Order ref | HD-100001, HD-100002, HD-100003 |
| Status | BOOKED, FULFILLED, COMPLETE |
| Customer name | Jane Smith, John Doe, Alice Johnson |
| Date | 2026-03-01, 2026-03-02, 2026-03-03 |
| Currency amount | $149.99, $89.50, $234.00 |
| Location | Sydney DC, Melbourne Store, Brisbane Hub |
| Product | Blue Widget (SKU-001), Red Gadget (SKU-002) |

Use 3-5 sample rows for lists/tables to give a realistic feel.

## Width Mapping

Manifest width values map to MUI grid columns (12-column grid):

| Width name | Grid columns | CSS |
|---|---|---|
| `quarter` | 3 | `width:25%` |
| `third` | 4 | `width:33.33%` |
| `half` | 6 | `width:50%` |
| `two-thirds` | 8 | `width:66.67%` |
| `full` | 12 | `width:100%` |

## Integration with Other Skills

### Reading from `/fluent-feature-plan`

If the feature plan has Section 10 (UI / Manifest Artifacts), parse it to auto-generate the preview target. The plan's Source column maps directly to preview modes:

```
Section 10 row: "Order appeasement card — fc.card.attribute — NEW — shows totalAmount, currency, reason"
  → Mode: ADD — inject fc.card.attribute with 3 fields after the order detail section

Section 10 row: "Orders list — fc.list — MODIFIED — add Priority column"
  → Mode: MODIFY — find existing orders table, add Priority column

Section 10 row: "Shipping estimate — fc.chart.line — REMOVED"
  → Mode: REMOVE — dim the shipping chart with red highlight
```

### Handoff to `/fluent-mystique-builder`

The approved preview report becomes the visual reference:
- Component aliases identified
- Layout structure captured
- Sample data shape known
- Width/positioning decided
- **Change manifest** — exact list of ADD/MODIFY/REMOVE operations to implement

### Verification via `/fluent-ui-test`

After the real manifest is built and deployed, `/fluent-ui-test` can compare the live page against the approved preview screenshots to verify visual fidelity.

## Error Handling

| Error | Recovery |
|---|---|
| Injection point not found | Fall back to appending at end of main content area. Warn user. |
| Existing element not found (MODIFY/REMOVE) | Try `scanComponents()` first (~400 tokens), then `take_snapshot` (~30K) if needed. Search for closest match, present options to user. |
| Page not loaded / login failed | Follow `/fluent-ui-test` login protocol. If still fails, BLOCK. |
| MUI classes not applying | Host app may use different MUI version. Fall back to inline styles. |
| Highlight styles conflict with app | Use `!important` and unique `__fc-` prefixed class names. |
| Screenshot capture fails | Retry once. If still fails, describe the injection verbally. |
| User closes browser tab | Re-navigate and re-inject from last approved state. |
| React re-render removes injection | Re-apply after navigation. Warn user that SPA routing may reset changes. |

## Output Artifacts

| Artifact | Path | Purpose |
|---|---|---|
| Baseline screenshot | `features/<slug>/assets/<ts>/baseline.png` | Original page state |
| Iteration screenshots | `features/<slug>/assets/<ts>/preview-iteration-<N>.png` | Each refinement step (with highlights) |
| Final highlighted screenshot | `features/<slug>/assets/<ts>/final-highlighted.png` | Approved state with all change highlights visible |
| Final clean screenshot | `features/<slug>/assets/<ts>/final-clean.png` | Approved state with highlights removed (true preview) |
| Before/after GIF | `features/<slug>/assets/<ts>/before-after.gif` | Animated toggle: baseline vs final (for plans, Slack, Jira) |
| 3-frame GIF | `features/<slug>/assets/<ts>/before-after-3frame.gif` | Animated: baseline → highlighted → clean |
| Evolution GIF | `features/<slug>/assets/<ts>/evolution.gif` | All iterations in sequence (if >2 iterations) |
| HTML evidence report | `features/<slug>/assets/<ts>/report.html` | Self-contained with CSS flip animation + iteration history |
| Iteration log | `features/<slug>/assets/<ts>/iterations.json` | Machine-readable feedback history |
| Change manifest | `features/<slug>/assets/<ts>/changes.json` | Structured list of all ADD/MODIFY/REMOVE operations |

## Examples

### Example 1: Preview a new card on the order detail page (ADD)

```
User: "Preview what an appeasement card would look like on the order detail page"

Phase 1: Parse → Mode: ADD, fc.card.attribute with fields: Total Amount, Currency, Reason, Created Date
Phase 2: Navigate → /oms/orders/456
Phase 3: Baseline → screenshot of current order detail
Phase 4: Inject MUI Card after Order Details card
         → Green dashed border + "ADDED: Appeasement Card" badge
         → Change summary: "1 added"
Phase 5: User says "make the total amount bold and bigger" → modify card CSS → re-highlight → screenshot
Phase 5: User says "looks good" → exit loop
Phase 6: Generate before-after.gif (baseline ↔ final toggle), HTML report with CSS flip widget
         Report: baseline | final-highlighted (green border) | final-clean (no highlights) | GIF
Phase 7: status.json updated, GIF embedded in plan.md §10, handoff to /fluent-mystique-builder
```

### Example 2: Modify existing orders list — add a column (MODIFY)

```
User: "Add a Priority column to the orders list"

Phase 1: Parse → Mode: MODIFY, target: orders list table, change: add Priority column
Phase 2: Navigate → /oms/orders
Phase 3: Baseline → screenshot of current orders list
Phase 4: Find existing <table>, add <th>Priority</th> + <td> cells with sample data
         → Table gets yellow border + "MODIFIED: Orders List" badge
         → New <th> and <td> cells each get green "ADDED" sub-highlights
         → Change summary: "1 modified (1 sub-element added)"
Phase 5: User says "use color dots instead of text" → replace text with colored circles → screenshot
Phase 5: User says "approved" → exit loop
Phase 6: Report with visual diff showing exactly which column was added
Phase 7: Handoff: "Add Priority column (position 3) to fc.list descendants"
```

### Example 3: Remove an element to see the layout without it (REMOVE)

```
User: "What would the order page look like without the shipping chart?"

Phase 1: Parse → Mode: REMOVE, target: shipping chart component
Phase 2: Navigate → /oms/orders/456
Phase 3: Baseline → screenshot with shipping chart visible
Phase 4: Find chart element, apply REMOVED highlight
         → Red border + "REMOVED: Shipping Chart" badge
         → Chart dimmed to 30% with strikethrough
         → Change summary: "1 removed"
Phase 5: User says "actually keep the chart but remove the sidebar instead"
         → Undo chart removal (restore opacity)
         → Apply REMOVED to sidebar
Phase 5: User says "looks good" → exit loop
Phase 6: Report shows what was removed and how the page looks without it
Phase 7: Handoff: "Remove sidebar section from manifest route"
```

### Example 4: Mixed operations from a feature plan

```
User: "/fluent-mystique-preview --feature return-order"

Phase 1: Read features/return-order/plan.md Section 10
         → ADD: Return Items card (fc.card.attribute, NEW)
         → ADD: Return Reason form (fc.modal.button, NEW)
         → MODIFY: Order Items list — add "Return Qty" column (MODIFIED)
         → REMOVE: "Reorder" button — not applicable for returns (REMOVED)
Phase 2: Navigate → /oms/orders/789
Phase 3: Baseline → current page
Phase 4: Apply all 4 operations:
         → 2 green (new card + new button)
         → 1 yellow (modified list with green sub-columns)
         → 1 red (dimmed reorder button)
         → Change summary: "2 added, 1 modified, 1 removed"
Phase 5: Iterate until approved
Phase 6: evolution.gif + before-after.gif + HTML report with CSS flip; GIF → plan.md §10
         Change manifest (4 operations) → build spec for /fluent-mystique-builder
```

### Example 5: Preview a manifest diff before deploying

```
User: "Preview what the updated ordermanagement fragment will look like"

Phase 1: Read local manifest JSON, diff against current deployed page
         → Detect: 2 new descendants added, 1 column removed, card title changed
         → Auto-classify: 2 ADD, 1 REMOVE, 1 MODIFY
Phase 2: Navigate → /oms/orders
Phase 3: Baseline → current deployed page
Phase 4: Apply diff operations with highlights
Phase 5: User confirms "this is what I expected" → approved
Phase 6: Report serves as pre-deploy verification evidence
Phase 7: Handoff to deployment pipeline with confidence
```

## Post-Execution: Feedback Capture

After completing this skill (whether success or failure), write a feedback record:

1. **Classify outcome:** `SUCCESS` (completed cleanly), `PARTIAL_SUCCESS` (completed after fixing issues), `FAILURE` (could not complete), `BLOCKED` (prereq missing), `USER_CORRECTED` (user overrode approach)
2. **Build record:** Create a `feedback-record-v1` JSON object with:
   - `skill`: `"fluent-mystique-preview"`
   - `feature`: feature slug if applicable, null otherwise
   - `phases`: trace of each phase with PASS/FAIL/SKIP
   - `learnings`: any gotchas discovered during execution
   - `userCorrections`: any corrections the user provided
3. **Redact secrets:** Strip tokens, passwords, API keys. Keep business identifiers.
4. **Append** one JSON line to `accounts/<PROFILE>/feedback/fluent-mystique-preview.jsonl`
5. **Update** `accounts/<PROFILE>/feedback/index.json` counters (create with `mkdir -p` if missing)

**Skip capture if:** The execution was trivially simple (e.g., user asked a question, no actual write operation happened).

## Known Pitfalls
<!-- feedback-promoted: managed section, updated by feedback loop -->

_(No promoted learnings yet.)_

<!-- end feedback-promoted -->
