---
format: typebulb/v1
name: Rhetorical Mirror
---

**code.tsx**

```tsx
import React, { useState } from "react";
import { createRoot } from "react-dom/client";

type Segment = { text: string; deviceId?: string };
type Device = { id: string; name: string; color: string; description: string };
type Panel = { label: string; author: string; segments: Segment[] };

interface MirrorData {
  title: string;
  original: Panel;
  mirrored: Panel;
  devices: Device[];
}

interface TooltipState {
  visible: boolean;
  x: number;
  y: number;
  device: Device | null;
}

// Helper for active/dimmed class names
const stateClasses = (id: string, activeId: string | null) => 
  activeId === id ? 'active' : activeId ? 'dimmed' : '';

function Header() {
  return (
    <>
      <h1>Rhetorical Mirror</h1>
      <p className="app-summary">
        See how the same persuasion techniques can argue opposite positions.
      </p>
    </>
  );
}

function Tooltip({ state }: { state: TooltipState }) {
  if (!state.visible || !state.device) return null;
  const { color } = state.device;

  return (
    <div 
      className="device-tooltip"
      style={{ left: state.x, top: state.y, borderColor: color, '--tooltip-color': color } as React.CSSProperties}
    >
      <div className="tooltip-header">
        <span className="tooltip-dot" style={{ backgroundColor: color }} />
        <span className="tooltip-name">{state.device.name}</span>
      </div>
      <div className="tooltip-desc">{state.device.description}</div>
    </div>
  );
}

function HighlightedText({ 
  segments, 
  devices,
  activeDevice,
  onHover,
  onTooltip
}: { 
  segments: Segment[]; 
  devices: Device[];
  activeDevice: string | null;
  onHover: (id: string | null) => void;
  onTooltip: (e: React.MouseEvent, device: Device | null) => void;
}) {
  const deviceMap = new Map(devices.map(d => [d.id, d]));

  return (
    <div className="highlighted-text">
      {segments.map((seg, i) => {
        const device = seg.deviceId ? deviceMap.get(seg.deviceId) : null;
        if (!device) return <span key={i}>{seg.text}</span>;

        const { color } = device;
        return (
          <span
            key={i}
            className={`highlight ${stateClasses(seg.deviceId!, activeDevice)}`}
            style={{ 
              backgroundColor: `${color}30`,
              borderBottom: `3px solid ${color}`,
              '--highlight-color': color 
            } as React.CSSProperties}
            onMouseEnter={(e) => { onHover(seg.deviceId!); onTooltip(e, device); }}
            onMouseMove={(e) => onTooltip(e, device)}
            onMouseLeave={(e) => { onHover(null); onTooltip(e, null); }}
          >
            {seg.text}
          </span>
        );
      })}
    </div>
  );
}

function TextPanel({ 
  panel, devices, activeDevice, onHover, onTooltip 
}: { 
  panel: Panel;
  devices: Device[];
  activeDevice: string | null;
  onHover: (id: string | null) => void;
  onTooltip: (e: React.MouseEvent, device: Device | null) => void;
}) {
  return (
    <div className="text-panel">
      <div className="panel-header">
        <span className="panel-label">{panel.label}</span>
        <span className="panel-author">{panel.author}</span>
      </div>
      <HighlightedText 
        segments={panel.segments} 
        devices={devices}
        activeDevice={activeDevice}
        onHover={onHover}
        onTooltip={onTooltip}
      />
    </div>
  );
}

function DeviceLegend({ devices, activeDevice, onHover }: { 
  devices: Device[];
  activeDevice: string | null;
  onHover: (id: string | null) => void;
}) {
  return (
    <div className="device-legend">
      {devices.map(({ id, color, name, description }) => (
        <div 
          key={id}
          className={`device-tag ${stateClasses(id, activeDevice)}`}
          style={{ borderColor: color, backgroundColor: activeDevice === id ? `${color}20` : undefined }}
          onMouseEnter={() => onHover(id)}
          onMouseLeave={() => onHover(null)}
        >
          <span className="device-dot" style={{ backgroundColor: color }} />
          <span className="device-name">{name}</span>
          <span className="device-desc">{description}</span>
        </div>
      ))}
    </div>
  );
}

function App() {
  const [data, setData] = useState<MirrorData | undefined>(tb.insight<MirrorData>());
  const [isAnalyzing, setIsAnalyzing] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [activeDevice, setActiveDevice] = useState<string | null>(null);
  const [tooltip, setTooltip] = useState<TooltipState>({ visible: false, x: 0, y: 0, device: null });

  const handleAnalyze = async () => {
    setIsAnalyzing(true);
    setError(null);
    try {
      const result = await tb.infer<MirrorData>();
      if (result) setData(result);
    } catch (err: any) {
      setError(err.message || 'Analysis failed');
    } finally {
      setIsAnalyzing(false);
    }
  };

  const handleTooltip = (e: React.MouseEvent, device: Device | null) => {
    if (!device) return setTooltip(prev => ({ ...prev, visible: false }));

    const { clientX, clientY } = e;
    const vw = window.visualViewport?.width ?? window.innerWidth;
    const vh = window.visualViewport?.height ?? window.innerHeight;
    const [w, h, pad, margin] = [280, 80, 12, 8];

    let x = clientX + pad, y = clientY + pad;
    if (x + w > vw - margin) x = clientX - w - pad;
    if (y + h > vh - margin) y = clientY - h - pad;

    setTooltip({ visible: true, x: Math.max(margin, Math.min(x, vw - w - margin)), y: Math.max(margin, Math.min(y, vh - h - margin)), device });
  };

  const sharedProps = { devices: data?.devices || [], activeDevice, onHover: setActiveDevice, onTooltip: handleTooltip };

  if (!data) {
    return (
      <div className="container" style={{ textAlign: 'center', paddingTop: '10vh' }}>
        <div className="mirror-icon" style={{ fontSize: '4rem', marginBottom: '1rem' }}>
          <span className="emoji">🪞</span>
        </div>
        <Header />
        {isAnalyzing && <p className="subtitle">Mapping persuasive techniques and generating the counter-mirror.</p>}
        {error && <p className="error-message">{error}</p>}
        {!isAnalyzing && (
          <button className="analyze-btn" onClick={handleAnalyze}>
            <span className="emoji">✨</span> Analyze with AI
          </button>
        )}
      </div>
    );
  }

  return (
    <div className="container">
      <Tooltip state={tooltip} />

      <header>
        <Header />
        <h2 className="dynamic-title">{data.title}</h2>
        <p className="subtitle">Original Statement — {data.original.author}</p>
      </header>

      <div className="action-bar">
        <button className="analyze-btn" onClick={handleAnalyze} disabled={isAnalyzing}>
          {isAnalyzing ? 'Analyzing...' : '🔄 Analyze New Quote'}
        </button>
        {error && <p className="error-message">{error}</p>}
      </div>

      <section className="mirror-section">
        <div className="mirror-grid">
          <TextPanel panel={data.original} {...sharedProps} />
          <div className="mirror-divider">
            <span className="mirror-icon"><span className="emoji">🪞</span></span>
          </div>
          <TextPanel panel={data.mirrored} {...sharedProps} />
        </div>
      </section>

      <section className="legend-section">
        <h2>🏷️ Rhetorical Devices</h2>
        <p className="legend-intro">Hover over any device to highlight it in both texts above</p>
        <DeviceLegend devices={data.devices} activeDevice={activeDevice} onHover={setActiveDevice} />
      </section>

      <section className="intro-section">
        <h2>About the Mirror</h2>
        <p>
          Skilled arguers use tricks: loaded words, implied threats, us-vs-them. 
          This tool finds them, then shows what they look like arguing for the opposing POV.
        </p>
      </section>
    </div>
  );
}

createRoot(document.getElementById("root")!).render(<App />);
```
**styles.css**

