Give any one research paper
artificial intelligence medicine
Find more recent papers on AI in mental health diagnosis
"artificial intelligence"[MeSH Terms] AND "mental disorders"[MeSH Terms] AND diagnosis
AI artificial intelligence mental health diagnosis research papers 2025 2026
| # | Title | Authors | Journal | Date | PMID |
|---|---|---|---|---|---|
| 1 | Use of generative AI in psychiatry and mental health care | Kolding S et al. | Acta Neuropsychiatr | Nov 2024 | 39523628 |
| 2 | Effectiveness of AI in detecting and managing depressive disorders | Park Y et al. | J Affect Disord | Sep 2024 | 38889858 |
| 3 | Recent developments in omics studies and AI in depression and suicide | Wang Q, Dwivedi Y | Transl Psychiatry | Aug 2025 | 40790016 |
| 4 | Application of AI in the field of mental health | Dehbozorgi R et al. | BMC Psychiatry | Feb 2025 | 39953464 |
| 5 | AI in mental health care: diagnosis, monitoring, and intervention | Cruz-Gonzalez P et al. | Psychol Med | Feb 2025 | 39911020 |
| 6 | Neuroimaging and machine learning in eating disorders | Monaco F et al. | Eat Weight Disord | Jun 2025 | 40450619 |
| 7 | Ethical implications of generative AI in mental health | Wang X et al. | JMIR Ment Health | Jun 2025 | 40577783 |
| 8 | Evaluating generative AI in mental health: capabilities and limitations | Wang L et al. | JMIR Ment Health | May 2025 | 40373033 |
| # | Title | Authors | Journal | Date | PMID |
|---|---|---|---|---|---|
| 9 | AI in psychotherapy: development of intelligent therapeutic systems | Spytska L | BMC Psychol | Feb 2025 | 40022267 |
Give some latest guidelines for chf
"heart failure"[MeSH Terms] AND "guidelines"
CHF congestive heart failure guidelines 2024 2025 ACC AHA ESC latest recommendations
https://reference.medscape.com/cc1/p10/heart-failure-reduced…
PMID: 40533340
| Type | EF | Key Features |
|---|---|---|
| HFrEF | < 40% | Reduced ejection fraction - most evidence-based therapies |
| HFmrEF | 40-49% | Mid-range - emerging evidence |
| HFpEF | ≥ 50% | Preserved EF - historically hard to treat |
| Pillar | Drug Class | Examples | Notes |
|---|---|---|---|
| 1 | ACEi / ARB / ARNI | Ramipril, Enalapril / Valsartan / Sacubitril-Valsartan | ESC: ACEi preferred first-line; ARNI if still symptomatic. AHA/ACC: ARNI may be used first-line |
| 2 | Beta-blocker | Carvedilol, Bisoprolol, Metoprolol succinate | Only these 3 have mortality evidence in HFrEF |
| 3 | MRA (Mineralocorticoid Receptor Antagonist) | Spironolactone, Eplerenone | Monitor K+ and renal function |
| 4 | SGLT2 inhibitor | Dapagliflozin, Empagliflozin | Class I recommendation across all major guidelines; once-daily, no titration needed |
2025 NICE key change: No longer recommends sequencing - start all 4 based on patient profile, frailty, and preferences.
| Drug | Indication | Guideline Rec |
|---|---|---|
| Ivabradine | HFrEF, LVEF ≤ 35%, HR ≥ 70 bpm on max beta-blocker, sinus rhythm | Class IIa (ESC, AHA) |
| Hydralazine + Isosorbide dinitrate | HFrEF intolerant of ACEi/ARB (esp. Black patients) | Class I (AHA/ACC) |
| Digoxin | HFrEF with persistent symptoms, AF | Class IIb |
| Vericiguat | Worsening HFrEF on optimal therapy | Class IIb (ESC); considered (AHA/ACC) |
| Guideline | Year | Publisher | Scope |
|---|---|---|---|
| AHA/ACC/HFSA Heart Failure Guideline | 2022 (updated 2024) | ACC/AHA | Comprehensive US guideline |
| ESC Focused Update | 2023 | European Society of Cardiology | HFrEF, HFmrEF, HFpEF |
| NICE NG106 Update | 2025 | NICE (UK) | Chronic HFrEF; removed sequencing |
| iCARDIO Global Guidelines | 2025 | Heart Lung Circ | Global, resource-adjusted |
| ASE Diastolic Function Update | 2025 | J Am Soc Echocardiogr | HFpEF diagnosis by echo |
Make flashcards for MI
Medical flashcard for advanced clinical exam. Clean dark navy blue background. FRONT side label at top left in small yellow text: "CARD 1 — PATHOPHYSIOLOGY" Large bold white question in center: "What is the pathophysiology of MI?" Small yellow subtitle: "Hint: Plaque → Thrombus → Necrosis" BACK side (right half of card, separated by a thin gold dividing line): Bold white heading: "ANSWER" Bullet points in light cyan text (small, neat): • Atherosclerotic plaque rupture or erosion • Platelet aggregation → thrombus formation • Complete occlusion → ischemia → infarction • Zone of necrosis (core) + zone of injury + zone of ischemia • Type 1 MI: plaque rupture | Type 2 MI: supply-demand mismatch • Irreversible necrosis begins at 20–40 min of ischemia Small white italic footer: "Source: Harrison's 22E | Fuster & Hurst 15E"
Medical flashcard for advanced clinical exam. Clean dark navy blue background. FRONT side label at top left in small yellow text: "CARD 2 — ECG DIAGNOSIS" Large bold white question in center: "ECG criteria for STEMI vs NSTEMI?" Small yellow subtitle: "Lead groupings, thresholds, evolution" BACK side (right half, separated by thin gold vertical line): Bold white heading: "ANSWER" Bullet points in light cyan (small neat): • STEMI: new ST elevation ≥0.1 mV (2 contiguous leads); V2-V3: ≥0.2 mV (men ≥40y), ≥0.25 mV (men <40y), ≥0.15 mV (women) • Earliest sign: Hyperacute T-waves (tall, broad, symmetric) • Evolution: Hyperacute T → ST elevation → Q waves → T-wave inversion • NSTEMI: ST depression or T-wave inversion + elevated troponin • Posterior MI: ST depression V1-V4 (mirror image) • New LBBB may represent STEMI equivalent • Contiguous leads: I/aVL (lateral), II/III/aVF (inferior), V1-V4 (anterior) Small white italic footer: "Source: Frameworks for Internal Medicine | Harrison's 22E"
Medical flashcard for advanced clinical exam. Clean dark navy blue background. FRONT side label at top left in small yellow text: "CARD 3 — BIOMARKERS" Large bold white question in center: "Cardiac biomarkers in MI — timing & significance" Small yellow subtitle: "Rise, peak, normalize" BACK side (right half, separated by thin gold vertical line): Bold white heading: "ANSWER" Small table in white/cyan text: Troponin I/T (hs-cTn): Rise 1-3h | Peak 12-24h | Normalizes 7-14d | MOST SENSITIVE & SPECIFIC CK-MB: Rise 3-4h | Peak 18-24h | Normalizes 48-72h | Useful for reinfarction Myoglobin: Rise 1-2h | Peak 6-9h | Normalizes 24h | Earliest but non-specific BNP/NT-proBNP: Reflects LV dysfunction / prognosis Key points in cyan bullets: • hs-cTn: diagnose MI if delta rise ≥20% at 1-3h (0h/1h or 0h/2h protocols) • Troponin rises in: PE, myocarditis, sepsis, AKI (non-MI causes) • Serial troponins mandatory if first negative but high suspicion Small white italic footer: "Source: Tietz Lab Medicine 7E | Goldman-Cecil"
Medical flashcard for advanced clinical exam. Clean dark navy blue background. FRONT side label at top left in small yellow text: "CARD 4 — STEMI MANAGEMENT" Large bold white question in center: "Acute STEMI management — time targets & reperfusion" Small yellow subtitle: "Door-to-balloon, fibrinolysis, MONA" BACK side (right half, separated by thin gold vertical line): Bold white heading: "ANSWER" Bullet points in light cyan (small neat): TIME TARGETS: • First medical contact → ECG: ≤10 minutes • Door-to-balloon (primary PCI): ≤90 min (PCI-capable center) • Door-to-needle (fibrinolysis): ≤30 min (non-PCI center) • FMC-to-device if transferred: ≤120 min PRIMARY PCI preferred if: • Skilled lab available within 90-120 min • Cardiogenic shock | failed fibrinolysis | late presentation FIBRINOLYSIS if PCI unavailable: • Alteplase, Tenecteplase, Reteplase • Contraindicated: prior ICH, active bleeding, aortic dissection INITIAL DRUGS (MONA-B): Morphine | Oxygen (if SpO2 <90%) | Nitrates | Aspirin 325mg + P2Y12 | Beta-blocker | Heparin Small white italic footer: "Source: Tintinalli EM | Fuster & Hurst 15E | Harrison's 22E"
Medical flashcard for advanced clinical exam. Clean dark navy blue background. FRONT side label at top left in small yellow text: "CARD 5 — ANTIPLATELET & ANTICOAGULATION" Large bold white question in center: "Antithrombotic therapy in ACS — drugs & indications" Small yellow subtitle: "Dual antiplatelet, heparin, GIIb/IIIa" BACK side (right half, separated by thin gold vertical line): Bold white heading: "ANSWER" Bullet points in light cyan (small neat): ANTIPLATELET (DAPT): • Aspirin 162-325mg load → 75-100mg daily (indefinite) • P2Y12 inhibitors: Ticagrelor 180mg load (preferred in NSTEMI/STEMI) | Prasugrel 60mg (STEMI with PCI) | Clopidogrel 600mg (if no access to above) • DAPT duration: 12 months post-ACS (can extend or shorten based on bleeding risk) ANTICOAGULATION: • UFH: weight-based IV bolus + infusion (preferred in STEMI PCI) • Enoxaparin: 1mg/kg SC q12h (NSTEMI/NSTE-ACS) • Fondaparinux: preferred in NSTEMI if no PCI planned (lower bleeding risk) • Bivalirudin: alternative in PCI (direct thrombin inhibitor) GIIb/IIIa inhibitors (abciximab, eptifibatide): selected high-risk PCI cases Small white italic footer: "Source: Harrison's 22E | Fuster & Hurst 15E"
Medical flashcard for advanced clinical exam. Clean dark navy blue background. FRONT side label at top left in small yellow text: "CARD 6 — COMPLICATIONS" Large bold white question in center: "Major complications of MI — timing & management" Small yellow subtitle: "Killip classification, mechanical, electrical" BACK side (right half, separated by thin gold vertical line): Bold white heading: "ANSWER" Text in light cyan (small neat): KILLIP CLASS (mortality ↑ with class): I: No HF (6%) | II: S3/rales (17%) | III: Pulmonary edema (38%) | IV: Cardiogenic shock (81%) MECHANICAL (days 3-7): • Free wall rupture → hemopericardium, tamponade (surgical emergency) • Papillary muscle rupture → acute MR → flash pulmonary edema • VSD (ventricular septal defect) → left-to-right shunt, step-up in O2 saturation at RV ELECTRICAL: • VF/VT: most common cause of death pre-hospital • Complete heart block: inferior MI (RCA) → atropine/pacing • Accelerated idioventricular rhythm: reperfusion arrhythmia (benign) OTHER: • Dressler syndrome (2-10 weeks): pericarditis + pleuritis (immune-mediated) • LV thrombus → anticoagulate • Cardiogenic shock: IABP ± mechanical support, revascularization Small white italic footer: "Source: Goldman-Cecil | Robbins Pathology | Harrison's 22E"
Medical flashcard for advanced clinical exam. Clean dark navy blue background. FRONT side label at top left in small yellow text: "CARD 7 — CORONARY ARTERY TERRITORIES" Large bold white question in center: "Which coronary artery supplies which territory?" Small yellow subtitle: "LAD, RCA, LCX — ECG leads & clinical correlates" BACK side (right half, separated by thin gold vertical line): Bold white heading: "ANSWER" Bullet points in light cyan (small neat): LAD (Left Anterior Descending): • Territory: Anterior wall, septum, apex, anterior papillary muscle • ECG leads: V1-V4 (anterior) • Risk: Large MI, LBBB, high-degree AV block, cardiogenic shock RCA (Right Coronary Artery): • Territory: Inferior wall, RV, SA node (60%), AV node (90%) • ECG leads: II, III, aVF (inferior); check right-sided leads (V4R) for RV MI • Risk: Bradycardia, AV block, RV infarction (hypotension + JVD + no pulmonary edema) LCX (Left Circumflex): • Territory: Lateral wall, posterior wall (in dominant LCx) • ECG leads: I, aVL, V5-V6 (lateral); posterior MI → ST depression V1-V3 • Often "silent" on standard ECG; use posterior leads V7-V9 Small white italic footer: "Source: Fuster & Hurst 15E | Harrison's 22E"
Medical flashcard for advanced clinical exam. Clean dark navy blue background. FRONT side label at top left in small yellow text: "CARD 8 — SECONDARY PREVENTION" Large bold white question in center: "Secondary prevention after MI — which drugs & why?" Small yellow subtitle: "Long-term medications, targets, duration" BACK side (right half, separated by thin gold vertical line): Bold white heading: "ANSWER" Bullet points in light cyan (small neat): MEDICATIONS (start before discharge): • Aspirin: 75-100mg daily — indefinite • P2Y12 inhibitor (ticagrelor/clopidogrel): 12 months post-ACS • High-intensity statin: Atorvastatin 40-80mg or Rosuvastatin 20-40mg — indefinite; target LDL <55 mg/dL (ESC) / <70 mg/dL (ACC) • Beta-blocker: All post-MI with HFrEF (LVEF ≤40%) for ≥3 years; reduces sudden death • ACE inhibitor / ARB: if LVEF ≤40%, diabetes, hypertension — indefinite • MRA (eplerenone/spironolactone): if LVEF ≤40% + HF symptoms or diabetes • SGLT2 inhibitor: if HFrEF post-MI (dapagliflozin / empagliflozin) TARGETS: • LDL <55 mg/dL | BP <130/80 | HbA1c <7% | BMI <25 | Stop smoking DEVICES: • ICD if LVEF ≤35% after ≥40 days post-MI + optimal GDMT ≥3 months Small white italic footer: "Source: Harrison's 22E | ESC 2023 ACS Guidelines | Fuster & Hurst 15E"








