Give any one research paper

Searching PubMed

artificial intelligence medicine

Here is one recent research paper:

Artificial Intelligence in Mental Health Care: A Systematic Review of Diagnosis, Monitoring, and Intervention Applications
  • Authors: Cruz-Gonzalez P, He AW, Lam EP
  • Journal: Psychological Medicine
  • Published: February 6, 2025
  • PMID: 39911020
  • Evidence Tier: Systematic Review (highest level of evidence)
What it covers: This systematic review examines how AI is being applied across three key areas of mental health care:
  1. Diagnosis - using AI models to detect conditions like depression, anxiety, schizophrenia, etc.
  2. Monitoring - tracking patient symptoms and mental health status over time using sensors, apps, or wearables
  3. Intervention - AI-driven tools (chatbots, digital therapeutics) that actively support or treat patients
This is a highly relevant topic given the global mental health burden and the rapid growth of AI-powered clinical tools. If you'd like a summary of its findings, want to explore a different topic, or need the full citation in APA/BibTeX format, just ask!

Find more recent papers on AI in mental health diagnosis

Searching PubMed

"artificial intelligence"[MeSH Terms] AND "mental disorders"[MeSH Terms] AND diagnosis

Searching the Web

AI artificial intelligence mental health diagnosis research papers 2025 2026

Here are 9 recent peer-reviewed papers on AI in mental health diagnosis (all from 2024-2025, all Systematic Reviews unless noted):

Systematic Reviews

#TitleAuthorsJournalDatePMID
1Use of generative AI in psychiatry and mental health careKolding S et al.Acta NeuropsychiatrNov 202439523628
2Effectiveness of AI in detecting and managing depressive disordersPark Y et al.J Affect DisordSep 202438889858
3Recent developments in omics studies and AI in depression and suicideWang Q, Dwivedi YTransl PsychiatryAug 202540790016
4Application of AI in the field of mental healthDehbozorgi R et al.BMC PsychiatryFeb 202539953464
5AI in mental health care: diagnosis, monitoring, and interventionCruz-Gonzalez P et al.Psychol MedFeb 202539911020
6Neuroimaging and machine learning in eating disordersMonaco F et al.Eat Weight DisordJun 202540450619
7Ethical implications of generative AI in mental healthWang X et al.JMIR Ment HealthJun 202540577783
8Evaluating generative AI in mental health: capabilities and limitationsWang L et al.JMIR Ment HealthMay 202540373033

Randomized Controlled Trial

#TitleAuthorsJournalDatePMID
9AI in psychotherapy: development of intelligent therapeutic systemsSpytska LBMC PsycholFeb 202540022267

Key themes across these papers:
  • Generative AI (LLMs/ChatGPT) is increasingly being studied for psychiatric assessment and therapy support
  • Depression detection is the most studied use case, using NLP, social media data, and EHR analysis
  • Neuroimaging + ML is being applied to eating disorders, schizophrenia, and autism
  • Ethical concerns (privacy, bias, explainability) are a growing focus in 2025 literature
Would you like me to fetch the full abstract of any specific paper, find related studies, or export citations in APA/BibTeX format?

Give some latest guidelines for chf

Reading File
Finding Sources
Searching PubMed

"heart failure"[MeSH Terms] AND "guidelines"

Searching the Web

CHF congestive heart failure guidelines 2024 2025 ACC AHA ESC latest recommendations

Finding Sources
Reading Web Page

https://reference.medscape.com/cc1/p10/heart-failure-reduced…

Reading File
Reading File
Finding Sources
Reading File
Reading Article

PMID: 40533340

Here is a comprehensive summary of the latest CHF (Heart Failure) guidelines, drawing from the 2025 NICE update, 2023 ESC Focused Update, 2022 AHA/ACC/HFSA guidelines, and the brand-new 2025 iCARDIO Global Implementation Guidelines (PMID 40533340):

Latest Guidelines for Congestive Heart Failure (CHF) - 2025


1. Classification (All Guidelines)

TypeEFKey Features
HFrEF< 40%Reduced ejection fraction - most evidence-based therapies
HFmrEF40-49%Mid-range - emerging evidence
HFpEF≥ 50%Preserved EF - historically hard to treat

2. Diagnosis

  • NT-proBNP / BNP is the cornerstone lab test for diagnosis across NICE, ESC, and AHA/ACC/HFSA
    • NT-proBNP 125-400 ng/L: specialist assessment + echo within 6 weeks
    • NT-proBNP > 2000 ng/L: urgent specialist assessment
  • Echocardiography is mandatory to classify HF type and determine LVEF
  • Cardiac MRI for selected cases (etiology, myocarditis, cardiomyopathy)
  • A new 2025 ASE guideline (PMID 40617625) updates echocardiographic criteria for diagnosing diastolic dysfunction and HFpEF

