feat(pe-guide): Cardiovascular system with APTM auscultation SVG

Fourth PE system added. Adolescent cardiovascular exam at the same
teaching-focused depth as respiratory/neuro: five components with
significance + pearls + detailed step methods + watch-for blocks.

APTM auscultation diagram (new, inline SVG, no image file):
- Stylised anterior chest with sternum, clavicles, ICS level lines,
  left mid-clavicular line
- Five colour-coded landmarks:
   A  Aortic    — 2nd ICS right sternal border
   P  Pulmonic  — 2nd ICS left sternal border
   E  Erb's pt  — 3rd ICS left sternal border
   T  Tricuspid — 4th ICS left sternal border
   M  Mitral    — 5th ICS mid-clavicular (apex)
- Side legend: location + what to listen for at each point
- Patient's-left / patient's-right labels to prevent mirror-image
  confusion

CV-specific grading scales (3 new entries in SCALES):
- Murmur grade Levine 1–6
- Pulse amplitude 0–4+
- Capillary refill time thresholds

CV components:
1. Inspection (general appearance, central/peripheral cyanosis,
   clubbing with Schamroth sign, precordial bulge, visible apex, JVP)
2. Palpation (apex position + character, parasternal heave, thrills
   at all 5 points, peripheral pulses upper + lower, radio-femoral
   delay for coarctation)
3. Auscultation — approach (positioning, diaphragm vs bell, systematic
   walk through all 5 points, left lateral decub for MS, leaning
   forward for AR)
4. Auscultation — heart sounds + murmurs (S1, S2 split, S3/S4 gallops,
   murmur characterisation by timing/location/radiation/character,
   Levine grading, dynamic maneuvers, innocent-murmur "7 S" pearl)
5. Peripheral vascular (four-limb BP for coarctation, radio-femoral
   delay, bounding pulse differential)

UI wiring:
- renderSystem() emits APTM diagram card at the top of cv system,
  before scales. Two-column layout: SVG on left, legend on right.
- accentMap/iconMap/labelMap extended with cv = rose accent,
  heart-pulse icon, "Cardiovascular" label
- New sub-tab pill in pe-guide.html
This commit is contained in:
Daniel 2026-04-22 20:14:59 +02:00
parent 859b5eb84c
commit b86c678366
2 changed files with 190 additions and 5 deletions

View file

@ -51,6 +51,9 @@
<button class="wv-subtab-btn" data-pesystem="resp">
<i class="fas fa-lungs"></i> Respiratory
</button>
<button class="wv-subtab-btn" data-pesystem="cv">
<i class="fas fa-heart-pulse"></i> Cardiovascular
</button>
</div>
<div id="pe-content">

View file

