/visualizeusingreact exchange of respiratory gases
mkdir -p /tmp/workspace/gas-exchange-react && echo "Directory ready"
~/gas-exchange-react/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Exchange of Respiratory Gases</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Segoe UI', Arial, sans-serif;
background: #0f1b2d;
color: #e8f0fe;
min-height: 100vh;
}
#root { width: 100%; }
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useRef } = React;
/* ─── colour palette ─── */
const C = {
o2Rich: '#e53e3e', // bright red – O2-rich blood
o2Poor: '#4a6fa5', // blue – deoxygenated blood
o2Gas: '#68d391', // green – O2 gas molecules
co2Gas: '#f6ad55', // amber – CO2 gas molecules
alveoli: '#bee3f8',
tissue: '#fefcbf',
airway: '#90cdf4',
bg1: '#1a2a4a',
bg2: '#162032',
text: '#e8f0fe',
accent: '#63b3ed',
panel: 'rgba(255,255,255,0.07)',
border: 'rgba(255,255,255,0.15)',
};
/* ══════════════════════════════════════════════════
ANIMATED MOLECULE PARTICLE
══════════════════════════════════════════════════ */
function Molecule({ x, y, type, size = 10, opacity = 1 }) {
const color = type === 'O2' ? C.o2Gas : C.co2Gas;
const label = type === 'O2' ? 'O₂' : 'CO₂';
return (
<g transform={`translate(${x},${y})`} opacity={opacity}>
<circle r={size} fill={color} />
<text
textAnchor="middle"
dominantBaseline="central"
fontSize={size * 0.9}
fontWeight="bold"
fill="#1a202c"
>{label}</text>
</g>
);
}
/* ══════════════════════════════════════════════════
ANIMATED ARROW
══════════════════════════════════════════════════ */
function Arrow({ x1, y1, x2, y2, color, label, dashed }) {
const id = `arr-${x1}-${y1}`;
const dx = x2 - x1, dy = y2 - y1;
const angle = Math.atan2(dy, dx) * 180 / Math.PI;
const len = Math.sqrt(dx*dx + dy*dy);
return (
<g>
<defs>
<marker id={id} markerWidth="8" markerHeight="8"
refX="6" refY="3" orient="auto">
<path d="M0,0 L0,6 L8,3 z" fill={color} />
</marker>
</defs>
<line
x1={x1} y1={y1} x2={x2 - 10 * Math.cos(angle * Math.PI/180)}
y2={y2 - 10 * Math.sin(angle * Math.PI/180)}
stroke={color} strokeWidth={2.5}
strokeDasharray={dashed ? '6,4' : 'none'}
markerEnd={`url(#${id})`}
/>
{label && (
<text
x={(x1+x2)/2}
y={(y1+y2)/2 - 8}
textAnchor="middle"
fontSize="11"
fill={color}
fontWeight="bold"
>{label}</text>
)}
</g>
);
}
/* ══════════════════════════════════════════════════
PARTIAL PRESSURE TABLE
══════════════════════════════════════════════════ */
const PP_DATA = [
{ site: 'Inspired air', pO2: 159, pCO2: 0.3 },
{ site: 'Alveolar air', pO2: 100, pCO2: 40 },
{ site: 'Pulmonary cap (venous)', pO2: 40, pCO2: 45 },
{ site: 'Pulmonary cap (arterial)', pO2: 100, pCO2: 40 },
{ site: 'Systemic cap (arterial)', pO2: 95, pCO2: 40 },
{ site: 'Systemic cap (venous)', pO2: 40, pCO2: 45 },
{ site: 'Tissue cells', pO2: 20, pCO2: 60 },
];
function PPTable() {
return (
<div style={{
background: C.panel,
border: `1px solid ${C.border}`,
borderRadius: 12,
padding: '16px 20px',
marginTop: 20,
}}>
<h3 style={{ color: C.accent, marginBottom: 12, fontSize: 15 }}>
Partial Pressures (mm Hg)
</h3>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
<thead>
<tr style={{ borderBottom: `1px solid ${C.border}` }}>
<th style={{ textAlign: 'left', padding: '4px 8px', color: '#a0aec0' }}>Site</th>
<th style={{ textAlign: 'center', padding: '4px 8px', color: C.o2Gas }}>PO₂</th>
<th style={{ textAlign: 'center', padding: '4px 8px', color: C.co2Gas }}>PCO₂</th>
</tr>
</thead>
<tbody>
{PP_DATA.map((row, i) => (
<tr key={i} style={{ borderBottom: `1px solid rgba(255,255,255,0.05)` }}>
<td style={{ padding: '5px 8px' }}>{row.site}</td>
<td style={{ padding: '5px 8px', textAlign: 'center', color: C.o2Gas, fontWeight: 'bold' }}>{row.pO2}</td>
<td style={{ padding: '5px 8px', textAlign: 'center', color: C.co2Gas, fontWeight: 'bold' }}>{row.pCO2}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
/* ══════════════════════════════════════════════════
MAIN DIAGRAM – SVG
══════════════════════════════════════════════════ */
function GasExchangeDiagram({ tick }) {
const W = 820, H = 560;
// Animate molecule positions
const phase = (tick % 60) / 60; // 0→1 cycle
// O2 molecule in alveolus moving into blood (lung)
const lungO2x = 310 + phase * 80;
const lungO2y = 185;
// CO2 molecule from blood to alveolus (lung)
const lungCO2x = 310 + (1 - phase) * 80;
const lungCO2y = 220;
// O2 molecule from blood to tissue
const tissO2x = 520 + phase * 70;
const tissO2y = 375;
// CO2 molecule from tissue to blood
const tissCO2x = 520 + (1 - phase) * 70;
const tissCO2y = 405;
return (
<svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', maxWidth: W, display: 'block', margin: '0 auto' }}>
{/* ── background gradient ── */}
<defs>
<linearGradient id="bgGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#1a2a4a" />
<stop offset="100%" stopColor="#162032" />
</linearGradient>
<linearGradient id="bloodGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={C.o2Rich} stopOpacity="0.7" />
<stop offset="100%" stopColor={C.o2Rich} stopOpacity="0.4" />
</linearGradient>
<linearGradient id="venGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={C.o2Poor} stopOpacity="0.7" />
<stop offset="100%" stopColor={C.o2Poor} stopOpacity="0.4" />
</linearGradient>
<filter id="glow">
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
<feMerge><feMergeNode in="coloredBlur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<rect width={W} height={H} fill="url(#bgGrad)" rx="16" />
{/* ══ TITLE ══ */}
<text x={W/2} y={34} textAnchor="middle" fontSize="20" fontWeight="bold" fill={C.accent} filter="url(#glow)">
Exchange of Respiratory Gases
</text>
<text x={W/2} y={52} textAnchor="middle" fontSize="12" fill="#90cdf4">
Pulmonary (Lung) ↔ Systemic (Tissue) Gas Exchange via Diffusion
</text>
{/* ══════════════════════════════════
SECTION A – LUNGS (left-centre)
══════════════════════════════════ */}
{/* Alveolus sac */}
<ellipse cx={210} cy={200} rx={120} ry={80} fill="rgba(190,227,248,0.12)" stroke={C.alveoli} strokeWidth={2} />
<text x={210} y={126} textAnchor="middle" fontSize="13" fontWeight="bold" fill={C.alveoli}>ALVEOLUS</text>
{/* Alveolar gas labels */}
<text x={155} y={188} fontSize="11" fill={C.o2Gas}>PO₂ = 100</text>
<text x={155} y={203} fontSize="11" fill={C.co2Gas}>PCO₂ = 40</text>
{/* Pulmonary capillary – venous side */}
<rect x={300} y={155} width={60} height={100} rx="10" fill="url(#venGrad)" stroke={C.o2Poor} strokeWidth={1.5} />
<text x={330} y={148} textAnchor="middle" fontSize="10" fill={C.o2Poor}>Mixed venous</text>
<text x={330} y={170} textAnchor="middle" fontSize="10" fill={C.text}>PO₂=40</text>
<text x={330} y={183} textAnchor="middle" fontSize="10" fill={C.text}>PCO₂=45</text>
{/* Pulmonary capillary – arterial side */}
<rect x={380} y={155} width={60} height={100} rx="10" fill="url(#bloodGrad)" stroke={C.o2Rich} strokeWidth={1.5} />
<text x={410} y={148} textAnchor="middle" fontSize="10" fill={C.o2Rich}>Oxygenated</text>
<text x={410} y={170} textAnchor="middle" fontSize="10" fill={C.text}>PO₂=100</text>
<text x={410} y={183} textAnchor="middle" fontSize="10" fill={C.text}>PCO₂=40</text>
{/* Alveolar–capillary membrane label */}
<line x1={299} y1={155} x2={299} y2={255} stroke="rgba(255,255,255,0.3)" strokeWidth={1} strokeDasharray="4,3" />
<text x={299} y={272} textAnchor="middle" fontSize="10" fill="rgba(255,255,255,0.5)" transform="rotate(-90,299,272)" dy="-4">
</text>
<text x={265} y={278} textAnchor="middle" fontSize="10" fill="#a0aec0">Alveolar–capillary membrane</text>
<text x={265} y={290} textAnchor="middle" fontSize="10" fill="#718096">(~0.5 µm thick)</text>
{/* O2 diffusion arrow: alveolus → capillary */}
<Arrow x1={248} y1={190} x2={296} y2={190} color={C.o2Gas} label="O₂ ↓ gradient" />
{/* CO2 diffusion arrow: capillary → alveolus */}
<Arrow x1={296} y1={218} x2={248} y2={218} color={C.co2Gas} label="CO₂ ↑ gradient" />
{/* Animated O2 molecule */}
<Molecule x={Math.min(lungO2x, 288)} y={lungO2y} type="O2" size={9} />
{/* Animated CO2 molecule */}
<Molecule x={Math.max(lungCO2x, 248)} y={lungCO2y} type="CO2" size={9} />
{/* Inspiration arrow */}
<Arrow x1={90} y1={120} x2={140} y2={160} color={C.airway} label="Inspiration" />
<text x={62} y={113} fontSize="11" fill={C.airway}>PO₂=159</text>
<text x={62} y={127} fontSize="11" fill={C.airway}>PCO₂≈0</text>
{/* Expiration arrow */}
<Arrow x1={140} y1={250} x2={90} y2={285} color={C.co2Gas} label="Expiration" dashed />
{/* ══════════════════════════════════
CIRCULATORY VESSELS
══════════════════════════════════ */}
{/* Pulmonary vein (oxygenated → heart → body) */}
<path d="M440 205 Q500 205 500 270 Q500 330 500 360" stroke={C.o2Rich} strokeWidth={14} fill="none" strokeOpacity="0.55" />
<text x={510} y={295} fontSize="11" fill={C.o2Rich} fontWeight="bold">Pulmonary vein</text>
<text x={510} y={310} fontSize="10" fill={C.o2Rich}>(oxygenated)</text>
{/* Pulmonary artery (deoxygenated → lungs) */}
<path d="M300 360 Q300 310 300 270 Q300 205 310 205" stroke={C.o2Poor} strokeWidth={14} fill="none" strokeOpacity="0.55" />
<text x={182} y={340} fontSize="11" fill={C.o2Poor} fontWeight="bold">Pulmonary artery</text>
<text x={182} y={355} fontSize="10" fill={C.o2Poor}>(deoxygenated)</text>
{/* Heart icon placeholder */}
<ellipse cx={400} cy={360} rx={38} ry={35} fill="rgba(229,62,62,0.2)" stroke={C.o2Rich} strokeWidth={2} />
<text x={400} y={355} textAnchor="middle" fontSize="22">♥</text>
<text x={400} y={378} textAnchor="middle" fontSize="11" fill={C.o2Rich}>Heart</text>
{/* Aorta / systemic artery */}
<path d="M438 360 Q500 360 540 360 Q580 360 600 370" stroke={C.o2Rich} strokeWidth={12} fill="none" strokeOpacity="0.55" />
<text x={550} y={352} fontSize="11" fill={C.o2Rich}>Systemic artery</text>
<text x={550} y={365} fontSize="10" fill={C.o2Rich}>PO₂=95, PCO₂=40</text>
{/* Systemic vein */}
<path d="M600 440 Q560 440 500 430 Q440 420 400 410 Q380 405 362 390" stroke={C.o2Poor} strokeWidth={12} fill="none" strokeOpacity="0.55" />
<text x={490} y={452} fontSize="11" fill={C.o2Poor}>Systemic vein</text>
<text x={490} y={466} fontSize="10" fill={C.o2Poor}>PO₂=40, PCO₂=45</text>
{/* ══════════════════════════════════
SECTION B – TISSUE (right)
══════════════════════════════════ */}
<rect x={600} y={340} width={150} height={120} rx={14} fill="rgba(254,252,191,0.1)" stroke={C.tissue} strokeWidth={2} />
<text x={675} y={328} textAnchor="middle" fontSize="13" fontWeight="bold" fill={C.tissue}>TISSUE / CELL</text>
<text x={620} y={370} fontSize="11" fill={C.o2Gas}>PO₂ ≈ 20 mm Hg</text>
<text x={620} y={385} fontSize="11" fill={C.co2Gas}>PCO₂ ≈ 60 mm Hg</text>
<text x={620} y={400} fontSize="10" fill="#a0aec0">(mitochondria consume O₂</text>
<text x={620} y={413} fontSize="10" fill="#a0aec0"> and produce CO₂)</text>
{/* O2 diffusion: blood → tissue */}
<Arrow x1={600} y1={375} x2={540} y2={385} color={C.o2Gas} label="O₂" />
{/* CO2 diffusion: tissue → blood */}
<Arrow x1={540} y1={405} x2={600} y2={410} color={C.co2Gas} label="CO₂" />
{/* Animated tissue O2 */}
<Molecule x={tissO2x} y={tissO2y} type="O2" size={9} />
{/* Animated tissue CO2 */}
<Molecule x={tissCO2x} y={tissCO2y} type="CO2" size={9} />
{/* ══════════════════════════════════
FICK'S LAW LABEL
══════════════════════════════════ */}
<rect x={24} y={340} width={180} height={80} rx={10} fill={C.panel} stroke={C.border} />
<text x={114} y={358} textAnchor="middle" fontSize="12" fontWeight="bold" fill={C.accent}>Fick's Law</text>
<text x={114} y={375} textAnchor="middle" fontSize="11" fill={C.text}>V = D · A · ΔP / Δx</text>
<text x={114} y={390} textAnchor="middle" fontSize="10" fill="#a0aec0">D(CO₂) ≈ 20× D(O₂)</text>
<text x={114} y={405} textAnchor="middle" fontSize="10" fill="#a0aec0">Area↑ → V↑ | Thickness↑ → V↓</text>
{/* ══════════════════════════════════
LEGEND
══════════════════════════════════ */}
<rect x={590} y={58} width={210} height={105} rx={10} fill={C.panel} stroke={C.border} />
<text x={695} y={76} textAnchor="middle" fontSize="12" fontWeight="bold" fill={C.accent}>Legend</text>
<circle cx={608} cy={92} r={7} fill={C.o2Gas} />
<text x={620} y={96} fontSize="11" fill={C.text}>O₂ molecule (diffusing)</text>
<circle cx={608} cy={112} r={7} fill={C.co2Gas} />
<text x={620} y={116} fontSize="11" fill={C.text}>CO₂ molecule (diffusing)</text>
<rect x={601} cy={128} width={14} height={8} fill={C.o2Rich} rx="2" />
<rect x={601} y={126} width={14} height={8} fill={C.o2Rich} rx="2" />
<text x={620} y={134} fontSize="11" fill={C.text}>Oxygenated blood</text>
<rect x={601} y={144} width={14} height={8} fill={C.o2Poor} rx="2" />
<text x={620} y={152} fontSize="11" fill={C.text}>Deoxygenated blood</text>
{/* ══ DIFFUSION COEFFICIENT NOTE ══ */}
<text x={W/2} y={H-14} textAnchor="middle" fontSize="10" fill="#718096">
CO₂ diffuses ~20× faster than O₂ despite similar partial pressure gradients (higher solubility × MW ratio)
</text>
</svg>
);
}
/* ══════════════════════════════════════════════════
INFO CARDS
══════════════════════════════════════════════════ */
const CARDS = [
{
title: 'Pulmonary Gas Exchange',
color: '#63b3ed',
icon: '🫁',
points: [
'O₂ diffuses from alveoli (PO₂=100) into pulmonary capillary blood (PO₂=40) — gradient 60 mm Hg',
'CO₂ diffuses from mixed venous blood (PCO₂=45) into alveoli (PCO₂=40) — gradient 5 mm Hg',
'Equilibration occurs within ~0.25 s (only 1/3 of capillary transit time)',
'Alveolar–capillary membrane is only ~0.5 µm thick',
],
},
{
title: 'Systemic / Tissue Gas Exchange',
color: '#f6ad55',
icon: '🔬',
points: [
'O₂ diffuses from systemic capillary blood (PO₂=95) into tissue cells (PO₂≈20) — gradient ~75 mm Hg',
'CO₂ produced by mitochondria raises tissue PCO₂ to ~60 mm Hg; diffuses into blood (PCO₂=40)',
'Result: arterialised blood becomes venous blood (PO₂=40, PCO₂=45)',
'Higher metabolic rate → steeper gradients → more gas exchanged',
],
},
{
title: "Fick's Law of Diffusion",
color: '#68d391',
icon: '📐',
points: [
'Rate ∝ Diffusion coefficient × Surface area × ΔPartial pressure',
'Rate ∝ 1 / Membrane thickness',
'D(CO₂) ≈ 20× D(O₂) because CO₂ is ~24× more soluble in water',
'Emphysema: surface area↓ → DL↓; Fibrosis/oedema: thickness↑ → DL↓',
],
},
{
title: 'Alveolar Gas Equation',
color: '#e9d8fd',
icon: '🧮',
points: [
'PAO₂ = PiO₂ − PACO₂ / R',
'PiO₂ (humidified inspired) = (760−47) × 0.21 ≈ 150 mm Hg',
'Respiratory exchange ratio R = VCO₂/VO₂ ≈ 0.8 at rest',
'PAO₂ ≈ 150 − 40/0.8 ≈ 100 mm Hg',
],
},
];
function InfoCard({ card }) {
return (
<div style={{
background: 'rgba(255,255,255,0.06)',
border: `1px solid ${card.color}44`,
borderLeft: `4px solid ${card.color}`,
borderRadius: 12,
padding: '16px 18px',
flex: '1 1 320px',
minWidth: 280,
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
<span style={{ fontSize: 22 }}>{card.icon}</span>
<h3 style={{ color: card.color, fontSize: 15 }}>{card.title}</h3>
</div>
<ul style={{ paddingLeft: 18 }}>
{card.points.map((p, i) => (
<li key={i} style={{ marginBottom: 6, fontSize: 13, lineHeight: 1.5, color: '#cbd5e0' }}>{p}</li>
))}
</ul>
</div>
);
}
/* ══════════════════════════════════════════════════
HAEMOGLOBIN O2 DISSOCIATION CURVE (mini SVG)
══════════════════════════════════════════════════ */
function ODC() {
// Sigmoid curve points (simplified Hill equation)
const W = 300, H = 160;
const points = [];
for (let pO2 = 0; pO2 <= 100; pO2 += 2) {
const sat = Math.pow(pO2, 2.7) / (Math.pow(26, 2.7) + Math.pow(pO2, 2.7)) * 100;
const px = 30 + (pO2 / 100) * 250;
const py = 140 - (sat / 100) * 120;
points.push(`${px},${py}`);
}
const pathD = 'M ' + points.join(' L ');
return (
<div style={{
background: C.panel,
border: `1px solid ${C.border}`,
borderRadius: 12,
padding: '16px 20px',
marginTop: 20,
}}>
<h3 style={{ color: C.accent, marginBottom: 10, fontSize: 15 }}>
Oxyhaemoglobin Dissociation Curve
</h3>
<svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', maxWidth: W }}>
<rect width={W} height={H} fill="rgba(0,0,0,0.2)" rx="8" />
{/* axes */}
<line x1={30} y1={140} x2={280} y2={140} stroke="#718096" strokeWidth={1.5} />
<line x1={30} y1={20} x2={30} y2={140} stroke="#718096" strokeWidth={1.5} />
{/* axis labels */}
<text x={155} y={H-2} textAnchor="middle" fontSize="11" fill="#a0aec0">PO₂ (mm Hg)</text>
<text x={8} y={80} textAnchor="middle" fontSize="11" fill="#a0aec0" transform="rotate(-90,8,80)">SaO₂ (%)</text>
{/* tick marks */}
{[0,25,50,75,100].map(v => (
<g key={v}>
<line x1={30 + v*2.5} y1={140} x2={30 + v*2.5} y2={144} stroke="#718096" />
<text x={30 + v*2.5} y={154} textAnchor="middle" fontSize="9" fill="#718096">{v}</text>
</g>
))}
{[0,25,50,75,100].map(v => (
<g key={v}>
<line x1={26} y1={140 - v*1.2} x2={30} y2={140 - v*1.2} stroke="#718096" />
<text x={22} y={140 - v*1.2 + 3} textAnchor="end" fontSize="9" fill="#718096">{v}</text>
</g>
))}
{/* sigmoid curve */}
<polyline points={points.join(' ')} fill="none" stroke={C.o2Rich} strokeWidth={2.5} />
{/* key points */}
{/* tissue (40 mmHg, ~75%) */}
<circle cx={30+40*2.5} cy={140 - (Math.pow(40,2.7)/(Math.pow(26,2.7)+Math.pow(40,2.7)))*120} r={5} fill={C.o2Poor} />
<text x={30+40*2.5+6} y={140 - (Math.pow(40,2.7)/(Math.pow(26,2.7)+Math.pow(40,2.7)))*120} fontSize="9" fill={C.o2Poor}>Tissues (40)</text>
{/* lung (100 mmHg, ~98%) */}
<circle cx={30+100*2.5} cy={140 - (Math.pow(100,2.7)/(Math.pow(26,2.7)+Math.pow(100,2.7)))*120} r={5} fill={C.o2Gas} />
<text x={30+100*2.5-50} y={140 - (Math.pow(100,2.7)/(Math.pow(26,2.7)+Math.pow(100,2.7)))*120 - 6} fontSize="9" fill={C.o2Gas}>Lungs (100)</text>
{/* P50 line */}
<line x1={30+26*2.5} y1={20} x2={30+26*2.5} y2={140} stroke="#718096" strokeDasharray="3,3" strokeWidth={1} />
<text x={30+26*2.5+2} y={30} fontSize="9" fill="#718096">P₅₀=26</text>
</svg>
<p style={{ fontSize: 12, color: '#a0aec0', marginTop: 8 }}>
At the lungs (PO₂=100), Hb is ~98% saturated. At the tissues (PO₂=40), Hb releases O₂ → saturation falls to ~75%.
This ~23% difference represents the O₂ delivered per cardiac cycle.
</p>
</div>
);
}
/* ══════════════════════════════════════════════════
TAB NAVIGATION
══════════════════════════════════════════════════ */
const TABS = ['Overview Diagram', 'Key Concepts', 'O₂-Hb Curve', 'Partial Pressures'];
/* ══════════════════════════════════════════════════
APP ROOT
══════════════════════════════════════════════════ */
function App() {
const [tab, setTab] = useState(0);
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 80);
return () => clearInterval(id);
}, []);
return (
<div style={{ maxWidth: 900, margin: '0 auto', padding: '20px 16px 40px' }}>
{/* HEADER */}
<div style={{
textAlign: 'center',
marginBottom: 24,
padding: '24px 20px 16px',
background: 'linear-gradient(135deg, rgba(99,179,237,0.15), rgba(104,211,145,0.1))',
borderRadius: 16,
border: `1px solid ${C.border}`,
}}>
<h1 style={{ fontSize: 28, fontWeight: 800, color: C.accent, marginBottom: 6 }}>
🫁 Exchange of Respiratory Gases
</h1>
<p style={{ color: '#90cdf4', fontSize: 14 }}>
Interactive visualization of O₂ and CO₂ exchange at the lungs and peripheral tissues
</p>
</div>
{/* TAB BAR */}
<div style={{ display: 'flex', gap: 8, marginBottom: 20, flexWrap: 'wrap' }}>
{TABS.map((t, i) => (
<button
key={i}
onClick={() => setTab(i)}
style={{
padding: '8px 18px',
borderRadius: 8,
border: 'none',
cursor: 'pointer',
fontSize: 13,
fontWeight: 600,
background: tab === i ? C.accent : 'rgba(255,255,255,0.08)',
color: tab === i ? '#1a202c' : C.text,
transition: 'all 0.2s',
}}
>{t}</button>
))}
</div>
{/* TAB CONTENT */}
{tab === 0 && (
<div style={{
background: C.panel,
border: `1px solid ${C.border}`,
borderRadius: 16,
padding: 16,
}}>
<GasExchangeDiagram tick={tick} />
<p style={{ textAlign: 'center', fontSize: 12, color: '#718096', marginTop: 8 }}>
Molecules animate continuously. Arrows show net diffusion direction driven by partial pressure gradients.
</p>
</div>
)}
{tab === 1 && (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 16 }}>
{CARDS.map((c, i) => <InfoCard key={i} card={c} />)}
</div>
)}
{tab === 2 && <ODC />}
{tab === 3 && <PPTable />}
{/* FOOTER CREDIT */}
<p style={{ textAlign: 'center', fontSize: 11, color: '#4a5568', marginTop: 28 }}>
Based on Costanzo Physiology 7th Ed · Murray & Nadel's Respiratory Medicine · Fick's Law of Diffusion
</p>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
index
HTML File · HTML