```css
:root {
  --bg-primary: #f8f9fa;
  --bg-secondary: #ffffff;
  --bg-tertiary: #e9ecef;
  --text-primary: #212529;
  --text-secondary: #6c757d;
  --accent: #6366f1;
  --border: #dee2e6;
  --shadow: rgba(0, 0, 0, 0.08);
}

html[data-theme="dark"] {
  --bg-primary: #0a0a0b;
  --bg-secondary: #141416;
  --bg-tertiary: #1e1e22;
  --text-primary: #f4f4f5;
  --text-secondary: #a1a1aa;
  --accent: #818cf8;
  --border: #2e2e33;
  --shadow: rgba(0, 0, 0, 0.4);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  background: var(--bg-primary);
  color: var(--text-primary);
  line-height: 1.7;
}

.container {
  max-width: 1400px;
  margin: 0 auto;
  padding: 2rem;
}

header {
  text-align: center;
  margin-bottom: 2rem;
}

header h1 {
  font-size: 2.8rem;
  margin: 0;
  background: linear-gradient(135deg, #e91e63, #9c27b0, #3f51b5);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.dynamic-title {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin: 0.5rem 0;
  color: var(--text-primary);
  opacity: 0.6;
  font-weight: 500;
}

.emoji {
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla", sans-serif;
  font-variant-emoji: emoji;
  -webkit-text-fill-color: initial;
  background: none;
}

@media (max-width: 600px) {
  .container {
    padding: 1rem 0;
  }

  header h1 {
    font-size: 1.75rem;
  }
}

.subtitle {
  color: var(--text-secondary);
  font-size: 1.1rem;
  margin-top: 0.5rem;
}

.app-summary {
  color: var(--text-secondary);
  font-size: 1.1rem;
  max-width: 600px;
  margin: 0.5rem auto 1rem;
  line-height: 1.5;
}

section {
  background: var(--bg-secondary);
  border-radius: 16px;
  padding: 2rem;
  margin-bottom: 2rem;
  box-shadow: 0 4px 24px var(--shadow);
}

@media (max-width: 600px) {
  section {
    border-radius: 0;
    padding: 1.25rem 1rem;
    margin-bottom: 0.5rem;
  }
}

h2 {
  margin-top: 0;
  font-size: 1.4rem;
  color: var(--text-primary);
}

/* Intro Section */
.intro-section {
  border-left: 4px solid var(--accent);
}

.intro-section p {
  margin: 0.75rem 0;
  color: var(--text-secondary);
}

/* Mirror Grid */
.mirror-section h2 {
  text-align: center;
  margin-bottom: 1.5rem;
}

.mirror-grid {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 1rem;
  align-items: stretch;
}

@media (max-width: 900px) {
  .mirror-grid {
    grid-template-columns: 1fr;
  }
  .mirror-divider {
    justify-self: center;
  }
}

@media (max-width: 600px) {
  .mirror-grid {
    gap: 0.5rem;
  }
}

.text-panel {
  background: var(--bg-tertiary);
  border-radius: 12px;
  padding: 1.5rem;
  min-height: 300px;
}

@media (max-width: 600px) {
  .text-panel {
    background: transparent;
    border-radius: 0;
    padding: 0;
    min-height: auto;
    border-top: 1px solid var(--border);
    padding-top: 1rem;
  }
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--border);
}

@media (max-width: 600px) {
  .panel-header {
    padding-bottom: 0.5rem;
    margin-bottom: 0.75rem;
  }
}

.panel-label {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--text-primary);
}

.panel-author {
  font-size: 0.8rem;
  color: var(--text-secondary);
  background: var(--bg-secondary);
  padding: 0.25rem 0.75rem;
  border-radius: 12px;
}

.mirror-divider {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0.5rem;
}

@media (max-width: 600px) {
  .mirror-divider {
    padding: 0.25rem 0;
  }
  .mirror-icon {
    font-size: 1.5rem;
  }
}

.mirror-icon {
  font-size: 2rem;
  opacity: 0.5;
}

/* Highlighted Text */
.highlighted-text {
  font-size: 1rem;
  line-height: 2;
}

.highlight {
  padding: 0.15rem 0.1rem;
  border-radius: 3px;
  cursor: pointer;
  transition: all 0.2s ease;
}

.highlight:hover,
.highlight.active {
  filter: brightness(1.1);
  box-shadow: 0 2px 8px var(--highlight-color, rgba(0,0,0,0.2));
}

.highlight.dimmed {
  opacity: 0.35;
}

/* Tooltip */
.device-tooltip {
  position: fixed;
  z-index: 1000;
  background: var(--bg-secondary);
  border: 2px solid;
  border-radius: 10px;
  padding: 0.75rem 1rem;
  max-width: 280px;
  box-shadow: 0 8px 24px var(--shadow), 0 0 0 1px var(--border);
  pointer-events: none;
  animation: tooltipFadeIn 0.15s ease-out;
}

@keyframes tooltipFadeIn {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.tooltip-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.4rem;
}

.tooltip-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}

.tooltip-name {
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--text-primary);
}

.tooltip-desc {
  font-size: 0.8rem;
  color: var(--text-secondary);
  line-height: 1.4;
}

/* Legend Section */
.legend-section {
  text-align: center;
}

.legend-intro {
  color: var(--text-secondary);
  margin-bottom: 1.5rem;
}

.device-legend {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1rem;
  justify-content: center;
}

.device-tag {
  flex-direction: column;
  align-items: flex-start;
  display: flex;
  gap: 0.5rem;
  padding: 0.6rem 1rem;
  background: var(--bg-tertiary);
  border-radius: 8px;
  border-left: 4px solid;
  cursor: pointer;
  transition: all 0.2s ease;
  text-align: left;
}

.device-tag:hover,
.device-tag.active {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px var(--shadow);
}

.device-tag.dimmed {
  opacity: 0.4;
}

.device-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  flex-shrink: 0;
  display: none;
}

.device-name {
  font-weight: 600;
  font-size: 0.85rem;
  white-space: nowrap;
}

.device-desc {
  font-size: 0.75rem;
  color: var(--text-secondary);
}

@media (max-width: 600px) {
  .device-tag {
    gap: 0.25rem;
  }
  .device-legend {
    grid-template-columns: 1fr;
  }
}

@media (min-width: 1200px) {
  .device-legend {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* Footer */
footer {
  text-align: center;
  padding: 1rem;
  color: var(--text-secondary);
}

footer code {
  background: var(--bg-tertiary);
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  font-size: 0.85rem;
  color: var(--accent);
}

@media (max-width: 600px) {
  footer {
    padding: 1rem;
    font-size: 0.85rem;
  }
}

/* Action Bar */
.action-bar {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0 0 2rem;
  gap: 1rem;
  width: 100%;
}

.action-bar .analyze-btn {
  margin: 0 auto;
  width: auto;
  min-width: 240px;
}

/* Analyze Button */
.analyze-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.875rem 1.75rem;
  font-size: 1.1rem;
  font-weight: 600;
  color: white;
  background: linear-gradient(135deg, #e91e63, #9c27b0, #3f51b5);
  border: none;
  border-radius: 12px;
  cursor: pointer;
  transition: all 0.2s ease;
  box-shadow: 0 4px 16px rgba(156, 39, 176, 0.3);
  margin-top: 1.5rem;
}

.analyze-btn:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 24px rgba(156, 39, 176, 0.4);
}

.analyze-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.analyze-btn-small {
  padding: 0.5rem 1rem;
  font-size: 0.9rem;
  margin-top: 0.75rem;
}

/* Error Message */
.error-message {
  color: #e74c3c;
  background: rgba(231, 76, 60, 0.1);
  padding: 0.75rem 1rem;
  border-radius: 8px;
  margin-top: 1rem;
  font-size: 0.9rem;
}

```
**index.html**