@ -112,15 +112,104 @@
['611', 'Severe — admit, continuous monitoring'],
['≥ 12', 'Impending respiratory failure — ICU / airway management']
]
},
murmurGrade: {
title: 'Heart-murmur grading (Levine 16)',
icon: 'fa-wave-square',
rows: [
['1/6', 'Very faint — heard only with concentration'],
['2/6', 'Soft but readily heard'],
['3/6', 'Moderately loud, no thrill'],
['4/6', 'Loud WITH a palpable thrill'],
['5/6', 'Very loud; audible with stethoscope just off the chest'],
['6/6', 'Audible without the stethoscope touching the chest']
]
},
pulseAmp: {
title: 'Pulse amplitude grade (04)',
icon: 'fa-heart-pulse',
rows: [
['0', 'Absent'],
['1+', 'Diminished, thready'],
['2+', 'Normal'],
['3+', 'Bounding'],
['4+', 'Bounding with visible pulsation (e.g., aortic regurgitation)']
]
},
capRefill: {
title: 'Capillary refill time',
icon: 'fa-hand',
rows: [
['< 2 sec', 'Normal'],
['23 sec', 'Borderline — consider hydration / perfusion'],
['≥ 3 sec', 'Delayed — dehydration, shock, low cardiac output']
]
}
};
// Which scales are relevant per system
var SYSTEM_SCALES = {
msk: ['atr', 'beighton'],
neuro: ['mrc', 'dtr', 'plantar'],
resp: ['rr', 'spo2', 'silverman', 'westley']
resp: ['rr', 'spo2', 'silverman', 'westley'],
cv: ['murmurGrade', 'pulseAmp', 'capRefill']
};
// ────────────────────────────────────────────────────────────
// APTM DIAGRAM — the 5 classic auscultation points
// ────────────────────────────────────────────────────────────
// Inline SVG showing: Aortic, Pulmonic, Erb's, Tricuspid, Mitral.
// Rendered at the top of the cardiovascular system view.
// Patient faces viewer — viewer's LEFT = patient's right.
var APTM_SVG =
'<svg viewBox="0 0 340 340" xmlns="http://www.w3.org/2000/svg" style="max-width:360px;width:100%;height:auto;display:block;margin:0 auto;" aria-label="Cardiac auscultation points — Aortic, Pulmonic, Erb\'s point, Tricuspid, Mitral">' +
// Chest outline
'<path d="M60,60 Q60,30 170,25 Q280,30 280,60 L280,290 Q280,320 220,325 L120,325 Q60,320 60,290 Z" fill="#f9fafb" stroke="#d1d5db" stroke-width="2"/>' +
// Clavicles
'<path d="M80,55 Q110,42 160,58" stroke="#cbd5e1" stroke-width="5" fill="none" stroke-linecap="round"/>' +
'<path d="M260,55 Q230,42 180,58" stroke="#cbd5e1" stroke-width="5" fill="none" stroke-linecap="round"/>' +
// Sternum
'<rect x="161" y="60" width="18" height="170" fill="#e2e8f0" stroke="#cbd5e1"/>' +
// ICS level dashed lines
'<line x1="80" y1="90" x2="260" y2="90" stroke="#e2e8f0" stroke-dasharray="3,3"/>' +
'<line x1="80" y1="120" x2="260" y2="120" stroke="#e2e8f0" stroke-dasharray="3,3"/>' +
'<line x1="80" y1="150" x2="260" y2="150" stroke="#e2e8f0" stroke-dasharray="3,3"/>' +
'<line x1="80" y1="180" x2="260" y2="180" stroke="#e2e8f0" stroke-dasharray="3,3"/>' +
// Midclavicular line (left — patient\'s, viewer\'s right)
'<line x1="230" y1="55" x2="230" y2="310" stroke="#cbd5e1" stroke-dasharray="2,5"/>' +
// ICS labels
'<text x="74" y="93" font-size="9" fill="#94a3b8" text-anchor="end">ICS 2</text>' +
'<text x="74" y="123" font-size="9" fill="#94a3b8" text-anchor="end">ICS 3</text>' +
'<text x="74" y="153" font-size="9" fill="#94a3b8" text-anchor="end">ICS 4</text>' +
'<text x="74" y="183" font-size="9" fill="#94a3b8" text-anchor="end">ICS 5</text>' +
'<text x="266" y="54" font-size="9" fill="#94a3b8" text-anchor="start">Mid-clavicular</text>' +
// Patient labels
'<text x="90" y="42" font-size="11" fill="#64748b" font-weight="600">Patient\'s RIGHT</text>' +
'<text x="250" y="42" font-size="11" fill="#64748b" font-weight="600" text-anchor="end">Patient\'s LEFT</text>' +
// Aortic — 2nd ICS right sternal border (viewer\'s left)
'<circle cx="145" cy="90" r="15" fill="#dc2626" stroke="#7f1d1d" stroke-width="2"/>' +
'<text x="145" y="95" font-size="15" font-weight="700" fill="white" text-anchor="middle">A</text>' +
// Pulmonic — 2nd ICS left sternal border (viewer\'s right)
'<circle cx="195" cy="90" r="15" fill="#2563eb" stroke="#1e3a8a" stroke-width="2"/>' +
'<text x="195" y="95" font-size="15" font-weight="700" fill="white" text-anchor="middle">P</text>' +
// Erb\'s point — 3rd ICS left sternal border
'<circle cx="195" cy="120" r="13" fill="#059669" stroke="#064e3b" stroke-width="2"/>' +
'<text x="195" y="125" font-size="13" font-weight="700" fill="white" text-anchor="middle">E</text>' +
// Tricuspid — 4th ICS left sternal border
'<circle cx="195" cy="175" r="15" fill="#d97706" stroke="#78350f" stroke-width="2"/>' +
'<text x="195" y="180" font-size="15" font-weight="700" fill="white" text-anchor="middle">T</text>' +
// Mitral — 5th ICS midclavicular (apex)
'<circle cx="230" cy="195" r="15" fill="#7c3aed" stroke="#4c1d95" stroke-width="2"/>' +
'<text x="230" y="200" font-size="15" font-weight="700" fill="white" text-anchor="middle">M</text>' +
'</svg>';
var APTM_LEGEND = [
{ letter: 'A', color: '#dc2626', title: 'Aortic area', location: '2nd ICS, right sternal border', listen: 'S2 (aortic component), aortic stenosis, aortic regurgitation' },
{ letter: 'P', color: '#2563eb', title: 'Pulmonic area', location: '2nd ICS, left sternal border', listen: 'S2 (pulmonic component), pulmonic stenosis, PDA, physiologic split of S2' },
{ letter: 'E', color: '#059669', title: 'Erb\'s point', location: '3rd ICS, left sternal border', listen: 'Aortic regurgitation (best here), innocent flow murmurs' },
{ letter: 'T', color: '#d97706', title: 'Tricuspid area', location: '4th5th ICS, lower left sternal border', listen: 'Tricuspid regurgitation, VSD, S3/S4, holosystolic murmurs' },
{ letter: 'M', color: '#7c3aed', title: 'Mitral area (apex)', location: '5th ICS, mid-clavicular line', listen: 'S1, mitral regurgitation, mitral stenosis (with bell, left-lateral decubitus)' }
];
// ────────────────────────────────────────────────────────────
// RESPIRATORY SOUNDS LIBRARY
// ────────────────────────────────────────────────────────────
@ -870,6 +959,77 @@
],
abnormalHints: ['Bronchophony increased — consolidation', 'Egophony positive ("ee" → "A" / "ay") — consolidation, sometimes top of an effusion', 'Whispered pectoriloquy positive (whisper clearly audible) — consolidation'] }
]
},
cv: {
overview: 'Systematic cardiovascular exam: inspection → palpation → auscultation at the five classic points → peripheral vascular exam. Always palpate the apex BEFORE auscultating — knowing where the apex lies tells you where to put the stethoscope and flags cardiomegaly immediately.',
components: [
{ name: 'Inspection',
significance: 'Detects obvious precordial activity, chest-wall signs of congenital heart disease, and systemic markers (cyanosis, clubbing, dysmorphic features).',
pearl: 'Clubbing + central cyanosis in a well-appearing adolescent = cyanotic congenital heart disease until proven otherwise. Inspect the fingernails before reaching for the stethoscope.',
steps: [
{ label: 'General appearance', method: 'Observe body habitus, features suggesting syndromic CHD (Turner, Down, Marfan, Williams).', normal: 'No dysmorphic features, appropriate growth' },
{ label: 'Central cyanosis', method: 'Inspect lips, tongue, and oral mucosa for bluish discoloration.', normal: 'Pink oral mucosa, no cyanosis' },
{ label: 'Peripheral cyanosis / clubbing', method: 'Inspect nail beds; do Schamroth\'s window (oppose nails of 4th fingers — normally forms a diamond-shaped window).', normal: 'Pink nail beds, Schamroth window present' },
{ label: 'Precordial bulge', method: 'Inspect anterior chest wall tangentially for asymmetric prominence over the heart.', normal: 'Symmetric chest, no bulge' },
{ label: 'Visible apex beat', method: 'Inspect for a visible cardiac impulse at the 5th ICS mid-clavicular line.', normal: 'Apex may be visible in thin patients; should not be displaced' },
{ label: 'Neck veins (JVP)', method: 'Patient reclined 45°, head turned slightly left. Observe the right internal jugular pulsation; measure vertical height above the sternal angle.', normal: '≤ 4 cm above sternal angle (≤ 9 cm H₂O from right atrium)' }
],
abnormalHints: ['Central cyanosis — right-to-left shunt, severe hypoxemia', 'Clubbing — cyanotic CHD, chronic hypoxemia', 'Precordial bulge — long-standing cardiomegaly (grew during skeletal growth)', 'Visible apex displaced lateral/inferior — cardiomegaly', 'Elevated JVP — right-heart failure, fluid overload, cardiac tamponade'] },
{ name: 'Palpation',
significance: 'Localises the apex (confirms cardiac size), detects thrills (loud murmurs), and identifies a parasternal heave (RV hypertrophy).',
pearl: 'If you feel a thrill, the murmur is at least grade 4/6 — grade your murmur as ≥4 even if it sounds less impressive. Thrill = loud, palpable turbulence.',
steps: [
{ label: 'Apex beat — localise', method: 'Feel with the tips of the fingers at the 5th ICS mid-clavicular line. If not found, roll the patient to the left lateral decubitus position.', normal: 'Located at 5th ICS, mid-clavicular line, less than 2 cm in diameter' },
{ label: 'Apex character', method: 'Describe: tapping (normal), heaving (pressure overload, e.g. AS/HTN), thrusting (volume overload, e.g. AR/MR), dyskinetic (MI/aneurysm).', normal: 'Brief tapping quality' },
{ label: 'Parasternal heave', method: 'Place the heel of the hand along the left sternal border. Sustained outward movement with each systole = heave.', normal: 'No heave' },
{ label: 'Thrills', method: 'Use the palmar aspect of the hand at each of the 5 auscultation areas (A, P, E, T, M). A thrill = palpable turbulence.', normal: 'No thrills' },
{ label: 'Peripheral pulses — upper', method: 'Palpate radial pulses bilaterally, then brachial. Note rate, rhythm, volume, and symmetry.', normal: 'Symmetric 2+ pulses, regular rhythm, age-appropriate rate' },
{ label: 'Peripheral pulses — lower', method: 'Palpate femoral pulses. Compare to brachial — radio-femoral or brachio-femoral delay suggests coarctation of the aorta.', normal: 'Femoral pulses 2+ symmetric, no delay relative to radial' }
],
abnormalHints: ['Apex displaced laterally/inferiorly — cardiomegaly', 'Heaving apex — pressure overload (AS, HTN)', 'Thrusting apex — volume overload (AR, MR)', 'Thrill over precordium — always pathological; at least grade 4/6 murmur', 'Parasternal heave — RV hypertrophy (pulmonary HTN, pulmonary stenosis, VSD with Eisenmenger)', 'Radio-femoral delay — coarctation of the aorta (always check in a hypertensive adolescent)'] },
{ name: 'Auscultation — approach',
significance: 'Systematic technique ensures every relevant finding is detected. Listen at all 5 points, with both diaphragm and bell, in supine/sitting/left-lateral positions as needed.',
pearl: 'Time every murmur by simultaneously palpating the carotid pulse with the fingers of your free hand. Pulse = systole. Murmur heard during the pulse = systolic; in between pulses = diastolic.',
steps: [
{ label: 'Positioning', method: 'Patient supine, head of bed at 30°. Exam room quiet, patient relaxed. Warm the stethoscope first.', normal: 'N/A — technique' },
{ label: 'Diaphragm technique', method: 'Firm contact with skin. Detects HIGH-pitched sounds: S1, S2, systolic ejection murmurs, AR, MR.', normal: 'N/A — technique' },
{ label: 'Bell technique', method: 'Very light contact — enough to make a seal but not stretch the skin. Detects LOW-pitched sounds: S3, S4, mitral stenosis rumble.', normal: 'N/A — technique' },
{ label: 'Listen at each of the 5 points', method: 'A → P → E → T → M in order, each with diaphragm then bell. Spend a full cycle at each zone.', normal: 'S1 crisp, S2 clear (splits physiologically on inspiration at P), no added sounds, no murmur' },
{ label: 'Left lateral decubitus position', method: 'If apex murmur suspected. Roll patient to left side. Listen at the apex with the BELL for mitral stenosis rumble or S3/S4.', normal: 'No added sounds, no diastolic rumble' },
{ label: 'Sitting forward, held expiration', method: 'Patient leans forward, exhales fully, holds. Listen at left lower sternal border and Erb\'s point with the DIAPHRAGM for aortic regurgitation (soft early diastolic decrescendo).', normal: 'No early-diastolic murmur' }
],
abnormalHints: ['Fixed split S2 (no change with respiration) — ASD', 'Loud S2 at pulmonic area — pulmonary HTN', 'S3 — volume overload, CHF (can be normal in young athletes)', 'S4 — stiff ventricle (HTN, HCM, ischemia)', 'Audible opening snap — mitral stenosis (rare in children)'] },
{ name: 'Auscultation — heart sounds and murmurs',
significance: 'Characterising a murmur by timing, location, radiation, pitch, quality, and dynamic maneuvers narrows the differential.',
pearl: 'Innocent murmurs in children share 7 "S" features: Soft (≤ grade 2), Systolic, Short, Single (no added S3/S4), Small (localised, non-radiating), Sweet (musical), Sensitive to position/respiration (louder supine, softer standing). Anything breaking this pattern deserves workup.',
steps: [
{ label: 'S1', method: 'Listen at the apex (mitral). Coincides with the carotid pulse upstroke. Mitral + tricuspid closure.', normal: 'Single, crisp, single-component sound' },
{ label: 'S2', method: 'Listen at the pulmonic area in HELD INSPIRATION and HELD EXPIRATION. Note whether S2 splits physiologically (wider in inspiration, narrower/absent in expiration).', normal: 'Physiologic split (widens on inspiration, narrows on expiration)' },
{ label: 'S3 / S4 gallops', method: 'Bell at the apex in left lateral decubitus. S3 = early diastole (after S2), low-pitched. S4 = late diastole (just before S1).', normal: 'Absent in adults; S3 can be normal in young athletes under age 30' },
{ label: 'Identify murmur — timing', method: 'Time vs carotid pulse. Systolic (during pulse) vs diastolic (between pulses) vs continuous.', normal: 'No murmur, or only soft innocent flow murmur' },
{ label: 'Identify murmur — location + radiation', method: 'Where loudest? Does it radiate? AS → carotids. MR → axilla. Coarctation → back.', normal: 'N/A — characterise only if murmur present' },
{ label: 'Identify murmur — character', method: 'Crescendo-decrescendo (ejection) vs holosystolic (plateau) vs decrescendo early-diastolic (AR, PR) vs mid-diastolic rumble (MS, TS).', normal: 'N/A — characterise only if murmur present' },
{ label: 'Grade intensity', method: 'Levine 16 scale (see scales card above).', normal: 'No murmur, or grade ≤ 2 soft innocent flow murmur' },
{ label: 'Dynamic maneuvers', method: 'Standing: ↑HOCM, ↑MVP click (earlier). Squatting: opposite. Valsalva: ↑HOCM, most others decrease.', normal: 'No significant change with posture' }
],
abnormalHints: ['Holosystolic murmur at apex → axilla — mitral regurgitation', 'Holosystolic at lower left sternal border (LLSB) — VSD, tricuspid regurgitation', 'Systolic ejection at upper right sternal border → carotids — aortic stenosis', 'Systolic ejection at upper left sternal border — pulmonary stenosis', 'Continuous "machinery" below left clavicle — PDA', 'Early diastolic at Erb\'s point, leaning forward — aortic regurgitation', 'Diastolic rumble at apex, bell in left-lateral — mitral stenosis', 'Fixed split S2 + systolic flow murmur — ASD'] },
{ name: 'Peripheral vascular exam',
significance: 'Coarctation of the aorta hides until BP and pulses are checked in all four extremities. Differential diagnosis of a hypertensive adolescent should include this in the first 60 seconds.',
pearl: 'Four-limb BP measurement is mandatory in any adolescent with hypertension or a murmur. Upper-extremity BP > lower-extremity BP (or brachio-femoral delay) = coarctation until excluded.',
steps: [
{ label: 'Four-limb blood pressure', method: 'Measure BP in right arm, left arm, and at least one leg. Use appropriately sized cuff (bladder width 40% of limb circumference, length 80100%).', normal: 'Arm BPs within 10 mmHg of each other; leg systolic within 20 mmHg of arm systolic (may be higher)' },
{ label: 'Radial pulses', method: 'Palpate both radials simultaneously — note any delay or asymmetry.', normal: 'Simultaneous, symmetric, 2+' },
{ label: 'Radio-femoral delay', method: 'Palpate radial and femoral simultaneously. Feel the femoral as clearly "after" the radial = delay.', normal: 'No delay' },
{ label: 'Femoral pulses', method: 'Palpate both femoral pulses at the mid-inguinal point. Compare amplitude to radials.', normal: 'Symmetric 2+ pulses, equal amplitude to radial' },
{ label: 'Dorsalis pedis + posterior tibialis', method: 'Palpate in both feet.', normal: '2+ pulses bilaterally' },
{ label: 'Capillary refill', method: 'Press and release the nail bed; time to normal colour.', normal: '< 2 sec' }
],
abnormalHints: ['Asymmetric upper-extremity BP (> 10 mmHg) — subclavian stenosis or coarctation at the origin', 'Upper >> lower-extremity BP — coarctation of the aorta', 'Diminished or absent femoral pulses with brachio-femoral delay — coarctation', 'Bounding pulses with wide pulse pressure — AR, PDA, arteriovenous fistula, thyrotoxicosis, anemia', 'Weak thready pulses — low output state (heart failure, shock, hypovolemia)', 'Prolonged capillary refill — dehydration, shock, cold stress'] }
]
}
}
};
@ -931,10 +1091,10 @@
return;
}
var section = group[currentSystem];
var accentMap = { msk: '#0891b2', neuro: '#7c3aed', resp: '#0ea5e9' }; // cyan / purple / sky
var accentTintMap = { msk: '#ecfeff', neuro: '#f5f3ff', resp: '#f0f9ff' };
var iconMap = { msk: 'bone', neuro: 'brain', resp: 'lungs' };
var labelMap = { msk: 'Musculoskeletal', neuro: 'Neurologic', resp: 'Respiratory' };
var accentMap = { msk: '#0891b2', neuro: '#7c3aed', resp: '#0ea5e9', cv: '#e11d48' }; // cyan / purple / sky / rose
var accentTintMap = { msk: '#ecfeff', neuro: '#f5f3ff', resp: '#f0f9ff', cv: '#fff1f2' };
var iconMap = { msk: 'bone', neuro: 'brain', resp: 'lungs', cv: 'heart-pulse' };
var labelMap = { msk: 'Musculoskeletal', neuro: 'Neurologic', resp: 'Respiratory', cv: 'Cardiovascular' };
var accent = accentMap[currentSystem] || '#0891b2';
var accentTint = accentTintMap[currentSystem] || '#ecfeff';
var icon = iconMap[currentSystem] || 'circle-info';
@ -947,6 +1107,28 @@
html += ' <div style="padding:0 16px 14px;font-size:13px;line-height:1.65;color:var(--g700);">' + esc(section.overview) + '</div>';
html += '</div>';
// ─ APTM cardiac auscultation diagram (only for cv system) ─
if (currentSystem === 'cv') {
html += '<div class="card" style="margin-bottom:14px;border:1px solid ' + accent + '33;">';
html += ' <div class="card-header" style="background:' + accentTint + ';"><h3 style="margin:0;font-size:14px;color:' + accent + ';"><i class="fas fa-stethoscope"></i> Auscultation landmarks — APTM + Erb\'s</h3></div>';
html += ' <div style="padding:14px;display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start;">';
html += ' <div>' + APTM_SVG + '</div>';
html += ' <div style="font-size:12px;line-height:1.55;">';
APTM_LEGEND.forEach(function (p) {
html += '<div style="display:flex;gap:10px;margin-bottom:10px;align-items:flex-start;">';
html += ' <div style="flex-shrink:0;width:24px;height:24px;border-radius:50%;background:' + p.color + ';color:#fff;font-weight:700;display:flex;align-items:center;justify-content:center;font-size:12px;">' + p.letter + '</div>';
html += ' <div style="flex:1;">';
html += ' <div style="font-weight:600;color:var(--g800);font-size:13px;">' + esc(p.title) + '</div>';
html += ' <div style="color:var(--g600);font-size:11px;margin-top:1px;">' + esc(p.location) + '</div>';
html += ' <div style="color:var(--g700);margin-top:3px;font-size:12px;"><strong>Listen for:</strong> ' + esc(p.listen) + '</div>';
html += ' </div>';
html += '</div>';
});
html += ' </div>';
html += ' </div>';
html += '</div>';
}
// ─ Respiratory sounds library (only for resp system) ─
if (currentSystem === 'resp') {
html += '<div class="card" style="margin-bottom:14px;border:1px solid ' + accent + '33;">';