didi-lot3-frontend/web/src/components/AnalysisResults/AnalysisResults.tsx
Top Clossers cec967f953 Livrare Lot 3 (Frontend): aplicație web, aplicație mobilă Android, extensie browser
- Surse complete web (React/Vite) + mobil (React Native/Expo) + extensie (MV3)
- Documentație de livrare: ghid utilizare, matrice trasabilitate cerințe, raport testare furnizor
- Artefacte binare: imagine Docker didi-frontend:lot3-1.0, APK, extensie v3.2.6 + SHA256SUMS
- Configurare adresă platformă externalizată (build args / .env / config.js)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 12:40:40 +03:00

2173 lines
65 KiB
TypeScript

import React from 'react';
import styled from '@emotion/styled';
import { colors } from '../../theme';
import {
getVerdictColor,
getHarmColor,
getConfidenceColor,
getReliabilityColor,
getStanceColor,
getClaimVerdictColor,
getContentUrgencyColor,
} from '../../utils/colors';
// Typography constants (matching theme)
const fontSize = {
small: '14px',
medium: '16px',
large: '18px',
xlarge: '20px',
};
const fontWeight = {
medium: 500,
semiBold: 600,
bold: 700,
};
// Aggregator-specific types (Universal Analyzer v3)
interface SignalBreakdown {
signal_name: string;
raw_value?: any;
transformed_value: number;
weight: number;
weighted_contribution: number;
is_default?: boolean;
default_reason?: string;
}
interface AnalysisQuality {
signals_computed: number;
signals_defaulted: number;
critical_signals_defaulted?: string[];
reliability: 'HIGH' | 'MEDIUM' | 'LOW';
reliability_reason: string;
}
interface AggregatorOutput {
score: number;
category: string;
category_description: string;
breakdown?: SignalBreakdown[];
signals_used: number;
signals_missing: number;
harm_level?: string;
analysis_quality?: AnalysisQuality;
}
// ==================== V3 EXTENDED TYPES ====================
// Individual source evaluation from source_evaluation node
interface SourceEvaluationItem {
url: string;
base_tier: number;
effective_tier: number;
stance: 'supports' | 'contradicts' | 'neutral' | 'unrelated';
stance_strength: number;
domain_authority?: 'expert' | 'general' | 'unknown';
recency_relevance?: 'current' | 'dated' | 'historical';
circular_sourcing_detected?: boolean;
circular_chain?: string[];
is_tier5_primary?: boolean;
tier5_primary_type?: string;
tier_reasoning?: string;
primary_source_linked?: boolean;
}
// Aggregated stance metrics
interface StanceSummary {
supporting_sources: number;
contradicting_sources: number;
neutral_sources: number;
unrelated_sources?: number;
weighted_stance_score: number;
has_relevant_evidence: boolean;
}
// Source evaluation output from source_evaluation node
interface SourceEvaluationOutput {
source_evaluations: SourceEvaluationItem[];
stance_summary: StanceSummary;
}
// Individual claim verdict from verdict node
interface ClaimVerdict {
claim_id?: number;
claim_text: string;
verdict: string;
verdict_score: number;
verdict_explanation: string;
evidence_used?: string[];
evidence_against?: string[];
epistemic_assessment?: {
evidence_basis?: string;
claim_nature?: string;
source_attribution?: string;
temporal_status?: string;
recommended_action?: string;
assessment_rationale?: string;
};
}
// Verdict output from verdict node
interface VerdictOutput {
claim_verdicts: ClaimVerdict[];
overall_assessment?: {
epistemic_assessment?: {
evidence_basis?: string;
temporal_status?: string;
};
};
manipulation_indicators?: string[];
}
// Triage output from triage node
interface TriageOutput {
content_type: 'news_article' | 'social_post' | 'image_text' | 'video' | 'audio' | 'mixed';
urgency: 'breaking' | 'viral' | 'routine' | 'historical';
primary_claims?: string[];
manipulation_detected?: boolean;
manipulation_indicators?: string[];
recommended_pipeline?: string;
}
// Claim extraction output
interface ClaimExtractionOutput {
final_claims?: Array<{
claim_text?: string;
claim_type?: string;
ambiguity_flags?: string[];
}>;
extraction_quality?: 'high' | 'medium' | 'low';
}
interface AnalysisData {
verdict: string;
confidence: number;
explanation: string;
harm_level?: string;
primary_category?: string;
aggregator?: AggregatorOutput; // v3 aggregator data for enhanced display
// NEW: v3 extended pipeline outputs
source_evaluation?: SourceEvaluationOutput; // Sources evaluated with stance analysis
verdict_data?: VerdictOutput; // Per-claim verdicts with epistemic assessment
triage?: TriageOutput; // Content type, urgency, manipulation detection
claim_extraction?: ClaimExtractionOutput; // Extracted claims with metadata
sub_categories?: string[];
key_claims?: Array<{
claim: string;
claim_type?: string;
verified?: string | boolean;
verification_method?: string;
sources?: string[];
harm_potential?: string;
notes?: string;
}>;
manipulation_techniques?: string[];
emotional_triggers?: {
detected: boolean;
types: string[];
severity: string;
};
media_authenticity?: {
type: string;
assessment: string;
manipulation_type?: string;
confidence?: number;
};
source_credibility?: string | {
assessment: string;
indicators?: string[];
trust_score?: number;
};
verification?: {
web_search_performed: boolean;
fact_check_sites_consulted?: string[];
verification_urls?: string[];
limitations?: string[];
};
viral_risk?: {
score: number;
level: string;
factors?: string[];
assessment?: string;
};
recommendations?: {
action: string;
urgency: string;
reason?: string;
suggested_interventions?: string[];
};
detailed_analysis?: {
content_type?: string;
tone?: string;
writing_quality?: string;
context_analysis?: string;
similar_claims?: string;
};
metadata?: {
pipeline_type?: string;
language_detected?: string;
};
confidence_factors?: string[];
temporal_context?: string;
transcription?: string;
}
interface AnalysisResultsProps {
data: AnalysisData;
}
export const AnalysisResults: React.FC<AnalysisResultsProps> = ({ data }) => {
const [showTranscription, setShowTranscription] = React.useState(false);
const [showDetailedAnalysis, setShowDetailedAnalysis] = React.useState(false);
const [showSourcesEvaluated, setShowSourcesEvaluated] = React.useState(false);
const [showClaimVerdicts, setShowClaimVerdicts] = React.useState(false);
// Helper for tier labels (not a color function)
const getTierLabel = (tier: number): string => {
switch (tier) {
case 1: return 'Primary Source';
case 2: return 'Major Wire';
case 3: return 'Quality News';
case 4: return 'Secondary';
case 5: return 'Social/Blog';
default: return `Tier ${tier}`;
}
};
// Check if we have aggregator data for enhanced display
const hasAggregator = data.aggregator && 'score' in data.aggregator;
const [showSignalBreakdown, setShowSignalBreakdown] = React.useState(false);
// Check if we have v3 extended data
const hasSourceEvaluation = data.source_evaluation && data.source_evaluation.source_evaluations?.length > 0;
const hasClaimVerdicts = data.verdict_data && data.verdict_data.claim_verdicts?.length > 0;
const hasTriage = data.triage && data.triage.content_type;
return (
<Container>
{/* Header Section - Verdict & Confidence */}
<HeaderSection>
<VerdictCard color={getVerdictColor(data.verdict)}>
<VerdictLabel>Verdict</VerdictLabel>
<VerdictValue>{data.verdict.replace(/_/g, ' ').toUpperCase()}</VerdictValue>
</VerdictCard>
<ConfidenceCard>
<ConfidenceLabel>Confidence</ConfidenceLabel>
<ConfidenceValue>{(data.confidence * 100).toFixed(0)}%</ConfidenceValue>
<ConfidenceBar>
<ConfidenceFill
width={(data.confidence * 100)}
color={getConfidenceColor(data.confidence)}
/>
</ConfidenceBar>
</ConfidenceCard>
{data.harm_level && (
<HarmCard color={getHarmColor(data.harm_level)}>
<HarmLabel>Harm Level</HarmLabel>
<HarmValue>{data.harm_level?.toUpperCase()}</HarmValue>
</HarmCard>
)}
</HeaderSection>
{/* Explanation */}
<ExplanationSection>
<SectionTitle>Explanation</SectionTitle>
<ExplanationText>{data.explanation}</ExplanationText>
</ExplanationSection>
{/* Category & Type */}
{(data.primary_category || data.metadata?.pipeline_type) && (
<CategorySection>
{data.primary_category && (
<CategoryBadge>{data.primary_category.replace(/_/g, ' ')}</CategoryBadge>
)}
{data.metadata?.pipeline_type && (
<TypeBadge>{data.metadata.pipeline_type}</TypeBadge>
)}
{data.metadata?.language_detected && (
<LangBadge>{data.metadata.language_detected.toUpperCase()}</LangBadge>
)}
</CategorySection>
)}
{/* Triage Badges (v3) - Content Type, Urgency, Manipulation Status */}
{hasTriage && (
<TriageSection>
<TriageBadge color={colors.deepBlue}>
{data.triage!.content_type.replace(/_/g, ' ')}
</TriageBadge>
<TriageBadge color={getContentUrgencyColor(data.triage!.urgency)}>
{data.triage!.urgency}
</TriageBadge>
{data.triage!.manipulation_detected !== undefined && (
<TriageBadge color={data.triage!.manipulation_detected ? colors.cautionRed : colors.truthGreen}>
{data.triage!.manipulation_detected ? 'Manipulation Detected' : 'No Manipulation'}
</TriageBadge>
)}
</TriageSection>
)}
{/* Analysis Quality Indicator (v3 Aggregator) */}
{hasAggregator && data.aggregator?.analysis_quality && (
<QualitySection reliability={data.aggregator.analysis_quality.reliability}>
<SectionTitle>Analysis Quality</SectionTitle>
<QualityHeader>
<ReliabilityBadge color={getReliabilityColor(data.aggregator.analysis_quality.reliability)}>
{data.aggregator.analysis_quality.reliability} Reliability
</ReliabilityBadge>
<SignalStats>
<StatItem>
<StatLabel>Signals Computed</StatLabel>
<StatValue>{data.aggregator.analysis_quality.signals_computed}</StatValue>
</StatItem>
<StatItem>
<StatLabel>Signals Defaulted</StatLabel>
<StatValue warning={data.aggregator.analysis_quality.signals_defaulted > 0}>
{data.aggregator.analysis_quality.signals_defaulted}
</StatValue>
</StatItem>
</SignalStats>
</QualityHeader>
<QualityReason>{data.aggregator.analysis_quality.reliability_reason}</QualityReason>
{data.aggregator.analysis_quality.critical_signals_defaulted &&
data.aggregator.analysis_quality.critical_signals_defaulted.length > 0 && (
<CriticalWarning>
Critical signals used defaults: {data.aggregator.analysis_quality.critical_signals_defaulted.join(', ')}
</CriticalWarning>
)}
</QualitySection>
)}
{/* Sources Evaluated Section (v3) - Collapsible */}
{hasSourceEvaluation && (
<SourcesEvaluatedSection>
<ToggleHeader onClick={() => setShowSourcesEvaluated(!showSourcesEvaluated)}>
<SectionTitle>Sources Evaluated ({data.source_evaluation!.source_evaluations.length} sources)</SectionTitle>
<ToggleIcon>{showSourcesEvaluated ? '▼' : '▶'}</ToggleIcon>
</ToggleHeader>
{showSourcesEvaluated && (
<SourcesEvaluatedContent>
{data.source_evaluation!.source_evaluations.map((source, idx) => (
<SourceCard key={idx} stance={source.stance}>
<SourceUrlRow>
<SourceUrl href={source.url} target="_blank" rel="noopener noreferrer">
{source.url.length > 60 ? source.url.substring(0, 60) + '...' : source.url}
</SourceUrl>
</SourceUrlRow>
<SourceMetaRow>
<TierBadge tier={source.effective_tier}>
Tier {source.effective_tier} ({getTierLabel(source.effective_tier)})
</TierBadge>
<StanceBadge color={getStanceColor(source.stance)}>
{source.stance} ({(source.stance_strength * 100).toFixed(0)}%)
</StanceBadge>
{source.domain_authority && source.domain_authority !== 'unknown' && (
<AuthorityBadge>{source.domain_authority}</AuthorityBadge>
)}
{source.recency_relevance && (
<RecencyBadge recency={source.recency_relevance}>{source.recency_relevance}</RecencyBadge>
)}
</SourceMetaRow>
{source.circular_sourcing_detected && (
<CircularWarning>
Circular sourcing detected
{source.circular_chain && source.circular_chain.length > 0 && (
<CircularChain>Chain: {source.circular_chain.join(' → ')}</CircularChain>
)}
</CircularWarning>
)}
{source.is_tier5_primary && source.tier5_primary_type && (
<PrimarySourceNote>
Social media IS the primary source ({source.tier5_primary_type})
</PrimarySourceNote>
)}
{source.tier_reasoning && (
<TierReasoning>{source.tier_reasoning}</TierReasoning>
)}
</SourceCard>
))}
</SourcesEvaluatedContent>
)}
</SourcesEvaluatedSection>
)}
{/* Stance Summary (v3) - Visual Bar */}
{hasSourceEvaluation && data.source_evaluation!.stance_summary && (
<StanceSummarySection>
<SectionTitle>Evidence Stance Summary</SectionTitle>
<StanceBars>
{data.source_evaluation!.stance_summary.supporting_sources > 0 && (
<StanceBarRow>
<StanceBarLabel>Supporting</StanceBarLabel>
<StanceBarContainer>
<StanceBarFill
width={(data.source_evaluation!.stance_summary.supporting_sources / data.source_evaluation!.source_evaluations.length) * 100}
color={colors.truthGreen}
/>
</StanceBarContainer>
<StanceBarCount>{data.source_evaluation!.stance_summary.supporting_sources} sources</StanceBarCount>
</StanceBarRow>
)}
{data.source_evaluation!.stance_summary.contradicting_sources > 0 && (
<StanceBarRow>
<StanceBarLabel>Contradicting</StanceBarLabel>
<StanceBarContainer>
<StanceBarFill
width={(data.source_evaluation!.stance_summary.contradicting_sources / data.source_evaluation!.source_evaluations.length) * 100}
color={colors.cautionRed}
/>
</StanceBarContainer>
<StanceBarCount>{data.source_evaluation!.stance_summary.contradicting_sources} sources</StanceBarCount>
</StanceBarRow>
)}
{data.source_evaluation!.stance_summary.neutral_sources > 0 && (
<StanceBarRow>
<StanceBarLabel>Neutral</StanceBarLabel>
<StanceBarContainer>
<StanceBarFill
width={(data.source_evaluation!.stance_summary.neutral_sources / data.source_evaluation!.source_evaluations.length) * 100}
color="#FFD700"
/>
</StanceBarContainer>
<StanceBarCount>{data.source_evaluation!.stance_summary.neutral_sources} sources</StanceBarCount>
</StanceBarRow>
)}
{(data.source_evaluation!.stance_summary.unrelated_sources ?? 0) > 0 && (
<StanceBarRow>
<StanceBarLabel>Unrelated</StanceBarLabel>
<StanceBarContainer>
<StanceBarFill
width={((data.source_evaluation!.stance_summary.unrelated_sources ?? 0) / data.source_evaluation!.source_evaluations.length) * 100}
color={colors.steelGray}
/>
</StanceBarContainer>
<StanceBarCount>{data.source_evaluation!.stance_summary.unrelated_sources} sources</StanceBarCount>
</StanceBarRow>
)}
</StanceBars>
<WeightedStanceRow>
<WeightedStanceLabel>Weighted Stance Score:</WeightedStanceLabel>
<WeightedStanceValue score={data.source_evaluation!.stance_summary.weighted_stance_score}>
{data.source_evaluation!.stance_summary.weighted_stance_score > 0 ? '+' : ''}
{data.source_evaluation!.stance_summary.weighted_stance_score.toFixed(2)}
<WeightedStanceInterpretation>
{data.source_evaluation!.stance_summary.weighted_stance_score > 0.5 ? ' (Strongly Supporting)' :
data.source_evaluation!.stance_summary.weighted_stance_score > 0.2 ? ' (Leans Supporting)' :
data.source_evaluation!.stance_summary.weighted_stance_score > -0.2 ? ' (Neutral/Mixed)' :
data.source_evaluation!.stance_summary.weighted_stance_score > -0.5 ? ' (Leans Contradicting)' :
' (Strongly Contradicting)'}
</WeightedStanceInterpretation>
</WeightedStanceValue>
</WeightedStanceRow>
{!data.source_evaluation!.stance_summary.has_relevant_evidence && (
<NoRelevantEvidenceWarning>
No relevant evidence found - claim may be unverifiable
</NoRelevantEvidenceWarning>
)}
</StanceSummarySection>
)}
{/* Signal Breakdown (v3 Aggregator) - Collapsible */}
{hasAggregator && data.aggregator?.breakdown && data.aggregator.breakdown.length > 0 && (
<SignalBreakdownSection>
<ToggleHeader onClick={() => setShowSignalBreakdown(!showSignalBreakdown)}>
<SectionTitle>Signal Breakdown ({data.aggregator.breakdown.length} signals)</SectionTitle>
<ToggleIcon>{showSignalBreakdown ? '▼' : '▶'}</ToggleIcon>
</ToggleHeader>
{showSignalBreakdown && (
<SignalBreakdownContent>
{data.aggregator.breakdown.map((signal, idx) => (
<SignalCard key={idx} isDefault={signal.is_default || false}>
<SignalHeader>
<SignalName>{signal.signal_name.replace(/_/g, ' ')}</SignalName>
<SignalWeight>Weight: {(signal.weight * 100).toFixed(0)}%</SignalWeight>
{signal.is_default && (
<DefaultBadge>Using Default</DefaultBadge>
)}
</SignalHeader>
<SignalBarContainer>
<SignalBar>
<SignalBarFill
width={signal.transformed_value * 100}
color={getConfidenceColor(signal.transformed_value)}
/>
</SignalBar>
<SignalScore>{(signal.transformed_value * 100).toFixed(0)}%</SignalScore>
</SignalBarContainer>
<SignalContribution>
Contribution to score: {(signal.weighted_contribution * 100).toFixed(1)}%
</SignalContribution>
{signal.default_reason && (
<DefaultReasonText>{signal.default_reason}</DefaultReasonText>
)}
</SignalCard>
))}
<TotalScoreCard>
<TotalLabel>Aggregated Score</TotalLabel>
<TotalScore color={getConfidenceColor(data.aggregator.score)}>
{(data.aggregator.score * 100).toFixed(0)}%
</TotalScore>
</TotalScoreCard>
</SignalBreakdownContent>
)}
</SignalBreakdownSection>
)}
{/* Per-Claim Verdicts (v3) - Enhanced claims with individual verdicts */}
{hasClaimVerdicts && (
<ClaimVerdictsSection>
<ToggleHeader onClick={() => setShowClaimVerdicts(!showClaimVerdicts)}>
<SectionTitle>Claim Analysis ({data.verdict_data!.claim_verdicts.length} claims)</SectionTitle>
<ToggleIcon>{showClaimVerdicts ? '▼' : '▶'}</ToggleIcon>
</ToggleHeader>
{showClaimVerdicts && (
<ClaimVerdictsContent>
{data.verdict_data!.claim_verdicts.map((claim, idx) => (
<ClaimVerdictCard key={idx}>
<ClaimVerdictHeader>
<ClaimVerdictNumber>Claim #{idx + 1}</ClaimVerdictNumber>
<ClaimVerdictBadge color={getClaimVerdictColor(claim.verdict)}>
{claim.verdict.replace(/_/g, ' ')}
</ClaimVerdictBadge>
<ClaimVerdictScore>
{(claim.verdict_score * 100).toFixed(0)}% confidence
</ClaimVerdictScore>
</ClaimVerdictHeader>
<ClaimVerdictText>"{claim.claim_text}"</ClaimVerdictText>
<ClaimVerdictExplanation>{claim.verdict_explanation}</ClaimVerdictExplanation>
{/* Evidence For */}
{claim.evidence_used && claim.evidence_used.length > 0 && (
<EvidenceSection>
<EvidenceLabel>Evidence For ({claim.evidence_used.length}):</EvidenceLabel>
<EvidenceList>
{claim.evidence_used.map((source, sidx) => (
<EvidenceItem key={sidx} type="for">
{source.startsWith('http') ? (
<EvidenceLink href={source} target="_blank" rel="noopener noreferrer">
{source.length > 50 ? source.substring(0, 50) + '...' : source}
</EvidenceLink>
) : (
source
)}
</EvidenceItem>
))}
</EvidenceList>
</EvidenceSection>
)}
{/* Evidence Against */}
{claim.evidence_against && claim.evidence_against.length > 0 && (
<EvidenceSection>
<EvidenceLabel>Evidence Against ({claim.evidence_against.length}):</EvidenceLabel>
<EvidenceList>
{claim.evidence_against.map((source, sidx) => (
<EvidenceItem key={sidx} type="against">
{source.startsWith('http') ? (
<EvidenceLink href={source} target="_blank" rel="noopener noreferrer">
{source.length > 50 ? source.substring(0, 50) + '...' : source}
</EvidenceLink>
) : (
source
)}
</EvidenceItem>
))}
</EvidenceList>
</EvidenceSection>
)}
{/* Epistemic Assessment */}
{claim.epistemic_assessment && (
<EpistemicSection>
<EpistemicRow>
{claim.epistemic_assessment.evidence_basis && (
<EpistemicBadge type="evidence">
{claim.epistemic_assessment.evidence_basis.replace(/_/g, ' ')}
</EpistemicBadge>
)}
{claim.epistemic_assessment.claim_nature && (
<EpistemicBadge type="nature">
{claim.epistemic_assessment.claim_nature.replace(/_/g, ' ')}
</EpistemicBadge>
)}
{claim.epistemic_assessment.temporal_status && (
<EpistemicBadge type="temporal">
{claim.epistemic_assessment.temporal_status.replace(/_/g, ' ')}
</EpistemicBadge>
)}
</EpistemicRow>
{claim.epistemic_assessment.recommended_action && (
<RecommendedActionRow>
<RecommendedActionLabel>Recommended:</RecommendedActionLabel>
<RecommendedActionValue>
{claim.epistemic_assessment.recommended_action.replace(/_/g, ' ')}
</RecommendedActionValue>
</RecommendedActionRow>
)}
</EpistemicSection>
)}
</ClaimVerdictCard>
))}
</ClaimVerdictsContent>
)}
</ClaimVerdictsSection>
)}
{/* Key Claims */}
{data.key_claims && data.key_claims.length > 0 && (
<ClaimsSection>
<SectionTitle>Key Claims ({data.key_claims.length})</SectionTitle>
<ClaimsList>
{data.key_claims.map((claim, idx) => (
<ClaimCard key={idx}>
<ClaimHeader>
<ClaimNumber>#{idx + 1}</ClaimNumber>
{claim.verified !== undefined && claim.verified !== 'unknown' && (
<VerifiedBadge verified={claim.verified === true || claim.verified === 'true'}>
{claim.verified === true || claim.verified === 'true' ? 'Verified' : 'False'}
</VerifiedBadge>
)}
{claim.harm_potential && claim.harm_potential !== 'none' && (
<HarmBadge level={claim.harm_potential}>
{claim.harm_potential}
</HarmBadge>
)}
</ClaimHeader>
<ClaimText>{claim.claim}</ClaimText>
{claim.sources && claim.sources.length > 0 && (
<SourcesList>
<SourcesLabel>Sources:</SourcesLabel>
{claim.sources.map((source, sidx) => (
<SourceLink key={sidx} href={source} target="_blank" rel="noopener noreferrer">
{source}
</SourceLink>
))}
</SourcesList>
)}
{claim.notes && <ClaimNotes>{claim.notes}</ClaimNotes>}
</ClaimCard>
))}
</ClaimsList>
</ClaimsSection>
)}
{/* Manipulation Techniques */}
{data.manipulation_techniques && data.manipulation_techniques.length > 0 && (
<ManipulationSection>
<SectionTitle>Manipulation Techniques Detected</SectionTitle>
<TechniquesList>
{data.manipulation_techniques.map((tech, idx) => (
<TechniqueTag key={idx}>{tech.replace(/_/g, ' ')}</TechniqueTag>
))}
</TechniquesList>
</ManipulationSection>
)}
{/* Emotional Triggers */}
{data.emotional_triggers?.detected && (
<EmotionalSection severity={data.emotional_triggers.severity}>
<SectionTitle>Emotional Triggers</SectionTitle>
<EmotionalContent>
<SeverityBadge severity={data.emotional_triggers.severity}>
{data.emotional_triggers.severity?.toUpperCase()} severity
</SeverityBadge>
<TriggersList>
{data.emotional_triggers.types.map((trigger, idx) => (
<TriggerTag key={idx}>{trigger}</TriggerTag>
))}
</TriggersList>
</EmotionalContent>
</EmotionalSection>
)}
{/* Media Authenticity */}
{data.media_authenticity && data.media_authenticity.type !== 'text' && (
<MediaSection>
<SectionTitle>Media Authenticity</SectionTitle>
<MediaGrid>
<MediaItem>
<MediaLabel>Type:</MediaLabel>
<MediaValue>{data.media_authenticity.type?.toUpperCase()}</MediaValue>
</MediaItem>
<MediaItem>
<MediaLabel>Assessment:</MediaLabel>
<AssessmentBadge assessment={data.media_authenticity.assessment}>
{data.media_authenticity.assessment}
</AssessmentBadge>
</MediaItem>
{data.media_authenticity.manipulation_type && data.media_authenticity.manipulation_type !== 'none' && (
<MediaItem>
<MediaLabel>Manipulation:</MediaLabel>
<MediaValue>{data.media_authenticity.manipulation_type}</MediaValue>
</MediaItem>
)}
</MediaGrid>
</MediaSection>
)}
{/* Verification */}
{data.verification && (
<VerificationSection performed={data.verification.web_search_performed}>
<SectionTitle>
{data.verification.web_search_performed ? 'Web Verification Performed' : 'Verification Status'}
</SectionTitle>
{data.verification.web_search_performed ? (
<>
{data.verification.fact_check_sites_consulted && data.verification.fact_check_sites_consulted.length > 0 && (
<VerifItem>
<VerifLabel>Fact-Check Sites:</VerifLabel>
<SitesList>
{data.verification.fact_check_sites_consulted.map((site, idx) => (
<SiteTag key={idx}>{site}</SiteTag>
))}
</SitesList>
</VerifItem>
)}
{data.verification.verification_urls && data.verification.verification_urls.length > 0 && (
<VerifItem>
<VerifLabel>Verification URLs:</VerifLabel>
<UrlsList>
{data.verification.verification_urls.map((url, idx) => (
<UrlLink key={idx} href={url} target="_blank" rel="noopener noreferrer">
{url}
</UrlLink>
))}
</UrlsList>
</VerifItem>
)}
</>
) : (
<LimitationText>
{data.verification.limitations && data.verification.limitations.length > 0
? data.verification.limitations.join(', ')
: 'Basic analysis without web verification'}
</LimitationText>
)}
</VerificationSection>
)}
{/* Viral Risk */}
{data.viral_risk && (
<ViralSection level={data.viral_risk.level}>
<SectionTitle>Viral Risk Assessment</SectionTitle>
<ViralGrid>
<ViralItem>
<ViralLabel>Risk Level:</ViralLabel>
<RiskBadge level={data.viral_risk.level}>
{data.viral_risk.level?.toUpperCase()}
</RiskBadge>
</ViralItem>
<ViralItem>
<ViralLabel>Score:</ViralLabel>
<ScoreValue>{(data.viral_risk.score * 100).toFixed(0)}%</ScoreValue>
</ViralItem>
</ViralGrid>
{data.viral_risk.factors && data.viral_risk.factors.length > 0 && (
<FactorsList>
{data.viral_risk.factors.map((factor, idx) => (
<FactorTag key={idx}>{factor.replace(/_/g, ' ')}</FactorTag>
))}
</FactorsList>
)}
{data.viral_risk.assessment && (
<AssessmentText>{data.viral_risk.assessment}</AssessmentText>
)}
</ViralSection>
)}
{/* Recommendations */}
{data.recommendations && (
<RecommendationsSection urgency={data.recommendations.urgency}>
<SectionTitle>Recommendations</SectionTitle>
<RecommendationGrid>
<RecommendItem>
<RecommendLabel>Action:</RecommendLabel>
<ActionBadge action={data.recommendations.action}>
{data.recommendations.action}
</ActionBadge>
</RecommendItem>
<RecommendItem>
<RecommendLabel>Urgency:</RecommendLabel>
<UrgencyBadge level={data.recommendations.urgency}>
{data.recommendations.urgency?.toUpperCase()}
</UrgencyBadge>
</RecommendItem>
</RecommendationGrid>
{data.recommendations.reason && (
<ReasonText>{data.recommendations.reason}</ReasonText>
)}
</RecommendationsSection>
)}
{/* Source Credibility */}
{data.source_credibility && (
<SourceCredibilitySection>
<SectionTitle>Source Credibility</SectionTitle>
<CredibilityText>
{typeof data.source_credibility === 'string'
? data.source_credibility
: data.source_credibility.assessment}
</CredibilityText>
</SourceCredibilitySection>
)}
{/* Temporal Context */}
{data.temporal_context && (
<TemporalSection>
<SectionTitle>Temporal Context</SectionTitle>
<TemporalText>{data.temporal_context}</TemporalText>
</TemporalSection>
)}
{/* Confidence Factors */}
{data.confidence_factors && data.confidence_factors.length > 0 && (
<ConfidenceFactorsSection>
<SectionTitle>Confidence Factors ({data.confidence_factors.length})</SectionTitle>
<ConfidenceFactorsList>
{data.confidence_factors.map((factor, idx) => (
<ConfidenceFactorItem key={idx}> {factor}</ConfidenceFactorItem>
))}
</ConfidenceFactorsList>
</ConfidenceFactorsSection>
)}
{/* Detailed Analysis - Collapsible */}
{data.detailed_analysis && (
<DetailedAnalysisSection>
<ToggleHeader onClick={() => setShowDetailedAnalysis(!showDetailedAnalysis)}>
<SectionTitle>Detailed Analysis</SectionTitle>
<ToggleIcon>{showDetailedAnalysis ? '▼' : '▶'}</ToggleIcon>
</ToggleHeader>
{showDetailedAnalysis && (
<DetailedContent>
{data.detailed_analysis.context_analysis && (
<DetailItem>
<DetailLabel>Context Analysis:</DetailLabel>
<DetailText>{data.detailed_analysis.context_analysis}</DetailText>
</DetailItem>
)}
{data.detailed_analysis.similar_claims && (
<DetailItem>
<DetailLabel>Similar Claims:</DetailLabel>
<DetailText>{data.detailed_analysis.similar_claims}</DetailText>
</DetailItem>
)}
{data.detailed_analysis.content_type && (
<DetailItem>
<DetailLabel>Content Type:</DetailLabel>
<DetailBadge>{data.detailed_analysis.content_type}</DetailBadge>
</DetailItem>
)}
{data.detailed_analysis.tone && (
<DetailItem>
<DetailLabel>Tone:</DetailLabel>
<DetailBadge>{data.detailed_analysis.tone}</DetailBadge>
</DetailItem>
)}
</DetailedContent>
)}
</DetailedAnalysisSection>
)}
{/* Transcription - Collapsible (for Audio/Video) */}
{data.transcription && (
<TranscriptionSection>
<ToggleHeader onClick={() => setShowTranscription(!showTranscription)}>
<SectionTitle>Transcription ({data.transcription.length} characters)</SectionTitle>
<ToggleIcon>{showTranscription ? '▼' : '▶'}</ToggleIcon>
</ToggleHeader>
{showTranscription && (
<TranscriptionContent>
{data.transcription}
</TranscriptionContent>
)}
</TranscriptionSection>
)}
</Container>
);
};
// ==================== STYLED COMPONENTS ====================
const Container = styled.div`
display: flex;
flex-direction: column;
gap: 32px; /* HUGE space between all major sections */
padding: 16px 0;
max-width: 100%;
`;
const HeaderSection = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 32px; /* More space between header cards */
`;
const Card = styled.div`
background: var(--bg-surface);
border-radius: 16px; /* Slightly rounder corners */
padding: 16px; /* More internal padding */
border: 1px solid var(--accent-border);
box-shadow: var(--shadow-sm); /* Subtle shadow for depth */
`;
const VerdictCard = styled(Card)<{ color: string }>`
border-left: 4px solid ${props => props.color};
`;
const VerdictLabel = styled.div`
font-size: ${fontSize.small};
color: var(--fg-secondary);
margin-bottom: 32px;
`;
const VerdictValue = styled.div`
font-size: ${fontSize.large};
font-weight: ${fontWeight.bold};
color: var(--fg-primary);
`;
const ConfidenceCard = styled(Card)``;
const ConfidenceLabel = styled(VerdictLabel)``;
const ConfidenceValue = styled.div`
font-size: ${fontSize.xlarge};
font-weight: ${fontWeight.bold};
color: var(--fg-primary);
margin-bottom: 32px;
`;
const ConfidenceBar = styled.div`
height: 8px;
background: var(--accent-subtle);
border-radius: 4px;
overflow: hidden;
`;
const ConfidenceFill = styled.div<{ width: number; color: string }>`
height: 100%;
width: ${props => props.width}%;
background: ${props => props.color};
transition: width 0.3s ease;
`;
const HarmCard = styled(Card)<{ color: string }>`
border-left: 4px solid ${props => props.color};
`;
const HarmLabel = styled(VerdictLabel)``;
const HarmValue = styled(VerdictValue)``;
const ExplanationSection = styled(Card)``;
const SectionTitle = styled.h3`
font-size: ${fontSize.large}; /* Bigger title */
font-weight: ${fontWeight.bold}; /* Bolder */
color: var(--fg-primary);
margin: 0 0 32px 0; /* More space below title */
display: flex;
align-items: center;
gap: 32px;
`;
const ExplanationText = styled.p`
font-size: ${fontSize.medium};
line-height: 1.7; /* More line height for readability */
color: var(--fg-secondary);
margin: 0;
`;
const CategorySection = styled.div`
display: flex;
gap: 32px;
flex-wrap: wrap;
`;
const Badge = styled.span`
padding: 8px 20px;
border-radius: 20px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
`;
const CategoryBadge = styled(Badge)`
background: var(--accent-subtle);
color: var(--accent-text);
border: 1px solid var(--accent-border);
`;
const TypeBadge = styled(Badge)`
background: ${colors.insightOrange}20;
color: ${colors.insightOrange};
border: 1px solid ${colors.insightOrange}40;
`;
const LangBadge = styled(Badge)`
background: var(--accent-subtle);
color: var(--fg-secondary);
border: 1px solid var(--accent-border);
`;
const ClaimsSection = styled(Card)``;
const ClaimsList = styled.div`
display: flex;
flex-direction: column;
gap: 24px; /* HUGE gap between each claim card */
`;
const ClaimCard = styled.div`
background: var(--accent-subtle);
border-radius: 12px; /* Rounder corners */
padding: 16px; /* More internal padding */
border: 1px solid var(--accent-border);
transition: all 0.2s ease;
&:hover {
background: var(--bg-hover);
border-color: var(--accent-border);
transform: translateX(4px);
}
`;
const ClaimHeader = styled.div`
display: flex;
align-items: center;
gap: 32px;
margin-bottom: 32px;
`;
const ClaimNumber = styled.span`
background: var(--accent-subtle);
color: var(--accent-text);
padding: 2px 8px;
border-radius: 4px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.semiBold};
`;
const VerifiedBadge = styled.span<{ verified: boolean }>`
background: ${props => props.verified ? colors.truthGreen : colors.cautionRed}20;
color: ${props => props.verified ? colors.truthGreen : colors.cautionRed};
padding: 2px 8px;
border-radius: 4px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
`;
const HarmBadge = styled.span<{ level: string }>`
background: ${props => {
const l = props.level?.toLowerCase();
if (l === 'critical') return '#8B0000';
if (l === 'high') return colors.cautionRed;
if (l === 'medium') return '#FFA500';
return '#FFD700';
}}20;
color: ${props => {
const l = props.level?.toLowerCase();
if (l === 'critical') return '#8B0000';
if (l === 'high') return colors.cautionRed;
if (l === 'medium') return '#FFA500';
return '#FFD700';
}};
padding: 2px 8px;
border-radius: 4px;
font-size: ${fontSize.small};
`;
const ClaimText = styled.p`
color: var(--fg-primary);
margin: 0 0 12px 0;
line-height: 1.5;
`;
const SourcesList = styled.div`
margin-top: 12px;
`;
const SourcesLabel = styled.div`
font-size: ${fontSize.small};
color: var(--fg-secondary);
margin-bottom: 4px;
`;
const SourceLink = styled.a`
display: block;
color: var(--accent-text);
font-size: ${fontSize.small};
text-decoration: none;
margin: 4px 0;
word-break: break-all;
&:hover {
text-decoration: underline;
}
`;
const ClaimNotes = styled.div`
font-size: ${fontSize.small};
color: var(--fg-secondary);
font-style: italic;
margin-top: 12px;
`;
const ManipulationSection = styled(Card)``;
const TechniquesList = styled.div`
display: flex;
flex-wrap: wrap;
gap: 20px; /* More space between tags */
`;
const TechniqueTag = styled.span`
background: ${colors.cautionRed}20;
color: ${colors.cautionRed};
padding: 12px 20px; /* Bigger tags */
border-radius: 8px; /* Rounder */
font-size: ${fontSize.small};
border: 1px solid ${colors.cautionRed}40;
font-weight: ${fontWeight.medium};
transition: all 0.2s ease;
&:hover {
background: ${colors.cautionRed}30;
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
`;
const EmotionalSection = styled(Card)<{ severity?: string }>`
border-left: 4px solid ${props => {
const s = props.severity?.toLowerCase();
if (s === 'high') return colors.cautionRed;
if (s === 'medium') return '#FFA500';
return '#FFD700';
}};
`;
const EmotionalContent = styled.div``;
const SeverityBadge = styled.span<{ severity?: string }>`
background: ${props => {
const s = props.severity?.toLowerCase();
if (s === 'high') return colors.cautionRed;
if (s === 'medium') return '#FFA500';
return '#FFD700';
}}20;
color: ${props => {
const s = props.severity?.toLowerCase();
if (s === 'high') return colors.cautionRed;
if (s === 'medium') return '#FFA500';
return '#FFD700';
}};
padding: 4px 12px;
border-radius: 6px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.semiBold};
display: inline-block;
margin-bottom: 32px;
`;
const TriggersList = styled(TechniquesList)``;
const TriggerTag = styled.span`
background: #FFA50020;
color: #FFA500;
padding: 8px 12px;
border-radius: 6px;
font-size: ${fontSize.small};
border: 1px solid #FFA50040;
`;
const MediaSection = styled(Card)``;
const MediaGrid = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
`;
const MediaItem = styled.div``;
const MediaLabel = styled.div`
font-size: ${fontSize.small};
color: var(--fg-secondary);
margin-bottom: 4px;
`;
const MediaValue = styled.div`
color: var(--fg-primary);
font-weight: ${fontWeight.medium};
`;
const AssessmentBadge = styled.span<{ assessment: string }>`
background: ${props => {
const a = props.assessment?.toLowerCase();
if (a === 'authentic') return colors.truthGreen;
if (a === 'manipulated') return colors.cautionRed;
if (a === 'suspicious') return '#FFA500';
return colors.steelGray;
}}20;
color: ${props => {
const a = props.assessment?.toLowerCase();
if (a === 'authentic') return colors.truthGreen;
if (a === 'manipulated') return colors.cautionRed;
if (a === 'suspicious') return '#FFA500';
return colors.steelGray;
}};
padding: 4px 12px;
border-radius: 6px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
`;
const VerificationSection = styled(Card)<{ performed: boolean }>`
border-left: 4px solid ${props => props.performed ? colors.truthGreen : 'var(--border-strong)'};
`;
const VerifItem = styled.div`
margin-bottom: 32px;
&:last-child {
margin-bottom: 0;
}
`;
const VerifLabel = styled.div`
font-size: ${fontSize.small};
color: var(--fg-secondary);
margin-bottom: 8px;
`;
const SitesList = styled.div`
display: flex;
flex-wrap: wrap;
gap: 8px;
`;
const SiteTag = styled.span`
background: ${colors.truthGreen}20;
color: ${colors.truthGreen};
padding: 4px 10px;
border-radius: 6px;
font-size: ${fontSize.small};
`;
const UrlsList = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
`;
const UrlLink = styled.a`
color: var(--accent-text);
font-size: ${fontSize.small};
text-decoration: none;
word-break: break-all;
&:hover {
text-decoration: underline;
}
`;
const LimitationText = styled.p`
color: var(--fg-secondary);
font-size: ${fontSize.small};
font-style: italic;
margin: 0;
`;
const ViralSection = styled(Card)<{ level?: string }>`
border-left: 4px solid ${props => {
const l = props.level?.toLowerCase();
if (l === 'critical') return '#8B0000';
if (l === 'high') return colors.cautionRed;
if (l === 'medium') return '#FFA500';
return colors.truthGreen;
}};
`;
const ViralGrid = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
margin-bottom: 32px;
`;
const ViralItem = styled.div``;
const ViralLabel = styled(MediaLabel)``;
const RiskBadge = styled.span<{ level: string }>`
background: ${props => {
const l = props.level?.toLowerCase();
if (l === 'critical') return '#8B0000';
if (l === 'high') return colors.cautionRed;
if (l === 'medium') return '#FFA500';
return colors.truthGreen;
}}20;
color: ${props => {
const l = props.level?.toLowerCase();
if (l === 'critical') return '#8B0000';
if (l === 'high') return colors.cautionRed;
if (l === 'medium') return '#FFA500';
return colors.truthGreen;
}};
padding: 4px 12px;
border-radius: 6px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.semiBold};
`;
const ScoreValue = styled.div`
color: var(--fg-primary);
font-size: ${fontSize.large};
font-weight: ${fontWeight.bold};
`;
const FactorsList = styled(TechniquesList)``;
const FactorTag = styled.span`
background: var(--accent-subtle);
color: var(--fg-secondary);
padding: 8px 12px;
border-radius: 6px;
font-size: ${fontSize.small};
border: 1px solid var(--accent-border);
`;
const AssessmentText = styled.p`
color: var(--fg-secondary);
font-size: ${fontSize.small};
margin: 20px 0 0 0;
line-height: 1.5;
`;
const RecommendationsSection = styled(Card)<{ urgency?: string }>`
border-left: 4px solid ${props => {
const u = props.urgency?.toLowerCase();
if (u === 'critical') return '#8B0000';
if (u === 'high') return colors.cautionRed;
if (u === 'medium') return '#FFA500';
return colors.truthGreen;
}};
`;
const RecommendationGrid = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
margin-bottom: 32px;
`;
const RecommendItem = styled.div``;
const RecommendLabel = styled(MediaLabel)``;
const ActionBadge = styled.span<{ action: string }>`
background: ${props => {
const a = props.action?.toLowerCase();
if (a === 'remove' || a === 'escalate') return colors.cautionRed;
if (a === 'flag') return '#FFA500';
if (a === 'fact_check') return colors.honestTeal;
return colors.truthGreen;
}}20;
color: ${props => {
const a = props.action?.toLowerCase();
if (a === 'remove' || a === 'escalate') return colors.cautionRed;
if (a === 'flag') return '#FFA500';
if (a === 'fact_check') return colors.honestTeal;
return colors.truthGreen;
}};
padding: 4px 12px;
border-radius: 6px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
`;
const UrgencyBadge = styled(RiskBadge)``;
const ReasonText = styled.p`
color: var(--fg-secondary);
font-size: ${fontSize.small};
margin: 0;
line-height: 1.5;
`;
// ==================== NEW SECTIONS ====================
const SourceCredibilitySection = styled(Card)`
border-left: 4px solid var(--accent);
`;
const CredibilityText = styled.p`
color: var(--fg-secondary);
font-size: ${fontSize.medium};
margin: 0;
line-height: 1.6;
`;
const TemporalSection = styled(Card)`
border-left: 4px solid ${colors.insightOrange};
`;
const TemporalText = styled.p`
color: var(--fg-secondary);
font-size: ${fontSize.medium};
margin: 0;
line-height: 1.6;
font-style: italic;
`;
const ConfidenceFactorsSection = styled(Card)`
border-left: 4px solid ${colors.truthGreen};
`;
const ConfidenceFactorsList = styled.div`
display: flex;
flex-direction: column;
gap: 12px;
`;
const ConfidenceFactorItem = styled.div`
color: var(--fg-secondary);
font-size: ${fontSize.medium};
line-height: 1.5;
padding-left: 8px;
`;
const DetailedAnalysisSection = styled(Card)`
border-left: 4px solid var(--border-strong);
`;
const ToggleHeader = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
user-select: none;
&:hover {
opacity: 0.8;
}
`;
const ToggleIcon = styled.span`
color: var(--fg-secondary);
font-size: ${fontSize.large};
margin-left: 16px;
`;
const DetailedContent = styled.div`
margin-top: 24px;
display: flex;
flex-direction: column;
gap: 20px;
`;
const DetailItem = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
`;
const DetailLabel = styled.div`
color: var(--fg-secondary);
font-size: ${fontSize.small};
font-weight: ${fontWeight.semiBold};
text-transform: uppercase;
letter-spacing: 0.5px;
`;
const DetailText = styled.p`
color: var(--fg-primary);
font-size: ${fontSize.medium};
margin: 0;
line-height: 1.6;
`;
const DetailBadge = styled.span`
background: var(--accent-subtle);
color: var(--fg-primary);
padding: 6px 14px;
border-radius: 6px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
display: inline-block;`;
const TranscriptionSection = styled(Card)`
border-left: 4px solid var(--accent);
`;
const TranscriptionContent = styled.pre`
background: var(--accent-subtle);
border: 1px solid var(--accent-border);
border-radius: 8px;
padding: 20px;
margin-top: 16px;
color: var(--fg-secondary);
font-size: ${fontSize.small};
line-height: 1.8;
white-space: pre-wrap;
word-wrap: break-word;
max-height: 400px;
overflow-y: auto;
font-family: 'Courier New', monospace;
/* Custom scrollbar */
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-track {
background: var(--bg-active);
border-radius: 4px;
}
&::-webkit-scrollbar-thumb {
background: var(--border-strong);
border-radius: 4px;
}
&::-webkit-scrollbar-thumb:hover {
background: var(--fg-muted);
}
`;
// ==================== AGGREGATOR QUALITY STYLES (v3) ====================
const QualitySection = styled(Card)<{ reliability: string }>`
border-left: 4px solid ${props => {
switch (props.reliability?.toUpperCase()) {
case 'HIGH': return colors.truthGreen;
case 'MEDIUM': return '#FFD700';
case 'LOW': return colors.cautionRed;
default: return colors.steelGray;
}
}};
`;
const QualityHeader = styled.div`
display: flex;
align-items: center;
gap: 24px;
flex-wrap: wrap;
margin-bottom: 16px;
`;
const ReliabilityBadge = styled.span<{ color: string }>`
background: ${props => props.color}20;
color: ${props => props.color};
padding: 8px 16px;
border-radius: 8px;
font-size: ${fontSize.medium};
font-weight: ${fontWeight.bold};
border: 1px solid ${props => props.color}40;
`;
const SignalStats = styled.div`
display: flex;
gap: 24px;
`;
const StatItem = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
`;
const StatLabel = styled.span`
font-size: ${fontSize.small};
color: var(--fg-secondary);
`;
const StatValue = styled.span<{ warning?: boolean }>`
font-size: ${fontSize.large};
font-weight: ${fontWeight.bold};
color: ${props => props.warning ? '#FFA500' : 'var(--fg-primary)'};
[data-theme="light"] & {
color: ${props => props.warning ? '#f97316' : 'var(--fg-primary)'};
}
`;
const QualityReason = styled.p`
color: var(--fg-secondary);
font-size: ${fontSize.medium};
margin: 0;
line-height: 1.6;
`;
const CriticalWarning = styled.div`
margin-top: 16px;
padding: 12px 16px;
background: ${colors.cautionRed}15;
border: 1px solid ${colors.cautionRed}40;
border-radius: 8px;
color: ${colors.cautionRed};
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
`;
// ==================== SIGNAL BREAKDOWN STYLES (v3) ====================
const SignalBreakdownSection = styled(Card)`
border-left: 4px solid var(--accent);
`;
const SignalBreakdownContent = styled.div`
display: flex;
flex-direction: column;
gap: 16px;
margin-top: 16px;
`;
const SignalCard = styled.div<{ isDefault?: boolean }>`
background: ${props => props.isDefault ? 'rgba(255, 165, 0, 0.05)' : 'var(--accent-subtle)'};
border: 1px solid ${props => props.isDefault ? 'rgba(255, 165, 0, 0.2)' : 'var(--accent-border)'};
border-radius: 12px;
padding: 16px;
transition: all 0.2s ease;
&:hover {
background: ${props => props.isDefault ? 'rgba(255, 165, 0, 0.08)' : 'var(--bg-hover)'};
}
`;
const SignalHeader = styled.div`
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 12px;
flex-wrap: wrap;
`;
const SignalName = styled.span`
color: var(--fg-primary);
font-size: ${fontSize.medium};
font-weight: ${fontWeight.semiBold};
text-transform: capitalize;
`;
const SignalWeight = styled.span`
color: var(--fg-secondary);
font-size: ${fontSize.small};
padding: 4px 8px;
background: var(--accent-subtle);
border-radius: 4px;
`;
const DefaultBadge = styled.span`
background: #FFA50020;
color: #FFA500;
padding: 4px 8px;
border-radius: 4px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
`;
const SignalBarContainer = styled.div`
display: flex;
align-items: center;
gap: 12px;
`;
const SignalBar = styled.div`
flex: 1;
height: 8px;
background: var(--accent-subtle);
border-radius: 4px;
overflow: hidden;
`;
const SignalBarFill = styled.div<{ width: number; color: string }>`
height: 100%;
width: ${props => Math.min(100, Math.max(0, props.width))}%;
background: ${props => props.color};
transition: width 0.3s ease;
`;
const SignalScore = styled.span`
color: var(--fg-primary);
font-size: ${fontSize.medium};
font-weight: ${fontWeight.bold};
min-width: 48px;
text-align: right;
`;
const SignalContribution = styled.div`
color: var(--fg-secondary);
font-size: ${fontSize.small};
margin-top: 8px;
`;
const DefaultReasonText = styled.div`
color: #FFA500;
font-size: ${fontSize.small};
margin-top: 8px;
font-style: italic;
`;
const TotalScoreCard = styled.div`
background: var(--accent-subtle);
border: 1px solid var(--accent-border);
border-radius: 12px;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 8px;
`;
const TotalLabel = styled.span`
color: var(--fg-primary);
font-size: ${fontSize.large};
font-weight: ${fontWeight.semiBold};
`;
const TotalScore = styled.span<{ color: string }>`
font-size: ${fontSize.xlarge};
font-weight: ${fontWeight.bold};
color: ${props => props.color};
`;
// ==================== TRIAGE BADGES STYLES (v3) ====================
const TriageSection = styled.div`
display: flex;
gap: 12px;
flex-wrap: wrap;
`;
const TriageBadge = styled.span<{ color: string }>`
background: ${props => props.color}20;
color: ${props => props.color};
padding: 8px 16px;
border-radius: 8px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.semiBold};
border: 1px solid ${props => props.color}40;
display: inline-flex;
align-items: center;
gap: 6px;
text-transform: capitalize;
`;
// ==================== SOURCES EVALUATED STYLES (v3) ====================
const SourcesEvaluatedSection = styled(Card)`
border-left: 4px solid var(--accent);
`;
const SourcesEvaluatedContent = styled.div`
display: flex;
flex-direction: column;
gap: 12px;
margin-top: 16px;
`;
const SourceCard = styled.div<{ stance: string }>`
background: var(--accent-subtle);
border: 1px solid ${props => {
switch (props.stance) {
case 'supports': return `${colors.truthGreen}30`;
case 'contradicts': return `${colors.cautionRed}30`;
case 'neutral': return '#FFD70030';
default: return 'var(--accent-border)';
}
}};
border-radius: 12px;
padding: 16px;
transition: all 0.2s ease;
&:hover {
background: var(--bg-hover);
}
`;
const SourceUrlRow = styled.div`
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
`;
const SourceIcon = styled.span`
font-size: ${fontSize.medium};
`;
const SourceUrl = styled.a`
color: var(--accent-text);
font-size: ${fontSize.medium};
text-decoration: none;
word-break: break-all;
font-weight: ${fontWeight.medium};
&:hover {
text-decoration: underline;
}
`;
const SourceMetaRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
`;
const TierBadge = styled.span<{ tier: number }>`
background: ${props => {
if (props.tier <= 2) return colors.truthGreen;
if (props.tier <= 3) return '#FFD700';
if (props.tier <= 4) return '#FFA500';
return colors.cautionRed;
}}20;
color: ${props => {
if (props.tier <= 2) return colors.truthGreen;
if (props.tier <= 3) return '#FFD700';
if (props.tier <= 4) return '#FFA500';
return colors.cautionRed;
}};
padding: 4px 10px;
border-radius: 6px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
`;
const StanceBadge = styled.span<{ color: string }>`
background: ${props => props.color}20;
color: ${props => props.color};
padding: 4px 10px;
border-radius: 6px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.semiBold};
text-transform: capitalize;
`;
const AuthorityBadge = styled.span`
background: var(--accent-subtle);
color: var(--fg-primary);
padding: 4px 10px;
border-radius: 6px;
font-size: ${fontSize.small};
text-transform: capitalize;
`;
const RecencyBadge = styled.span<{ recency: string }>`
background: ${props => {
switch (props.recency) {
case 'current': return colors.truthGreen;
case 'dated': return '#FFA500';
case 'historical': return colors.steelGray;
default: return colors.steelGray;
}
}}20;
color: ${props => {
switch (props.recency) {
case 'current': return colors.truthGreen;
case 'dated': return '#FFA500';
case 'historical': return colors.steelGray;
default: return colors.steelGray;
}
}};
padding: 4px 10px;
border-radius: 6px;
font-size: ${fontSize.small};
text-transform: capitalize;
`;
const CircularWarning = styled.div`
margin-top: 12px;
padding: 8px 12px;
background: ${colors.cautionRed}15;
border: 1px solid ${colors.cautionRed}40;
border-radius: 6px;
color: ${colors.cautionRed};
font-size: ${fontSize.small};
`;
const CircularChain = styled.div`
margin-top: 4px;
font-size: ${fontSize.small};
opacity: 0.8;
`;
const PrimarySourceNote = styled.div`
margin-top: 12px;
padding: 8px 12px;
background: var(--accent-subtle);
border: 1px solid var(--accent-border);
border-radius: 6px;
color: var(--accent-text);
font-size: ${fontSize.small};
`;
const TierReasoning = styled.div`
margin-top: 12px;
color: var(--fg-secondary);
font-size: ${fontSize.small};
font-style: italic;
line-height: 1.5;
`;
// ==================== STANCE SUMMARY STYLES (v3) ====================
const StanceSummarySection = styled(Card)`
border-left: 4px solid ${colors.insightOrange};
`;
const StanceBars = styled.div`
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 20px;
`;
const StanceBarRow = styled.div`
display: flex;
align-items: center;
gap: 12px;
`;
const StanceBarLabel = styled.span`
min-width: 100px;
color: var(--fg-secondary);
font-size: ${fontSize.small};
`;
const StanceBarContainer = styled.div`
flex: 1;
height: 12px;
background: var(--accent-subtle);
border-radius: 6px;
overflow: hidden;
`;
const StanceBarFill = styled.div<{ width: number; color: string }>`
height: 100%;
width: ${props => Math.min(100, Math.max(0, props.width))}%;
background: ${props => props.color};
transition: width 0.3s ease;
`;
const StanceBarCount = styled.span`
min-width: 80px;
color: var(--fg-primary);
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
text-align: right;
`;
const WeightedStanceRow = styled.div`
display: flex;
align-items: center;
gap: 12px;
padding: 16px;
background: var(--accent-subtle);
border-radius: 12px;
margin-top: 8px;
`;
const WeightedStanceLabel = styled.span`
color: var(--fg-secondary);
font-size: ${fontSize.medium};
`;
const WeightedStanceValue = styled.div<{ score: number }>`
color: ${props => {
if (props.score > 0.3) return colors.truthGreen;
if (props.score > -0.3) return '#FFD700';
return colors.cautionRed;
}};
font-size: ${fontSize.large};
font-weight: ${fontWeight.bold};
display: flex;
align-items: center;
gap: 8px;
`;
const WeightedStanceInterpretation = styled.span`
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
opacity: 0.8;
`;
const NoRelevantEvidenceWarning = styled.div`
margin-top: 16px;
padding: 12px 16px;
background: ${colors.cautionRed}15;
border: 1px solid ${colors.cautionRed}40;
border-radius: 8px;
color: ${colors.cautionRed};
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
`;
// ==================== CLAIM VERDICTS STYLES (v3) ====================
const ClaimVerdictsSection = styled(Card)`
border-left: 4px solid var(--accent);
`;
const ClaimVerdictsContent = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
margin-top: 16px;
`;
const ClaimVerdictCard = styled.div`
background: var(--accent-subtle);
border: 1px solid var(--accent-border);
border-radius: 12px;
padding: 20px;
transition: all 0.2s ease;
&:hover {
background: var(--bg-hover);
border-color: var(--accent-border);
}
`;
const ClaimVerdictHeader = styled.div`
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
flex-wrap: wrap;
`;
const ClaimVerdictNumber = styled.span`
background: var(--accent-subtle);
color: var(--fg-primary);
padding: 4px 10px;
border-radius: 6px;
font-size: ${fontSize.small};
font-weight: ${fontWeight.semiBold};
`;
const ClaimVerdictBadge = styled.span<{ color: string }>`
background: ${props => props.color}20;
color: ${props => props.color};
padding: 6px 14px;
border-radius: 8px;
font-size: ${fontSize.medium};
font-weight: ${fontWeight.bold};
text-transform: uppercase;
border: 1px solid ${props => props.color}40;
`;
const ClaimVerdictScore = styled.span`
color: var(--fg-secondary);
font-size: ${fontSize.small};
`;
const ClaimVerdictText = styled.div`
color: var(--fg-primary);
font-size: ${fontSize.medium};
line-height: 1.6;
margin-bottom: 12px;
font-style: italic;
padding-left: 16px;
border-left: 2px solid var(--accent-border);
`;
const ClaimVerdictExplanation = styled.p`
color: var(--fg-secondary);
font-size: ${fontSize.medium};
line-height: 1.6;
margin: 0 0 16px 0;
`;
const EvidenceSection = styled.div`
margin-top: 12px;
`;
const EvidenceLabel = styled.div`
color: var(--fg-secondary);
font-size: ${fontSize.small};
font-weight: ${fontWeight.semiBold};
margin-bottom: 8px;
`;
const EvidenceList = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
padding-left: 16px;
`;
const EvidenceItem = styled.div<{ type: 'for' | 'against' }>`
color: ${props => props.type === 'for' ? colors.truthGreen : colors.cautionRed};
font-size: ${fontSize.small};
line-height: 1.5;
`;
const EvidenceLink = styled.a`
color: inherit;
text-decoration: none;
word-break: break-all;
&:hover {
text-decoration: underline;
}
`;
const EpistemicSection = styled.div`
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid var(--accent-border);
`;
const EpistemicRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 8px;
`;
const EpistemicBadge = styled.span<{ type: 'evidence' | 'nature' | 'temporal' }>`
background: ${props => {
switch (props.type) {
case 'evidence': return colors.honestTeal;
case 'nature': return colors.insightOrange;
case 'temporal': return colors.deepBlue;
default: return colors.steelGray;
}
}}20;
color: ${props => {
switch (props.type) {
case 'evidence': return colors.honestTeal;
case 'nature': return colors.insightOrange;
case 'temporal': return colors.deepBlue;
default: return colors.steelGray;
}
}};
padding: 4px 10px;
border-radius: 6px;
font-size: ${fontSize.small};
text-transform: capitalize;
`;
const RecommendedActionRow = styled.div`
display: flex;
align-items: center;
gap: 8px;
margin-top: 8px;
`;
const RecommendedActionLabel = styled.span`
color: var(--fg-secondary);
font-size: ${fontSize.small};
`;
const RecommendedActionValue = styled.span`
color: var(--fg-primary);
font-size: ${fontSize.small};
font-weight: ${fontWeight.medium};
text-transform: capitalize;
`;