Export flashcards as a PPTX deck for studying
python /tmp/skills/shared/scripts/fetch_images.py \ "https://cdn.orris.care/image-gen/52444ccd5da64fae8d91ae1f24ff029a.png" \ "https://cdn.orris.care/image-gen/aaf1b08d735b4ec392556c3082f9391a.png" \ "https://cdn.orris.care/image-gen/c925c65156f4447696a0e50acb66aab0.png" \ "https://cdn.orris.care/image-gen/9cf95434868f42e79bf09a9abe480ee3.png" \ "https://cdn.orris.care/image-gen/eb8f4e9c78ba4066bb5453bfdab71da8.png" \ "https://cdn.orris.care/image-gen/dfde69a1dd6f4471bb5deab3e04ecc28.png" \ "https://cdn.orris.care/image-gen/48147771a3f142168b4e1476133c8b63.png" \ "https://cdn.orris.care/image-gen/b8f9906fd783433aa6a57bad471bb9f9.png" \ 2>/dev/null | python3 -c "import sys,json; data=json.load(sys.stdin); print('OK:', len([x for x in data if x['base64']]), 'images fetched'); [print('FAIL:', x['url']) for x in data if not x['base64']]"
mkdir -p /tmp/workspace/mi-flashcards && python3 << 'PYEOF' import json, subprocess, base64 from io import BytesIO from pptx import Presentation from pptx.util import Inches, Pt, Emu from pptx.dml.color import RGBColor from pptx.enum.text import PP_ALIGN, MSO_ANCHOR URLS = [ ("Card 1 — Pathophysiology", "https://cdn.orris.care/image-gen/52444ccd5da64fae8d91ae1f24ff029a.png"), ("Card 2 — ECG Diagnosis", "https://cdn.orris.care/image-gen/aaf1b08d735b4ec392556c3082f9391a.png"), ("Card 3 — Biomarkers", "https://cdn.orris.care/image-gen/c925c65156f4447696a0e50acb66aab0.png"), ("Card 4 — STEMI Management", "https://cdn.orris.care/image-gen/9cf95434868f42e79bf09a9abe480ee3.png"), ("Card 5 — Antiplatelet & Anticoag", "https://cdn.orris.care/image-gen/eb8f4e9c78ba4066bb5453bfdab71da8.png"), ("Card 6 — Complications", "https://cdn.orris.care/image-gen/dfde69a1dd6f4471bb5deab3e04ecc28.png"), ("Card 7 — Coronary Territories", "https://cdn.orris.care/image-gen/48147771a3f142168b4e1476133c8b63.png"), ("Card 8 — Secondary Prevention", "https://cdn.orris.care/image-gen/b8f9906fd783433aa6a57bad471bb9f9.png"), ] # Fetch all images raw_result = subprocess.check_output( ["python", "/tmp/skills/shared/scripts/fetch_images.py"] + [u for _, u in URLS] ) img_data = json.loads(raw_result) # Build presentation prs = Presentation() prs.slide_width = Inches(13.333) prs.slide_height = Inches(7.5) blank_layout = prs.slide_layouts[6] # ── Title slide ───────────────────────────────────────────────────────────── title_slide = prs.slides.add_slide(blank_layout) # Navy background bg = title_slide.shapes.add_shape( 1, 0, 0, prs.slide_width, prs.slide_height) # MSO_SHAPE.RECTANGLE = 1 bg.fill.solid(); bg.fill.fore_color.rgb = RGBColor(0x0D, 0x1B, 0x3E) bg.line.fill.background() # Gold accent bar bar = title_slide.shapes.add_shape( 1, Inches(1.5), Inches(2.8), Inches(10.3), Inches(0.06)) bar.fill.solid(); bar.fill.fore_color.rgb = RGBColor(0xF5, 0xC5, 0x18) bar.line.fill.background() # Title text tb = title_slide.shapes.add_textbox(Inches(1.5), Inches(1.3), Inches(10.3), Inches(1.3)) tf = tb.text_frame; tf.word_wrap = True p = tf.paragraphs[0] p.alignment = PP_ALIGN.CENTER r = p.add_run() r.text = "MYOCARDIAL INFARCTION" r.font.name = "Arial"; r.font.size = Pt(46); r.font.bold = True r.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF) # Subtitle tb2 = title_slide.shapes.add_textbox(Inches(1.5), Inches(3.1), Inches(10.3), Inches(0.7)) tf2 = tb2.text_frame p2 = tf2.paragraphs[0] p2.alignment = PP_ALIGN.CENTER r2 = p2.add_run() r2.text = "Advanced Clinical Flashcards — 8 Cards" r2.font.name = "Arial"; r2.font.size = Pt(22); r2.font.bold = False r2.font.color.rgb = RGBColor(0xF5, 0xC5, 0x18) # Source line tb3 = title_slide.shapes.add_textbox(Inches(1.5), Inches(4.0), Inches(10.3), Inches(0.55)) tf3 = tb3.text_frame p3 = tf3.paragraphs[0] p3.alignment = PP_ALIGN.CENTER r3 = p3.add_run() r3.text = "Harrison's 22E | Fuster & Hurst 15E | Goldman-Cecil | Tintinalli EM" r3.font.name = "Arial"; r3.font.size = Pt(14) r3.font.color.rgb = RGBColor(0xAA, 0xBB, 0xDD) # Topics list topics = [ "Pathophysiology", "ECG Diagnosis", "Biomarkers", "STEMI Management", "Antiplatelet & Anticoagulation", "Complications", "Coronary Territories", "Secondary Prevention" ] tb4 = title_slide.shapes.add_textbox(Inches(2.0), Inches(4.8), Inches(9.3), Inches(2.2)) tf4 = tb4.text_frame; tf4.word_wrap = True first = True for i, topic in enumerate(topics, 1): if first: p4 = tf4.paragraphs[0]; first = False else: p4 = tf4.add_paragraph() p4.alignment = PP_ALIGN.CENTER r4 = p4.add_run() r4.text = f"{' '.join([''] * 0)}{i}. {topic}" r4.font.name = "Arial"; r4.font.size = Pt(12) r4.font.color.rgb = RGBColor(0xCC, 0xDD, 0xEE) # ── Flashcard slides (one per card) ───────────────────────────────────────── for (title, url), img_info in zip(URLS, img_data): slide = prs.slides.add_slide(blank_layout) # Dark background bg2 = slide.shapes.add_shape(1, 0, 0, prs.slide_width, prs.slide_height) bg2.fill.solid(); bg2.fill.fore_color.rgb = RGBColor(0x0A, 0x14, 0x30) bg2.line.fill.background() if img_info.get("base64"): raw_bytes = base64.b64decode(img_info["base64"].split(",")[-1]) img_stream = BytesIO(raw_bytes) # Full-slide image (slight padding) slide.shapes.add_picture(img_stream, Inches(0.15), Inches(0.15), Inches(13.0), Inches(7.2)) else: # Fallback text if image failed tb_err = slide.shapes.add_textbox(Inches(1), Inches(2), Inches(11), Inches(3)) tf_err = tb_err.text_frame tf_err.paragraphs[0].text = f"[Image unavailable: {title}]" # ── Save ───────────────────────────────────────────────────────────────────── out_path = "/tmp/workspace/mi-flashcards/MI_Flashcards_Advanced.pptx" buf = BytesIO() prs.save(buf) with open(out_path, "wb") as f: f.write(buf.getvalue()) print(f"Saved: {out_path}") import os print(f"Size: {os.path.getsize(out_path):,} bytes") print(f"Slides: {len(prs.slides)}") PYEOF
MI Flashcards Advanced
Presentation · PPTX
| Slide | Content |
|---|---|
| 1 | Title slide — topics overview |
| 2 | Card 1 — Pathophysiology |
| 3 | Card 2 — ECG Diagnosis |
| 4 | Card 3 — Biomarkers |
| 5 | Card 4 — STEMI Management |
| 6 | Card 5 — Antiplatelet & Anticoagulation |
| 7 | Card 6 — Complications (Killip + mechanical + electrical) |
| 8 | Card 7 — Coronary Territories |
| 9 | Card 8 — Secondary Prevention |
Make flashcards to remember , make it flip and reveal answer
mkdir -p /tmp/workspace/mi-flashcards && ls /tmp/workspace/mi-flashcards/
~/mi-flashcards/MI_Flashcards.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>MI Flashcards — Advanced Clinical</title>
<style>
/* ── Reset & base ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: #0b1120;
font-family: 'Segoe UI', Arial, sans-serif;
color: #e8edf5;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 24px 16px 40px;
}
/* ── Header ── */
header {
text-align: center;
margin-bottom: 28px;
}
header h1 {
font-size: 1.9rem;
font-weight: 700;
letter-spacing: 1px;
color: #fff;
}
header h1 span { color: #f5c518; }
header p { font-size: 0.9rem; color: #8899bb; margin-top: 6px; }
/* ── Progress bar ── */
.progress-wrap {
width: 100%;
max-width: 760px;
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 18px;
}
.progress-bar-bg {
flex: 1;
height: 6px;
background: #1e2d50;
border-radius: 4px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background: linear-gradient(90deg, #f5c518, #ff8c42);
border-radius: 4px;
transition: width 0.4s ease;
}
.progress-text { font-size: 0.85rem; color: #8899bb; white-space: nowrap; }
/* ── Flashcard scene ── */
.scene {
width: 100%;
max-width: 760px;
height: 420px;
perspective: 1200px;
cursor: pointer;
margin-bottom: 24px;
}
.card {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.65s cubic-bezier(0.4, 0.2, 0.2, 1);
}
.card.flipped { transform: rotateY(180deg); }
.face {
position: absolute;
inset: 0;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
border-radius: 18px;
padding: 32px 36px;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Front face */
.face-front {
background: linear-gradient(145deg, #0d1f45 0%, #162b60 100%);
border: 1.5px solid #2a3f70;
box-shadow: 0 8px 40px rgba(0,0,0,0.55);
justify-content: center;
align-items: center;
text-align: center;
}
.face-front .card-num {
position: absolute;
top: 18px; left: 24px;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 2px;
color: #f5c518;
text-transform: uppercase;
}
.face-front .flip-hint {
position: absolute;
bottom: 16px; right: 22px;
font-size: 0.72rem;
color: #445577;
}
.face-front .card-topic {
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 2px;
color: #f5c518;
text-transform: uppercase;
margin-bottom: 18px;
}
.face-front .card-question {
font-size: 1.55rem;
font-weight: 700;
color: #ffffff;
line-height: 1.4;
max-width: 560px;
}
.face-front .card-hint {
margin-top: 18px;
font-size: 0.82rem;
color: #6680aa;
font-style: italic;
}
/* Back face */
.face-back {
background: linear-gradient(145deg, #0e2210 0%, #122a18 100%);
border: 1.5px solid #1e4428;
box-shadow: 0 8px 40px rgba(0,0,0,0.55);
transform: rotateY(180deg);
justify-content: flex-start;
overflow-y: auto;
}
.face-back .card-num {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 2px;
color: #4ade80;
text-transform: uppercase;
margin-bottom: 6px;
flex-shrink: 0;
}
.face-back .answer-heading {
font-size: 0.85rem;
font-weight: 700;
color: #4ade80;
text-transform: uppercase;
letter-spacing: 1.5px;
margin-bottom: 14px;
padding-bottom: 8px;
border-bottom: 1px solid #1e4428;
flex-shrink: 0;
}
.face-back .answer-body {
font-size: 0.88rem;
line-height: 1.75;
color: #cce8d4;
flex: 1;
}
.face-back .answer-body .section {
margin-bottom: 10px;
}
.face-back .answer-body .section-title {
font-weight: 700;
color: #86efac;
font-size: 0.82rem;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 4px;
}
.face-back .answer-body ul {
list-style: none;
padding: 0;
}
.face-back .answer-body ul li {
padding-left: 16px;
position: relative;
margin-bottom: 3px;
}
.face-back .answer-body ul li::before {
content: '▸';
position: absolute;
left: 0;
color: #4ade80;
font-size: 0.75rem;
top: 2px;
}
.face-back .answer-body .highlight {
color: #fde68a;
font-weight: 700;
}
.face-back .source {
flex-shrink: 0;
margin-top: 12px;
font-size: 0.7rem;
color: #2d5a38;
font-style: italic;
border-top: 1px solid #1e4428;
padding-top: 8px;
}
/* ── Nav controls ── */
.controls {
display: flex;
gap: 14px;
align-items: center;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 20px;
}
.btn {
padding: 11px 26px;
border: none;
border-radius: 10px;
font-size: 0.88rem;
font-weight: 700;
cursor: pointer;
transition: all 0.2s;
letter-spacing: 0.5px;
}
.btn-nav {
background: #1a2d55;
color: #c8d8f0;
border: 1px solid #2a3f70;
}
.btn-nav:hover:not(:disabled) { background: #243c70; color: #fff; }
.btn-nav:disabled { opacity: 0.3; cursor: default; }
.btn-flip {
background: linear-gradient(135deg, #f5c518, #e0a800);
color: #0b1120;
}
.btn-flip:hover { background: linear-gradient(135deg, #ffd740, #f5c518); transform: translateY(-1px); }
.btn-shuffle {
background: #1e2d4a;
color: #8899bb;
border: 1px solid #2a3f70;
font-size: 0.8rem;
padding: 11px 18px;
}
.btn-shuffle:hover { background: #263656; color: #c8d8f0; }
/* ── Keyboard hint ── */
.kbd-hint {
font-size: 0.75rem;
color: #3a4f70;
text-align: center;
margin-bottom: 10px;
}
.kbd-hint kbd {
background: #1a2540;
border: 1px solid #2a3f70;
border-radius: 4px;
padding: 1px 6px;
font-size: 0.7rem;
color: #7799cc;
}
/* ── Dot indicators ── */
.dots {
display: flex;
gap: 8px;
flex-wrap: wrap;
justify-content: center;
margin-top: 6px;
}
.dot {
width: 10px; height: 10px;
border-radius: 50%;
background: #1e2d50;
border: 1.5px solid #2a3f70;
cursor: pointer;
transition: all 0.2s;
}
.dot.active { background: #f5c518; border-color: #f5c518; transform: scale(1.25); }
.dot.seen { background: #3a5230; border-color: #4ade80; }
/* ── Scrollbar for back face ── */
.face-back::-webkit-scrollbar { width: 4px; }
.face-back::-webkit-scrollbar-track { background: transparent; }
.face-back::-webkit-scrollbar-thumb { background: #2a4a30; border-radius: 4px; }
</style>
</head>
<body>
<header>
<h1>🫀 <span>Myocardial Infarction</span> — Advanced Flashcards</h1>
<p>Click card or press <strong>Space</strong> to flip · ← → to navigate · 8 cards total</p>
</header>
<div class="progress-wrap">
<div class="progress-bar-bg"><div class="progress-bar-fill" id="progressFill" style="width:12.5%"></div></div>
<span class="progress-text" id="progressText">1 / 8</span>
</div>
<div class="scene" id="scene" onclick="flipCard()">
<div class="card" id="card">
<div class="face face-front" id="frontFace">
<div class="card-num" id="cardNum">Card 1 of 8</div>
<div class="flip-hint" id="flipHint">click to reveal ↩</div>
<div class="card-topic" id="frontTopic"></div>
<div class="card-question" id="frontQuestion"></div>
<div class="card-hint" id="frontHint"></div>
</div>
<div class="face face-back" id="backFace">
<div class="card-num" id="backNum">✓ ANSWER</div>
<div class="answer-heading" id="backHeading"></div>
<div class="answer-body" id="backBody"></div>
<div class="source" id="backSource"></div>
</div>
</div>
</div>
<div class="controls">
<button class="btn btn-nav" id="btnPrev" onclick="prevCard()" disabled>◀ Prev</button>
<button class="btn btn-flip" onclick="flipCard()">↩ Flip Card</button>
<button class="btn btn-nav" id="btnNext" onclick="nextCard()">Next ▶</button>
<button class="btn btn-shuffle" onclick="shuffleCards()">⇄ Shuffle</button>
</div>
<div class="kbd-hint">
<kbd>Space</kbd> flip <kbd>←</kbd> prev <kbd>→</kbd> next <kbd>S</kbd> shuffle
</div>
<div class="dots" id="dots"></div>
<script>
const CARDS = [
{
topic: "Pathophysiology",
question: "What is the pathophysiology of myocardial infarction?",
hint: "Plaque → Thrombus → Necrosis zones",
heading: "Pathophysiology of MI",
body: `
<div class="section">
<div class="section-title">Core Mechanism</div>
<ul>
<li>Atherosclerotic plaque <span class="highlight">rupture or erosion</span> → platelet aggregation → thrombus</li>
<li>Complete coronary occlusion → ischemia → irreversible necrosis</li>
<li>Irreversible injury begins at <span class="highlight">20–40 min</span> of sustained ischemia</li>
<li>Wavefront phenomenon: subendocardium first → transmural over 3–6 h</li>
</ul>
</div>
<div class="section">
<div class="section-title">Zones of Injury</div>
<ul>
<li><span class="highlight">Necrosis</span> (core) → <span class="highlight">Injury</span> → <span class="highlight">Ischemia</span> (outer)</li>
<li>ECG correlate: Q-wave | ST elevation | T-wave inversion</li>
</ul>
</div>
<div class="section">
<div class="section-title">MI Classification</div>
<ul>
<li><span class="highlight">Type 1:</span> Spontaneous plaque rupture/erosion</li>
<li><span class="highlight">Type 2:</span> Supply-demand mismatch (e.g. spasm, anaemia, tachycardia)</li>
<li>Types 3–5: Cardiac death, PCI-related, CABG-related</li>
</ul>
</div>`,
source: "Harrison's 22E | Fuster & Hurst 15E | Robbins Pathology"
},
{
topic: "ECG Diagnosis",
question: "What are the ECG criteria for STEMI vs NSTEMI, and how do changes evolve?",
hint: "Lead thresholds vary by sex & age; evolution: T → ST → Q",
heading: "ECG Criteria & Evolution",
body: `
<div class="section">
<div class="section-title">STEMI Criteria (J-point elevation, 2 contiguous leads)</div>
<ul>
<li>Leads other than V2–V3: <span class="highlight">≥ 0.1 mV</span></li>
<li>V2–V3 men ≥ 40 y: ≥ 0.2 mV | men < 40 y: ≥ 0.25 mV | women: ≥ 0.15 mV</li>
<li>New LBBB in ischaemic context = STEMI equivalent</li>
</ul>
</div>
<div class="section">
<div class="section-title">ECG Evolution (STEMI)</div>
<ul>
<li><span class="highlight">Minutes:</span> Hyperacute T-waves (tall, broad, symmetric)</li>
<li><span class="highlight">Hours:</span> ST elevation → Q waves begin</li>
<li><span class="highlight">Days:</span> T-wave inversion, Q waves deepen</li>
<li><span class="highlight">Weeks:</span> ST normalises; Q waves may persist</li>
</ul>
</div>
<div class="section">
<div class="section-title">NSTEMI / UA</div>
<ul>
<li>ST depression ≥ 0.5 mm or T-wave inversion + <span class="highlight">elevated troponin</span></li>
<li>UA = same ECG changes but <span class="highlight">troponin negative</span></li>
</ul>
</div>
<div class="section">
<div class="section-title">Contiguous Lead Groups</div>
<ul>
<li>Inferior: II, III, aVF | Anterior: V1–V4 | Lateral: I, aVL, V5–V6</li>
<li>Posterior MI: ST depression V1–V3 (mirror); confirm with V7–V9</li>
<li>RV MI (inferior STEMI): ST elevation in V4R</li>
</ul>
</div>`,
source: "Frameworks for Internal Medicine | Harrison's 22E"
},
{
topic: "Biomarkers",
question: "What are the key cardiac biomarkers in MI — rise, peak, and normalisation times?",
hint: "hs-cTn is most sensitive; delta rise protocol at 0h/1h or 0h/2h",
heading: "Cardiac Biomarker Kinetics",
body: `
<div class="section">
<div class="section-title">High-Sensitivity Troponin I/T (hs-cTn) — Gold Standard</div>
<ul>
<li>Rise: <span class="highlight">1–3 h</span> | Peak: 12–24 h | Normalise: 7–14 days</li>
<li>Delta rise ≥ 20% at 1–2 h = diagnose MI (0h/1h or 0h/2h protocol)</li>
<li>Most sensitive AND most specific for myocardial necrosis</li>
</ul>
</div>
<div class="section">
<div class="section-title">CK-MB</div>
<ul>
<li>Rise: 3–4 h | Peak: 18–24 h | Normalise: <span class="highlight">48–72 h</span></li>
<li>Useful for detecting <span class="highlight">reinfarction</span> (normalises faster than troponin)</li>
</ul>
</div>
<div class="section">
<div class="section-title">Myoglobin</div>
<ul>
<li>Rise: <span class="highlight">1–2 h</span> (earliest) | Peak: 6–9 h | Normalise: 24 h</li>
<li>Non-specific — also elevated in skeletal muscle injury</li>
</ul>
</div>
<div class="section">
<div class="section-title">Non-MI causes of Troponin Rise</div>
<ul>
<li>PE, myocarditis, sepsis, AKI, HF, stroke, cardioversion</li>
<li><span class="highlight">Demand ischaemia</span> (Type 2 MI) — context is key</li>
</ul>
</div>`,
source: "Tietz Laboratory Medicine 7E | Goldman-Cecil"
},
{
topic: "STEMI Management",
question: "What are the time targets and reperfusion strategy for STEMI?",
hint: "Door-to-balloon ≤ 90 min; fibrinolysis ≤ 30 min; MONA-B",
heading: "Acute STEMI Management",
body: `
<div class="section">
<div class="section-title">Time Targets</div>
<ul>
<li>First medical contact → ECG: <span class="highlight">≤ 10 min</span></li>
<li>Door-to-balloon (primary PCI): <span class="highlight">≤ 90 min</span></li>
<li>Door-to-needle (fibrinolysis): <span class="highlight">≤ 30 min</span></li>
<li>FMC-to-device if transferred: <span class="highlight">≤ 120 min</span></li>
</ul>
</div>
<div class="section">
<div class="section-title">Primary PCI — Preferred When</div>
<ul>
<li>Skilled lab available within 90–120 min of FMC</li>
<li>Cardiogenic shock, failed fibrinolysis, late presentation (> 3 h)</li>
</ul>
</div>
<div class="section">
<div class="section-title">Fibrinolysis — Use When PCI Unavailable in Time</div>
<ul>
<li>Agents: Alteplase, Tenecteplase (weight-based bolus), Reteplase</li>
<li>Absolute contraindications: prior ICH, active bleeding, aortic dissection, ischaemic stroke < 3 months</li>
</ul>
</div>
<div class="section">
<div class="section-title">Initial Drug Therapy (MONA-B)</div>
<ul>
<li><span class="highlight">M</span>orphine (pain/anxiety) | <span class="highlight">O</span>xygen only if SpO₂ < 90%</li>
<li><span class="highlight">N</span>itrates (avoid if RV MI, hypotension, PDE-5 use)</li>
<li><span class="highlight">A</span>spirin 300 mg + P2Y12 inhibitor loading dose</li>
<li><span class="highlight">B</span>eta-blocker (oral, early if no contraindications) + Heparin</li>
<li>High-intensity <span class="highlight">statin</span> initiated before discharge</li>
</ul>
</div>`,
source: "Tintinalli EM | Fuster & Hurst 15E | Harrison's 22E"
},
{
topic: "Antiplatelet & Anticoagulation",
question: "What is the antithrombotic regimen in ACS — DAPT, anticoagulation choices?",
hint: "Ticagrelor > Prasugrel > Clopidogrel; DAPT 12 months",
heading: "Antithrombotic Therapy in ACS",
body: `
<div class="section">
<div class="section-title">Dual Antiplatelet Therapy (DAPT)</div>
<ul>
<li><span class="highlight">Aspirin</span> 162–325 mg load → 75–100 mg daily (indefinite)</li>
<li><span class="highlight">Ticagrelor</span> 180 mg load (preferred — PLATO trial, ↓ CV death vs clopidogrel)</li>
<li><span class="highlight">Prasugrel</span> 60 mg load (STEMI + PCI; avoid if prior TIA/stroke, age ≥ 75, < 60 kg)</li>
<li><span class="highlight">Clopidogrel</span> 600 mg load (if ticagrelor/prasugrel unavailable or on OAC)</li>
<li>DAPT duration: <span class="highlight">12 months</span> post-ACS (shorter if high bleed risk; longer if high ischaemic risk)</li>
</ul>
</div>
<div class="section">
<div class="section-title">Anticoagulation</div>
<ul>
<li><span class="highlight">UFH</span>: IV bolus + infusion — preferred for STEMI PCI (easy reversal)</li>
<li><span class="highlight">Enoxaparin</span>: 1 mg/kg SC q12h — NSTEMI/NSTE-ACS (SYNERGY trial)</li>
<li><span class="highlight">Fondaparinux</span>: 2.5 mg SC daily — preferred NSTEMI if no PCI planned (lowest bleed risk)</li>
<li><span class="highlight">Bivalirudin</span>: direct thrombin inhibitor; alternative in PCI (less HIT risk)</li>
</ul>
</div>
<div class="section">
<div class="section-title">GPIIb/IIIa Inhibitors</div>
<ul>
<li>Abciximab, Eptifibatide, Tirofiban — bail-out or high-thrombus-burden PCI only</li>
</ul>
</div>`,
source: "Harrison's 22E | Fuster & Hurst 15E | ESC ACS Guidelines 2023"
},
{
topic: "Complications",
question: "What are the major complications of MI — mechanical, electrical, and Killip?",
hint: "Killip class I–IV; mechanical days 3–7; Dressler's at 2–10 weeks",
heading: "MI Complications",
body: `
<div class="section">
<div class="section-title">Killip Classification (in-hospital mortality)</div>
<ul>
<li><span class="highlight">Class I</span>: No HF signs — 6% mortality</li>
<li><span class="highlight">Class II</span>: S3 gallop / basal rales — 17%</li>
<li><span class="highlight">Class III</span>: Acute pulmonary oedema — 38%</li>
<li><span class="highlight">Class IV</span>: Cardiogenic shock (BP < 90, cool peripheries) — 81%</li>
</ul>
</div>
<div class="section">
<div class="section-title">Mechanical Complications (days 3–7)</div>
<ul>
<li><span class="highlight">Free wall rupture</span>: haemopericardium → tamponade → PEA arrest (surgical emergency)</li>
<li><span class="highlight">Papillary muscle rupture</span>: acute severe MR → flash pulmonary oedema; new loud systolic murmur</li>
<li><span class="highlight">VSD</span>: left-to-right shunt; step-up in O₂ sat at RV; new harsh pansystolic murmur; echo diagnostic</li>
</ul>
</div>
<div class="section">
<div class="section-title">Electrical Complications</div>
<ul>
<li><span class="highlight">VF/VT</span>: #1 cause of pre-hospital death; defibrillate immediately</li>
<li><span class="highlight">Complete heart block</span>: inferior MI (RCA) → atropine/temporary pacing</li>
<li><span class="highlight">AIVR</span>: accelerated idioventricular rhythm = reperfusion arrhythmia; usually benign</li>
</ul>
</div>
<div class="section">
<div class="section-title">Other</div>
<ul>
<li><span class="highlight">Dressler's syndrome</span> (2–10 weeks): immune pericarditis + pleural effusion → treat with aspirin/colchicine</li>
<li><span class="highlight">LV thrombus</span>: anterior STEMI → anticoagulate 3–6 months</li>
</ul>
</div>`,
source: "Goldman-Cecil | Robbins Pathology | Harrison's 22E"
},
{
topic: "Coronary Territories",
question: "Which coronary artery supplies which territory — ECG leads and clinical correlates?",
hint: "LAD → anterior; RCA → inferior + AV node; LCX → lateral/posterior",
heading: "Coronary Artery Territories",
body: `
<div class="section">
<div class="section-title">LAD (Left Anterior Descending)</div>
<ul>
<li>Territory: Anterior wall, <span class="highlight">septum</span>, apex, anterior papillary muscle</li>
<li>ECG: V1–V4 (anterior STEMI)</li>
<li>Risks: Large MI, new LBBB, high-degree AV block, cardiogenic shock, apical thrombus</li>
</ul>
</div>
<div class="section">
<div class="section-title">RCA (Right Coronary Artery)</div>
<ul>
<li>Territory: <span class="highlight">Inferior wall</span>, RV, SA node (60%), AV node (90%) in right-dominant hearts</li>
<li>ECG: II, III, aVF; check V4R for RV infarction</li>
<li><span class="highlight">RV MI triad</span>: hypotension + raised JVP + clear lungs (no diuretics/nitrates!)</li>
<li>Risks: Brady/AV block → atropine or pacing</li>
</ul>
</div>
<div class="section">
<div class="section-title">LCX (Left Circumflex)</div>
<ul>
<li>Territory: <span class="highlight">Lateral wall</span>; posterior wall if dominant LCx</li>
<li>ECG: I, aVL, V5–V6; posterior MI → ST depression V1–V3, confirm V7–V9</li>
<li>Often "electrically silent" on standard 12-lead — do posterior leads if suspicious</li>
</ul>
</div>`,
source: "Fuster & Hurst 15E | Harrison's 22E"
},
{
topic: "Secondary Prevention",
question: "What medications and targets are used for secondary prevention after MI?",
hint: "DAPT + statin + ACEi/ARB + BB + MRA + SGLT2i; LDL < 55 mg/dL",
heading: "Secondary Prevention Post-MI",
body: `
<div class="section">
<div class="section-title">Mandatory Medications (start before discharge)</div>
<ul>
<li><span class="highlight">Aspirin</span> 75–100 mg — indefinite</li>
<li><span class="highlight">P2Y12 inhibitor</span> (ticagrelor/clopidogrel) — 12 months</li>
<li><span class="highlight">High-intensity statin</span> — atorvastatin 40–80 mg or rosuvastatin 20–40 mg, indefinite</li>
<li><span class="highlight">Beta-blocker</span> — all with LVEF ≤ 40% for ≥ 3 years; reduces sudden cardiac death</li>
<li><span class="highlight">ACE inhibitor / ARB</span> — if LVEF ≤ 40%, DM, or HTN — indefinite</li>
</ul>
</div>
<div class="section">
<div class="section-title">Additional (if LVEF ≤ 40%)</div>
<ul>
<li><span class="highlight">MRA</span> (eplerenone/spironolactone) — if HF symptoms or DM</li>
<li><span class="highlight">SGLT2 inhibitor</span> (dapagliflozin/empagliflozin) — if HFrEF post-MI</li>
</ul>
</div>
<div class="section">
<div class="section-title">Treatment Targets</div>
<ul>
<li>LDL: <span class="highlight">< 55 mg/dL</span> (ESC) / < 70 mg/dL (ACC) — add ezetimibe or PCSK9i if needed</li>
<li>BP: < 130/80 mmHg | HbA1c: < 7% | BMI: < 25 | Smoking: cessation</li>
</ul>
</div>
<div class="section">
<div class="section-title">Device Therapy</div>
<ul>
<li><span class="highlight">ICD</span>: LVEF ≤ 35% after ≥ 40 days post-MI + GDMT ≥ 3 months + life expectancy > 1 year</li>
<li>Cardiac rehabilitation — all post-MI patients</li>
</ul>
</div>`,
source: "Harrison's 22E | ESC ACS Guidelines 2023 | Fuster & Hurst 15E"
}
];
let current = 0;
let isFlipped = false;
let order = CARDS.map((_, i) => i);
let seen = new Set();
function renderCard(idx, flip) {
const c = CARDS[order[idx]];
document.getElementById('cardNum').textContent = `Card ${idx + 1} of ${CARDS.length}`;
document.getElementById('backNum').textContent = `✓ CARD ${idx + 1} — ANSWER`;
document.getElementById('frontTopic').textContent = c.topic;
document.getElementById('frontQuestion').textContent = c.question;
document.getElementById('frontHint').textContent = c.hint ? `💡 ${c.hint}` : '';
document.getElementById('backHeading').textContent = c.heading;
document.getElementById('backBody').innerHTML = c.body;
document.getElementById('backSource').textContent = `📚 ${c.source}`;
const cardEl = document.getElementById('card');
// Remove flip, apply instantly
cardEl.style.transition = 'none';
cardEl.classList.remove('flipped');
// Force reflow
void cardEl.offsetWidth;
cardEl.style.transition = '';
isFlipped = false;
if (flip) { cardEl.classList.add('flipped'); isFlipped = true; }
// Progress
const pct = ((idx + 1) / CARDS.length) * 100;
document.getElementById('progressFill').style.width = pct + '%';
document.getElementById('progressText').textContent = `${idx + 1} / ${CARDS.length}`;
// Buttons
document.getElementById('btnPrev').disabled = (idx === 0);
document.getElementById('btnNext').disabled = (idx === CARDS.length - 1);
// Dots
document.querySelectorAll('.dot').forEach((d, i) => {
d.className = 'dot' + (i === idx ? ' active' : '') + (seen.has(i) && i !== idx ? ' seen' : '');
});
}
function flipCard() {
const cardEl = document.getElementById('card');
if (isFlipped) {
cardEl.classList.remove('flipped');
isFlipped = false;
} else {
cardEl.classList.add('flipped');
isFlipped = true;
seen.add(current);
renderDots();
}
}
function renderDots() {
document.querySelectorAll('.dot').forEach((d, i) => {
d.className = 'dot' + (i === current ? ' active' : '') + (seen.has(i) && i !== current ? ' seen' : '');
});
}
function nextCard() {
if (current < CARDS.length - 1) {
current++;
renderCard(current, false);
}
}
function prevCard() {
if (current > 0) {
current--;
renderCard(current, false);
}
}
function goToCard(idx) {
current = idx;
renderCard(current, false);
}
function shuffleCards() {
for (let i = order.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[order[i], order[j]] = [order[j], order[i]];
}
current = 0;
seen.clear();
renderCard(current, false);
}
// Build dots
const dotsEl = document.getElementById('dots');
CARDS.forEach((_, i) => {
const d = document.createElement('div');
d.className = 'dot' + (i === 0 ? ' active' : '');
d.title = CARDS[i].topic;
d.onclick = () => goToCard(i);
dotsEl.appendChild(d);
});
// Keyboard nav
document.addEventListener('keydown', e => {
if (e.code === 'Space' || e.key === ' ') { e.preventDefault(); flipCard(); }
if (e.key === 'ArrowRight') { nextCard(); }
if (e.key === 'ArrowLeft') { prevCard(); }
if (e.key === 's' || e.key === 'S') { shuffleCards(); }
});
// Initial render
renderCard(0, false);
</script>
</body>
</html>
python3 -c " from html.parser import HTMLParser import os path = '/tmp/workspace/mi-flashcards/MI_Flashcards.html' size = os.path.getsize(path) print(f'File size: {size:,} bytes') # Basic validation with open(path) as f: content = f.read() checks = [ ('DOCTYPE', '<!DOCTYPE html>' in content), ('8 cards defined', content.count('topic:') == 8), ('Flip animation CSS', 'rotateY(180deg)' in content), ('backface-visibility', 'backface-visibility: hidden' in content), ('Keyboard nav', 'ArrowRight' in content), ('Shuffle function', 'shuffleCards' in content), ('Progress bar', 'progressFill' in content), ('Dot indicators', 'dot active' in content or \"'dot'\" in content), ('All 8 topics present', all(t in content for t in [ 'Pathophysiology','ECG Diagnosis','Biomarkers','STEMI Management', 'Antiplatelet','Complications','Coronary Territories','Secondary Prevention' ])), ] for label, ok in checks: print(f\" {'✓' if ok else '✗'} {label}\") "
MI Flashcards
HTML File · HTML
| Action | Control |
|---|---|
| Flip card (reveal answer) | Click the card, click "Flip Card" button, or press Space |
| Next card | Click "Next ▶" or press → |
| Previous card | Click "◀ Prev" or press ← |
| Shuffle | Click "⇄ Shuffle" or press S |
| Jump to any card | Click the dot indicators at the bottom |
Export flashcards as a mobile-friendly app (PWA)
mkdir -p /tmp/workspace/mi-pwa/icons && ls /tmp/workspace/mi-pwa/
python3 << 'PYEOF' # Generate PNG icons (192x192 and 512x512) with Pillow from PIL import Image, ImageDraw, ImageFont import os def make_icon(size, path): img = Image.new("RGB", (size, size), color=(13, 27, 62)) d = ImageDraw.Draw(img) # Gold circle background margin = int(size * 0.08) d.ellipse([margin, margin, size-margin, size-margin], fill=(245, 197, 24)) # Heart emoji approximation: red heart shape # Draw a simple red heart using two circles + triangle cx, cy = size // 2, int(size * 0.47) r = int(size * 0.18) # Two circles d.ellipse([cx - r*2, cy - r, cx, cy + r], fill=(200, 30, 40)) d.ellipse([cx, cy - r, cx + r*2, cy + r], fill=(200, 30, 40)) # Triangle bottom d.polygon([ (cx - r*2, cy + int(r*0.3)), (cx + r*2, cy + int(r*0.3)), (cx, cy + int(r*2.2)) ], fill=(200, 30, 40)) # White "MI" text try: font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", int(size * 0.13)) except: font = ImageFont.load_default() text = "MI" bbox = d.textbbox((0,0), text, font=font) tw, th = bbox[2]-bbox[0], bbox[3]-bbox[1] d.text((cx - tw//2, cy + int(r*2.5) - th//2), text, fill=(255,255,255), font=font) img.save(path, "PNG") print(f" Created {path} ({size}x{size})") make_icon(192, "/tmp/workspace/mi-pwa/icons/icon-192.png") make_icon(512, "/tmp/workspace/mi-pwa/icons/icon-512.png") print("Icons done.") PYEOF
~/mi-pwa/manifest.json
{
"name": "MI Flashcards — Advanced Clinical",
"short_name": "MI Cards",
"description": "Advanced myocardial infarction flashcards for clinical exam preparation. Covers pathophysiology, ECG, biomarkers, STEMI management, complications, and secondary prevention.",
"start_url": "./index.html",
"display": "standalone",
"background_color": "#0b1120",
"theme_color": "#0b1120",
"orientation": "any",
"scope": "./",
"lang": "en",
"categories": ["education", "medical", "health"],
"icons": [
{
"src": "icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}
~/mi-pwa/sw.js
// MI Flashcards PWA — Service Worker
// Cache version — bump this string to force cache refresh
const CACHE_NAME = 'mi-flashcards-v1';
// All files to cache for offline use
const ASSETS = [
'./index.html',
'./manifest.json',
'./icons/icon-192.png',
'./icons/icon-512.png'
];
// ── Install: pre-cache all assets ──────────────────────────────────────────
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
return cache.addAll(ASSETS);
})
);
self.skipWaiting();
});
// ── Activate: remove old caches ────────────────────────────────────────────
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys =>
Promise.all(
keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k))
)
)
);
self.clients.claim();
});
// ── Fetch: cache-first strategy ────────────────────────────────────────────
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cached => {
if (cached) return cached;
return fetch(event.request).then(response => {
// Cache new successful GET responses
if (event.request.method === 'GET' && response.status === 200) {
const clone = response.clone();
caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
}
return response;
}).catch(() => {
// Return index.html as fallback for navigation requests
if (event.request.mode === 'navigate') {
return caches.match('./index.html');
}
});
})
);
});
~/mi-pwa/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"/>
<meta name="theme-color" content="#0b1120"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
<meta name="apple-mobile-web-app-title" content="MI Cards"/>
<meta name="description" content="Advanced MI flashcards for clinical exam preparation — flip cards, offline-ready PWA"/>
<link rel="manifest" href="manifest.json"/>
<link rel="apple-touch-icon" href="icons/icon-192.png"/>
<title>MI Flashcards</title>
<style>
/* ═══════════════════════════════════════════════════════════
RESET & TOKENS
═══════════════════════════════════════════════════════════ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #0b1120;
--surface: #111d38;
--border: #1e2e50;
--gold: #f5c518;
--gold-dim: #c49a10;
--green: #4ade80;
--green-dim: #1e4428;
--text: #e0e8f8;
--muted: #6677aa;
--front-bg1: #0d1f45;
--front-bg2: #162b60;
--back-bg1: #0e2210;
--back-bg2: #122a18;
--radius: 20px;
--safe-top: env(safe-area-inset-top, 0px);
--safe-bot: env(safe-area-inset-bottom, 0px);
}
html, body {
height: 100%;
overflow: hidden; /* prevent scroll bounce on iOS */
}
body {
background: var(--bg);
font-family: -apple-system, 'Segoe UI', Arial, sans-serif;
color: var(--text);
display: flex;
flex-direction: column;
padding-top: var(--safe-top);
padding-bottom: var(--safe-bot);
touch-action: pan-y;
user-select: none;
-webkit-user-select: none;
}
/* ═══════════════════════════════════════════════════════════
TOP BAR
═══════════════════════════════════════════════════════════ */
.topbar {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px 8px;
background: var(--bg);
border-bottom: 1px solid var(--border);
}
.topbar-title {
font-size: 1rem;
font-weight: 700;
color: #fff;
letter-spacing: 0.5px;
display: flex;
align-items: center;
gap: 6px;
}
.topbar-title .heart { color: #e74c3c; font-size: 1.1rem; }
.topbar-counter {
font-size: 0.8rem;
font-weight: 700;
color: var(--gold);
background: rgba(245,197,24,0.1);
border: 1px solid rgba(245,197,24,0.25);
border-radius: 8px;
padding: 3px 10px;
}
/* ═══════════════════════════════════════════════════════════
PROGRESS BAR
═══════════════════════════════════════════════════════════ */
.progress-track {
flex-shrink: 0;
height: 3px;
background: var(--border);
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--gold), #ff8c42);
transition: width 0.4s ease;
border-radius: 0 2px 2px 0;
}
/* ═══════════════════════════════════════════════════════════
TOPIC LABEL
═══════════════════════════════════════════════════════════ */
.topic-row {
flex-shrink: 0;
display: flex;
justify-content: center;
padding: 8px 16px 4px;
}
.topic-pill {
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 1.5px;
text-transform: uppercase;
color: var(--gold);
background: rgba(245,197,24,0.08);
border: 1px solid rgba(245,197,24,0.2);
border-radius: 20px;
padding: 3px 14px;
}
/* ═══════════════════════════════════════════════════════════
CARD SCENE
═══════════════════════════════════════════════════════════ */
.scene-wrap {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 8px 12px;
overflow: hidden;
}
.scene {
width: 100%;
max-width: 560px;
/* Maintain a good aspect ratio across screen sizes */
aspect-ratio: 3 / 2.2;
perspective: 1400px;
cursor: pointer;
}
@media (max-height: 680px) {
.scene { aspect-ratio: 3 / 1.9; }
}
.card {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.6s cubic-bezier(0.35, 0.1, 0.15, 1);
border-radius: var(--radius);
}
.card.flipped { transform: rotateY(180deg); }
.face {
position: absolute;
inset: 0;
border-radius: var(--radius);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* ── Front ── */
.face-front {
background: linear-gradient(145deg, var(--front-bg1) 0%, var(--front-bg2) 100%);
border: 1.5px solid #2a3f72;
box-shadow: 0 12px 48px rgba(0,0,0,0.6);
align-items: center;
justify-content: center;
text-align: center;
padding: 24px 28px 16px;
gap: 0;
}
.front-hint-top {
position: absolute;
top: 14px; right: 16px;
font-size: 0.65rem;
color: rgba(255,255,255,0.2);
display: flex;
align-items: center;
gap: 4px;
}
.front-q-label {
font-size: 0.62rem;
font-weight: 700;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 14px;
}
.front-question {
font-size: clamp(1rem, 3.5vw, 1.45rem);
font-weight: 700;
color: #fff;
line-height: 1.45;
max-width: 420px;
}
.front-sub {
margin-top: 16px;
font-size: 0.72rem;
color: #445577;
font-style: italic;
max-width: 380px;
}
.tap-indicator {
position: absolute;
bottom: 14px;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 5px;
font-size: 0.65rem;
color: rgba(255,255,255,0.18);
}
/* ── Back ── */
.face-back {
background: linear-gradient(145deg, var(--back-bg1) 0%, var(--back-bg2) 100%);
border: 1.5px solid #1e4428;
box-shadow: 0 12px 48px rgba(0,0,0,0.6);
transform: rotateY(180deg);
padding: 16px 20px 12px;
overflow: hidden;
}
.back-top {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
border-bottom: 1px solid var(--green-dim);
padding-bottom: 8px;
}
.back-badge {
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 1.5px;
text-transform: uppercase;
background: var(--green-dim);
color: var(--green);
border-radius: 6px;
padding: 2px 8px;
}
.back-heading {
font-size: 0.78rem;
font-weight: 700;
color: var(--green);
}
.back-scroll {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
}
.back-scroll::-webkit-scrollbar { display: none; }
.answer-body {
font-size: clamp(0.7rem, 2.2vw, 0.82rem);
line-height: 1.7;
color: #b8d8c0;
}
.section { margin-bottom: 9px; }
.section-title {
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 1.2px;
text-transform: uppercase;
color: #86efac;
margin-bottom: 3px;
}
.answer-body ul { list-style: none; padding: 0; }
.answer-body ul li {
padding-left: 14px;
position: relative;
margin-bottom: 2px;
}
.answer-body ul li::before {
content: '▸';
position: absolute;
left: 0;
color: var(--green);
font-size: 0.65rem;
top: 3px;
}
.highlight { color: #fde68a; font-weight: 700; }
.back-source {
flex-shrink: 0;
font-size: 0.62rem;
color: #2a5235;
font-style: italic;
margin-top: 6px;
padding-top: 6px;
border-top: 1px solid var(--green-dim);
}
/* ═══════════════════════════════════════════════════════════
SWIPE HINT (mobile, shown once)
═══════════════════════════════════════════════════════════ */
.swipe-hint {
flex-shrink: 0;
text-align: center;
font-size: 0.65rem;
color: var(--muted);
padding: 2px 0 4px;
opacity: 1;
transition: opacity 0.5s;
}
.swipe-hint.hidden { opacity: 0; pointer-events: none; }
/* ═══════════════════════════════════════════════════════════
BOTTOM CONTROLS
═══════════════════════════════════════════════════════════ */
.controls {
flex-shrink: 0;
padding: 8px 16px 10px;
display: flex;
gap: 8px;
align-items: center;
}
.btn {
border: none;
border-radius: 12px;
font-size: 0.82rem;
font-weight: 700;
cursor: pointer;
padding: 12px 0;
transition: all 0.15s;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
.btn:active { transform: scale(0.95); }
.btn-nav {
flex: 1;
background: var(--surface);
color: var(--text);
border: 1px solid var(--border);
}
.btn-nav:disabled { opacity: 0.25; cursor: default; transform: none; }
.btn-flip {
flex: 2;
background: linear-gradient(135deg, var(--gold) 0%, #e0a800 100%);
color: #0b1120;
box-shadow: 0 4px 14px rgba(245,197,24,0.3);
font-size: 0.88rem;
}
.btn-flip:active { box-shadow: none; }
.btn-shuffle {
width: 44px;
padding: 12px 0;
background: var(--surface);
color: var(--muted);
border: 1px solid var(--border);
font-size: 1rem;
flex-shrink: 0;
}
/* ═══════════════════════════════════════════════════════════
DOT INDICATORS
═══════════════════════════════════════════════════════════ */
.dots-row {
flex-shrink: 0;
display: flex;
gap: 6px;
justify-content: center;
padding: 2px 16px 10px;
flex-wrap: wrap;
}
.dot {
width: 8px; height: 8px;
border-radius: 50%;
background: var(--border);
border: 1.5px solid #2a3f70;
cursor: pointer;
transition: all 0.2s;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
.dot.active { background: var(--gold); border-color: var(--gold); transform: scale(1.4); }
.dot.seen { background: var(--green-dim); border-color: var(--green); }
/* ═══════════════════════════════════════════════════════════
INSTALL BANNER
═══════════════════════════════════════════════════════════ */
.install-banner {
position: fixed;
bottom: calc(var(--safe-bot) + 12px);
left: 12px; right: 12px;
background: #162b60;
border: 1px solid var(--gold);
border-radius: 14px;
padding: 12px 16px;
display: flex;
align-items: center;
gap: 12px;
box-shadow: 0 8px 30px rgba(0,0,0,0.5);
z-index: 100;
transform: translateY(120%);
transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.install-banner.visible { transform: translateY(0); }
.install-icon { font-size: 1.5rem; flex-shrink: 0; }
.install-text { flex: 1; }
.install-text strong { display: block; font-size: 0.82rem; color: #fff; }
.install-text span { font-size: 0.72rem; color: var(--muted); }
.btn-install {
background: var(--gold);
color: #0b1120;
border: none;
border-radius: 8px;
padding: 7px 14px;
font-size: 0.78rem;
font-weight: 700;
cursor: pointer;
flex-shrink: 0;
-webkit-tap-highlight-color: transparent;
}
.btn-install-dismiss {
background: none;
border: none;
color: var(--muted);
font-size: 1.1rem;
cursor: pointer;
flex-shrink: 0;
padding: 4px;
-webkit-tap-highlight-color: transparent;
}
/* ═══════════════════════════════════════════════════════════
RESPONSIVE TWEAKS
═══════════════════════════════════════════════════════════ */
@media (min-width: 600px) {
.topbar { padding: 14px 24px 10px; }
.controls { padding: 10px 24px 12px; gap: 12px; }
.dots-row { padding: 4px 24px 12px; gap: 8px; }
.dot { width: 10px; height: 10px; }
}
</style>
</head>
<body>
<!-- TOP BAR -->
<div class="topbar">
<div class="topbar-title"><span class="heart">♥</span> MI Flashcards</div>
<div class="topbar-counter" id="counterLabel">1 / 8</div>
</div>
<!-- PROGRESS -->
<div class="progress-track">
<div class="progress-fill" id="progressFill" style="width:12.5%"></div>
</div>
<!-- TOPIC LABEL -->
<div class="topic-row">
<div class="topic-pill" id="topicPill">Pathophysiology</div>
</div>
<!-- CARD SCENE -->
<div class="scene-wrap">
<div class="scene" id="scene">
<div class="card" id="card">
<!-- FRONT -->
<div class="face face-front" id="frontFace">
<div class="front-hint-top">QUESTION</div>
<div class="front-q-label">Tap to reveal answer</div>
<div class="front-question" id="frontQuestion"></div>
<div class="front-sub" id="frontSub"></div>
<div class="tap-indicator">
<span>↩</span><span>tap anywhere on card</span>
</div>
</div>
<!-- BACK -->
<div class="face face-back" id="backFace">
<div class="back-top">
<span class="back-badge">ANSWER</span>
<span class="back-heading" id="backHeading"></span>
</div>
<div class="back-scroll">
<div class="answer-body" id="backBody"></div>
</div>
<div class="back-source" id="backSource"></div>
</div>
</div><!-- .card -->
</div><!-- .scene -->
</div>
<!-- SWIPE HINT -->
<div class="swipe-hint" id="swipeHint">← swipe to navigate · tap card to flip →</div>
<!-- CONTROLS -->
<div class="controls">
<button class="btn btn-nav" id="btnPrev" onclick="prevCard()" disabled>◀</button>
<button class="btn btn-flip" onclick="flipCard()">↩ Flip</button>
<button class="btn btn-nav" id="btnNext" onclick="nextCard()">▶</button>
<button class="btn btn-shuffle" onclick="shuffleCards()" title="Shuffle">⇄</button>
</div>
<!-- DOTS -->
<div class="dots-row" id="dotsRow"></div>
<!-- INSTALL BANNER -->
<div class="install-banner" id="installBanner">
<div class="install-icon">📲</div>
<div class="install-text">
<strong>Add to Home Screen</strong>
<span>Study offline, anytime</span>
</div>
<button class="btn-install" id="btnInstall">Install</button>
<button class="btn-install-dismiss" id="btnDismiss">✕</button>
</div>
<script>
/* ═══════════════════════════════════════════════════════════
CARD DATA
═══════════════════════════════════════════════════════════ */
const CARDS = [
{
topic: "Pathophysiology",
question: "What is the pathophysiology of myocardial infarction?",
hint: "Plaque → Thrombus → Necrosis zones",
heading: "Pathophysiology",
body: `
<div class="section"><div class="section-title">Core Mechanism</div><ul>
<li>Atherosclerotic plaque <span class="highlight">rupture or erosion</span> → platelet aggregation → thrombus formation</li>
<li>Complete occlusion → ischaemia → irreversible necrosis from <span class="highlight">20–40 min</span></li>
<li>Wavefront: subendocardium first → transmural over 3–6 h</li>
</ul></div>
<div class="section"><div class="section-title">Zones (ECG correlate)</div><ul>
<li><span class="highlight">Necrosis</span> → Q wave | <span class="highlight">Injury</span> → ST elevation | <span class="highlight">Ischaemia</span> → T inversion</li>
</ul></div>
<div class="section"><div class="section-title">MI Classification</div><ul>
<li><span class="highlight">Type 1:</span> Spontaneous plaque rupture/erosion</li>
<li><span class="highlight">Type 2:</span> Supply-demand mismatch (spasm, anaemia, tachycardia)</li>
<li>Types 3–5: Cardiac death, PCI-related, CABG-related</li>
</ul></div>`,
source: "Harrison's 22E · Fuster & Hurst 15E · Robbins Pathology"
},
{
topic: "ECG Diagnosis",
question: "ECG criteria for STEMI vs NSTEMI — thresholds and evolution?",
hint: "Leads V2-V3 have higher thresholds; earliest change = hyperacute T",
heading: "ECG Criteria & Evolution",
body: `
<div class="section"><div class="section-title">STEMI — J-point elevation, 2 contiguous leads</div><ul>
<li>All leads (except V2–V3): <span class="highlight">≥ 0.1 mV</span></li>
<li>V2–V3: men ≥ 40 y → <span class="highlight">≥ 0.2 mV</span> | men < 40 y → ≥ 0.25 mV | women → ≥ 0.15 mV</li>
<li>New LBBB in ischaemic context = STEMI equivalent</li>
</ul></div>
<div class="section"><div class="section-title">ECG Evolution (STEMI)</div><ul>
<li>Minutes: <span class="highlight">Hyperacute T-waves</span> (tall, broad, symmetric)</li>
<li>Hours: ST elevation + early Q waves | Days: T inversion, Q deepen</li>
</ul></div>
<div class="section"><div class="section-title">NSTEMI / UA</div><ul>
<li>ST depression ≥ 0.5 mm or T inversion + <span class="highlight">elevated troponin</span> = NSTEMI</li>
<li>Same ECG but troponin <span class="highlight">negative</span> = Unstable Angina</li>
</ul></div>
<div class="section"><div class="section-title">Lead Groups</div><ul>
<li>Inferior: II, III, aVF | Anterior: V1–V4 | Lateral: I, aVL, V5–V6</li>
<li>Posterior: ST depression V1–V3 → confirm with V7–V9 | RV: V4R</li>
</ul></div>`,
source: "Frameworks for Internal Medicine · Harrison's 22E"
},
{
topic: "Biomarkers",
question: "Cardiac biomarker kinetics in MI — rise, peak, normalise times?",
hint: "hs-cTn is gold standard; CK-MB detects reinfarction",
heading: "Biomarker Kinetics",
body: `
<div class="section"><div class="section-title">hs-Troponin I/T — Gold Standard</div><ul>
<li>Rise: <span class="highlight">1–3 h</span> | Peak: 12–24 h | Normalise: 7–14 days</li>
<li>Delta rise <span class="highlight">≥ 20%</span> at 1–2 h = diagnose MI (0h/1h or 0h/2h protocol)</li>
</ul></div>
<div class="section"><div class="section-title">CK-MB</div><ul>
<li>Rise: 3–4 h | Peak: 18–24 h | Normalise: <span class="highlight">48–72 h</span></li>
<li>Use for: detecting <span class="highlight">reinfarction</span> (normalises faster than troponin)</li>
</ul></div>
<div class="section"><div class="section-title">Myoglobin</div><ul>
<li>Rise: <span class="highlight">1–2 h</span> (earliest) | Peak: 6–9 h | Normalise: 24 h</li>
<li>Non-specific — elevated in skeletal muscle injury too</li>
</ul></div>
<div class="section"><div class="section-title">Non-MI Troponin Rise (Type 2)</div><ul>
<li>PE, myocarditis, sepsis, AKI, HF, stroke, cardioversion</li>
</ul></div>`,
source: "Tietz Lab Medicine 7E · Goldman-Cecil"
},
{
topic: "STEMI Management",
question: "Time targets and reperfusion strategy for STEMI?",
hint: "D2B ≤ 90 min; D2N ≤ 30 min; FMC→ECG ≤ 10 min",
heading: "Acute STEMI Management",
body: `
<div class="section"><div class="section-title">Time Targets</div><ul>
<li>FMC → ECG: <span class="highlight">≤ 10 min</span></li>
<li>Door-to-balloon (primary PCI): <span class="highlight">≤ 90 min</span></li>
<li>Door-to-needle (fibrinolysis): <span class="highlight">≤ 30 min</span></li>
<li>FMC-to-device if transferred: <span class="highlight">≤ 120 min</span></li>
</ul></div>
<div class="section"><div class="section-title">Reperfusion Choice</div><ul>
<li>Primary PCI preferred if skilled lab within 90–120 min</li>
<li>Fibrinolysis (alteplase/tenecteplase) if PCI unavailable in time</li>
<li>Absolute CI fibrinolysis: prior ICH, active bleed, aortic dissection</li>
</ul></div>
<div class="section"><div class="section-title">Initial Drug Therapy (MONA-B)</div><ul>
<li><span class="highlight">M</span>orphine · <span class="highlight">O</span>xygen (SpO₂ < 90% only) · <span class="highlight">N</span>itrates</li>
<li><span class="highlight">A</span>spirin 300 mg + P2Y12 load · <span class="highlight">B</span>eta-blocker + Heparin</li>
<li>High-intensity <span class="highlight">statin</span> before discharge</li>
</ul></div>`,
source: "Tintinalli EM · Fuster & Hurst 15E · Harrison's 22E"
},
{
topic: "Antithrombotic Therapy",
question: "DAPT and anticoagulation regimen in ACS — agents and duration?",
hint: "Ticagrelor > Prasugrel > Clopidogrel; DAPT = 12 months",
heading: "Antithrombotic Therapy",
body: `
<div class="section"><div class="section-title">DAPT Loading Doses</div><ul>
<li>Aspirin 162–325 mg → 75–100 mg daily (indefinite)</li>
<li><span class="highlight">Ticagrelor</span> 180 mg — preferred (PLATO: ↓ CV death vs clopidogrel)</li>
<li><span class="highlight">Prasugrel</span> 60 mg — STEMI + PCI (avoid: prior TIA/stroke, ≥ 75 y, < 60 kg)</li>
<li><span class="highlight">Clopidogrel</span> 600 mg — if above unavailable or triple therapy</li>
<li>DAPT duration: <span class="highlight">12 months</span> post-ACS</li>
</ul></div>
<div class="section"><div class="section-title">Anticoagulation</div><ul>
<li>UFH IV — STEMI PCI (easy reversal with protamine)</li>
<li>Enoxaparin 1 mg/kg SC q12h — NSTEMI</li>
<li>Fondaparinux 2.5 mg SC — NSTEMI conservative (lowest bleed risk)</li>
<li>Bivalirudin — direct thrombin inhibitor; PCI alternative</li>
</ul></div>`,
source: "Harrison's 22E · Fuster & Hurst 15E · ESC ACS 2023"
},
{
topic: "Complications",
question: "Major complications of MI — Killip, mechanical, and electrical?",
hint: "Mechanical: days 3–7; Dressler's: 2–10 weeks; Killip IV = 81% mortality",
heading: "MI Complications",
body: `
<div class="section"><div class="section-title">Killip Classification</div><ul>
<li>I: No HF — <span class="highlight">6%</span> | II: S3/rales — <span class="highlight">17%</span></li>
<li>III: Pulmonary oedema — <span class="highlight">38%</span> | IV: Cardiogenic shock — <span class="highlight">81%</span></li>
</ul></div>
<div class="section"><div class="section-title">Mechanical (days 3–7)</div><ul>
<li><span class="highlight">Free wall rupture</span> → tamponade → PEA (surgical emergency)</li>
<li><span class="highlight">Papillary muscle rupture</span> → acute MR, flash pulmonary oedema</li>
<li><span class="highlight">VSD</span> → left-to-right shunt; step-up O₂ sat in RV; echo confirms</li>
</ul></div>
<div class="section"><div class="section-title">Electrical</div><ul>
<li>VF/VT — #1 pre-hospital death; defibrillate immediately</li>
<li>Complete heart block (inferior MI) → atropine / temporary pacing</li>
<li>AIVR — reperfusion arrhythmia; benign, no treatment needed</li>
</ul></div>
<div class="section"><div class="section-title">Late</div><ul>
<li><span class="highlight">Dressler's syndrome</span> (2–10 wk): immune pericarditis → aspirin/colchicine</li>
<li><span class="highlight">LV thrombus</span> (anterior STEMI) → anticoagulate 3–6 months</li>
</ul></div>`,
source: "Goldman-Cecil · Robbins Pathology · Harrison's 22E"
},
{
topic: "Coronary Territories",
question: "Which artery, which territory, which ECG leads — and clinical correlates?",
hint: "LAD=anterior; RCA=inferior+AV node; LCX=lateral/posterior",
heading: "Coronary Territories",
body: `
<div class="section"><div class="section-title">LAD (Left Anterior Descending)</div><ul>
<li>Wall: anterior, septum, apex, anterior papillary</li>
<li>Leads: <span class="highlight">V1–V4</span></li>
<li>Risks: large MI, new LBBB, cardiogenic shock, apical thrombus</li>
</ul></div>
<div class="section"><div class="section-title">RCA (Right Coronary)</div><ul>
<li>Wall: inferior, RV free wall, SA node (60%), AV node (90%)</li>
<li>Leads: <span class="highlight">II, III, aVF</span>; RV MI → V4R</li>
<li><span class="highlight">RV MI triad:</span> hypotension + raised JVP + clear lungs (no diuretics/nitrates!)</li>
</ul></div>
<div class="section"><div class="section-title">LCX (Left Circumflex)</div><ul>
<li>Wall: lateral, posterior (dominant LCx)</li>
<li>Leads: <span class="highlight">I, aVL, V5–V6</span>; posterior → ST depression V1–V3, confirm V7–V9</li>
<li>Often "electrically silent" on standard 12-lead</li>
</ul></div>`,
source: "Fuster & Hurst 15E · Harrison's 22E"
},
{
topic: "Secondary Prevention",
question: "Secondary prevention medications, targets, and device criteria after MI?",
hint: "Quadruplet GDMT + statin; LDL < 55 mg/dL; ICD if EF ≤ 35%",
heading: "Secondary Prevention",
body: `
<div class="section"><div class="section-title">Mandatory Medications</div><ul>
<li><span class="highlight">Aspirin</span> 75–100 mg — indefinite</li>
<li><span class="highlight">P2Y12</span> (ticagrelor/clopidogrel) — 12 months</li>
<li><span class="highlight">High-intensity statin</span> — atorvastatin 40–80 mg or rosuvastatin 20–40 mg</li>
<li><span class="highlight">Beta-blocker</span> — all with LVEF ≤ 40% for ≥ 3 years</li>
<li><span class="highlight">ACEi / ARB</span> — if LVEF ≤ 40%, DM, or HTN (indefinite)</li>
</ul></div>
<div class="section"><div class="section-title">If LVEF ≤ 40%</div><ul>
<li><span class="highlight">MRA</span> (eplerenone/spironolactone) + <span class="highlight">SGLT2i</span> (dapagliflozin)</li>
</ul></div>
<div class="section"><div class="section-title">Targets</div><ul>
<li>LDL: <span class="highlight">< 55 mg/dL</span> (ESC) / < 70 (ACC) — add ezetimibe or PCSK9i if needed</li>
<li>BP < 130/80 | HbA1c < 7% | Smoking cessation | Cardiac rehab</li>
</ul></div>
<div class="section"><div class="section-title">Device (ICD)</div><ul>
<li>LVEF ≤ 35% + ≥ 40 days post-MI + optimal GDMT ≥ 3 months + life expectancy > 1 yr</li>
</ul></div>`,
source: "Harrison's 22E · ESC ACS Guidelines 2023 · Fuster & Hurst 15E"
}
];
/* ═══════════════════════════════════════════════════════════
STATE
═══════════════════════════════════════════════════════════ */
let current = 0;
let isFlipped = false;
let order = CARDS.map((_, i) => i);
let seen = new Set();
let swipeHintHidden = false;
/* ═══════════════════════════════════════════════════════════
RENDER
═══════════════════════════════════════════════════════════ */
function renderCard(idx, keepFlipped) {
const c = CARDS[order[idx]];
document.getElementById('frontQuestion').textContent = c.question;
document.getElementById('frontSub').textContent = c.hint ? '💡 ' + c.hint : '';
document.getElementById('backHeading').textContent = c.heading;
document.getElementById('backBody').innerHTML = c.body;
document.getElementById('backSource').textContent = '📚 ' + c.source;
document.getElementById('topicPill').textContent = c.topic;
document.getElementById('counterLabel').textContent = (idx + 1) + ' / ' + CARDS.length;
// Progress
const pct = ((idx + 1) / CARDS.length) * 100;
document.getElementById('progressFill').style.width = pct + '%';
// Buttons
document.getElementById('btnPrev').disabled = (idx === 0);
document.getElementById('btnNext').disabled = (idx === CARDS.length - 1);
// Reset flip without animation then re-apply if needed
const cardEl = document.getElementById('card');
cardEl.style.transition = 'none';
cardEl.classList.remove('flipped');
void cardEl.offsetWidth; // force reflow
cardEl.style.transition = '';
isFlipped = false;
if (keepFlipped) { cardEl.classList.add('flipped'); isFlipped = true; }
// Scroll back body to top
document.querySelector('.back-scroll').scrollTop = 0;
updateDots(idx);
}
function updateDots(idx) {
document.querySelectorAll('.dot').forEach((d, i) => {
d.className = 'dot'
+ (i === idx ? ' active' : '')
+ (seen.has(i) && i !== idx ? ' seen' : '');
});
}
/* ═══════════════════════════════════════════════════════════
ACTIONS
═══════════════════════════════════════════════════════════ */
function flipCard() {
const cardEl = document.getElementById('card');
if (isFlipped) {
cardEl.classList.remove('flipped');
isFlipped = false;
} else {
cardEl.classList.add('flipped');
isFlipped = true;
seen.add(current);
updateDots(current);
}
}
function nextCard() {
if (current < CARDS.length - 1) {
current++;
renderCard(current, false);
hideSwipeHint();
}
}
function prevCard() {
if (current > 0) {
current--;
renderCard(current, false);
hideSwipeHint();
}
}
function goToCard(idx) {
current = idx;
renderCard(current, false);
}
function shuffleCards() {
for (let i = order.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[order[i], order[j]] = [order[j], order[i]];
}
current = 0;
seen.clear();
renderCard(current, false);
}
function hideSwipeHint() {
if (!swipeHintHidden) {
swipeHintHidden = true;
document.getElementById('swipeHint').classList.add('hidden');
}
}
/* ═══════════════════════════════════════════════════════════
BUILD DOTS
═══════════════════════════════════════════════════════════ */
const dotsRow = document.getElementById('dotsRow');
CARDS.forEach((c, i) => {
const d = document.createElement('div');
d.className = 'dot' + (i === 0 ? ' active' : '');
d.title = c.topic;
d.addEventListener('click', () => goToCard(i));
dotsRow.appendChild(d);
});
/* ═══════════════════════════════════════════════════════════
TOUCH SWIPE (left/right to navigate, vertical to scroll back)
═══════════════════════════════════════════════════════════ */
let touchStartX = 0, touchStartY = 0, touchStartTime = 0;
const scene = document.getElementById('scene');
scene.addEventListener('touchstart', e => {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
touchStartTime = Date.now();
}, { passive: true });
scene.addEventListener('touchend', e => {
const dx = e.changedTouches[0].clientX - touchStartX;
const dy = e.changedTouches[0].clientY - touchStartY;
const dt = Date.now() - touchStartTime;
const absDx = Math.abs(dx), absDy = Math.abs(dy);
// Tap (short, small movement) = flip
if (dt < 300 && absDx < 15 && absDy < 15) {
flipCard();
return;
}
// Horizontal swipe to navigate
if (absDx > 50 && absDx > absDy * 1.5) {
if (dx < 0) nextCard();
else prevCard();
hideSwipeHint();
}
}, { passive: true });
/* ═══════════════════════════════════════════════════════════
KEYBOARD
═══════════════════════════════════════════════════════════ */
document.addEventListener('keydown', e => {
if (e.code === 'Space' || e.key === ' ') { e.preventDefault(); flipCard(); }
if (e.key === 'ArrowRight') nextCard();
if (e.key === 'ArrowLeft') prevCard();
if (e.key === 's' || e.key === 'S') shuffleCards();
});
/* ═══════════════════════════════════════════════════════════
SERVICE WORKER
═══════════════════════════════════════════════════════════ */
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('./sw.js')
.then(reg => console.log('SW registered:', reg.scope))
.catch(err => console.log('SW error:', err));
});
}
/* ═══════════════════════════════════════════════════════════
INSTALL PROMPT (PWA)
═══════════════════════════════════════════════════════════ */
let deferredPrompt = null;
const banner = document.getElementById('installBanner');
const btnInst = document.getElementById('btnInstall');
const btnDism = document.getElementById('btnDismiss');
window.addEventListener('beforeinstallprompt', e => {
e.preventDefault();
deferredPrompt = e;
// Show banner after 2 s
setTimeout(() => banner.classList.add('visible'), 2000);
});
btnInst.addEventListener('click', async () => {
banner.classList.remove('visible');
if (deferredPrompt) {
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
console.log('Install outcome:', outcome);
deferredPrompt = null;
}
});
btnDism.addEventListener('click', () => {
banner.classList.remove('visible');
});
window.addEventListener('appinstalled', () => {
banner.classList.remove('visible');
deferredPrompt = null;
});
/* ═══════════════════════════════════════════════════════════
INIT
═══════════════════════════════════════════════════════════ */
renderCard(0, false);
</script>
</body>
</html>
# Validate all files exist and run a quick sanity check on the HTML python3 << 'PYEOF' import os, json base = '/tmp/workspace/mi-pwa' files = { 'index.html': os.path.join(base, 'index.html'), 'sw.js': os.path.join(base, 'sw.js'), 'manifest.json': os.path.join(base, 'manifest.json'), 'icon-192.png': os.path.join(base, 'icons/icon-192.png'), 'icon-512.png': os.path.join(base, 'icons/icon-512.png'), } print("File checks:") for name, path in files.items(): size = os.path.getsize(path) print(f" ✓ {name:20s} {size:>8,} bytes") # Validate manifest JSON with open(files['manifest.json']) as f: m = json.load(f) print(f"\nManifest: name='{m['name']}', display='{m['display']}', icons={len(m['icons'])}") # HTML checks with open(files['index.html']) as f: html = f.read() checks = [ ('SW registration', "serviceWorker.register('./sw.js')" in html), ('manifest link', 'manifest.json' in html), ('apple-touch-icon', 'apple-touch-icon' in html), ('theme-color', 'theme-color' in html), ('viewport-fit=cover', 'viewport-fit=cover' in html), ('safe-area-inset', 'safe-area-inset' in html), ('Touch swipe', 'touchstart' in html and 'touchend' in html), ('Flip animation', 'rotateY(180deg)' in html), ('Install banner', 'installBanner' in html), ('8 CARDS', html.count("topic:") == 8), ('Keyboard nav', 'ArrowRight' in html), ('Shuffle', 'shuffleCards' in html), ] print("\nHTML checks:") for label, ok in checks: print(f" {'✓' if ok else '✗'} {label}") PYEOF # Zip it up cd /tmp/workspace && python3 -c " import zipfile, os base = '/tmp/workspace/mi-pwa' zpath = '/tmp/workspace/MI_Flashcards_PWA.zip' with zipfile.ZipFile(zpath, 'w', zipfile.ZIP_DEFLATED) as zf: for root, dirs, files in os.walk(base): for fname in files: full = os.path.join(root, fname) arc = os.path.relpath(full, base) zf.write(full, arc) print(f' + {arc}') print(f'ZIP size: {os.path.getsize(zpath):,} bytes') "
MI Flashcards PWA
Archive · ZIP
index
HTML File · HTML
cd /path/to/mi-pwa
python3 -m http.server 8080
# then open http://localhost:8080 in Chrome
| File | Purpose |
|---|---|
index.html | Full app - cards, flip animation, swipe, install prompt |
sw.js | Service worker - caches everything for offline use |
manifest.json | App metadata - name, icon, theme, standalone mode |
icons/ | App icons (192px + 512px) for home screen |
| Gesture / Key | Action |
|---|---|
Tap card or Space | Flip to reveal answer |
| Swipe left/right on card | Next / prev card |
← → arrow keys | Navigate |
S | Shuffle deck |
| Dot at bottom | Jump to any card |
| Green dots | Cards you've already seen |