3. The Four Pillars of GDMT (HFrEF) - 2025 Consensus

All three major guidelines (NICE 2025, ESC, AHA/ACC/HFSA) now recommend quadruplet therapy - start ALL four and uptitrate to the maximum tolerated dose:
PillarDrug ClassExamplesNotes
1ACEi / ARB / ARNIRamipril, Enalapril / Valsartan / Sacubitril-ValsartanESC: ACEi preferred first-line; ARNI if still symptomatic. AHA/ACC: ARNI may be used first-line
2Beta-blockerCarvedilol, Bisoprolol, Metoprolol succinateOnly these 3 have mortality evidence in HFrEF
3MRA (Mineralocorticoid Receptor Antagonist)Spironolactone, EplerenoneMonitor K+ and renal function
4SGLT2 inhibitorDapagliflozin, EmpagliflozinClass 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.

4. SGLT2 Inhibitors - 2025 Update

The DAPA-HF and EMPEROR-Reduced trials confirmed that SGLT2 inhibitors reduce CV death + HF hospitalizations by ~25% in HFrEF regardless of diabetes status. Key advantages per Fuster and Hurst's The Heart, 15th Ed:
  • Once-daily, no dose titration
  • Minimal drug-drug interactions
  • Osmotic diuresis synergistic with loop diuretics
  • Kidney-protective benefits
  • Favorable electrolyte profile (no hyperkalemia)

5. Symptomatic / Volume Management

  • Loop diuretics (furosemide, torsemide) for fluid overload - essential but NOT monotherapy
  • IV loop diuretics within 1 hour of presentation in hospitalized patients, at or above the chronic oral dose
  • Avoid thiazides when GFR < 30 mL/min (except combined with loop diuretics)
  • Daily weight monitoring and flexible diuretic regimens recommended
  • IV iron (ferric carboxymaltose): emerging evidence supports its use in HFrEF with iron deficiency - recommended in ESC and 2025 guidelines

6. Additional Pharmacotherapy

DrugIndicationGuideline Rec
IvabradineHFrEF, LVEF ≤ 35%, HR ≥ 70 bpm on max beta-blocker, sinus rhythmClass IIa (ESC, AHA)
Hydralazine + Isosorbide dinitrateHFrEF intolerant of ACEi/ARB (esp. Black patients)Class I (AHA/ACC)
DigoxinHFrEF with persistent symptoms, AFClass IIb
VericiguatWorsening HFrEF on optimal therapyClass IIb (ESC); considered (AHA/ACC)

7. Device Therapy

  • ICD for primary prevention: LVEF ≤ 35%, NYHA Class II-III on optimal GDMT for ≥ 3 months, life expectancy > 1 year
  • CRT: LVEF ≤ 35%, LBBB with QRS ≥ 130-150 ms, NYHA II-IV - mortality and symptom benefit in both sexes
  • LVAD: Bridge to transplant or destination therapy in advanced HFrEF
  • Note from Braunwald's Heart Disease: ICDs and CRT are significantly underused in women despite equal or greater benefit

8. HFpEF (EF ≥ 50%)

  • Previously had no proven mortality-reducing therapies
  • SGLT2 inhibitors now have emerging evidence for HFpEF (EMPEROR-Preserved trial) - recommended by ESC and AHA/ACC
  • ARNI (sacubitril/valsartan): PARAGON-HF showed significant benefit specifically in women with HFpEF (28% reduction in HF hospitalization/CV death) - Braunwald's Heart Disease
  • Treat underlying causes: HTN control, rate control in AF, weight loss in obesity

9. New 2025 Global Guidelines

The iCARDIO Alliance Global Implementation Guidelines 2025 (PMID 40533340, Heart Lung Circ, Jul 2025) address a major gap - providing tiered recommendations based on resource availability:
  • Standard resources: full quadruplet GDMT + devices
  • Limited resources: prioritize ACEi/ARB + beta-blocker + diuretic
  • Severely limited resources: minimum viable regimen with locally available drugs

Key Guidelines Reference Summary

