social class view ui/ux improvements

This commit is contained in:
Harivansh Rathi 2024-12-04 16:40:02 -05:00
parent 2cf48254c4
commit 34516c0aa5
3 changed files with 1009 additions and 448 deletions

View file

@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Box, Typography, Paper, Grid, Tooltip, Card, CardContent, Chip, Dialog, DialogTitle, DialogContent } from '@mui/material';
import { Box, Typography, Paper, Grid, Tooltip, Card, CardContent, Chip, Dialog, DialogTitle, DialogContent, Divider } from '@mui/material';
import { SocialClass, Character } from '../types/timeline';
const socialClasses: SocialClass[] = [
@ -124,6 +124,9 @@ export default function SocialClassView() {
} else if (selectedCharacter.id !== character.id) {
setComparisonCharacter(character);
setDialogOpen(true);
} else {
// Deselect if clicking the same character
setSelectedCharacter(null);
}
};
@ -133,121 +136,286 @@ export default function SocialClassView() {
setComparisonCharacter(null);
};
// Color constants for the gradient
const pyramidColors = {
upper: {
start: '#4A5D52', // darker sage
end: '#6B7F75' // lighter sage
},
middle: {
start: '#5B6E65',
end: '#7C8F86'
},
lower: {
start: '#6B7F75',
end: '#8C9F96'
}
};
return (
<Box sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom>
Social Class in Austen's Novels
</Typography>
{/* Social Pyramid */}
<Box sx={{ mb: 6, position: 'relative', height: 300 }}>
{socialClasses.map((socialClass, index) => (
<Tooltip
key={socialClass.name}
title={
<Box>
<Typography variant="subtitle2">{socialClass.name}</Typography>
<Typography variant="body2">{socialClass.description}</Typography>
<Typography variant="caption">Income: {socialClass.incomeRange}</Typography>
</Box>
}
arrow
>
<Paper
elevation={3}
sx={{
position: 'absolute',
left: '50%',
width: `${100 - index * 20}%`,
height: 80,
transform: `translateX(-50%) translateY(${index * 100}px)`,
bgcolor: `primary.${100 + index * 100}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
transition: 'all 0.2s',
'&:hover': {
transform: `translateX(-50%) translateY(${index * 100}px) scale(1.02)`,
}
}}
>
<Typography variant="h6" color="text.primary">
{socialClass.name}
<div className="container mx-auto py-6 space-y-6">
<Grid container spacing={3}>
{/* Social Pyramid */}
<Grid item xs={12} md={6}>
<Card className="border border-sage-200 hover:border-sage-300 transition-all">
<CardContent className="p-6">
<Typography variant="h5" className="font-cormorant text-sage-900 mb-4">
Social Hierarchy
</Typography>
</Paper>
</Tooltip>
))}
</Box>
{/* Character Grid */}
<Typography variant="h6" gutterBottom sx={{ mt: 4 }}>
Character Examples {selectedCharacter && '(Select another character to compare)'}
</Typography>
<Grid container spacing={2}>
{characters.map((character) => (
<Grid item xs={12} sm={6} md={4} key={character.id}>
<Card
onClick={() => handleCharacterClick(character)}
sx={{
cursor: 'pointer',
transition: 'all 0.2s',
'&:hover': {
transform: 'scale(1.02)',
},
bgcolor: selectedCharacter?.id === character.id ? 'primary.50' : 'background.paper'
}}
>
<CardContent>
<Typography variant="h6" gutterBottom>
{character.name}
</Typography>
<Typography variant="body2" color="text.secondary" gutterBottom>
{character.novel}
</Typography>
<Chip
label={character.socialClass.charAt(0).toUpperCase() + character.socialClass.slice(1)}
size="small"
sx={{ mb: 1 }}
/>
<Typography variant="body2">
Annual Income: {character.annualIncome}
</Typography>
<Typography variant="caption" color="text.secondary" display="block">
Modern Equivalent: {character.modernEquivalent}
</Typography>
</CardContent>
</Card>
</Grid>
))}
<Box sx={{ position: 'relative', height: 350, mb: 4 }}>
{socialClasses.map((socialClass, index) => {
const colors = index === 0 ? pyramidColors.upper :
index === 1 ? pyramidColors.middle :
pyramidColors.lower;
return (
<Tooltip
key={socialClass.name}
title={
<Box className="p-4 max-w-md">
<Typography variant="subtitle1" className="font-cormorant text-sage-900 mb-2 font-semibold">
{socialClass.name}
</Typography>
<Typography variant="body2" className="text-sage-700 mb-3">
{socialClass.description}
</Typography>
<Divider className="bg-sage-200 my-3" />
<div className="space-y-2">
<Typography variant="caption" className="text-sage-700 block font-medium">
Income Range: {socialClass.incomeRange}
</Typography>
<Typography variant="caption" className="text-sage-700 block font-medium">
Modern Equivalent: {socialClass.modernEquivalent}
</Typography>
</div>
<Divider className="bg-sage-200 my-3" />
<div className="space-y-2">
<Typography variant="caption" className="text-sage-800 font-semibold block">
Key Characteristics:
</Typography>
<ul className="list-disc list-inside text-sage-700 text-sm space-y-1">
{socialClass.characteristics.map((char, idx) => (
<li key={idx}>{char}</li>
))}
</ul>
</div>
</Box>
}
arrow
placement="right"
classes={{
tooltip: "bg-white shadow-lg",
arrow: "text-white"
}}
sx={{
"& .MuiTooltip-tooltip": {
backgroundColor: "white",
color: "inherit",
boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.1)",
opacity: 1,
maxWidth: "none"
},
"& .MuiTooltip-arrow": {
color: "white",
"&::before": {
backgroundColor: "white",
boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.1)"
}
}
}}
>
<Paper
elevation={2}
sx={{
position: 'absolute',
left: '50%',
width: `${100 - index * 20}%`,
height: 90,
transform: `translateX(-50%) translateY(${index * 110}px)`,
background: `linear-gradient(45deg, ${colors.start}, ${colors.end})`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
transition: 'all 0.3s ease',
'&:hover': {
transform: `translateX(-50%) translateY(${index * 110}px) scale(1.02)`,
boxShadow: 3
}
}}
>
<Typography variant="h6" className="text-white font-cormorant">
{socialClass.name}
</Typography>
</Paper>
</Tooltip>
);
})}
</Box>
</CardContent>
</Card>
</Grid>
{/* Character Examples */}
<Grid item xs={12} md={6}>
<Card className="border border-sage-200 hover:border-sage-300 transition-all">
<CardContent className="p-6">
<Typography variant="h5" className="font-cormorant text-sage-900 mb-4">
Notable Characters
</Typography>
{selectedCharacter && (
<div className="mb-4 p-3 bg-sage-50 border border-sage-200 rounded-lg">
<Typography className="text-sage-700 text-sm">
<span className="font-semibold">{selectedCharacter.name}</span> selected.
Click another character to compare, or click again to deselect.
</Typography>
</div>
)}
<div className="space-y-4">
{characters.map((character) => (
<Card
key={character.id}
onClick={() => handleCharacterClick(character)}
className={`
border transition-all cursor-pointer
${selectedCharacter?.id === character.id
? 'border-sage-500 bg-sage-50 shadow-md transform scale-[1.02]'
: 'border-sage-200 bg-white hover:border-sage-300 hover:shadow-sm hover:bg-sage-50/30'
}
`}
sx={{
position: 'relative',
'&:after': selectedCharacter?.id === character.id ? {
content: '""',
position: 'absolute',
left: -2,
top: '50%',
transform: 'translateY(-50%)',
width: 4,
height: '70%',
backgroundColor: '#4A5D52',
borderRadius: '0 2px 2px 0'
} : {}
}}
>
<CardContent className="p-4">
<Box className="flex justify-between items-start">
<div>
<Typography variant="h6" className={`font-cormorant ${selectedCharacter?.id === character.id ? 'text-sage-900' : 'text-sage-800'}`}>
{character.name}
</Typography>
<Typography variant="body2" className="text-sage-600 mb-2">
{character.novel}
</Typography>
</div>
<Chip
label={character.socialClass.charAt(0).toUpperCase() + character.socialClass.slice(1)}
size="small"
sx={{
bgcolor: character.socialClass === 'upper' ? pyramidColors.upper.start :
character.socialClass === 'middle' ? pyramidColors.middle.start :
pyramidColors.lower.start,
color: 'white',
fontFamily: '"Lato", sans-serif',
transition: 'all 0.2s ease',
transform: selectedCharacter?.id === character.id ? 'scale(1.05)' : 'scale(1)'
}}
/>
</Box>
<Box className="mt-2 text-sage-700">
<div className="flex justify-between text-sm">
<span>Annual Income:</span>
<span className="font-semibold">{character.annualIncome}</span>
</div>
<div className="flex justify-between text-sm text-sage-600">
<span>Modern Equivalent:</span>
<span>{character.modernEquivalent}</span>
</div>
</Box>
</CardContent>
</Card>
))}
</div>
</CardContent>
</Card>
</Grid>
</Grid>
{/* Comparison Dialog */}
<Dialog open={dialogOpen} onClose={handleCloseDialog} maxWidth="md" fullWidth>
<DialogTitle>Character Comparison</DialogTitle>
<DialogContent>
<Dialog
open={dialogOpen}
onClose={handleCloseDialog}
maxWidth="md"
fullWidth
PaperProps={{
className: "rounded-lg overflow-hidden"
}}
>
<DialogTitle className="bg-sage-700 text-white font-cormorant py-4">
Character Comparison
</DialogTitle>
<DialogContent className="p-6">
{selectedCharacter && comparisonCharacter && (
<Grid container spacing={3}>
<Grid item xs={6}>
<Typography variant="h6" gutterBottom>{selectedCharacter.name}</Typography>
<Typography variant="body2" gutterBottom>Novel: {selectedCharacter.novel}</Typography>
<Typography variant="body2" gutterBottom>Class: {selectedCharacter.socialClass}</Typography>
<Typography variant="body2" gutterBottom>Income: {selectedCharacter.annualIncome}</Typography>
<Typography variant="body2" gutterBottom>Modern: {selectedCharacter.modernEquivalent}</Typography>
<Typography variant="body2">{selectedCharacter.description}</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="h6" gutterBottom>{comparisonCharacter.name}</Typography>
<Typography variant="body2" gutterBottom>Novel: {comparisonCharacter.novel}</Typography>
<Typography variant="body2" gutterBottom>Class: {comparisonCharacter.socialClass}</Typography>
<Typography variant="body2" gutterBottom>Income: {comparisonCharacter.annualIncome}</Typography>
<Typography variant="body2" gutterBottom>Modern: {comparisonCharacter.modernEquivalent}</Typography>
<Typography variant="body2">{comparisonCharacter.description}</Typography>
</Grid>
<Grid container spacing={4}>
{[selectedCharacter, comparisonCharacter].map((char, index) => (
<Grid item xs={6} key={char.id}>
<Typography variant="h6" className="font-cormorant text-sage-900 mb-3">
{char.name}
</Typography>
<Chip
label={char.socialClass.charAt(0).toUpperCase() + char.socialClass.slice(1)}
size="small"
sx={{
bgcolor: char.socialClass === 'upper' ? pyramidColors.upper.start :
char.socialClass === 'middle' ? pyramidColors.middle.start :
pyramidColors.lower.start,
color: 'white',
fontFamily: '"Lato", sans-serif'
}}
/>
<div className="space-y-3 text-sage-700 mt-4">
<div>
<Typography variant="body2" className="text-sage-600 font-semibold">
Novel
</Typography>
<Typography variant="body1">
{char.novel}
</Typography>
</div>
<div>
<Typography variant="body2" className="text-sage-600 font-semibold">
Annual Income
</Typography>
<Typography variant="body1">
{char.annualIncome}
</Typography>
</div>
<div>
<Typography variant="body2" className="text-sage-600 font-semibold">
Modern Equivalent
</Typography>
<Typography variant="body1">
{char.modernEquivalent}
</Typography>
</div>
<div>
<Typography variant="body2" className="text-sage-600 font-semibold">
Description
</Typography>
<Typography variant="body1" className="text-sage-700 leading-relaxed">
{char.description}
</Typography>
</div>
</div>
{index === 0 && (
<Divider orientation="vertical" className="absolute right-0 top-0 bottom-0 bg-sage-200" />
)}
</Grid>
))}
</Grid>
)}
</DialogContent>
</Dialog>
</Box>
</div>
);
}

View file

@ -1,9 +1,9 @@
import React, { useState, useCallback, useRef } from 'react';
import React, { useState, useCallback, useRef, useEffect } from 'react';
import { characterNetwork } from '../data/character-network';
import { CharacterNode, Relationship, BookNode } from '../types/character-network';
import { Box, Typography, Paper, Grid, Card, CardContent, IconButton, Tooltip, Popper } from '@mui/material';
import { Box, Typography, Paper, Grid, Card, CardContent, IconButton, Tooltip, Popper, Chip, Divider, Container, CircularProgress, Fade } from '@mui/material';
import { ForceGraph2D } from 'react-force-graph';
import { ArrowBack, Help } from '@mui/icons-material';
import { ArrowBack, Help, ZoomIn, ZoomOut, CenterFocusStrong, Search } from '@mui/icons-material';
import * as d3 from 'd3';
interface NetworkNode {
@ -40,12 +40,40 @@ interface TooltipState {
y: number;
}
// Proper typing for ForceGraph methods
interface ForceGraphMethods {
zoom: (k: number) => void;
zoomToFit: (durationMs?: number, padding?: number) => void;
d3Force: (forceName: string, force: any) => void;
getZoom: () => number;
centerAt: (x?: number, y?: number, durationMs?: number) => void;
}
export default function NetworkVisualization() {
const [selectedNode, setSelectedNode] = useState<CharacterNode | BookNode | null>(null);
const [selectedRelationships, setSelectedRelationships] = useState<Relationship[]>([]);
const [selectedBook, setSelectedBook] = useState<string | null>(null);
const [tooltip, setTooltip] = useState<TooltipState>({ open: false, content: '', x: 0, y: 0 });
const containerRef = useRef<HTMLDivElement>(null);
const graphRef = useRef<ForceGraphMethods>();
const [isLoading, setIsLoading] = useState(true);
const [isGraphReady, setIsGraphReady] = useState(false);
// Add loading effect when data changes
useEffect(() => {
setIsLoading(true);
const timer = setTimeout(() => {
setIsLoading(false);
}, 1000);
return () => clearTimeout(timer);
}, [selectedBook]);
// Track when graph is ready
useEffect(() => {
if (graphRef.current) {
setIsGraphReady(true);
}
}, [graphRef.current]);
const handleNodeHover = useCallback((node: NetworkNode | null) => {
if (node) {
@ -173,6 +201,12 @@ export default function NetworkVisualization() {
const size = node.val || 5;
const fontSize = Math.max(12 / scale, 2);
// Add glow effect for highlighted nodes
if (node.id === selectedNode?.id) {
ctx.shadowColor = node.color || '#fff';
ctx.shadowBlur = 15;
}
// Draw node
ctx.beginPath();
ctx.arc(node.x || 0, node.y || 0, size, 0, 2 * Math.PI);
@ -182,6 +216,10 @@ export default function NetworkVisualization() {
ctx.lineWidth = 2;
ctx.stroke();
// Clear shadow effect
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
// Only draw labels when zoomed in enough or for book nodes
if (scale > 0.7 || node.type === 'book') {
const labelDistance = size + fontSize;
@ -189,7 +227,7 @@ export default function NetworkVisualization() {
const textWidth = ctx.measureText(node.name).width;
// Semi-transparent background for better readability
ctx.fillStyle = 'rgba(255, 255, 255, 0.85)';
ctx.fillStyle = 'rgba(255, 255, 255, 0.95)';
const padding = fontSize * 0.3;
const backgroundHeight = fontSize + padding * 2;
@ -206,7 +244,7 @@ export default function NetworkVisualization() {
);
// Draw text
ctx.fillStyle = '#000';
ctx.fillStyle = node.id === selectedNode?.id ? '#1976d2' : '#000';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(
@ -215,7 +253,7 @@ export default function NetworkVisualization() {
(node.y || 0) + labelDistance
);
}
}, []);
}, [selectedNode]);
// Helper function for drawing rounded rectangles
const roundRect = (
@ -267,171 +305,389 @@ export default function NetworkVisualization() {
</Box>
);
const handleZoomIn = () => {
if (graphRef.current) {
const currentZoom = graphRef.current.getZoom();
graphRef.current.zoom(currentZoom * 1.5);
}
};
const handleZoomOut = () => {
if (graphRef.current) {
const currentZoom = graphRef.current.getZoom();
graphRef.current.zoom(currentZoom / 1.5);
}
};
const handleCenterGraph = () => {
if (graphRef.current) {
graphRef.current.zoomToFit(400, 50);
}
};
return (
<Box sx={{
width: '100%',
height: '100%',
height: '100vh',
display: 'flex',
flexDirection: 'column'
flexDirection: 'column',
bgcolor: 'background.default',
overflow: 'hidden'
}}>
<Box sx={{
display: 'flex',
alignItems: 'center',
mb: 2,
px: 3,
pt: 2
}}>
<Typography variant="h4">
Character Network
</Typography>
<Tooltip title={getLegendTooltip()} arrow placement="right">
<IconButton size="small" sx={{ ml: 2 }}>
<Help />
</IconButton>
</Tooltip>
</Box>
<Grid container spacing={0} sx={{ flex: 1, minHeight: 0 }}>
<Grid item xs={12} md={8}>
<Paper
elevation={3}
sx={{
height: '100%',
minHeight: 700,
position: 'relative',
borderRadius: 0,
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
ref={containerRef}
>
{selectedBook && (
<Tooltip title="Return to book overview" arrow>
<IconButton
onClick={handleBackClick}
sx={{ position: 'absolute', top: 8, left: 8, zIndex: 1 }}
>
<ArrowBack />
</IconButton>
</Tooltip>
)}
<Box sx={{
position: 'absolute',
top: 8,
right: 8,
zIndex: 1,
bgcolor: 'rgba(255, 255, 255, 0.9)',
p: 1,
borderRadius: 1
<Container maxWidth="xl" sx={{ flex: 1, display: 'flex', flexDirection: 'column', py: 3 }}>
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
mb: 3,
pb: 2,
borderBottom: '1px solid',
borderColor: 'divider'
}}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant="h4" sx={{
fontWeight: 500,
color: 'primary.main',
fontFamily: '"Cormorant", serif'
}}>
<Typography variant="body2" color="textSecondary">
{!selectedBook ? 'Click a book to explore its characters' : 'Click characters to view relationships'}
</Typography>
</Box>
<ForceGraph2D
graphData={getGraphData()}
onNodeHover={handleNodeHover}
onNodeClick={handleNodeClick}
nodeCanvasObject={(node: NetworkNode, ctx: CanvasRenderingContext2D, scale: number) =>
renderNodeCanvas(node, ctx, scale)}
linkColor={(link: NetworkLink) => link.color}
linkWidth={2}
nodeRelSize={6}
width={800}
height={700}
cooldownTicks={50}
cooldownTime={3000}
linkDirectionalParticles={2}
linkDirectionalParticleSpeed={0.003}
d3AlphaDecay={0.1}
d3VelocityDecay={0.4}
minZoom={0.5}
maxZoom={4}
dagMode={selectedBook ? undefined : 'radialin'}
dagLevelDistance={100}
enablePanInteraction={true}
enableZoomInteraction={true}
onEngineStop={() => {
if (!selectedBook) {
centerGraph(getGraphData());
}
}}
onNodeDragEnd={(node: NetworkNode) => {
if (node.x && node.y) {
node.fx = node.x;
node.fy = node.y;
}
}}
warmupTicks={100}
onZoom={() => centerGraph(getGraphData())}
centerAt={[400, 350]}
zoom={2}
enableNodeDrag={true}
enableZoomPanInteraction={true}
/>
</Paper>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={{
height: '100%',
minHeight: 700,
overflow: 'auto',
borderLeft: '1px solid rgba(0, 0, 0, 0.12)'
}}>
{selectedNode && (
<Card sx={{ boxShadow: 'none', borderRadius: 0 }}>
<CardContent>
<Typography variant="h5" gutterBottom>
{selectedNode.name}
</Typography>
<Typography color="textSecondary" gutterBottom>
{selectedNode.type === 'book' ? `Published: ${(selectedNode as BookNode).year}` : selectedNode.novel}
</Typography>
<Typography variant="body1" paragraph>
{selectedNode.description}
</Typography>
{selectedNode.type !== 'book' && (
<>
<Typography variant="body2" sx={{ mt: 2 }} color="textSecondary">
Social Class: {(selectedNode as CharacterNode).class}
<br />
Character Role: {selectedNode.type}
</Typography>
<Typography variant="h6" sx={{ mt: 3, mb: 2 }}>
Relationships
</Typography>
{selectedRelationships.map((rel, index) => (
<Box key={index} sx={{ mt: 2, p: 2, bgcolor: 'rgba(0, 0, 0, 0.02)', borderRadius: 1 }}>
<Typography variant="subtitle1" color="primary">
{rel.description}
</Typography>
<Typography variant="body2" color="textSecondary" gutterBottom>
Type: {rel.type}
</Typography>
<Typography variant="body2" sx={{ mt: 1 }}>
Development:
</Typography>
<ul style={{ margin: '8px 0', paddingLeft: 20 }}>
{rel.development.map((step, i) => (
<li key={i}>
<Typography variant="body2">{step}</Typography>
</li>
))}
</ul>
</Box>
))}
</>
)}
</CardContent>
</Card>
)}
Character Network
</Typography>
<Tooltip title={getLegendTooltip()} arrow placement="right">
<IconButton size="small" sx={{ ml: 2 }}>
<Help />
</IconButton>
</Tooltip>
</Box>
<Box sx={{
display: 'flex',
gap: 1,
bgcolor: 'background.paper',
p: 0.5,
borderRadius: 2,
boxShadow: 1
}}>
<Tooltip title="Zoom in" arrow>
<IconButton onClick={handleZoomIn} size="small">
<ZoomIn />
</IconButton>
</Tooltip>
<Tooltip title="Zoom out" arrow>
<IconButton onClick={handleZoomOut} size="small">
<ZoomOut />
</IconButton>
</Tooltip>
<Tooltip title="Center graph" arrow>
<IconButton onClick={handleCenterGraph} size="small">
<CenterFocusStrong />
</IconButton>
</Tooltip>
</Box>
</Box>
<Grid container spacing={3} sx={{ flex: 1, minHeight: 0 }}>
<Grid item xs={12} md={8}>
<Paper
elevation={4}
sx={{
height: '100%',
position: 'relative',
borderRadius: 2,
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
bgcolor: '#fafafa',
boxShadow: theme => `0 0 20px ${theme.palette.divider}`
}}
ref={containerRef}
>
{/* Loading overlay */}
<Fade in={isLoading} timeout={300}>
<Box sx={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
bgcolor: 'rgba(255, 255, 255, 0.8)',
zIndex: 10,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
gap: 2
}}>
<CircularProgress />
<Typography variant="body1" color="text.secondary">
{selectedBook ? 'Loading character relationships...' : 'Loading books...'}
</Typography>
</Box>
</Fade>
{/* Graph container */}
<Fade in={!isLoading && isGraphReady} timeout={500}>
<Box sx={{ width: '100%', height: '100%' }}>
<ForceGraph2D
// @ts-expect-error - type mismatch with ref
ref={graphRef}
graphData={getGraphData()}
onNodeHover={handleNodeHover}
onNodeClick={handleNodeClick}
nodeCanvasObject={renderNodeCanvas}
linkColor={(link: NetworkLink) => link.color}
linkWidth={3}
nodeRelSize={8}
width={800}
height={700}
cooldownTicks={50}
cooldownTime={3000}
linkDirectionalParticles={3}
linkDirectionalParticleWidth={2}
linkDirectionalParticleSpeed={0.004}
d3AlphaDecay={0.1}
d3VelocityDecay={0.4}
minZoom={0.5}
maxZoom={4}
dagMode={selectedBook ? undefined : 'radialin'}
dagLevelDistance={100}
enablePanInteraction={true}
enableZoomInteraction={true}
onEngineStop={() => {
if (!selectedBook) {
centerGraph(getGraphData());
}
}}
onNodeDragEnd={(node: NetworkNode) => {
if (node.x && node.y) {
node.fx = node.x;
node.fy = node.y;
}
}}
warmupTicks={100}
onZoom={(zoom: number) => {
if (!selectedBook) {
centerGraph(getGraphData());
}
}}
enableNodeDrag={true}
enableZoomPanInteraction={true}
/>
</Box>
</Fade>
{/* Controls */}
{!isLoading && (
<>
{selectedBook && (
<Tooltip title="Return to book overview" arrow>
<IconButton
onClick={handleBackClick}
sx={{
position: 'absolute',
top: 16,
left: 16,
zIndex: 1,
bgcolor: 'background.paper',
boxShadow: 2,
'&:hover': {
bgcolor: 'background.paper',
transform: 'scale(1.1)',
transition: 'transform 0.2s'
}
}}
>
<ArrowBack />
</IconButton>
</Tooltip>
)}
<Box sx={{
position: 'absolute',
top: 16,
right: 16,
zIndex: 1,
bgcolor: 'background.paper',
p: 2,
borderRadius: 2,
boxShadow: 2,
maxWidth: 250,
backdropFilter: 'blur(8px)',
border: '1px solid',
borderColor: 'divider',
transition: 'opacity 0.3s',
opacity: isGraphReady ? 1 : 0
}}>
<Typography variant="body2" color="text.secondary" sx={{
fontStyle: 'italic',
fontFamily: '"Lato", sans-serif'
}}>
{!selectedBook ? 'Click a book to explore its characters' : 'Click characters to view relationships'}
</Typography>
</Box>
</>
)}
</Paper>
</Grid>
<Grid item xs={12} md={4} sx={{ height: '100%' }}>
<Paper sx={{
height: '100%',
maxHeight: 'calc(100vh - 120px)', // Account for header and padding
display: 'flex',
flexDirection: 'column',
borderRadius: 2,
bgcolor: 'background.paper',
boxShadow: theme => `0 0 20px ${theme.palette.divider}`,
position: 'sticky',
top: 24
}}>
<Box sx={{
flex: 1,
overflowY: 'auto',
'&::-webkit-scrollbar': {
width: '8px',
},
'&::-webkit-scrollbar-track': {
background: 'transparent',
},
'&::-webkit-scrollbar-thumb': {
background: theme => theme.palette.divider,
borderRadius: '4px',
},
'&::-webkit-scrollbar-thumb:hover': {
background: theme => theme.palette.action.hover,
}
}}>
{selectedNode ? (
<Box sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom sx={{
color: 'primary.main',
fontWeight: 500,
fontFamily: '"Cormorant", serif'
}}>
{selectedNode.name}
</Typography>
<Box sx={{ display: 'flex', gap: 1, mb: 2, flexWrap: 'wrap' }}>
<Chip
label={selectedNode.type === 'book' ? 'Novel' : selectedNode.type}
size="small"
color={selectedNode.type === 'protagonist' ? 'primary' :
selectedNode.type === 'antagonist' ? 'error' : 'default'}
sx={{ borderRadius: 1 }}
/>
{selectedNode.type !== 'book' && (
<Chip
label={(selectedNode as CharacterNode).class}
size="small"
variant="outlined"
sx={{ borderRadius: 1 }}
/>
)}
</Box>
<Typography color="text.secondary" gutterBottom sx={{
fontFamily: '"Lato", sans-serif'
}}>
{selectedNode.type === 'book' ? `Published: ${(selectedNode as BookNode).year}` : selectedNode.novel}
</Typography>
<Typography variant="body1" paragraph sx={{
mt: 2,
fontFamily: '"Lato", sans-serif',
lineHeight: 1.7
}}>
{selectedNode.description}
</Typography>
{selectedNode.type !== 'book' && (
<>
<Divider sx={{ my: 3 }} />
<Typography variant="h6" sx={{
mb: 2,
color: 'primary.main',
fontFamily: '"Cormorant", serif'
}}>
Relationships
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
{selectedRelationships.map((rel, index) => (
<Box key={index} sx={{
p: 2.5,
bgcolor: 'background.default',
borderRadius: 2,
border: '1px solid',
borderColor: 'divider',
transition: 'transform 0.2s, box-shadow 0.2s',
'&:hover': {
transform: 'translateY(-2px)',
boxShadow: 1
}
}}>
<Typography variant="subtitle1" color="primary" gutterBottom sx={{
fontFamily: '"Cormorant", serif',
fontWeight: 600
}}>
{rel.description}
</Typography>
<Chip
label={rel.type}
size="small"
sx={{
mb: 2,
bgcolor: rel.type === 'family' ? 'success.light' :
rel.type === 'romance' ? 'error.light' :
rel.type === 'friendship' ? 'primary.light' : 'warning.light',
color: '#fff',
borderRadius: 1,
fontFamily: '"Lato", sans-serif'
}}
/>
<Typography variant="body2" sx={{
fontWeight: 500,
mt: 1,
fontFamily: '"Lato", sans-serif'
}}>
Development:
</Typography>
<Box component="ul" sx={{
mt: 1,
pl: 2,
mb: 0 // Remove bottom margin from list
}}>
{rel.development.map((step, i) => (
<Box component="li" key={i} sx={{
mb: i === rel.development.length - 1 ? 0 : 1.5 // Remove margin from last item
}}>
<Typography variant="body2" color="text.secondary" sx={{
fontFamily: '"Lato", sans-serif',
lineHeight: 1.6
}}>
{step}
</Typography>
</Box>
))}
</Box>
</Box>
))}
</Box>
</>
)}
</Box>
) : (
<Box sx={{
p: 3,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
color: 'text.secondary'
}}>
<Typography variant="body1" sx={{ fontStyle: 'italic' }}>
Select a node to view details
</Typography>
</Box>
)}
</Box>
</Paper>
</Grid>
</Grid>
</Grid>
</Container>
<Popper
open={tooltip.open}
@ -447,11 +703,16 @@ export default function NetworkVisualization() {
<Paper sx={{
p: 2,
maxWidth: 300,
bgcolor: 'rgba(255, 255, 255, 0.95)',
boxShadow: 3,
borderRadius: 1
bgcolor: 'background.paper',
boxShadow: 4,
borderRadius: 2,
border: '1px solid',
borderColor: 'divider',
backdropFilter: 'blur(8px)'
}}>
<Typography variant="body2" whiteSpace="pre-line">
<Typography variant="body2" whiteSpace="pre-line" sx={{
fontFamily: '"Lato", sans-serif'
}}>
{tooltip.content}
</Typography>
</Paper>

View file

@ -1,194 +1,326 @@
import React from 'react';
import { Box, Typography, Paper, Grid, Chip, Accordion, AccordionSummary, AccordionDetails } from '@mui/material';
import { Typography, Paper, Grid, Chip, Accordion, AccordionSummary, AccordionDetails } from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import SocialClassView from '../components/SocialClassView';
export default function SocialClass() {
return (
<Box sx={{ width: '100%', p: 3, maxWidth: 1200, mx: 'auto' }}>
<div className="container mx-auto px-4 py-8 max-w-7xl">
{/* Header Section */}
<Box sx={{ mb: 6, textAlign: 'center' }}>
<Typography variant="h3" component="h1" gutterBottom fontFamily="cormorant" color="primary.main">
<div className="text-center mb-12">
<h1 className="font-cormorant text-4xl md:text-5xl text-sage-900 mb-4">
Social Class in Jane Austen's Works
</Typography>
<Typography variant="h6" color="text.secondary" sx={{ mb: 3, maxWidth: 800, mx: 'auto' }}>
</h1>
<p className="text-sage-600 text-lg max-w-3xl mx-auto mb-6">
An analysis of how social class shapes character relationships, marriage prospects, and social mobility across Austen's novels
and their modern retellings.
</Typography>
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'center', flexWrap: 'wrap' }}>
<Chip label="Class Mobility" color="primary" />
<Chip label="Marriage Market" color="secondary" />
<Chip label="Social Status" color="success" />
<Chip label="Wealth & Property" color="warning" />
</Box>
</Box>
{/* Character Analysis Section */}
<Paper elevation={3} sx={{ p: 4, mb: 4, borderRadius: 2 }}>
<Typography variant="h5" gutterBottom color="primary" fontFamily="cormorant">
Character Studies Across Class Lines
</Typography>
<Accordion defaultExpanded>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h6" color="secondary">Pride and Prejudice: The Economics of Marriage</Typography>
</AccordionSummary>
<AccordionDetails>
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" color="primary" gutterBottom>Upper Class</Typography>
<Typography paragraph>
<strong>Mr. Darcy (£10,000 per year)</strong>: Represents the pinnacle of landed gentry, whose wealth
allows him to transcend local social barriers. His initial pride stems from his position, but his
character development shows wealth doesn't guarantee happiness or love.
</Typography>
<Typography paragraph>
<strong>Lady Catherine de Bourgh</strong>: Embodies the aristocracy's resistance to social mobility,
particularly in her opposition to Darcy and Elizabeth's marriage. Her character critiques the
assumption that high birth equals moral superiority.
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" color="primary" gutterBottom>Middle & Lower Classes</Typography>
<Typography paragraph>
<strong>The Bennet Family</strong>: Despite their genteel status, their precarious financial position
(with the entailed estate) demonstrates the vulnerability of women in the period. Mrs. Bennet's
anxiety about her daughters' marriages stems from real economic concerns.
</Typography>
<Typography paragraph>
<strong>The Servants (via Longbourn)</strong>: Jo Baker's retelling gives voice to characters like
Sarah and Mrs. Hill, revealing the hidden labor that maintains the genteel lifestyle of the main characters.
</Typography>
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h6" color="secondary">Mansfield Park: Moral Worth vs. Social Status</Typography>
</AccordionSummary>
<AccordionDetails>
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" color="primary" gutterBottom>The Privileged Circle</Typography>
<Typography paragraph>
<strong>The Bertram Family</strong>: Sir Thomas's wealth from colonial enterprises in Antigua
raises questions about the source of aristocratic wealth. His children's poor moral education
despite their privileged upbringing challenges assumptions about class and character.
</Typography>
<Typography paragraph>
<strong>Mary and Henry Crawford</strong>: Their London sophistication and wealth mask moral
bankruptcy, contrasting with Fanny's humble virtue. They represent the corruption of urban wealth
versus rural values.
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" color="primary" gutterBottom>The Dependent Relations</Typography>
<Typography paragraph>
<strong>Fanny Price</strong>: Her position as a poor relation taken in by wealthy relatives
highlights the complex dynamics of dependency and gratitude. Her moral strength despite her low
status challenges class-based assumptions about worth.
</Typography>
<Typography paragraph>
<strong>The Price Family in Portsmouth</strong>: Their cramped, chaotic household provides a
stark contrast to Mansfield's luxury, highlighting the material realities of class difference
in the period.
</Typography>
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h6" color="secondary">Contemporary Perspectives: Modern Retellings</Typography>
</AccordionSummary>
<AccordionDetails>
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" color="primary" gutterBottom>Pride by Ibi Zoboi</Typography>
<Typography paragraph>
<strong>Zuri Benitez</strong>: Reimagines Elizabeth Bennet as a proud Afro-Latina teenager in
Brooklyn, exploring how gentrification and cultural identity intersect with class in contemporary
America.
</Typography>
<Typography paragraph>
<strong>Darius Darcy</strong>: As a wealthy African American teenager, his character explores the
complexities of privilege within modern racial and social contexts, updating Austen's examination
of pride and prejudice.
</Typography>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" color="primary" gutterBottom>Longbourn's Legacy</Typography>
<Typography paragraph>
<strong>The Servants' Perspective</strong>: Baker's novel reveals the physical labor, limited
opportunities, and complex relationships that supported the genteel world of Pride and Prejudice,
giving voice to historically silenced characters.
</Typography>
<Typography paragraph>
<strong>Class Intersections</strong>: Through characters like James Smith, the novel explores how
war, servitude, and social mobility operated for those below stairs, expanding our understanding
of Austen's world.
</Typography>
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
</Paper>
</p>
<div className="flex gap-2 justify-center flex-wrap">
<Chip
label="Class Mobility"
sx={{
bgcolor: '#4A5D52',
color: 'white',
'&:hover': { bgcolor: '#3A4D42' }
}}
/>
<Chip
label="Marriage Market"
sx={{
bgcolor: '#5B6E65',
color: 'white',
'&:hover': { bgcolor: '#4B5E55' }
}}
/>
<Chip
label="Social Status"
sx={{
bgcolor: '#6B7F75',
color: 'white',
'&:hover': { bgcolor: '#5B6F65' }
}}
/>
<Chip
label="Wealth & Property"
sx={{
bgcolor: '#7C8F86',
color: 'white',
'&:hover': { bgcolor: '#6C7F76' }
}}
/>
</div>
</div>
{/* Interactive View */}
<Paper elevation={3} sx={{ p: 4, mb: 4, borderRadius: 2 }}>
<Typography variant="h5" gutterBottom color="primary" fontFamily="cormorant" sx={{ mb: 3 }}>
Interactive Class Analysis
</Typography>
<SocialClassView />
<Paper className="mb-8 rounded-lg border border-sage-200" elevation={0}>
<div className="p-6 md:p-8">
<Typography variant="h5" className="font-cormorant text-2xl text-sage-900 mb-4">
Interactive Class Analysis
</Typography>
<div className="bg-sage-50 p-6 rounded-lg mb-6">
<Typography variant="h6" className="font-cormorant text-xl text-sage-800 mb-3">
Explore Social Class Differences
</Typography>
<div className="space-y-4 text-sage-700">
<div className="mb-4">
<p className="text-sage-800 font-medium mb-2">How to Compare Characters:</p>
<div className="pl-4 space-y-2">
<p className="leading-relaxed flex items-start">
<span className="text-sage-900 font-semibold mr-2">1.</span>
Select your first character by clicking on any character card below
</p>
<p className="leading-relaxed flex items-start">
<span className="text-sage-900 font-semibold mr-2">2.</span>
Choose a second character to initiate the comparison
</p>
<p className="leading-relaxed flex items-start">
<span className="text-sage-900 font-semibold mr-2">3.</span>
Examine their social positions, incomes, and circumstances in the comparison view
</p>
</div>
</div>
<div className="bg-white/50 p-4 rounded-lg">
<p className="text-sage-800 font-medium mb-2">Suggested Comparisons:</p>
<ul className="list-disc list-inside space-y-1 text-sage-600">
<li>Mr. Darcy vs. Elizabeth Bennet - Explore the class divide in courtship</li>
<li>The Bennet Family vs. The Servants - Understand the full spectrum of social hierarchy</li>
<li>Upper Class vs. Working Class - See the economic disparities of Regency England</li>
</ul>
</div>
<p className="text-sage-600 italic border-l-2 border-sage-300 pl-4 mt-4">
💡 Tip: Compare characters from different social classes to understand how wealth, status, and opportunities
varied dramatically in Austen's world.
</p>
</div>
</div>
<SocialClassView />
</div>
</Paper>
{/* Character Analysis Section */}
<Paper className="mb-8 rounded-lg border border-sage-200" elevation={0}>
<div className="p-6 md:p-8">
<Typography variant="h5" className="font-cormorant text-2xl text-sage-900 mb-6">
Character Studies Across Class Lines
</Typography>
<Accordion
defaultExpanded
sx={{
border: '1px solid #E2E8F0',
'&:before': { display: 'none' },
borderRadius: '0.5rem',
mb: 2,
'&:first-of-type': { borderRadius: '0.5rem' },
'&:last-of-type': { borderRadius: '0.5rem' }
}}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon className="text-sage-600" />}
className="bg-sage-50 hover:bg-sage-100"
>
<Typography className="font-cormorant text-xl text-sage-800">
Pride and Prejudice: The Economics of Marriage
</Typography>
</AccordionSummary>
<AccordionDetails className="bg-white p-6">
<Grid container spacing={4}>
<Grid item xs={12} md={6}>
<Typography className="font-cormorant text-lg text-sage-800 mb-3">
Upper Class
</Typography>
<div className="space-y-4 text-sage-700">
<Typography paragraph className="leading-relaxed">
<strong>Mr. Darcy (£10,000 per year)</strong>: Represents the pinnacle of landed gentry, whose wealth
allows him to transcend local social barriers. His initial pride stems from his position, but his
character development shows wealth doesn't guarantee happiness or love.
</Typography>
<Typography paragraph className="leading-relaxed">
<strong>Lady Catherine de Bourgh</strong>: Embodies the aristocracy's resistance to social mobility,
particularly in her opposition to Darcy and Elizabeth's marriage. Her character critiques the
assumption that high birth equals moral superiority.
</Typography>
</div>
</Grid>
<Grid item xs={12} md={6}>
<Typography className="font-cormorant text-lg text-sage-800 mb-3">
Middle & Lower Classes
</Typography>
<div className="space-y-4 text-sage-700">
<Typography paragraph className="leading-relaxed">
<strong>The Bennet Family</strong>: Despite their genteel status, their precarious financial position
(with the entailed estate) demonstrates the vulnerability of women in the period. Mrs. Bennet's
anxiety about her daughters' marriages stems from real economic concerns.
</Typography>
<Typography paragraph className="leading-relaxed">
<strong>The Servants (via Longbourn)</strong>: Jo Baker's retelling gives voice to characters like
Sarah and Mrs. Hill, revealing the hidden labor that maintains the genteel lifestyle of the main characters.
</Typography>
</div>
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
<Accordion
sx={{
border: '1px solid #E2E8F0',
'&:before': { display: 'none' },
borderRadius: '0.5rem',
mb: 2
}}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon className="text-sage-600" />}
className="bg-sage-50 hover:bg-sage-100"
>
<Typography className="font-cormorant text-xl text-sage-800">
Mansfield Park: Moral Worth vs. Social Status
</Typography>
</AccordionSummary>
<AccordionDetails className="bg-white p-6">
<Grid container spacing={4}>
<Grid item xs={12} md={6}>
<Typography className="font-cormorant text-lg text-sage-800 mb-3">
The Privileged Circle
</Typography>
<div className="space-y-4 text-sage-700">
<Typography paragraph className="leading-relaxed">
<strong>The Bertram Family</strong>: Sir Thomas's wealth from colonial enterprises in Antigua
raises questions about the source of aristocratic wealth. His children's poor moral education
despite their privileged upbringing challenges assumptions about class and character.
</Typography>
<Typography paragraph className="leading-relaxed">
<strong>Mary and Henry Crawford</strong>: Their London sophistication and wealth mask moral
bankruptcy, contrasting with Fanny's humble virtue. They represent the corruption of urban wealth
versus rural values.
</Typography>
</div>
</Grid>
<Grid item xs={12} md={6}>
<Typography className="font-cormorant text-lg text-sage-800 mb-3">
The Dependent Relations
</Typography>
<div className="space-y-4 text-sage-700">
<Typography paragraph className="leading-relaxed">
<strong>Fanny Price</strong>: Her position as a poor relation taken in by wealthy relatives
highlights the complex dynamics of dependency and gratitude. Her moral strength despite her low
status challenges class-based assumptions about worth.
</Typography>
<Typography paragraph className="leading-relaxed">
<strong>The Price Family in Portsmouth</strong>: Their cramped, chaotic household provides a
stark contrast to Mansfield's luxury, highlighting the material realities of class difference
in the period.
</Typography>
</div>
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
<Accordion
sx={{
border: '1px solid #E2E8F0',
'&:before': { display: 'none' },
borderRadius: '0.5rem'
}}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon className="text-sage-600" />}
className="bg-sage-50 hover:bg-sage-100"
>
<Typography className="font-cormorant text-xl text-sage-800">
Contemporary Perspectives: Modern Retellings
</Typography>
</AccordionSummary>
<AccordionDetails className="bg-white p-6">
<Grid container spacing={4}>
<Grid item xs={12} md={6}>
<Typography className="font-cormorant text-lg text-sage-800 mb-3">
Pride by Ibi Zoboi
</Typography>
<div className="space-y-4 text-sage-700">
<Typography paragraph className="leading-relaxed">
<strong>Zuri Benitez</strong>: Reimagines Elizabeth Bennet as a proud Afro-Latina teenager in
Brooklyn, exploring how gentrification and cultural identity intersect with class in contemporary
America.
</Typography>
<Typography paragraph className="leading-relaxed">
<strong>Darius Darcy</strong>: As a wealthy African American teenager, his character explores the
complexities of privilege within modern racial and social contexts, updating Austen's examination
of pride and prejudice.
</Typography>
</div>
</Grid>
<Grid item xs={12} md={6}>
<Typography className="font-cormorant text-lg text-sage-800 mb-3">
Longbourn's Legacy
</Typography>
<div className="space-y-4 text-sage-700">
<Typography paragraph className="leading-relaxed">
<strong>The Servants' Perspective</strong>: Baker's novel reveals the physical labor, limited
opportunities, and complex relationships that supported the genteel world of Pride and Prejudice,
giving voice to historically silenced characters.
</Typography>
<Typography paragraph className="leading-relaxed">
<strong>Class Intersections</strong>: Through characters like James Smith, the novel explores how
war, servitude, and social mobility operated for those below stairs, expanding our understanding
of Austen's world.
</Typography>
</div>
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
</div>
</Paper>
{/* Critical Analysis */}
<Paper elevation={3} sx={{ p: 4, borderRadius: 2 }}>
<Typography variant="h5" gutterBottom color="primary" fontFamily="cormorant">
Critical Insights
</Typography>
<Grid container spacing={3}>
<Grid item xs={12} md={4}>
<Box sx={{ p: 2, bgcolor: 'background.default', borderRadius: 1 }}>
<Typography variant="h6" gutterBottom color="secondary">
Economic Realities
</Typography>
<Typography>
Austen's precise attention to characters' incomes and property reflects the real economic pressures
that shaped marriage choices and social mobility in the Regency period. Her novels acknowledge both
the practical necessity of financial security and its moral dangers.
</Typography>
</Box>
<Paper className="rounded-lg border border-sage-200" elevation={0}>
<div className="p-6 md:p-8">
<Typography variant="h5" className="font-cormorant text-2xl text-sage-900 mb-6">
Critical Insights
</Typography>
<Grid container spacing={4}>
<Grid item xs={12} md={4}>
<div className="bg-sage-50 p-6 rounded-lg">
<Typography className="font-cormorant text-xl text-sage-800 mb-3">
Economic Realities
</Typography>
<Typography className="text-sage-700 leading-relaxed">
Austen's precise attention to characters' incomes and property reflects the real economic pressures
that shaped marriage choices and social mobility in the Regency period. Her novels acknowledge both
the practical necessity of financial security and its moral dangers.
</Typography>
</div>
</Grid>
<Grid item xs={12} md={4}>
<div className="bg-sage-50 p-6 rounded-lg">
<Typography className="font-cormorant text-xl text-sage-800 mb-3">
Social Mobility
</Typography>
<Typography className="text-sage-700 leading-relaxed">
Through characters like Emma Woodhouse and Elizabeth Bennet, Austen explores the possibilities and
limitations of social mobility through marriage, education, and moral development, while acknowledging
the rigid class structures of her time.
</Typography>
</div>
</Grid>
<Grid item xs={12} md={4}>
<div className="bg-sage-50 p-6 rounded-lg">
<Typography className="font-cormorant text-xl text-sage-800 mb-3">
Modern Relevance
</Typography>
<Typography className="text-sage-700 leading-relaxed">
Contemporary adaptations like Pride and Longbourn demonstrate how Austen's exploration of class,
privilege, and social mobility remains relevant to modern discussions of inequality, opportunity,
and social justice.
</Typography>
</div>
</Grid>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={{ p: 2, bgcolor: 'background.default', borderRadius: 1 }}>
<Typography variant="h6" gutterBottom color="secondary">
Social Mobility
</Typography>
<Typography>
Through characters like Emma Woodhouse and Elizabeth Bennet, Austen explores the possibilities and
limitations of social mobility through marriage, education, and moral development, while acknowledging
the rigid class structures of her time.
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={{ p: 2, bgcolor: 'background.default', borderRadius: 1 }}>
<Typography variant="h6" gutterBottom color="secondary">
Modern Relevance
</Typography>
<Typography>
Contemporary adaptations like Pride and Longbourn demonstrate how Austen's exploration of class,
privilege, and social mobility remains relevant to modern discussions of inequality, opportunity,
and social justice.
</Typography>
</Box>
</Grid>
</Grid>
</div>
</Paper>
</Box>
</div>
);
}