Files
Brancheneinstufung2/gtm-architect/v2/App.tsx

1952 lines
90 KiB
TypeScript

import React, { useState, useEffect, useRef } from 'react';
import { Layout } from './components/Layout';
import { Phase, AppState, Phase1Data, Phase2Data, Phase3Data, Phase4Data, Phase6Data, Phase7Data, Phase8Data, Phase9Data, Language, Theme } from './types';
import * as Gemini from './geminiService';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { AlertTriangle, ArrowRight, ArrowLeft, Check, Database, Globe, Search, ShieldAlert, Cpu, Building2, UserCircle, Briefcase, Zap, Terminal, Target, Crosshair, Loader2, Plus, X, Download, ShieldCheck, Image as ImageIcon, Copy, Sparkles, Upload, CheckCircle, PenTool, Eraser, Undo, Save, RefreshCw, Pencil, Trash2, LayoutTemplate, TrendingUp, Shield, Languages } from 'lucide-react';
const TRANSLATIONS = {
en: {
phase1: 'Product & Constraints',
phase2: 'ICP Discovery',
phase3: 'Whale Hunting',
phase4: 'Strategy Matrix',
phase5: 'Asset Generation',
phase6: 'Sales Enablement',
phase7: 'Vertical Landing Page',
phase8: 'Business Case (ROI)',
phase9: 'Feature-to-Value Translator',
initTitle: 'Initialize New Product Sequence',
inputLabel: 'Product URL or Technical Specification',
inputPlaceholder: 'Paste raw technical data, brochure text, or product URL here...',
startBtn: 'Start Analysis',
backBtn: 'Back',
processing: 'Processing...',
features: 'Detected Features',
constraints: 'Hard Constraints',
conflictTitle: 'Portfolio Conflict Detected',
conflictPass: 'Portfolio Conflict Check Passed: Unique Value Proposition Identified.',
confirmICP: 'Confirm & Proceed to ICP',
targetId: 'Target Identification',
dataProxies: 'Data Proxies (Digital Footprints)',
method: 'Method',
huntWhales: 'Lock Targets & Hunt Whales',
region: 'Region: DACH (Germany, Austria, Switzerland)',
whales: 'Key Accounts (Whales)',
roles: 'Buying Center Roles',
confirmStrat: 'Confirm Accounts & Strategize',
stratAngles: 'Strategic Angles',
segment: 'Segment',
pain: 'Pain Point',
angle: 'Our Angle',
diff: 'Differentiation',
writeCopy: 'Generate Assets',
genAssets: 'Generated Strategy Report & Assets',
complete: 'Process Complete. Strategy locked.',
restart: 'Start New Session',
addPlaceholder: 'Add item...',
download: 'Download Full Report (.md)',
downloadAsset: 'Download Asset',
translateBtn: 'Translate Report to English',
downloadEn: 'Download English Report (.md)',
translating: 'Translating...',
toPhase6: 'Proceed to Sales Enablement (Phase 6)',
toPhase7: 'Proceed to Landing Pages (Phase 7)',
toPhase8: 'Proceed to Business Case (Phase 8)',
toPhase9: 'Proceed to Feature-to-Value (Phase 9)',
phase6Title: 'Sales Enablement & Visuals',
phase7Title: 'Vertical Landing Pages',
phase8Title: 'Business Case Builder',
phase9Title: 'Feature-to-Value Translator',
battlecards: 'Kill-Critique Battlecards',
visuals: 'Visual Briefings (Midjourney Prompts)',
objection: 'Objection',
response: 'Response Script',
copyPrompt: 'Copy Prompt',
copied: 'Copied!',
genImage: 'Generate Concept',
editImage: 'Edit & Iterate',
regenerateSketch: 'Regenerate with Sketch',
cancelEdit: 'Cancel Edit',
generating: 'Generating...',
uploadImage: 'Product Reference Images',
uploadHint: 'Upload multiple photos (Front, Side, Detail) for better 3D understanding.',
imageUploaded: 'Reference Images',
canvasClear: 'Clear',
canvasMode: 'Sketch Mode',
// New placeholders
addName: 'Name...',
addRationale: 'Rationale...',
addTarget: 'Target criteria...',
addMethod: 'Method...',
addAccount: 'Account name...',
addRole: 'Job title...',
loading: [
"Initializing quantum analysis engines...",
"Parsing raw technical specifications...",
"Simulating physical constraints & boundary conditions...",
"Cross-referencing internal product matrix...",
"Detecting potential portfolio conflicts...",
"Synthesizing GTM parameters..."
]
},
de: {
phase1: 'Produkt & Constraints',
phase2: 'ICP Entdeckung',
phase3: 'Whale Hunting',
phase4: 'Strategie-Matrix',
phase5: 'Asset Generierung',
phase6: 'Sales Enablement',
phase7: 'Vertical Landing Page',
phase8: 'Business Case (ROI)',
phase9: 'Feature-to-Value Translator',
initTitle: 'Neue Produktsequenz initialisieren',
inputLabel: 'Produkt-URL oder Technische Spezifikation',
inputPlaceholder: 'Füge hier rohe technische Daten, Broschürentexte oder Produkt-URL ein...',
startBtn: 'Analyse starten',
backBtn: 'Zurück',
processing: 'Verarbeite...',
features: 'Erkannte Features',
constraints: 'Harte Constraints',
conflictTitle: 'Portfolio-Konflikt erkannt',
conflictPass: 'Portfolio-Konfliktprüfung bestanden: Alleinstellungsmerkmal identifiziert.',
confirmICP: 'Bestätigen & weiter zu ICP',
targetId: 'Zielgruppen-Identifikation',
dataProxies: 'Data Proxies (Digitale Spuren)',
method: 'Methode',
huntWhales: 'Ziele fixieren & Whales jagen',
region: 'Region: DACH (Deutschland, Österreich, Schweiz)',
whales: 'Key Accounts (Whales)',
roles: 'Buying Center Rollen',
confirmStrat: 'Accounts bestätigen & Strategie',
stratAngles: 'Strategische Angles',
segment: 'Segment',
pain: 'Pain Point',
angle: 'Unser Angle',
diff: 'Differenzierung',
writeCopy: 'Report & Assets generieren',
genAssets: 'Generierter Strategie-Report & Assets',
complete: 'Prozess abgeschlossen. Strategie fixiert.',
restart: 'Neue Session starten',
addPlaceholder: 'Punkt hinzufügen...',
download: 'Gesamtbericht herunterladen (.md)',
downloadAsset: 'Bild speichern',
translateBtn: 'Bericht ins Englische übersetzen',
downloadEn: 'Englischen Bericht herunterladen (.md)',
translating: 'Übersetze...',
toPhase6: 'Weiter zu Sales Enablement (Phase 6)',
toPhase7: 'Weiter zu Landing Pages (Phase 7)',
toPhase8: 'Weiter zu Business Case (Phase 8)',
toPhase9: 'Weiter zu Feature-to-Value (Phase 9)',
phase6Title: 'Sales Enablement & Visuals',
phase7Title: 'Vertical Landing Pages',
phase8Title: 'Business Case Builder',
phase9Title: 'Feature-to-Value Translator',
battlecards: 'Kill-Critique Battlecards',
visuals: 'Visual Briefings (Midjourney Prompts)',
objection: 'Einwand',
response: 'Antwort-Skript',
copyPrompt: 'Prompt kopieren',
copied: 'Kopiert!',
genImage: 'Konzept erstellen',
editImage: 'Zeichnen & Iterieren',
regenerateSketch: 'Mit Skizze neu generieren',
cancelEdit: 'Abbrechen',
generating: 'Generiere...',
uploadImage: 'Produkt-Referenzbilder',
uploadHint: 'Lade mehrere Fotos hoch (Front, Seite, Detail), um das 3D-Verständnis zu verbessern.',
imageUploaded: 'Referenzbilder',
canvasClear: 'Leeren',
canvasMode: 'Zeichenmodus',
// New placeholders
addName: 'Name...',
addRationale: 'Begründung...',
addTarget: 'Zielkriterium...',
addMethod: 'Suchmethode...',
addAccount: 'Firmenname...',
addRole: 'Jobtitel...',
loading: [
"Initialisiere Quanten-Analyse-Engines...",
"Analysiere technische Spezifikationen...",
"Simuliere physikalische Constraints...",
"Prüfe interne Produkt-Matrix...",
"Erkenne potenzielle Portfolio-Konflikte...",
"Synthetisiere GTM-Parameter..."
]
}
};
const App: React.FC = () => {
const [state, setState] = useState<AppState>({
currentPhase: Phase.Input,
isLoading: false,
history: [],
productInput: '',
productImages: [], // Now an array
language: 'light', // temporary init, fix in useEffect
theme: 'light' // Default to light
});
// Fix initial state type matching
const [language, setLanguage] = useState<Language>('de'); // Default to German per prompt
const [theme, setTheme] = useState<Theme>('light');
const [error, setError] = useState<string | null>(null);
const [loadingMessage, setLoadingMessage] = useState("");
const [isTranslating, setIsTranslating] = useState(false);
// Local state for adding new items (Human in the Loop inputs)
// Phase 1
const [newFeatureInput, setNewFeatureInput] = useState("");
const [newConstraintInput, setNewConstraintInput] = useState("");
// Phase 2
const [newICPName, setNewICPName] = useState("");
const [newICPRationale, setNewICPRationale] = useState("");
const [newProxyTarget, setNewProxyTarget] = useState("");
const [newProxyMethod, setNewProxyMethod] = useState("");
// Phase 3
// Changed to use a map to track input per group index
const [newAccountInputs, setNewAccountInputs] = useState<Record<number, string>>({});
const [newRoleInput, setNewRoleInput] = useState("");
const [copiedPromptIndex, setCopiedPromptIndex] = useState<number | null>(null);
// Phase 6 Image Generation State
const [generatingImages, setGeneratingImages] = useState<Record<number, boolean>>({});
const [generatedImages, setGeneratedImages] = useState<Record<number, string>>({});
// Phase 6 Editing State
const [editingIndex, setEditingIndex] = useState<number | null>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);
const [isDrawing, setIsDrawing] = useState(false);
const [brushColor, setBrushColor] = useState('#ef4444'); // Red for annotations
const [brushSize, setBrushSize] = useState(4);
const labels = TRANSLATIONS[language];
// Apply theme to body
useEffect(() => {
if (theme === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [theme]);
useEffect(() => {
if (!state.isLoading && !isTranslating) return;
const messages = labels.loading;
let i = 0;
setLoadingMessage(messages[0]);
const interval = setInterval(() => {
i = (i + 1) % messages.length;
setLoadingMessage(messages[i]);
}, 2500);
return () => clearInterval(interval);
}, [state.isLoading, isTranslating, labels.loading]);
// Canvas Initialization for Editing
useEffect(() => {
if (editingIndex !== null && canvasRef.current && generatedImages[editingIndex]) {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
const img = new Image();
img.crossOrigin = "anonymous";
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx?.drawImage(img, 0, 0);
};
img.src = generatedImages[editingIndex];
}
}, [editingIndex, generatedImages]);
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
const goBack = () => {
if (state.currentPhase > Phase.Input) {
setState(s => ({ ...s, currentPhase: s.currentPhase - 1 }));
}
};
// Determine the highest phase the user has completed data for.
// This allows navigation back and forth.
const getMaxAllowedPhase = (): Phase => {
if (state.phase9Result) return Phase.TechTranslator;
if (state.phase8Result) return Phase.BusinessCase;
if (state.phase7Result) return Phase.LandingPage;
if (state.phase6Result) return Phase.SalesEnablement;
if (state.phase5Result) return Phase.AssetGeneration;
if (state.phase4Result) return Phase.Strategy;
if (state.phase3Result) return Phase.WhaleHunting;
if (state.phase2Result) return Phase.ICPDiscovery;
if (state.phase1Result) return Phase.ProductAnalysis;
return Phase.Input;
};
const handlePhaseSelect = (phase: Phase) => {
if (state.isLoading) return; // Prevent navigation while generating
setState(s => ({ ...s, currentPhase: phase }));
};
const generateFullReportMarkdown = (): string => {
if (!state.phase5Result) return "";
let fullReport = state.phase5Result;
if (state.phase6Result) {
fullReport += `\n\n# SALES ENABLEMENT & VISUALS (PHASE 6)\n\n`;
fullReport += `## Kill-Critique Battlecards\n\n`;
state.phase6Result.battlecards.forEach(card => {
fullReport += `### Persona: ${card.persona}\n`;
fullReport += `> **Objection:** "${card.objection}"\n\n`;
fullReport += `**Response:** ${card.responseScript}\n\n---\n\n`;
});
fullReport += `## Visual Briefings (Prompts)\n\n`;
state.phase6Result.visualPrompts.forEach(prompt => {
fullReport += `### ${prompt.title}\n`;
fullReport += `*Context: ${prompt.context}*\n\n`;
fullReport += `\`\`\`\n${prompt.prompt}\n\`\`\`\n\n`;
});
}
if (state.phase7Result) {
fullReport += `\n\n# VERTICAL LANDING PAGES (PHASE 7)\n\n`;
state.phase7Result.landingPages.forEach(lp => {
fullReport += `## ${lp.industry}\n`;
fullReport += `**Headline:** ${lp.headline}\n\n`;
fullReport += `**Subline:** ${lp.subline}\n\n`;
fullReport += `**Benefits:**\n`;
lp.bullets.forEach(b => fullReport += `- ${b}\n`);
fullReport += `\n**CTA:** ${lp.cta}\n\n---\n\n`;
});
}
if (state.phase8Result) {
fullReport += `\n\n# BUSINESS CASE & ROI (PHASE 8)\n\n`;
state.phase8Result.businessCases.forEach(bc => {
fullReport += `## ${bc.industry}\n`;
fullReport += `**Cost Driver:** ${bc.costDriver}\n\n`;
fullReport += `**Efficiency Gain:** ${bc.efficiencyGain}\n\n`;
fullReport += `**Risk Argument:** ${bc.riskArgument}\n\n---\n\n`;
});
}
if (state.phase9Result) {
fullReport += `\n\n# FEATURE-TO-VALUE TRANSLATOR (PHASE 9)\n\n`;
fullReport += `| Feature | The Story (Benefit) | Headline |\n`;
fullReport += `| :--- | :--- | :--- |\n`;
state.phase9Result.techTranslations.forEach(tt => {
fullReport += `| ${tt.feature} | ${tt.story} | ${tt.headline} |\n`;
});
}
return fullReport;
};
const downloadReport = (content?: string, filenamePrefix: string = "") => {
const reportContent = content || generateFullReportMarkdown();
if (!reportContent) return;
const element = document.createElement("a");
const file = new Blob([reportContent], {type: 'text/markdown'});
element.href = URL.createObjectURL(file);
element.download = `roboplanet-gtm-strategy-${filenamePrefix}${new Date().toISOString().slice(0,10)}.md`;
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
};
const handleTranslateReport = async () => {
const fullReport = generateFullReportMarkdown();
if (!fullReport) return;
setIsTranslating(true);
setError(null);
try {
const translated = await Gemini.translateReportToEnglish(fullReport);
setState(s => ({ ...s, translatedReport: translated }));
} catch (e: any) {
setError("Translation failed: " + e.message);
} finally {
setIsTranslating(false);
}
};
const downloadAsset = (dataUrl: string, index: number) => {
const element = document.createElement("a");
element.href = dataUrl;
element.download = `roboplanet-visual-${index + 1}.png`;
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
};
const copyToClipboard = (text: string, index: number) => {
navigator.clipboard.writeText(text);
setCopiedPromptIndex(index);
setTimeout(() => setCopiedPromptIndex(null), 2000);
};
// --- Image Generation Handlers ---
const handleGenerateImage = async (prompt: string, index: number, overrideReference?: string) => {
setGeneratingImages(prev => ({ ...prev, [index]: true }));
try {
// If sketching, use only the sketch as the strong reference
// If not sketching, use the array of uploaded product images
const refImages = overrideReference ? [overrideReference] : state.productImages;
const imageUrl = await Gemini.generateConceptImage(prompt, refImages);
setGeneratedImages(prev => ({ ...prev, [index]: imageUrl }));
// If we were editing, close edit mode
if (editingIndex === index) {
setEditingIndex(null);
}
} catch (e: any) {
console.error("Failed to generate image", e);
setError("Image generation failed: " + e.message);
} finally {
setGeneratingImages(prev => ({ ...prev, [index]: false }));
}
};
const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
if (files && files.length > 0) {
Array.from(files).forEach((file: File) => {
const reader = new FileReader();
reader.onloadend = () => {
if (reader.result) {
setState(s => ({ ...s, productImages: [...s.productImages, reader.result as string] }));
}
};
reader.readAsDataURL(file);
});
}
};
const removeUploadedImage = (index: number) => {
setState(s => ({
...s,
productImages: s.productImages.filter((_, i) => i !== index)
}));
};
// --- Drawing / Editing Handlers ---
const startDrawing = (e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>) => {
setIsDrawing(true);
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const x = (('touches' in e ? e.touches[0].clientX : e.clientX) - rect.left) * scaleX;
const y = (('touches' in e ? e.touches[0].clientY : e.clientY) - rect.top) * scaleY;
ctx.beginPath();
ctx.moveTo(x, y);
};
const draw = (e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>) => {
if (!isDrawing) return;
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const x = (('touches' in e ? e.touches[0].clientX : e.clientX) - rect.left) * scaleX;
const y = (('touches' in e ? e.touches[0].clientY : e.clientY) - rect.top) * scaleY;
ctx.lineTo(x, y);
ctx.strokeStyle = brushColor;
ctx.lineWidth = brushSize;
ctx.lineCap = 'round';
ctx.stroke();
};
const stopDrawing = () => {
setIsDrawing(false);
};
const clearCanvas = () => {
const canvas = canvasRef.current;
if (canvas && editingIndex !== null) {
const ctx = canvas.getContext('2d');
if (ctx) {
const img = new Image();
img.onload = () => {
ctx.clearRect(0,0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
}
img.src = generatedImages[editingIndex];
}
}
};
const handleRegenerateWithSketch = (index: number, prompt: string) => {
const canvas = canvasRef.current;
if (canvas) {
const sketchData = canvas.toDataURL('image/png');
handleGenerateImage(prompt, index, sketchData);
}
};
// --- Handlers ---
const handlePhase1Submit = async () => {
if (!state.productInput.trim()) return;
setState(s => ({ ...s, isLoading: true }));
setError(null);
try {
const result = await Gemini.analyzeProduct(state.productInput, language);
setState(s => ({ ...s, currentPhase: Phase.ProductAnalysis, phase1Result: result, isLoading: false }));
} catch (e: any) {
setError(e.message || "Analysis failed");
setState(s => ({ ...s, isLoading: false }));
}
};
const handlePhase2Submit = async () => {
if (!state.phase1Result) return;
setState(s => ({ ...s, isLoading: true }));
try {
const result = await Gemini.discoverICPs(state.phase1Result, language);
setState(s => ({ ...s, currentPhase: Phase.ICPDiscovery, phase2Result: result, isLoading: false }));
} catch (e: any) {
setError(e.message);
setState(s => ({ ...s, isLoading: false }));
}
};
const handlePhase3Submit = async () => {
if (!state.phase2Result) return;
setState(s => ({ ...s, isLoading: true }));
try {
const result = await Gemini.huntWhales(state.phase2Result, language);
setState(s => ({ ...s, currentPhase: Phase.WhaleHunting, phase3Result: result, isLoading: false }));
} catch (e: any) {
setError(e.message);
setState(s => ({ ...s, isLoading: false }));
}
};
const handlePhase4Submit = async () => {
if (!state.phase3Result || !state.phase1Result) return;
setState(s => ({ ...s, isLoading: true }));
try {
const result = await Gemini.developStrategy(state.phase3Result, state.phase1Result, language);
setState(s => ({ ...s, currentPhase: Phase.Strategy, phase4Result: result, isLoading: false }));
} catch (e: any) {
setError(e.message);
setState(s => ({ ...s, isLoading: false }));
}
};
const handlePhase5Submit = async () => {
// Requires all previous data for the final report
if (!state.phase4Result || !state.phase3Result || !state.phase2Result || !state.phase1Result) return;
setState(s => ({ ...s, isLoading: true }));
try {
const result = await Gemini.generateAssets(
state.phase4Result,
state.phase3Result,
state.phase2Result,
state.phase1Result,
language
);
setState(s => ({ ...s, currentPhase: Phase.AssetGeneration, phase5Result: result, isLoading: false }));
} catch (e: any) {
setError(e.message);
setState(s => ({ ...s, isLoading: false }));
}
};
const handlePhase6Submit = async () => {
if (!state.phase4Result || !state.phase3Result || !state.phase1Result) return;
setState(s => ({ ...s, isLoading: true }));
try {
const result = await Gemini.generateSalesEnablement(
state.phase4Result,
state.phase3Result,
state.phase1Result,
language
);
setState(s => ({ ...s, currentPhase: Phase.SalesEnablement, phase6Result: result, isLoading: false }));
} catch (e: any) {
setError(e.message);
setState(s => ({ ...s, isLoading: false }));
}
};
const handlePhase7Submit = async () => {
if (!state.phase4Result || !state.phase2Result) return;
setState(s => ({ ...s, isLoading: true }));
try {
const result = await Gemini.generateLandingPageCopy(
state.phase4Result,
state.phase2Result,
language
);
setState(s => ({ ...s, currentPhase: Phase.LandingPage, phase7Result: result, isLoading: false }));
} catch (e: any) {
setError(e.message);
setState(s => ({ ...s, isLoading: false }));
}
};
const handlePhase8Submit = async () => {
if (!state.phase2Result || !state.phase1Result) return;
setState(s => ({ ...s, isLoading: true }));
try {
const result = await Gemini.buildBusinessCase(
state.phase2Result,
state.phase1Result,
language
);
setState(s => ({ ...s, currentPhase: Phase.BusinessCase, phase8Result: result, isLoading: false }));
} catch (e: any) {
setError(e.message);
setState(s => ({ ...s, isLoading: false }));
}
};
const handlePhase9Submit = async () => {
if (!state.phase1Result || !state.phase4Result) return;
setState(s => ({ ...s, isLoading: true }));
try {
const result = await Gemini.translateTech(
state.phase1Result,
state.phase4Result,
language
);
setState(s => ({ ...s, currentPhase: Phase.TechTranslator, phase9Result: result, isLoading: false }));
} catch (e: any) {
setError(e.message);
setState(s => ({ ...s, isLoading: false }));
}
};
// --- List Mutation Handlers (Phase 1) ---
const addFeature = () => {
if (!newFeatureInput.trim() || !state.phase1Result) return;
const newItem = newFeatureInput.trim();
setState(s => ({
...s,
phase1Result: s.phase1Result ? {
...s.phase1Result,
features: [...s.phase1Result.features, newItem]
} : undefined
}));
setNewFeatureInput("");
};
const removeFeature = (index: number) => {
setState(s => ({
...s,
phase1Result: s.phase1Result ? {
...s.phase1Result,
features: s.phase1Result.features.filter((_, i) => i !== index)
} : undefined
}));
};
const addConstraint = () => {
if (!newConstraintInput.trim() || !state.phase1Result) return;
const newItem = newConstraintInput.trim();
setState(s => ({
...s,
phase1Result: s.phase1Result ? {
...s.phase1Result,
constraints: [...s.phase1Result.constraints, newItem]
} : undefined
}));
setNewConstraintInput("");
};
const removeConstraint = (index: number) => {
setState(s => ({
...s,
phase1Result: s.phase1Result ? {
...s.phase1Result,
constraints: s.phase1Result.constraints.filter((_, i) => i !== index)
} : undefined
}));
};
// --- List Mutation Handlers (Phase 2) ---
const addICP = () => {
if (!newICPName.trim() || !newICPRationale.trim() || !state.phase2Result) return;
const newItem = { name: newICPName.trim(), rationale: newICPRationale.trim() };
setState(s => ({
...s,
phase2Result: s.phase2Result ? {
...s.phase2Result,
icps: [...s.phase2Result.icps, newItem]
} : undefined
}));
setNewICPName("");
setNewICPRationale("");
};
const removeICP = (index: number) => {
setState(s => ({
...s,
phase2Result: s.phase2Result ? {
...s.phase2Result,
icps: s.phase2Result.icps.filter((_, i) => i !== index)
} : undefined
}));
};
const addProxy = () => {
if (!newProxyTarget.trim() || !newProxyMethod.trim() || !state.phase2Result) return;
const newItem = { target: newProxyTarget.trim(), method: newProxyMethod.trim() };
setState(s => ({
...s,
phase2Result: s.phase2Result ? {
...s.phase2Result,
dataProxies: [...s.phase2Result.dataProxies, newItem]
} : undefined
}));
setNewProxyTarget("");
setNewProxyMethod("");
};
const removeProxy = (index: number) => {
setState(s => ({
...s,
phase2Result: s.phase2Result ? {
...s.phase2Result,
dataProxies: s.phase2Result.dataProxies.filter((_, i) => i !== index)
} : undefined
}));
};
// --- List Mutation Handlers (Phase 3) ---
const addAccount = (groupIndex: number) => {
const inputValue = newAccountInputs[groupIndex];
if (!inputValue?.trim() || !state.phase3Result) return;
const newItem = inputValue.trim();
const newWhales = [...state.phase3Result.whales];
newWhales[groupIndex] = {
...newWhales[groupIndex],
accounts: [...newWhales[groupIndex].accounts, newItem]
};
setState(s => ({
...s,
phase3Result: s.phase3Result ? {
...s.phase3Result,
whales: newWhales
} : undefined
}));
setNewAccountInputs(prev => ({...prev, [groupIndex]: ""}));
};
const removeAccount = (groupIndex: number, accountIndex: number) => {
if (!state.phase3Result) return;
const newWhales = [...state.phase3Result.whales];
newWhales[groupIndex] = {
...newWhales[groupIndex],
accounts: newWhales[groupIndex].accounts.filter((_, i) => i !== accountIndex)
};
setState(s => ({
...s,
phase3Result: s.phase3Result ? {
...s.phase3Result,
whales: newWhales
} : undefined
}));
};
const addRole = () => {
if (!newRoleInput.trim() || !state.phase3Result) return;
const newItem = newRoleInput.trim();
setState(s => ({
...s,
phase3Result: s.phase3Result ? {
...s.phase3Result,
roles: [...s.phase3Result.roles, newItem]
} : undefined
}));
setNewRoleInput("");
};
const removeRole = (index: number) => {
setState(s => ({
...s,
phase3Result: s.phase3Result ? {
...s.phase3Result,
roles: s.phase3Result.roles.filter((_, i) => i !== index)
} : undefined
}));
};
const handlePromptChange = (index: number, newText: string) => {
if (!state.phase6Result) return;
const newPrompts = [...state.phase6Result.visualPrompts];
newPrompts[index] = { ...newPrompts[index], prompt: newText };
setState(s => ({
...s,
phase6Result: s.phase6Result ? {
...s.phase6Result,
visualPrompts: newPrompts
} : undefined
}));
};
// --- Render Helpers ---
const renderInputPhase = () => (
<div className="flex flex-col items-center justify-center min-h-[60vh] animate-in fade-in slide-in-from-bottom-4 duration-700">
<div className="w-full max-w-2xl p-8 rounded-xl shadow-2xl transition-colors duration-300
bg-white border border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<h2 className="text-2xl font-bold mb-6 flex items-center gap-2 text-slate-800 dark:text-white">
<Terminal className="text-blue-600 dark:text-robo-accent" />
{labels.initTitle}
</h2>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-slate-500 dark:text-slate-400 mb-2">
{labels.inputLabel}
</label>
<textarea
className="w-full h-40 rounded-lg p-4 font-mono text-sm resize-none focus:ring-2 focus:outline-none transition-colors
bg-slate-50 border border-slate-300 text-slate-900 focus:ring-blue-500
dark:bg-robo-900 dark:border-robo-700 dark:text-slate-200 dark:focus:ring-robo-500"
placeholder={labels.inputPlaceholder}
value={state.productInput}
onChange={(e) => setState(s => ({ ...s, productInput: e.target.value }))}
disabled={state.isLoading}
/>
</div>
<div className="p-4 rounded-lg border border-dashed transition-colors
bg-slate-50 border-slate-300
dark:bg-robo-900/50 dark:border-robo-600
">
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2 flex items-center gap-2">
<ImageIcon size={16}/> {labels.uploadImage}
</label>
<div className="grid grid-cols-4 gap-4 mb-4">
{state.productImages.map((img, idx) => (
<div key={idx} className="relative group aspect-square rounded-lg overflow-hidden border border-slate-200 dark:border-robo-700 bg-white">
<img src={img} alt={`Ref ${idx}`} className="w-full h-full object-cover" />
<button
onClick={() => removeUploadedImage(idx)}
className="absolute top-1 right-1 bg-red-500 text-white p-1 rounded-full opacity-0 group-hover:opacity-100 transition-opacity"
>
<X size={12}/>
</button>
</div>
))}
<label className="flex flex-col items-center justify-center aspect-square rounded-lg border-2 border-dashed border-slate-300 dark:border-robo-600 hover:border-blue-400 dark:hover:border-robo-400 cursor-pointer transition-colors bg-slate-50 dark:bg-robo-900/30">
<input
type="file"
accept="image/*"
multiple
onChange={handleImageUpload}
className="hidden"
/>
<Plus className="text-slate-400 mb-1" size={24}/>
<span className="text-xs text-slate-500">Add</span>
</label>
</div>
<p className="text-xs text-slate-500 text-center">{labels.uploadHint}</p>
</div>
<button
onClick={handlePhase1Submit}
disabled={state.isLoading || !state.productInput}
className="w-full font-bold py-3 px-4 rounded-lg transition-all flex items-center justify-center gap-2
bg-blue-600 hover:bg-blue-700 text-white disabled:opacity-50
dark:bg-robo-500 dark:hover:bg-robo-400"
>
{state.isLoading ? (
<span className="flex items-center gap-2">
<Loader2 className="animate-spin" /> {labels.processing}
</span>
) : (
<>
{labels.startBtn} <ArrowRight size={18} />
</>
)}
</button>
{state.isLoading && (
<div className="mt-4 p-4 rounded-lg border font-mono text-sm animate-in fade-in slide-in-from-top-2
bg-white border-slate-200
dark:bg-robo-900/80 dark:border-robo-700
">
<div className="flex items-center gap-3 text-emerald-600 dark:text-emerald-400">
<div className="relative flex h-3 w-3">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-3 w-3 bg-emerald-500"></span>
</div>
<span className="animate-pulse">{loadingMessage}</span>
</div>
<div className="mt-2 h-1 w-full bg-slate-200 dark:bg-robo-800 rounded-full overflow-hidden">
<div className="h-full bg-emerald-500/50 animate-pulse w-2/3 rounded-full"></div>
</div>
</div>
)}
</div>
</div>
</div>
);
const renderPhase1 = () => (
<div className="space-y-6 animate-in fade-in">
<div className="p-6 rounded-xl border transition-colors
bg-white border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<h2 className="text-xl font-bold mb-4 flex items-center gap-2 text-slate-900 dark:text-white">
<Cpu className="text-purple-500 dark:text-purple-400" /> {labels.features} & Constraints
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Features List */}
<div>
<h3 className="text-sm font-bold uppercase tracking-wider text-slate-500 dark:text-slate-400 mb-2">{labels.features}</h3>
<ul className="space-y-2">
{state.phase1Result?.features.map((f, i) => (
<li key={i} className="flex justify-between items-center group text-sm p-2 rounded border transition-colors
bg-slate-50 border-slate-200 text-slate-700
dark:bg-robo-900 dark:border-robo-700 dark:text-slate-200
">
<div className="flex items-start gap-2">
<div className="w-1.5 h-1.5 rounded-full bg-green-500 mt-1.5 shrink-0" />
<span>{f}</span>
</div>
<button
onClick={() => removeFeature(i)}
className="opacity-0 group-hover:opacity-100 p-1 hover:bg-red-100 text-red-500 rounded transition-all dark:hover:bg-red-900/30"
>
<X size={14}/>
</button>
</li>
))}
</ul>
<div className="flex gap-2 mt-3">
<input
value={newFeatureInput}
onChange={e => setNewFeatureInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && addFeature()}
className="flex-1 text-sm border rounded px-3 py-2 outline-none focus:ring-2 focus:ring-blue-500
bg-white border-slate-200 text-slate-800
dark:bg-robo-900 dark:border-robo-600 dark:text-white
"
placeholder={labels.addPlaceholder}
/>
<button onClick={addFeature} className="p-2 bg-slate-100 dark:bg-robo-700 rounded hover:bg-slate-200 dark:hover:bg-robo-600 text-slate-600 dark:text-slate-300">
<Plus size={16}/>
</button>
</div>
</div>
{/* Constraints List */}
<div>
<h3 className="text-sm font-bold uppercase tracking-wider text-slate-500 dark:text-slate-400 mb-2">{labels.constraints}</h3>
<ul className="space-y-2">
{state.phase1Result?.constraints.map((c, i) => (
<li key={i} className="flex justify-between items-center group text-sm p-2 rounded border transition-colors
bg-red-50 border-red-200 text-red-700
dark:bg-robo-900 dark:border-red-900/30 dark:text-red-200
">
<div className="flex items-start gap-2">
<div className="w-1.5 h-1.5 rounded-full bg-red-500 mt-1.5 shrink-0" />
<span>{c}</span>
</div>
<button
onClick={() => removeConstraint(i)}
className="opacity-0 group-hover:opacity-100 p-1 hover:bg-red-100 text-red-600 rounded transition-all dark:hover:bg-red-900/50"
>
<X size={14}/>
</button>
</li>
))}
</ul>
<div className="flex gap-2 mt-3">
<input
value={newConstraintInput}
onChange={e => setNewConstraintInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && addConstraint()}
className="flex-1 text-sm border rounded px-3 py-2 outline-none focus:ring-2 focus:ring-red-500
bg-white border-slate-200 text-slate-800
dark:bg-robo-900 dark:border-robo-600 dark:text-white
"
placeholder={labels.addPlaceholder}
/>
<button onClick={addConstraint} className="p-2 bg-slate-100 dark:bg-robo-700 rounded hover:bg-slate-200 dark:hover:bg-robo-600 text-slate-600 dark:text-slate-300">
<Plus size={16}/>
</button>
</div>
</div>
</div>
</div>
{state.phase1Result?.conflictCheck.hasConflict ? (
<div className="border p-6 rounded-xl flex gap-4 transition-colors
bg-red-50 border-red-200
dark:bg-red-950/50 dark:border-red-500/50
">
<AlertTriangle className="text-red-600 dark:text-red-500 shrink-0" size={32} />
<div>
<h3 className="font-bold text-lg text-red-800 dark:text-red-200">{labels.conflictTitle}</h3>
<p className="mt-1 text-red-700 dark:text-red-300">{state.phase1Result.conflictCheck.details}</p>
{state.phase1Result.conflictCheck.relatedProduct && (
<p className="text-sm font-mono mt-2 inline-block px-2 py-1 rounded
bg-red-100 text-red-800
dark:bg-red-900/50 dark:text-red-200
">
Conflict with: {state.phase1Result.conflictCheck.relatedProduct}
</p>
)}
</div>
</div>
) : (
<div className="border p-4 rounded-xl flex items-center gap-3 transition-colors
bg-emerald-50 border-emerald-200
dark:bg-emerald-950/30 dark:border-emerald-500/30
">
<Check className="text-emerald-600 dark:text-emerald-500" />
<span className="text-emerald-800 dark:text-emerald-200">{labels.conflictPass}</span>
</div>
)}
<div className="flex justify-between pt-4">
<button
onClick={goBack}
disabled={state.isLoading}
className="text-sm font-bold text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-white flex items-center gap-2 px-4 py-2"
>
<ArrowLeft size={16}/> {labels.backBtn}
</button>
<button
onClick={handlePhase2Submit}
disabled={state.isLoading}
className="font-bold py-3 px-6 rounded-lg transition-all flex items-center gap-2
bg-slate-900 text-white hover:bg-slate-700
dark:bg-white dark:text-robo-900 dark:hover:bg-slate-200"
>
{state.isLoading ? (
<span className="flex items-center gap-2"><Loader2 className="animate-spin"/> {labels.processing}</span>
) : (
<> {labels.confirmICP} <ArrowRight size={18} /></>
)}
</button>
</div>
</div>
);
const renderPhase2 = () => (
<div className="space-y-6 animate-in fade-in">
<div className="grid grid-cols-1 gap-6">
<h2 className="text-2xl font-bold flex items-center gap-2 text-slate-900 dark:text-white">
<Target className="text-blue-600 dark:text-robo-accent"/> {labels.targetId}
</h2>
{state.phase2Result?.icps.map((icp, i) => (
<div key={i} className="relative p-6 rounded-xl border transition-colors cursor-default group
bg-white border-slate-200 hover:border-blue-400
dark:bg-robo-800 dark:border-robo-700 dark:hover:border-robo-500
">
<button
onClick={() => removeICP(i)}
className="absolute top-4 right-4 p-1.5 rounded-full opacity-0 group-hover:opacity-100 transition-opacity
bg-slate-100 hover:bg-red-100 text-slate-500 hover:text-red-500
dark:bg-robo-700 dark:hover:bg-red-900/50 dark:text-slate-400 dark:hover:text-red-400"
>
<X size={16}/>
</button>
<h3 className="text-xl font-bold mb-2 transition-colors pr-8
text-slate-800 group-hover:text-blue-600
dark:text-white dark:group-hover:text-robo-400
">
{i + 1}. {icp.name}
</h3>
<p className="text-sm leading-relaxed text-slate-600 dark:text-slate-300">{icp.rationale}</p>
</div>
))}
{/* ADD ICP INPUT */}
<div className="p-4 rounded-xl border border-dashed transition-colors
bg-slate-50 border-slate-300
dark:bg-robo-900/50 dark:border-robo-600
">
<div className="flex flex-col gap-2">
<input
value={newICPName}
onChange={e => setNewICPName(e.target.value)}
className="text-sm border rounded px-3 py-2 outline-none focus:ring-2 focus:ring-blue-500
bg-white border-slate-200 text-slate-800
dark:bg-robo-900 dark:border-robo-600 dark:text-white"
placeholder={labels.addName}
/>
<div className="flex gap-2">
<input
value={newICPRationale}
onChange={e => setNewICPRationale(e.target.value)}
onKeyDown={e => e.key === 'Enter' && addICP()}
className="flex-1 text-sm border rounded px-3 py-2 outline-none focus:ring-2 focus:ring-blue-500
bg-white border-slate-200 text-slate-800
dark:bg-robo-900 dark:border-robo-600 dark:text-white"
placeholder={labels.addRationale}
/>
<button onClick={addICP} className="p-2 bg-blue-100 dark:bg-robo-600 rounded hover:bg-blue-200 dark:hover:bg-robo-500 text-blue-700 dark:text-white">
<Plus size={16}/>
</button>
</div>
</div>
</div>
</div>
<div className="p-6 rounded-xl border transition-colors
bg-slate-100 border-slate-200
dark:bg-slate-900 dark:border-slate-700
">
<h3 className="text-sm font-bold uppercase tracking-wider mb-4 flex items-center gap-2 text-slate-500 dark:text-slate-400">
<Database size={16}/> {labels.dataProxies}
</h3>
<div className="space-y-3">
{state.phase2Result?.dataProxies.map((proxy, i) => (
<div key={i} className="flex gap-4 items-center p-3 rounded-lg group relative
bg-white border border-slate-200
dark:bg-black/20 dark:border-transparent
">
<div className="font-mono text-xs shrink-0 whitespace-nowrap px-2 py-1 rounded
bg-slate-100 text-blue-600
dark:bg-robo-900 dark:text-robo-400
">
PROXY_0{i+1}
</div>
<div className="flex-1">
<div className="font-medium text-slate-800 dark:text-slate-200">{proxy.target}</div>
<div className="text-xs mt-0.5 text-slate-500">{labels.method}: {proxy.method}</div>
</div>
<button
onClick={() => removeProxy(i)}
className="opacity-0 group-hover:opacity-100 p-1 hover:bg-red-100 text-red-500 rounded transition-all dark:hover:bg-red-900/30"
>
<X size={14}/>
</button>
</div>
))}
{/* ADD PROXY INPUT */}
<div className="flex gap-2 mt-2">
<input
value={newProxyTarget}
onChange={e => setNewProxyTarget(e.target.value)}
className="flex-1 text-sm border rounded px-3 py-2 outline-none focus:ring-2 focus:ring-blue-500
bg-white border-slate-200 text-slate-800
dark:bg-robo-900 dark:border-robo-600 dark:text-white"
placeholder={labels.addTarget}
/>
<input
value={newProxyMethod}
onChange={e => setNewProxyMethod(e.target.value)}
onKeyDown={e => e.key === 'Enter' && addProxy()}
className="flex-1 text-sm border rounded px-3 py-2 outline-none focus:ring-2 focus:ring-blue-500
bg-white border-slate-200 text-slate-800
dark:bg-robo-900 dark:border-robo-600 dark:text-white"
placeholder={labels.addMethod}
/>
<button onClick={addProxy} className="p-2 bg-slate-200 dark:bg-robo-700 rounded hover:bg-slate-300 dark:hover:bg-robo-600 text-slate-700 dark:text-white">
<Plus size={16}/>
</button>
</div>
</div>
</div>
<div className="flex justify-between pt-4">
<button
onClick={goBack}
disabled={state.isLoading}
className="text-sm font-bold text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-white flex items-center gap-2 px-4 py-2"
>
<ArrowLeft size={16}/> {labels.backBtn}
</button>
<button
onClick={handlePhase3Submit}
disabled={state.isLoading}
className="font-bold py-3 px-6 rounded-lg transition-all flex items-center gap-2
bg-slate-900 text-white hover:bg-slate-700
dark:bg-white dark:text-robo-900 dark:hover:bg-slate-200"
>
{state.isLoading ? (
<span className="flex items-center gap-2"><Loader2 className="animate-spin"/> {labels.processing}</span>
) : (
<> {labels.huntWhales} <Crosshair size={18} /></>
)}
</button>
</div>
</div>
);
const renderPhase3 = () => (
<div className="space-y-8 animate-in fade-in">
<div className="flex gap-2 items-center p-2 rounded border inline-flex
bg-emerald-50 text-emerald-700 border-emerald-200
dark:bg-emerald-950/20 dark:text-emerald-400 dark:border-emerald-900/50
">
<Globe size={16}/> {labels.region}
</div>
<div className="grid grid-cols-1 gap-8">
{/* Whales Groups */}
<div className="space-y-6">
<h3 className="text-xl font-bold flex items-center gap-2 text-slate-800 dark:text-white">
<Building2 className="text-amber-500 dark:text-amber-400"/> {labels.whales}
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{state.phase3Result?.whales.map((whaleGroup, groupIdx) => (
<div key={groupIdx} className="p-6 rounded-xl border transition-colors flex flex-col
bg-white border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<div className="mb-4 pb-2 border-b border-slate-100 dark:border-robo-600">
<h4 className="font-bold text-slate-700 dark:text-slate-200 flex items-center gap-2">
<span className="bg-amber-100 text-amber-600 dark:bg-amber-900/30 dark:text-amber-400 px-2 py-0.5 rounded text-xs uppercase tracking-wider">Industry</span>
{whaleGroup.industry}
</h4>
</div>
<ul className="space-y-3 flex-1">
{whaleGroup.accounts.map((acc, accIdx) => (
<li key={accIdx} className="flex items-center justify-between group p-3 rounded-lg border transition-colors
bg-slate-50 border-slate-200
dark:bg-robo-900 dark:border-robo-800
">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded flex items-center justify-center font-bold text-xs shrink-0
bg-slate-200 text-slate-500
dark:bg-slate-700 dark:text-slate-400
">
{acc.substring(0,2).toUpperCase()}
</div>
<span className="font-medium text-slate-800 dark:text-slate-200">{acc}</span>
</div>
<button
onClick={() => removeAccount(groupIdx, accIdx)}
className="opacity-0 group-hover:opacity-100 p-1 hover:bg-red-100 text-red-500 rounded transition-all dark:hover:bg-red-900/30"
>
<X size={14}/>
</button>
</li>
))}
</ul>
<div className="flex gap-2 mt-4 pt-2 border-t border-slate-100 dark:border-robo-700/50">
<input
value={newAccountInputs[groupIdx] || ""}
onChange={e => setNewAccountInputs({...newAccountInputs, [groupIdx]: e.target.value})}
onKeyDown={e => e.key === 'Enter' && addAccount(groupIdx)}
className="flex-1 text-sm border rounded px-3 py-2 outline-none focus:ring-2 focus:ring-amber-500
bg-slate-50 border-slate-200 text-slate-800
dark:bg-robo-900 dark:border-robo-600 dark:text-white
"
placeholder={labels.addAccount}
/>
<button onClick={() => addAccount(groupIdx)} className="p-2 bg-slate-100 dark:bg-robo-700 rounded hover:bg-slate-200 dark:hover:bg-robo-600 text-slate-600 dark:text-slate-300">
<Plus size={16}/>
</button>
</div>
</div>
))}
</div>
</div>
{/* Personas */}
<div className="p-6 rounded-xl border transition-colors
bg-white border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<h3 className="text-lg font-bold mb-4 flex items-center gap-2 text-slate-800 dark:text-white">
<UserCircle className="text-blue-500 dark:text-blue-400"/> {labels.roles}
</h3>
<ul className="space-y-3">
{state.phase3Result?.roles.map((role, i) => (
<li key={i} className="flex items-center justify-between group p-3 rounded-lg border transition-colors
bg-slate-50 border-slate-200
dark:bg-robo-900 dark:border-robo-800
">
<div className="flex items-center gap-3">
<div className="p-1.5 rounded-full
bg-blue-100 text-blue-500
dark:bg-blue-900/30 dark:text-blue-400
">
<Briefcase size={14}/>
</div>
<span className="font-medium text-slate-800 dark:text-slate-200">{role}</span>
</div>
<button
onClick={() => removeRole(i)}
className="opacity-0 group-hover:opacity-100 p-1 hover:bg-red-100 text-red-500 rounded transition-all dark:hover:bg-red-900/30"
>
<X size={14}/>
</button>
</li>
))}
</ul>
<div className="flex gap-2 mt-3">
<input
value={newRoleInput}
onChange={e => setNewRoleInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && addRole()}
className="flex-1 text-sm border rounded px-3 py-2 outline-none focus:ring-2 focus:ring-blue-500
bg-white border-slate-200 text-slate-800
dark:bg-robo-900 dark:border-robo-600 dark:text-white
"
placeholder={labels.addRole}
/>
<button onClick={addRole} className="p-2 bg-slate-100 dark:bg-robo-700 rounded hover:bg-slate-200 dark:hover:bg-robo-600 text-slate-600 dark:text-slate-300">
<Plus size={16}/>
</button>
</div>
</div>
</div>
<div className="flex justify-between pt-4">
<button
onClick={goBack}
disabled={state.isLoading}
className="text-sm font-bold text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-white flex items-center gap-2 px-4 py-2"
>
<ArrowLeft size={16}/> {labels.backBtn}
</button>
<button
onClick={handlePhase4Submit}
disabled={state.isLoading}
className="font-bold py-3 px-6 rounded-lg transition-all flex items-center gap-2
bg-slate-900 text-white hover:bg-slate-700
dark:bg-white dark:text-robo-900 dark:hover:bg-slate-200"
>
{state.isLoading ? (
<span className="flex items-center gap-2"><Loader2 className="animate-spin"/> {labels.processing}</span>
) : (
<> {labels.confirmStrat} <Zap size={18} /></>
)}
</button>
</div>
</div>
);
const renderPhase4 = () => (
<div className="space-y-8 animate-in fade-in">
<h2 className="text-2xl font-bold flex items-center gap-2 text-slate-900 dark:text-white">
<Zap className="text-yellow-500 dark:text-yellow-400"/> {labels.stratAngles}
</h2>
<div className="overflow-x-auto rounded-xl border transition-colors border-slate-200 dark:border-robo-700">
<table className="w-full text-left border-collapse">
<thead>
<tr className="text-xs uppercase tracking-wider
bg-slate-100 text-slate-500
dark:bg-robo-900 dark:text-slate-400
">
<th className="p-4 border-b border-slate-200 dark:border-robo-700">{labels.segment}</th>
<th className="p-4 border-b border-slate-200 dark:border-robo-700">{labels.pain}</th>
<th className="p-4 border-b border-slate-200 dark:border-robo-700">{labels.angle}</th>
<th className="p-4 border-b border-slate-200 dark:border-robo-700">{labels.diff}</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-200 dark:divide-robo-700 bg-white dark:bg-robo-800">
{state.phase4Result?.strategyMatrix.map((row, i) => (
<tr key={i} className="hover:bg-slate-50 dark:hover:bg-robo-700/50 transition-colors">
<td className="p-4 font-bold text-slate-800 dark:text-white">{row.segment}</td>
<td className="p-4 text-sm text-red-600 dark:text-red-300">{row.painPoint}</td>
<td className="p-4 font-medium text-emerald-600 dark:text-emerald-300">{row.angle}</td>
<td className="p-4 text-sm italic border-l border-slate-100 dark:border-robo-700 text-slate-500 dark:text-slate-400">{row.differentiation}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="flex justify-between pt-4">
<button
onClick={goBack}
disabled={state.isLoading}
className="text-sm font-bold text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-white flex items-center gap-2 px-4 py-2"
>
<ArrowLeft size={16}/> {labels.backBtn}
</button>
<button
onClick={handlePhase5Submit}
disabled={state.isLoading}
className="font-bold py-3 px-6 rounded-lg transition-all flex items-center gap-2
bg-slate-900 text-white hover:bg-slate-700
dark:bg-white dark:text-robo-900 dark:hover:bg-slate-200"
>
{state.isLoading ? (
<span className="flex items-center gap-2"><Loader2 className="animate-spin"/> {labels.processing}</span>
) : (
<> {labels.writeCopy} <Terminal size={18} /></>
)}
</button>
</div>
</div>
);
const renderPhase5 = () => (
<div className="space-y-6 animate-in fade-in">
<div className="flex justify-between items-center">
<h2 className="text-2xl font-bold flex items-center gap-2 text-slate-900 dark:text-white">
<Terminal className="text-pink-600 dark:text-pink-400"/> {labels.genAssets}
</h2>
<div className="flex items-center gap-4">
<div className="text-xs font-mono text-slate-500">format: markdown</div>
</div>
</div>
<div className="p-8 rounded-xl border font-mono text-sm leading-relaxed overflow-x-auto shadow-inner transition-colors
bg-slate-50 border-slate-200 text-slate-800
dark:bg-slate-900 dark:border-slate-700 dark:text-slate-300
">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
h1: ({node, ...props}) => <h1 className="text-2xl font-bold mt-6 mb-4 border-b pb-2 text-slate-900 border-slate-300 dark:text-white dark:border-slate-700" {...props} />,
h2: ({node, ...props}) => <h2 className="text-xl font-bold mt-6 mb-3 text-blue-600 dark:text-robo-400" {...props} />,
h3: ({node, ...props}) => <h3 className="text-lg font-bold mt-4 mb-2 text-slate-800 dark:text-slate-200" {...props} />,
p: ({node, ...props}) => <p className="mb-4" {...props} />,
ul: ({node, ...props}) => <ul className="list-disc pl-5 mb-4" {...props} />,
li: ({node, ...props}) => <li className="mb-1" {...props} />,
strong: ({node, ...props}) => <strong className="font-bold text-emerald-600 dark:text-emerald-400" {...props} />,
// Table support
table: ({node, ...props}) => (
<div className="overflow-x-auto my-6 rounded-lg border border-slate-200 dark:border-slate-700 shadow-sm">
<table className="w-full text-left border-collapse text-sm" {...props} />
</div>
),
thead: ({node, ...props}) => (
<thead className="bg-slate-50 dark:bg-robo-800 text-slate-500 dark:text-slate-400 uppercase text-xs font-bold tracking-wider" {...props} />
),
tbody: ({node, ...props}) => (
<tbody className="bg-white dark:bg-robo-900 divide-y divide-slate-200 dark:divide-slate-700" {...props} />
),
tr: ({node, ...props}) => (
<tr className="hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors" {...props} />
),
th: ({node, ...props}) => (
<th className="px-6 py-3 border-b border-slate-200 dark:border-slate-700 whitespace-nowrap" {...props} />
),
td: ({node, ...props}) => (
<td className="px-6 py-4 border-b border-slate-200 dark:border-slate-700 align-top text-slate-700 dark:text-slate-300" {...props} />
),
}}
>
{state.phase5Result || ''}
</ReactMarkdown>
</div>
<div className="flex justify-between pt-4">
<button
onClick={goBack}
disabled={state.isLoading}
className="text-sm font-bold text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-white flex items-center gap-2 px-4 py-2"
>
<ArrowLeft size={16}/> {labels.backBtn}
</button>
<button
onClick={handlePhase6Submit}
disabled={state.isLoading}
className="font-bold py-3 px-6 rounded-lg transition-all flex items-center gap-2
bg-slate-900 text-white hover:bg-slate-700
dark:bg-white dark:text-robo-900 dark:hover:bg-slate-200"
>
{state.isLoading ? (
<span className="flex items-center gap-2"><Loader2 className="animate-spin"/> {labels.processing}</span>
) : (
<> {labels.toPhase6} <ShieldCheck size={18} /></>
)}
</button>
</div>
</div>
);
const renderPhase6 = () => (
<div className="space-y-8 animate-in fade-in">
<div className="flex justify-between items-center">
<h2 className="text-2xl font-bold flex items-center gap-2 text-slate-900 dark:text-white">
<ShieldCheck className="text-emerald-600 dark:text-emerald-400"/> {labels.phase6Title}
</h2>
</div>
<div className="grid grid-cols-1 gap-8">
{/* Battlecards */}
<div className="space-y-4">
<h3 className="text-lg font-bold flex items-center gap-2 text-slate-800 dark:text-white">
<ShieldAlert className="text-red-500"/> {labels.battlecards}
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{state.phase6Result?.battlecards.map((card, i) => (
<div key={i} className="rounded-xl border shadow-sm overflow-hidden flex flex-col
bg-white border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<div className="p-4 bg-slate-100 dark:bg-robo-900 border-b border-slate-200 dark:border-robo-700 flex justify-between items-center">
<span className="font-bold text-slate-700 dark:text-slate-300 flex items-center gap-2">
<UserCircle size={16}/> {card.persona}
</span>
</div>
<div className="p-5 flex-1 flex flex-col gap-4">
<div className="p-3 bg-red-50 dark:bg-red-900/20 border border-red-100 dark:border-red-900/30 rounded-lg text-red-800 dark:text-red-300 text-sm italic">
<span className="font-bold not-italic block mb-1 text-xs uppercase tracking-wide text-red-500">{labels.objection}</span>
"{card.objection}"
</div>
<div className="p-3 bg-emerald-50 dark:bg-emerald-900/20 border border-emerald-100 dark:border-emerald-900/30 rounded-lg text-emerald-800 dark:text-emerald-300 text-sm">
<span className="font-bold block mb-1 text-xs uppercase tracking-wide text-emerald-600 dark:text-emerald-500">{labels.response}</span>
{card.responseScript}
</div>
</div>
</div>
))}
</div>
</div>
{/* Visual Prompts */}
<div className="space-y-4">
<h3 className="text-lg font-bold flex items-center gap-2 text-slate-800 dark:text-white">
<ImageIcon className="text-blue-500"/> {labels.visuals}
</h3>
<div className="space-y-4">
{state.phase6Result?.visualPrompts.map((prompt, i) => (
<div key={i} className="rounded-xl border p-5 transition-colors
bg-slate-50 border-slate-200
dark:bg-robo-900/50 dark:border-robo-700
">
<div className="flex flex-col gap-4">
<div className="flex justify-between items-start">
<div>
<h4 className="font-bold text-slate-800 dark:text-white">{prompt.title}</h4>
<p className="text-xs text-slate-500 dark:text-slate-400 uppercase tracking-wide mt-1">{prompt.context}</p>
</div>
<div className="flex gap-2">
<button
onClick={() => handleGenerateImage(prompt.prompt, i)}
disabled={generatingImages[i]}
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-bold rounded bg-purple-100 dark:bg-purple-900/30 border border-purple-200 dark:border-purple-800 hover:bg-purple-200 dark:hover:bg-purple-900/50 text-purple-700 dark:text-purple-300 transition-colors disabled:opacity-50"
>
{generatingImages[i] ? <Loader2 size={14} className="animate-spin"/> : <Sparkles size={14}/>}
{generatingImages[i] ? labels.generating : labels.genImage}
</button>
<button
onClick={() => copyToClipboard(prompt.prompt, i)}
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-bold rounded bg-white dark:bg-robo-800 border border-slate-200 dark:border-robo-600 hover:border-blue-400 dark:hover:border-robo-500 transition-colors text-slate-600 dark:text-slate-300"
>
{copiedPromptIndex === i ? <Check size={14} className="text-emerald-500"/> : <Copy size={14}/>}
{copiedPromptIndex === i ? labels.copied : labels.copyPrompt}
</button>
</div>
</div>
<textarea
className="w-full h-32 text-xs p-3 rounded bg-white dark:bg-black/30 text-slate-600 dark:text-slate-400 leading-relaxed border border-slate-200 dark:border-robo-800 focus:ring-2 focus:ring-purple-500 outline-none resize-none font-mono"
value={prompt.prompt}
onChange={(e) => handlePromptChange(i, e.target.value)}
/>
</div>
{generatedImages[i] && (
<div className="mt-4 animate-in fade-in slide-in-from-bottom-2 relative group">
{editingIndex === i ? (
<div className="relative border border-blue-400 rounded-lg overflow-hidden bg-slate-100 dark:bg-slate-900">
<div className="absolute top-2 left-2 z-10 flex gap-2 bg-white/90 dark:bg-black/80 p-1.5 rounded-lg shadow-sm backdrop-blur-sm">
<button onClick={() => setBrushColor('#ef4444')} className={`w-6 h-6 rounded-full border-2 ${brushColor === '#ef4444' ? 'border-slate-900 dark:border-white scale-110' : 'border-transparent'}`} style={{backgroundColor: '#ef4444'}} />
<button onClick={() => setBrushColor('#3b82f6')} className={`w-6 h-6 rounded-full border-2 ${brushColor === '#3b82f6' ? 'border-slate-900 dark:border-white scale-110' : 'border-transparent'}`} style={{backgroundColor: '#3b82f6'}} />
<button onClick={() => setBrushColor('#10b981')} className={`w-6 h-6 rounded-full border-2 ${brushColor === '#10b981' ? 'border-slate-900 dark:border-white scale-110' : 'border-transparent'}`} style={{backgroundColor: '#10b981'}} />
<div className="w-px h-6 bg-slate-300 dark:bg-slate-700 mx-1" />
<button onClick={clearCanvas} className="p-1 hover:bg-slate-200 dark:hover:bg-slate-700 rounded text-slate-600 dark:text-slate-300" title={labels.canvasClear}>
<Eraser size={16}/>
</button>
</div>
<canvas
ref={canvasRef}
className="w-full h-auto cursor-crosshair touch-none block"
onMouseDown={startDrawing}
onMouseMove={draw}
onMouseUp={stopDrawing}
onMouseLeave={stopDrawing}
onTouchStart={startDrawing}
onTouchMove={draw}
onTouchEnd={stopDrawing}
/>
<div className="absolute bottom-2 right-2 z-10 flex gap-2">
<button
onClick={() => setEditingIndex(null)}
className="px-3 py-1.5 text-xs font-bold bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200 rounded shadow-md hover:bg-slate-100 dark:hover:bg-slate-700"
>
{labels.cancelEdit}
</button>
<button
onClick={() => handleRegenerateWithSketch(i, prompt.prompt)}
className="px-3 py-1.5 text-xs font-bold bg-blue-600 text-white rounded shadow-md hover:bg-blue-700 flex items-center gap-1.5"
>
<RefreshCw size={14}/> {labels.regenerateSketch}
</button>
</div>
</div>
) : (
<div className="relative">
<img
src={generatedImages[i]}
alt={`Generated concept for ${prompt.title}`}
className="w-full h-auto rounded-lg shadow-md border border-slate-200 dark:border-robo-700 block"
/>
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={() => setEditingIndex(i)}
className="bg-white/90 dark:bg-black/70 hover:bg-white dark:hover:bg-black p-2 rounded-full text-slate-800 dark:text-white shadow-sm backdrop-blur-sm"
title={labels.editImage}
>
<Pencil size={16}/>
</button>
<button
onClick={() => downloadAsset(generatedImages[i], i)}
className="bg-white/90 dark:bg-black/70 hover:bg-white dark:hover:bg-black p-2 rounded-full text-slate-800 dark:text-white shadow-sm backdrop-blur-sm"
title={labels.downloadAsset}
>
<Download size={16}/>
</button>
</div>
<p className="text-xs text-slate-500 mt-2 italic text-center">Generated by Gemini 2.5 Flash Image (Nano Banana)</p>
</div>
)}
</div>
)}
</div>
))}
</div>
</div>
</div>
<div className="p-4 rounded-lg border flex items-center justify-between transition-colors
bg-white border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<div className="flex gap-2">
<button
onClick={goBack}
disabled={state.isLoading}
className="text-sm font-bold text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-white flex items-center gap-2 px-4 py-2"
>
<ArrowLeft size={16}/> {labels.backBtn}
</button>
<span className="text-sm text-slate-600 dark:text-slate-400 self-center border-l pl-2 border-slate-300 dark:border-slate-600">{labels.complete}</span>
</div>
<button
onClick={handlePhase7Submit}
className="font-bold py-3 px-6 rounded-lg transition-all flex items-center gap-2
bg-slate-900 text-white hover:bg-slate-700
dark:bg-white dark:text-robo-900 dark:hover:bg-slate-200"
>
{state.isLoading ? (
<span className="flex items-center gap-2"><Loader2 className="animate-spin"/> {labels.processing}</span>
) : (
<> {labels.toPhase7} <LayoutTemplate size={18} /></>
)}
</button>
</div>
</div>
);
const renderPhase7 = () => (
<div className="space-y-8 animate-in fade-in">
<h2 className="text-2xl font-bold flex items-center gap-2 text-slate-900 dark:text-white">
<LayoutTemplate className="text-purple-600 dark:text-purple-400"/> {labels.phase7Title}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{state.phase7Result?.landingPages.map((lp, i) => (
<div key={i} className="flex flex-col rounded-xl border overflow-hidden shadow-lg transition-colors
bg-white border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<div className="p-6 bg-slate-900 text-white">
<span className="inline-block px-2 py-1 mb-3 text-xs font-bold uppercase tracking-wider bg-purple-500 rounded text-white">
{lp.industry}
</span>
<h3 className="text-2xl font-bold leading-tight">{lp.headline}</h3>
<p className="mt-3 text-purple-100 opacity-90 text-sm">{lp.subline}</p>
</div>
<div className="p-6 flex-1 flex flex-col">
<ul className="space-y-3 mb-6 flex-1">
{lp.bullets.map((bullet, bi) => (
<li key={bi} className="flex items-start gap-3 text-sm text-slate-700 dark:text-slate-300">
<CheckCircle size={16} className="text-green-500 mt-0.5 shrink-0"/>
<span>{bullet}</span>
</li>
))}
</ul>
<button className="w-full py-3 bg-purple-600 hover:bg-purple-700 text-white font-bold rounded-lg transition-colors">
{lp.cta}
</button>
</div>
</div>
))}
</div>
<div className="p-4 rounded-lg border flex items-center justify-between transition-colors
bg-white border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<div className="flex gap-2">
<button
onClick={goBack}
disabled={state.isLoading}
className="text-sm font-bold text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-white flex items-center gap-2 px-4 py-2"
>
<ArrowLeft size={16}/> {labels.backBtn}
</button>
</div>
<button
onClick={handlePhase8Submit}
disabled={state.isLoading}
className="font-bold py-3 px-6 rounded-lg transition-all flex items-center gap-2
bg-slate-900 text-white hover:bg-slate-700
dark:bg-white dark:text-robo-900 dark:hover:bg-slate-200"
>
{state.isLoading ? (
<span className="flex items-center gap-2"><Loader2 className="animate-spin"/> {labels.processing}</span>
) : (
<> {labels.toPhase8} <TrendingUp size={18} /></>
)}
</button>
</div>
</div>
);
const renderPhase8 = () => (
<div className="space-y-8 animate-in fade-in">
<h2 className="text-2xl font-bold flex items-center gap-2 text-slate-900 dark:text-white">
<TrendingUp className="text-green-600 dark:text-green-400"/> {labels.phase8Title}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{state.phase8Result?.businessCases.map((bc, i) => (
<div key={i} className="rounded-xl border p-6 transition-colors flex flex-col gap-4
bg-white border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<div className="flex items-center gap-3 pb-4 border-b border-slate-100 dark:border-robo-700">
<div className="p-2 bg-green-100 dark:bg-green-900/30 text-green-600 dark:text-green-400 rounded-lg">
<Building2 size={20}/>
</div>
<h3 className="font-bold text-lg text-slate-800 dark:text-white">{bc.industry}</h3>
</div>
<div className="space-y-4">
<div>
<span className="text-xs font-bold uppercase tracking-wider text-red-500">Cost Driver (Pain)</span>
<p className="mt-1 text-sm text-slate-700 dark:text-slate-300 font-medium">{bc.costDriver}</p>
</div>
<div>
<span className="text-xs font-bold uppercase tracking-wider text-green-500">Efficiency Gain (Gain)</span>
<p className="mt-1 text-sm text-slate-700 dark:text-slate-300 font-medium">{bc.efficiencyGain}</p>
</div>
<div className="p-3 bg-slate-50 dark:bg-robo-900 rounded-lg border border-slate-100 dark:border-robo-700">
<span className="text-xs font-bold uppercase tracking-wider text-slate-500 dark:text-slate-400 flex items-center gap-1.5 mb-1">
<ShieldCheck size={12}/> Risk Argument
</span>
<p className="text-sm text-slate-600 dark:text-slate-400 italic">"{bc.riskArgument}"</p>
</div>
</div>
</div>
))}
</div>
<div className="p-4 rounded-lg border flex items-center justify-between transition-colors
bg-white border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<div className="flex gap-2">
<button
onClick={goBack}
disabled={state.isLoading}
className="text-sm font-bold text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-white flex items-center gap-2 px-4 py-2"
>
<ArrowLeft size={16}/> {labels.backBtn}
</button>
</div>
<button
onClick={handlePhase9Submit}
disabled={state.isLoading}
className="font-bold py-3 px-6 rounded-lg transition-all flex items-center gap-2
bg-slate-900 text-white hover:bg-slate-700
dark:bg-white dark:text-robo-900 dark:hover:bg-slate-200"
>
{state.isLoading ? (
<span className="flex items-center gap-2"><Loader2 className="animate-spin"/> {labels.processing}</span>
) : (
<> {labels.toPhase9} <Shield size={18} /></>
)}
</button>
</div>
</div>
);
const renderPhase9 = () => (
<div className="space-y-8 animate-in fade-in">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
<h2 className="text-2xl font-bold flex items-center gap-2 text-slate-900 dark:text-white">
<Shield className="text-blue-600 dark:text-blue-400"/> {labels.phase9Title}
</h2>
<div className="flex flex-wrap gap-2">
{language === 'de' && (
<>
<button
onClick={handleTranslateReport}
disabled={isTranslating}
className="flex items-center gap-2 px-3 py-1.5 text-sm font-bold rounded-lg transition-all bg-white border border-slate-300 text-slate-700 hover:bg-slate-50 dark:bg-robo-800 dark:border-robo-600 dark:text-slate-300 dark:hover:bg-robo-700"
>
{isTranslating ? (
<><Loader2 size={14} className="animate-spin"/> {labels.translating}</>
) : (
<><Languages size={14}/> {labels.translateBtn}</>
)}
</button>
{state.translatedReport && (
<button
onClick={() => downloadReport(state.translatedReport, "EN-")}
className="flex items-center gap-2 px-3 py-1.5 text-sm font-bold rounded-lg transition-all
bg-emerald-600 text-white hover:bg-emerald-700
dark:bg-emerald-600 dark:hover:bg-emerald-500"
>
<Download size={14}/> {labels.downloadEn}
</button>
)}
</>
)}
<button
onClick={() => downloadReport()}
className="flex items-center gap-2 px-3 py-1.5 text-sm font-bold rounded-lg transition-all
bg-slate-900 text-white hover:bg-slate-700
dark:bg-white dark:text-robo-900 dark:hover:bg-slate-200"
>
<Download size={14}/> {labels.download}
</button>
</div>
</div>
<div className="overflow-x-auto rounded-xl border border-slate-200 dark:border-robo-700">
<table className="w-full text-left border-collapse">
<thead>
<tr className="bg-slate-100 dark:bg-robo-900 text-xs uppercase tracking-wider text-slate-500 dark:text-slate-400 font-bold">
<th className="p-4 border-b border-slate-200 dark:border-robo-700 w-1/4">Tech Feature</th>
<th className="p-4 border-b border-slate-200 dark:border-robo-700 w-1/2">The Story (Benefit)</th>
<th className="p-4 border-b border-slate-200 dark:border-robo-700 w-1/4">Headline</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-200 dark:divide-robo-700 bg-white dark:bg-robo-800">
{state.phase9Result?.techTranslations.map((item, i) => (
<tr key={i} className="hover:bg-slate-50 dark:hover:bg-robo-700/50 transition-colors">
<td className="p-4 font-mono text-sm text-slate-600 dark:text-slate-300 border-r border-slate-100 dark:border-robo-700/50 align-top">
{item.feature}
</td>
<td className="p-4 text-sm text-slate-700 dark:text-slate-300 leading-relaxed align-top">
{item.story}
</td>
<td className="p-4 align-top">
<div className="inline-block px-3 py-1.5 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-900/30 text-blue-800 dark:text-blue-300 font-bold text-sm">
{item.headline}
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="p-4 rounded-lg border flex items-center justify-between transition-colors
bg-white border-slate-200
dark:bg-robo-800 dark:border-robo-700
">
<div className="flex gap-2">
<button
onClick={goBack}
disabled={state.isLoading}
className="text-sm font-bold text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-white flex items-center gap-2 px-4 py-2"
>
<ArrowLeft size={16}/> {labels.backBtn}
</button>
<span className="text-sm text-slate-600 dark:text-slate-400 self-center border-l pl-2 border-slate-300 dark:border-slate-600">{labels.complete}</span>
</div>
<button
onClick={() => window.location.reload()}
className="text-sm font-medium hover:underline text-slate-500 hover:text-slate-800 dark:text-slate-300 dark:hover:text-white"
>
{labels.restart}
</button>
</div>
</div>
);
return (
<Layout
currentPhase={state.currentPhase}
maxAllowedPhase={getMaxAllowedPhase()}
onPhaseSelect={handlePhaseSelect}
theme={theme}
toggleTheme={toggleTheme}
language={language}
setLanguage={setLanguage}
labels={labels}
>
{error && (
<div className="mb-6 p-4 rounded-lg flex items-center gap-3 transition-colors
bg-red-50 border border-red-200 text-red-700
dark:bg-red-500/10 dark:border-red-500/50 dark:text-red-200
">
<ShieldAlert size={20} />
{error}
</div>
)}
{state.currentPhase === Phase.Input && renderInputPhase()}
{state.currentPhase === Phase.ProductAnalysis && renderPhase1()}
{state.currentPhase === Phase.ICPDiscovery && renderPhase2()}
{state.currentPhase === Phase.WhaleHunting && renderPhase3()}
{state.currentPhase === Phase.Strategy && renderPhase4()}
{state.currentPhase === Phase.AssetGeneration && renderPhase5()}
{state.currentPhase === Phase.SalesEnablement && renderPhase6()}
{state.currentPhase === Phase.LandingPage && renderPhase7()}
{state.currentPhase === Phase.BusinessCase && renderPhase8()}
{state.currentPhase === Phase.TechTranslator && renderPhase9()}
</Layout>
);
};
export default App;