```html
<div id="root"></div>

```
**data.txt**

```txt
Shockingly, our “brilliant” NATO Ally, the United Kingdom, is currently planning to give away the Island of Diego Garcia, the site of a vital U.S. Military Base, to Mauritius, and to do so FOR NO REASON WHATSOEVER. There is no doubt that China and Russia have noticed this act of total weakness. These are International Powers who only recognize STRENGTH, which is why the United States of America, under my leadership, is now, after only one year, respected like never before. The UK giving away extremely important land is an act of GREAT STUPIDITY, and is another in a very long line of National Security reasons why Greenland has to be acquired. Denmark and its European Allies have to DO THE RIGHT THING. Thank you for your attention to this matter. PRESIDENT DONALD J. TRUMP
```
**infer.md**

```md
# Rhetorical Mirror Analysis

Analyze the provided text (`data.txt`) to identify key rhetorical devices used for persuasion. Then, create a "Mirror" of the statement that argues the exact opposite position using the identical set of rhetorical techniques in the same order.

## Objectives

1.  **Deconstruct Rhetoric**: Identify at least 8-12 distinct rhetorical devices (e.g., Catastrophizing, Appeal to Authority, Framing, Ridicule, Innuendo).
2.  **Segment Original**: Reconstruct the original text exactly, but break it into segments where persuasive phrases are tagged with a `deviceId`.
3.  **Synthesize Mirror**: Write a counter-statement from the opposing viewpoint. 
    - If the original is an attack, the mirror is a defense (or a counter-attack).
    - If the original demands compliance, the mirror demands sovereignty.
4.  **Mirror the Structure**: The mirror must use the *exact same* sequence of rhetorical devices as the original. If the original starts with "One-Sided Framing", the mirror must start with "One-Sided Framing" (applied to the opposite side).

## Definitions

- **deviceId**: A short slug (e.g., "fear", "history", "label").
- **segments**: An array of `{ text: string, deviceId?: string }`.

## Voice Mirroring

The mirror must adopt the original speaker's **linguistic fingerprint**:
- Sentence structure (fragments, run-ons, parentheticals)
- Register (formal, vernacular, aggressive)
- Punctuation patterns (caps for emphasis, ellipses, exclamations)
- Idiosyncrasies (repetition, self-reference, hyperbole)

The goal: the mirror should sound like it was written by the same person, just arguing the opposite position. If the original is grammatically loose and punchy, the mirror must be too — not a polished diplomatic response.

## Constraints

- Ensure the `deviceId` used in segments matches an `id` in the `devices` list.
- The `devices` list should include a `color` (hex), `name`, and `description` for each identified technique.

```
**insight.json**