GuidelineYearPublisherScope
AHA/ACC/HFSA Heart Failure Guideline2022 (updated 2024)ACC/AHAComprehensive US guideline
ESC Focused Update2023European Society of CardiologyHFrEF, HFmrEF, HFpEF
NICE NG106 Update2025NICE (UK)Chronic HFrEF; removed sequencing
iCARDIO Global Guidelines2025Heart Lung CircGlobal, resource-adjusted
ASE Diastolic Function Update2025J Am Soc EchocardiogrHFpEF diagnosis by echo

Sources: Fuster and Hurst's The Heart, 15th Ed.; Braunwald's Heart Disease; Medscape 2026 Guideline Essentials; PMID 40533340; PMID 40617625
Would you like me to go deeper into any specific area - HFpEF management, acute decompensated HF, or device therapy?

Make flashcards for MI

Asking for Preferences
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Finding Sources
Finding Sources
Generating Image

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"

Generating Image

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"

Generating Image

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"

Generating Image

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"

Generating Image

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"

Generating Image

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"

Generating Image

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"

Generating Image

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"

Here are your 8 Advanced MI Flashcards, sourced from Harrison's 22E, Fuster & Hurst's The Heart 15E, Goldman-Cecil, and Tintinalli's EM:

🃏 Card 1 — Pathophysiology

Card 1 - Pathophysiology

🃏 Card 2 — ECG Diagnosis

Card 2 - ECG Diagnosis

🃏 Card 3 — Biomarkers

Card 3 - Biomarkers

🃏 Card 4 — STEMI Management & Reperfusion

Card 4 - STEMI Management

🃏 Card 5 — Antiplatelet & Anticoagulation

Card 5 - Antithrombotic Therapy

🃏 Card 6 — Complications (Killip + Mechanical + Electrical)

Card 6 - Complications

🃏 Card 7 — Coronary Artery Territories

Card 7 - Coronary Territories

🃏 Card 8 — Secondary Prevention

Card 8 - Secondary Prevention

Topics covered:
  1. Pathophysiology (plaque rupture, ischemia timeline)
  2. ECG criteria (STEMI vs NSTEMI, lead territories, evolution)
  3. Biomarkers (troponin, CK-MB timing + delta rise protocols)
  4. STEMI management (door-to-balloon targets, fibrinolysis, MONA-B)
  5. Antithrombotic therapy (DAPT, heparin types, GIIb/IIIa)
  6. Complications (Killip class, free wall rupture, VSD, arrhythmias, Dressler's)
  7. Coronary territories (LAD/RCA/LCX and ECG correlates)
  8. Secondary prevention (statins, GDMT, LDL targets, ICD criteria)
Want me to add more cards (e.g., RV infarction, NSTEMI risk stratification, cardiogenic shock), or export these as a downloadable PDF/PPTX deck?

Export flashcards as a PPTX deck for studying

Reading File
Reading File
Reading File
Running Command

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']]"

Running Command

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

Your PPTX deck is ready:

MI Flashcards Advanced

Presentation · PPTX

What's inside (9 slides):
SlideContent
1Title slide — topics overview
2Card 1 — Pathophysiology
3Card 2 — ECG Diagnosis
4Card 3 — Biomarkers
5Card 4 — STEMI Management
6Card 5 — Antiplatelet & Anticoagulation
7Card 6 — Complications (Killip + mechanical + electrical)
8Card 7 — Coronary Territories
9Card 8 — Secondary Prevention
Study tips:
  • Use Presenter View in PowerPoint to quiz yourself before revealing the answer side
  • Each card has both the question and answer on one slide - great for review mode
  • Works on PowerPoint, Google Slides (upload), and LibreOffice Impress

Make flashcards to remember , make it flip and reveal answer

Running Command

mkdir -p /tmp/workspace/mi-flashcards && ls /tmp/workspace/mi-flashcards/

Writing File

~/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 &nbsp;·&nbsp; ← → to navigate &nbsp;·&nbsp; 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 &nbsp; <kbd>←</kbd> prev &nbsp; <kbd>→</kbd> next &nbsp; <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 &nbsp;|&nbsp; men &lt; 40 y: ≥ 0.25 mV &nbsp;|&nbsp; 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 &nbsp;|&nbsp; Anterior: V1–V4 &nbsp;|&nbsp; 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> &nbsp;|&nbsp; Peak: 12–24 h &nbsp;|&nbsp; 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 &nbsp;|&nbsp; Peak: 18–24 h &nbsp;|&nbsp; 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) &nbsp;|&nbsp; Peak: 6–9 h &nbsp;|&nbsp; 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 (&gt; 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 &lt; 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) &nbsp;|&nbsp; <span class="highlight">O</span>xygen only if SpO₂ &lt; 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, &lt; 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 &lt; 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">&lt; 55 mg/dL</span> (ESC) / &lt; 70 mg/dL (ACC) — add ezetimibe or PCSK9i if needed</li>
          <li>BP: &lt; 130/80 mmHg &nbsp;|&nbsp; HbA1c: &lt; 7% &nbsp;|&nbsp; BMI: &lt; 25 &nbsp;|&nbsp; 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 &gt; 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>
Running Command

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}\") "

All 9 checks passed. Here's your interactive flip flashcard deck:

MI Flashcards

HTML File · HTML

Download and open in any browser (Chrome, Firefox, Safari, Edge) - no internet needed.

How it works:

ActionControl
Flip card (reveal answer)Click the card, click "Flip Card" button, or press Space
Next cardClick "Next ▶" or press
Previous cardClick "◀ Prev" or press
ShuffleClick "⇄ Shuffle" or press S
Jump to any cardClick the dot indicators at the bottom

Features:

  • Smooth 3D flip animation - front (question) flips to reveal the back (answer)
  • Front - dark navy with yellow topic label and question
  • Back - dark green with structured bullet answers and highlighted key facts
  • Progress bar showing how far through the deck you are
  • Dot indicators that turn green once you've seen a card
  • Shuffle mode to randomise the order for self-testing
  • Works fully offline after downloading

Export flashcards as a mobile-friendly app (PWA)

Running Command

mkdir -p /tmp/workspace/mi-pwa/icons && ls /tmp/workspace/mi-pwa/

Running Command

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

Writing File

~/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"
    }
  ]
}
Writing File

~/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');
        }
      });
    })
  );
});
Writing File

~/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 &nbsp;·&nbsp; 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()">↩ &nbsp;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 &nbsp;|&nbsp; <span class="highlight">Injury</span> → ST elevation &nbsp;|&nbsp; <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> &nbsp;|&nbsp; men &lt; 40 y → ≥ 0.25 mV &nbsp;|&nbsp; 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 &nbsp;|&nbsp; 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 &nbsp;|&nbsp; Anterior: V1–V4 &nbsp;|&nbsp; Lateral: I, aVL, V5–V6</li>
        <li>Posterior: ST depression V1–V3 → confirm with V7–V9 &nbsp;|&nbsp; 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> &nbsp;|&nbsp; Peak: 12–24 h &nbsp;|&nbsp; 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 &nbsp;|&nbsp; Peak: 18–24 h &nbsp;|&nbsp; 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) &nbsp;|&nbsp; Peak: 6–9 h &nbsp;|&nbsp; 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₂ &lt; 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, &lt; 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> &nbsp;|&nbsp; II: S3/rales — <span class="highlight">17%</span></li>
        <li>III: Pulmonary oedema — <span class="highlight">38%</span> &nbsp;|&nbsp; 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">&lt; 55 mg/dL</span> (ESC) / &lt; 70 (ACC) — add ezetimibe or PCSK9i if needed</li>
        <li>BP &lt; 130/80 &nbsp;|&nbsp; HbA1c &lt; 7% &nbsp;|&nbsp; Smoking cessation &nbsp;|&nbsp; 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 &gt; 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>
Running Command

# 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') "

Your PWA is ready. Download the ZIP:

MI Flashcards PWA

Archive · ZIP

Or grab just the main file:

index

HTML File · HTML


How to install on your phone

Android (Chrome):
  1. Unzip the folder and serve it locally, OR host on any web server (GitHub Pages, Netlify, etc.)
  2. Open in Chrome → tap the ⋮ menu → "Add to Home screen"
  3. The app installs like a native app - full screen, no browser bar
iPhone (Safari):
  1. Host the folder on any HTTPS server
  2. Open in Safari → tap Share → "Add to Home Screen"
  3. It appears on your home screen with the heart icon
Quick local test (your computer):
cd /path/to/mi-pwa
python3 -m http.server 8080
# then open http://localhost:8080 in Chrome

What's in the PWA

FilePurpose
index.htmlFull app - cards, flip animation, swipe, install prompt
sw.jsService worker - caches everything for offline use
manifest.jsonApp metadata - name, icon, theme, standalone mode
icons/App icons (192px + 512px) for home screen

Interactions

Gesture / KeyAction
Tap card or SpaceFlip to reveal answer
Swipe left/right on cardNext / prev card
arrow keysNavigate
SShuffle deck
Dot at bottomJump to any card
Green dotsCards you've already seen
This is a shared conversation. Sign in to Orris to start your own chat.