```json
{
  "title": "Diego Garcia Cession / Greenland Sovereignty Dispute",
  "original": {
    "label": "Original Statement",
    "author": "Donald J. Trump",
    "segments": [
      { "text": "Shockingly", "deviceId": "alarmism" },
      { "text": ", our " },
      { "text": "“brilliant”", "deviceId": "ridicule" },
      { "text": " NATO Ally, the United Kingdom, is currently " },
      { "text": "planning to give away", "deviceId": "loss_framing" },
      { "text": " the Island of Diego Garcia, the site of a vital U.S. Military Base, to Mauritius, and to do so " },
      { "text": "FOR NO REASON WHATSOEVER", "deviceId": "absolutism" },
      { "text": ". There is no doubt that " },
      { "text": "China and Russia have noticed", "deviceId": "adversary_threat" },
      { "text": " " },
      { "text": "this act of total weakness", "deviceId": "weakness_labeling" },
      { "text": ". These are " },
      { "text": "International Powers who only recognize STRENGTH", "deviceId": "strength_logic" },
      { "text": ", which is why the United States of America, " },
      { "text": "under my leadership, is now, after only one year, respected like never before", "deviceId": "aggrandize" },
      { "text": ". The UK giving away extremely important land is " },
      { "text": "an act of GREAT STUPIDITY", "deviceId": "insult" },
      { "text": ", and is another in a very long line of National Security reasons " },
      { "text": "why Greenland has to be acquired", "deviceId": "pivot" },
      { "text": ". Denmark and its European Allies have to " },
      { "text": "DO THE RIGHT THING", "deviceId": "imperative" },
      { "text": ". " },
      { "text": "Thank you for your attention to this matter.", "deviceId": "formal" },
      { "text": " PRESIDENT DONALD J. TRUMP" }
    ]
  },
  "mirrored": {
    "label": "Mirrored Counter-Statement",
    "author": "[Rhetorical Mirror]",
    "segments": [
      { "text": "Predictably", "deviceId": "alarmism" },
      { "text": ", the " },
      { "text": "“generous”", "deviceId": "ridicule" },
      { "text": " American administration is currently " },
      { "text": "trying to seize", "deviceId": "loss_framing" },
      { "text": " the territory of Greenland, the home of a sovereign Arctic people, from Denmark, and to do so " },
      { "text": "WITHOUT ANY LEGAL JUSTIFICATION", "deviceId": "absolutism" },
      { "text": ". There is no doubt that " },
      { "text": "the United Nations and the EU have observed", "deviceId": "adversary_threat" },
      { "text": " " },
      { "text": "this display of imperial arrogance", "deviceId": "weakness_labeling" },
      { "text": ". These are " },
      { "text": "Global Actors who only value SOVEREIGNTY", "deviceId": "strength_logic" },
      { "text": ", which is why the United Kingdom, " },
      { "text": "through international cooperation, is now, more than ever, leading with principle", "deviceId": "aggrandize" },
      { "text": ". The U.S. attempting to block decolonization is " },
      { "text": "an act of DESPERATE OVERREACH", "deviceId": "insult" },
      { "text": ", and is another in a very long line of diplomatic reasons " },
      { "text": "why Diego Garcia must be returned", "deviceId": "pivot" },
      { "text": ". America and its isolated supporters have to " },
      { "text": "OBSERVE INTERNATIONAL LAW", "deviceId": "imperative" },
      { "text": ". " },
      { "text": "Thank you for your attention to this matter.", "deviceId": "formal" },
      { "text": " INTERNATIONAL SOVEREIGNTY COALITION" }
    ]
  },
  "devices": [
    { "id": "alarmism", "name": "Initial Alarmism", "color": "#e74c3c", "description": "Opening with an emotionally charged adverb to signal a crisis." },
    { "id": "ridicule", "name": "Sarcastic Ridicule", "color": "#9b59b6", "description": "Using scare quotes around positive terms to mock the opponent's competence." },
    { "id": "loss_framing", "name": "Strategic Loss Framing", "color": "#e67e22", "description": "Characterizing a policy or legal transfer as an impulsive 'giving away' of assets." },
    { "id": "absolutism", "name": "Context Erasure", "color": "#f1c40f", "description": "Asserting that an action has zero justification to bypass complex legal or historical context." },
    { "id": "adversary_threat", "name": "Third-Party Menace", "color": "#f44336", "description": "Invoking the watchful eye of rivals to heighten the sense of danger." },
    { "id": "weakness_labeling", "name": "Character Labeling", "color": "#673ab7", "description": "Summarizing complex geopolitical moves as simple character flaws like 'weakness' or 'arrogance'." },
    { "id": "strength_logic", "name": "Power Binary", "color": "#1abc9c", "description": "Simplifying international relations into a binary of 'strength' versus 'weakness'." },
    { "id": "aggrandize", "name": "In-Group Aggrandizement", "color": "#ff9800", "description": "Claiming unprecedented success or respect for one's own side." },
    { "id": "insult", "name": "Hyperbolic Insult", "color": "#00bcd4", "description": "Using capitalized, extreme descriptors to shut down debate." },
    { "id": "pivot", "name": "Non-Sequitur Pivot", "color": "#e91e63", "description": "Linking two unrelated territorial issues to create a false sense of strategic necessity." },
    { "id": "imperative", "name": "Moral Imperative", "color": "#8bc34a", "description": "Ending with a vague, capitalized demand that assumes the moral high ground." },
    { "id": "formal", "name": "Pseudo-Official Sign-off", "color": "#9e9e9e", "description": "Using formal administrative language to give personal opinions the weight of an official decree." }
  ]
}
```
**config.json**

```json
{
  "dependencies": {
    "react": "^19.2.3",
    "react-dom": "^19.2.3"
  },
  "description": "See how the same persuasion techniques can argue opposite positions.",
  "inference": {
    "title": "Create Rhetorical Mirror",
    "dataTitle": "Original Statement",
    "submitTitle": "Generate Mirror"   
  }
}
```