1<!DOCTYPE html><html lang="en-IN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="Astro v5.18.1"><!-- 2 Google Analytics 4. Loads on every chrome'd page. Measures: 3 - Pageviews, sessions, users 4 - Traffic source (Google, Instagram, WhatsApp shares, direct) 5 - Device, geography, language 6 - Engagement time per page 7 Custom button events (Book CTA, WhatsApp, Apply to join, paywall 8 signup, area chips) are wired up in Phase 2 via a `data-track="..."` 9 attribute and a global click listener â see the matching script 10 lower in this layout. 11 -->
vendor: 63 bytes, line 11
11<script async src="https://www.googletagmanager.com/gtag/js?id=
11G-YMLGE47TQ9
vendor: 11 bytes, line 11
11"></script>
11<script> 12
vendor: 145 bytes, lines 12-15
12window.dataLayer = window.dataLayer || []; 13 function gtag(){dataLayer.push(arguments);} 14 gtag('js', new Date()); 15 gtag('config', '
15G-YMLGE47TQ9
vendor: 8 bytes, lines 15-16
15'); 16
16</script>
16<title>Blog â Plain-language medical guidance from qualified doctors | JanaVaidya</title><meta name="description" content="Clear, trustworthy answers to everyday medical questions for Bengaluru families, written by qualified doctors and reviewed for accuracy."><link rel="canonical" href="https://janavaidya.com/blog/"><!-- Favicons / touch icons --><link rel="icon" href="/favicon.ico" sizes="any"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><!-- hreflang --><link rel="alternate" hreflang="en-IN" href="https://janavaidya.com/blog/"><link rel="alternate" hreflang="kn-IN" href="https://janavaidya.com/kn/blog/"><link rel="alternate" hreflang="x-default" href="https://janavaidya.com/blog/"><!-- Open Graph --><meta property="og:type" content="website"><meta property="og:site_name" content="JanaVaidya"><meta property="og:title" content="Blog â Plain-language medical guidance from qualified doctors | JanaVaidya"><meta property="og:description" content="Clear, trustworthy answers to everyday medical questions for Bengaluru families, written by qualified doctors and reviewed for accuracy."><meta property="og:url" content="https://janavaidya.com/blog/"><meta property="og:image" content="https://janavaidya.com/og-default.png"><meta property="og:locale" content="en_IN"><!-- Twitter --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="Blog â Plain-language medical guidance from qualified doctors | JanaVaidya"><meta name="twitter:description" content="Clear, trustworthy answers to everyday medical questions for Bengaluru families, written by qualified doctors and reviewed for accuracy."><meta name="twitter:image" content="https://janavaidya.com/og-default.png"><!-- Fonts --><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Fraunces:opsz,[email protected],400;9..144,500;9..144,600;9..144,700&display=swap"><!-- Organization JSON-LD (on every page) -->
16<script type="application/ld+json">{"@context":"https://schema.org","@type":"MedicalOrganization","name":"JanaVaidya","legalName":"Amrutha Carings Private Limited","url":"https://janavaidya.com/","logo":"https://janavaidya.com/logo.png","areaServed":{"@type":"City","name":"Bengaluru"},"medicalSpecialty":"GeneralPractice","founder":{"@type":"Person","name":"Dr. Abheet B Shetty","jobTitle":"Founder","hasCredential":[{"@type":"EducationalOccupationalCredential","name":"MBBS"},{"@type":"EducationalOccupationalCredential","name":"MD (Community Medicine)"},{"@type":"EducationalOccupationalCredential","name":"PGMLE (NLSIU Bangalore)"}]}}</script>
16<!-- 17 Referral code auto-capture. Runs on every page load: 18 1) If the URL has ?ref=XXX, validate format, store in localStorage and 19 cookie (domain .janavaidya.com so it carries to app.janavaidya.com 20 on signup), strip from URL. 21 2) The cookie persists 90 days. localStorage is the durability backup 22 in case the cookie is cleared but site data isn't. 23 -->
23<script> 24 (function captureRefCode() { 25 try { 26 const params = new URLSearchParams(window.location.search); 27 const ref = params.get('ref'); 28 if (!ref) return; 29 // Allow alphanumeric, 4â20 chars 30 if (!/^[A-Z0-9]{4,20}$/i.test(ref)) return; 31 32 try { localStorage.setItem('jv_referral_code', ref); } catch (_) {} 33 try { localStorage.setItem('jv_referral_captured_at', String(Date.now())); } catch (_) {} 34 35 var ninetyDays = 90 * 24 * 60 * 60; 36 var isProd = location.hostname.endsWith('janavaidya.com'); 37 var cookie = 38 'jv_ref=' + encodeURIComponent(ref) + 39 ';max-age=' + ninetyDays + 40 ';path=/' + 41 ';samesite=lax' + 42 (isProd ? ';domain=.janavaidya.com' : '') + 43 (location.protocol === 'https:' ? ';secure' : ''); 44 document.cookie = cookie; 45 46 // Clean the URL so ?ref= doesn't keep appearing on every page view 47 params.delete('ref'); 48 var cleanUrl = 49 window.location.pathname + 50 (params.toString() ? '?' + params.toString() : '') + 51 window.location.hash; 52 window.history.replaceState({}, '', cleanUrl); 53 } catch (_) { 54 // Best-effort. Never throw from this snippet. 55 } 56 })(); 57 </script>
57<!-- 58 Outbound signup-link enrichment. When a referral code is in localStorage 59 (captured via ?ref= on any prior page view â see the capture script 60 above), append it to every "Sign up / Apply to join" link that points 61 to app.janavaidya.com/signup. This is what carries the referral credit 62 across the website â app domain boundary, since the app's /signup page 63 reads ?ref= from its URL (it does not currently read the shared cookie). 64 Runs on DOMContentLoaded so the body's <a> tags are present. 65 -->
65<script> 66 (function appendRefToSignupLinks() { 67 function rewrite() { 68 try { 69 var ref = localStorage.getItem('jv_referral_code'); 70 if (!ref) return; 71 var target = 'https://app.janavaidya.com/signup'; 72 var withRef = target + '?ref=' + encodeURIComponent(ref); 73 document.querySelectorAll('a[href="' + target + '"]').forEach(function (a) { 74 a.href = withRef; 75 }); 76 } catch (_) { 77 // Best-effort. Never throw from this snippet. 78 } 79 } 80 if (document.readyState === 'loading') { 81 document.addEventListener('DOMContentLoaded', rewrite); 82 } else { 83 rewrite(); 84 } 85 })(); 86 </script>
86<link rel="stylesheet" href="/_astro/about.DLprd-Uz.css"></head> <body class="min-h-screen flex flex-col"> <a href="#jv-main" class="jv-skip-link">Skip to content</a> <header class="sticky top-0 z-40 w-full border-b border-border bg-background/85 backdrop-blur shadow-soft"> <div class="mx-auto max-w-6xl px-4 sm:px-6 h-16 flex items-center justify-between gap-4"> <!-- Logo --> <a href="/" class="flex items-center group" aria-label="JanaVaidya â home"> <img src="/logo-stacked.png" alt="JanaVaidya" width="120" height="80" class="h-10 w-auto object-contain mix-blend-multiply" loading="eager"> </a> <!-- Desktop nav --> <nav class="hidden md:flex items-center gap-7 text-sm font-medium text-muted-foreground"> <a href="/for-patients" class="hover:text-foreground transition-colors">For patients</a><a href="/for-doctors" class="hover:text-foreground transition-colors">For doctors</a><a href="/for-doctors/academy" class="hover:text-foreground transition-colors">Academy</a><a href="/how-it-works" class="hover:text-foreground transition-colors">How it works</a><a href="/blog" class="hover:text-foreground transition-colors">Blog</a> </nav> <!-- Right side: language + CTA + mobile menu button --> <div class="flex items-center gap-3"> <div class="inline-flex items-center rounded-full border border-border bg-card p-1 text-sm font-medium"><a href="/blog/" class="px-3 py-1 rounded-full transition bg-primary text-primary-foreground shadow-soft" hreflang="en-IN" aria-current="true">EN</a><a href="/kn/blog/" class="px-3 py-1 rounded-full transition font-sans text-muted-foreground hover:text-foreground" hreflang="kn-IN" aria-current="false">à²à²¨à³à²¨à²¡</a></div> <a href="https://book.janavaidya.com" data-track="book_cta_click" data-track-source="header_desktop" class="hidden sm:inline-flex rounded-lg bg-primary text-primary-foreground px-4 py-2 text-sm font-medium shadow-card hover-elevate active-elevate-2">Book a home visit</a> <button type="button" id="jv-menu-button" aria-label="Menu" aria-expanded="false" aria-controls="jv-mobile-menu" class="md:hidden inline-flex items-center justify-center w-10 h-10 rounded-lg border border-border bg-card hover-elevate active-elevate-2"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <line x1="3" y1="6" x2="21" y2="6"></line> <line x1="3" y1="12" x2="21" y2="12"></line> <line x1="3" y1="18" x2="21" y2="18"></line> </svg> </button> </div> </div> <!-- Mobile slide-down panel --> <div id="jv-mobile-menu" hidden class="md:hidden border-t border-border bg-background"> <nav class="mx-auto max-w-6xl px-4 py-4 flex flex-col gap-3 text-base font-medium"> <a href="/for-patients" class="py-2 text-foreground hover:text-primary">For patients</a><a href="/for-doctors" class="py-2 text-foreground hover:text-primary">For doctors</a><a href="/for-doctors/academy" class="py-2 text-foreground hover:text-primary">Academy</a><a href="/how-it-works" class="py-2 text-foreground hover:text-primary">How it works</a><a href="/blog" class="py-2 text-foreground hover:text-primary">Blog</a> <a href="https://book.janavaidya.com" data-track="book_cta_click" data-track-source="header_mobile_menu" class="mt-2 rounded-lg bg-primary text-primary-foreground px-4 py-3 text-center font-medium shadow-card hover-elevate active-elevate-2">Book a home visit</a> </nav> </div> </header>
86<script> 87 (function () { 88 const btn = document.getElementById('jv-menu-button'); 89 const panel = document.getElementById('jv-mobile-menu'); 90 if (!btn || !panel) return; 91 btn.addEventListener('click', () => { 92 const isOpen = !panel.hidden; 93 panel.hidden = isOpen; 94 btn.setAttribute('aria-expanded', String(!isOpen)); 95 }); 96 })(); 97</script>
97 <main id="jv-main" class="flex-1 jv-page-content"> <section class="mx-auto max-w-6xl px-4 sm:px-6 pt-14 pb-10"> <p class="text-xs sm:text-sm uppercase tracking-widest text-secondary font-medium font-sans"> 98Health library 99</p> <h1 class="mt-3 font-serif font-semibold text-foreground text-4xl md:text-5xl max-w-3xl"> 100Plain-language medical guidance for Bengaluru families. 101</h1> <p class="mt-4 text-lg text-muted-foreground max-w-2xl"> 102Written by qualified doctors. Reviewed for accuracy. No jargon, no fear-mongering, no hidden ads. 103</p> </section> <section class="mx-auto max-w-6xl px-4 sm:px-6 pb-8"> <div class="flex flex-wrap gap-2"> <a href="/blog/category/elderly-care" class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-sm px-3 py-1 hover:bg-secondary/20 transition-colors">Elderly care</a><a href="/blog/category/childrens-health" class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-sm px-3 py-1 hover:bg-secondary/20 transition-colors">Children's health</a><a href="/blog/category/chronic-conditions" class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-sm px-3 py-1 hover:bg-secondary/20 transition-colors">Chronic conditions</a><a href="/blog/category/acute-illness" class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-sm px-3 py-1 hover:bg-secondary/20 transition-colors">Acute illness</a><a href="/blog/category/mental-health" class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-sm px-3 py-1 hover:bg-secondary/20 transition-colors">Mental health</a><a href="/blog/category/medication-safety" class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-sm px-3 py-1 hover:bg-secondary/20 transition-colors">Medication safety</a><a href="/blog/category/when-to-see-a-doctor" class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-sm px-3 py-1 hover:bg-secondary/20 transition-colors">When to see a doctor</a><a href="/blog/category/for-caregivers" class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-sm px-3 py-1 hover:bg-secondary/20 transition-colors">For caregivers</a><a href="/blog/category/home-visit-explainer" class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-sm px-3 py-1 hover:bg-secondary/20 transition-colors">About home visits</a><a href="/blog/category/bengaluru-health" class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-sm px-3 py-1 hover:bg-secondary/20 transition-colors">Bengaluru health</a> </div> </section> <section class="mx-auto max-w-6xl px-4 sm:px-6 pb-10"> <!-- Editorial featured layout: full-width image top, large headline below --> 104 <a href="/blog/caring-for-elderly-parent-after-hospital-discharge" class="group block rounded-xl border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-6 sm:p-8 md:p-10"><div class="flex flex-wrap items-center gap-3"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">For caregivers</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 8min</span></div><h3 class="mt-4 font-serif font-semibold text-foreground text-2xl sm:text-3xl md:text-4xl leading-[1.1] group-hover:text-primary transition-colors max-w-3xl">Caring for an elderly parent after hospital discharge</h3><p class="mt-3 text-muted-foreground text-base sm:text-lg leading-relaxed max-w-2xl">Elderly parent care after hospital discharge: a calm guide to medicines, wound care, fall prevention, follow-up, and red flags for readmission.</p><p class="mt-4 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a> </section> <section class="mx-auto max-w-6xl px-4 sm:px-6 pb-24"> <h2 class="text-2xl md:text-3xl font-semibold text-foreground mb-6">More articles</h2> <div class="grid gap-5 md:grid-cols-2 lg:grid-cols-3"> <!-- Standard card layout --> 105 <a href="/blog/blood-pressure-diabetes-management-at-home" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">Chronic conditions</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 8min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">Managing blood pressure and diabetes at home: a family guide</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">A plain-language family guide to blood pressure and diabetes management at home: monitoring basics, what the numbers mean, diet, and red flags to wat
105ch.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a><!-- Standard card layout --> 106 <a href="/blog/antibiotics-when-you-need-them" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">Medication safety</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 7min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">Antibiotics: when you need them and when you do not</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">When do you need antibiotics for fever? A calm Bengaluru doctor's guide to when antibiotics actually help, when they do not, and why misuse is harmful.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a><!-- Standard card layout --> 107 <a href="/blog/dengue-viral-fever-home-care-bengaluru" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">Acute illness</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 7min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">Dengue and viral fever in Bengaluru: caring for an adult at home</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">A calm guide to dengue viral fever home care in Bengaluru: hydration, dengue warning signs, when a home doctor helps, and when to rush to hospital.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a><!-- Standard card layout --> 108 <a href="/blog/home-visit-doctor-vs-online-consultation" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">About home visits</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 7min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">Home visit doctor vs online consultation: which to choose</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">Home visit doctor vs online consultation in Bengaluru: when a tele-consult is enough, when an examination matters, and when to call 108 instead.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a><!-- Standard card layout --> 109 <a href="/blog/how-to-choose-a-home-visit-doctor" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">About home visits</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 7min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">How to choose a home-visit doctor: questions to ask first</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">How to choose a home visit doctor in Bengaluru: verify the qualification, council registration, scope, and what a proper home exam should include.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a><!-- Standard card layout --> 110 <a href="/blog/monsoon-illnesses-in-bengaluru-stay-ahead" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">Bengaluru health</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 8min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">Monsoon illnesses in Bengaluru: common ones and prevention</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">A doctor's plain-English guide to monsoon illnesses in Bengaluru â dengue, viral fevers, stomach bugs and coughs â with prevention and danger signs.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a><!-- Standard card layout --> 111 <a href="/blog/fever-in-children-when-to-call-a-doctor" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">Children's health</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 7min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">
111Child fever: when to watch at home and when to call a doctor</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">Child fever: when to see a doctor, when to watch at home, and the danger signs that mean call 108 now. Age-based guidance from a qualified doctor in Bengaluru.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a><!-- Standard card layout --> 112 <a href="/blog/stomach-bug-vomiting-loose-motions-home-care" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">Acute illness</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 7min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">Stomach bug at home: managing vomiting and loose motions</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">A calm guide to loose motions and vomiting treatment at home in Bengaluru: how to rehydrate, signs of dehydration, simple food, and when to call a doctor.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a><!-- Standard card layout --> 113 <a href="/blog/typhoid-symptoms-treatment-bengaluru" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">Bengaluru health</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 8min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">Typhoid in Bengaluru: symptoms, tests, and home care</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">A calm guide to typhoid symptoms and treatment in Bengaluru: the step-ladder fever, why a blood test is needed, antibiotic safety, and red flags.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a><!-- Standard card layout --> 114 <a href="/blog/when-does-a-cough-need-a-doctor" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">When to see a doctor</span><span class="text-xs text-muted-foreground font-sans">5 June 2026 · 7min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">When does a cough need a doctor? A simple guide</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">Most coughs settle on their own. A calm guide to home soothing measures and exactly when to see a doctor for a cough in children, adults and the elderly.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a><!-- Standard card layout --> 115 <a href="/blog/when-to-call-a-doctor-for-elderly-parents" class="group block rounded-lg border border-card-border bg-card shadow-card hover-elevate overflow-hidden"><div class="p-5"><div class="flex flex-wrap items-center gap-2"><span class="inline-flex items-center rounded-full border border-secondary/30 bg-secondary/10 text-secondary font-medium font-sans text-xs px-2.5 py-0.5">Elderly care</span><span class="text-xs text-muted-foreground font-sans">26 May 2026 · 5min</span></div><h3 class="mt-3 font-serif font-semibold text-foreground text-xl group-hover:text-primary transition-colors">When to call a doctor for elderly parents â a practical guide</h3><p class="mt-2 text-sm text-muted-foreground leading-relaxed">A plain-language guide on when ageing parents need a doctor at home, when they can wait, and when to go to the hospital. Written by a qualified doctor.</p><p class="mt-3 text-sm text-muted-foreground font-sans"><span class="text-foreground font-medium">Dr. Abheet B Shetty</span> · MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore)</p></div></a> </div> </section> </main> <footer class="border-t border-border bg-card mt-24"> <div class="mx-auto max-w-6xl px-4 sm:px-6 py-14 grid gap-10 md:grid-cols-4"> <!-- Brand + tagline --> <div class="md:col-span-1"> <a href="/" class="inline-flex items-center gap-1"> <span class="font-serif text-2xl font-semibold tracking-tight text-foreground">JanaVaidya</span> <span class="w-1.5 h-1.5 rounded-full bg-primary mt-3" aria-hidden="true"></span> </a> <p class="mt-3 text-sm text-muted-foreground max-w-xs"> Verified doctors. Transparent qualifications. At your home. </p> </div> <!-- Company --> <div> <h3 class="text-sm font-semibold text-foreground font-sans">Company</h3> <ul class="mt-3 space-y-2 text-sm text-muted-foreground"> <li><a href="/about" class="hover:text-foreground">About</a></li> <li><a href="/how-it-works" class="hover:text-foreground">How it works</a></li> <li><a href="/blog" class="hover:text-foreground">Blog</a></li> <li><a href="/contact" class="hover:text-foreground">Contact</a></li> </ul> </div> <!-- For patients / For doctors --> <div> <h3 class="text-sm font-semibold text-foreground font-sans">For patients</h3> <ul class="mt-3 space-y-2 text-sm text-muted-foreground"> <li><a href="/for-patients" class="hover:text-foreground">Why home visits</a></li> <li><a href="/home-visit-doctor" class="hover:text-foreground">Areas we serve</a></li> <li><a href="/how-it-works" class="hover:text-foreground">How it works</a></li> </ul> <h3 class="mt-6 text-sm font-semibold text-foreground font-sans">For doctors</h3> <ul class="mt-3 space-y-2 text-sm text-muted-foreground"> <li><a href="/for-doctors" class="hover:text-foreground">Join as a doctor</a></li> </ul> </div> <!-- Legal --> <div> <h3 class="text-sm font-semibold text-foreground font-sans">Legal</h3> <ul class="mt-3 space-y-2 text-sm text-muted-foreground"> <li><a href="https://app.janavaidya.com/terms" class="hover:text-foreground">Terms of service</a></li> <li><a href="https://app.janavaidya.com/privacy" class="hover:text-foreground">Privacy policy</a></li> <li><a href="https://app.janavaidya.com/refund-policy" class="hover:text-foreground">Refund policy</a></li> <li><a href="https://app.janavaidya.com/account-deletion" class="hover:text-foreground">Delete your account</a></li> </ul> </div> </div> <!-- Bottom strip --> <div class="border-t border-border"> <div class="mx-auto max-w-6xl px-4 sm:px-6 py-6 text-xs text-muted-foreground space-y-1.5"> <p>Founded by Dr. Abheet B Shetty â MBBS, MD (Community Medicine), PGMLE (NLSIU Bangalore).</p> <p>A service of Amrutha Carings Private Limited, Bengaluru.</p> <p>© 2026 Amrutha Carings Private Limited. All rights reserved.</p> </div> </div> </footer> <!-- Floating WhatsApp button (opens modal) -->
115<script type="module">const o=document.getElementById("wa-modal"),w=document.getElementById("wa-backdrop"),h=document.getElementById("wa-trigger"),p=document.getElementById("wa-close"),m=document.getElementById("wa-form-state"),u=document.getElementById("wa-thanks-state"),r=document.getElementById("wa-form"),t=document.getElementById("wa-submit"),g=o?.dataset.waHref??"#",E="https://admin.automaitnow.com/api/automations/6a16f82593801/execute",b="3cab3e11c1104722a842f0095235881f";let f=null;function v(){return o?Array.from(o.querySelectorAll('a[href],button:not([disabled]),input:not([disabled]),textarea:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])')):[]}function B(){f=document.activeElement,o?.classList.remove("hidden"),document.body.style.overflow="hidden",setTimeout(()=>{document.getElementById("wa-name")?.focus()},60)}function I(){r?.reset(),m?.classList.remove("hidden"),u?.classList.add("hidden"),t&&(t.disabled=!1,t.textContent=t.dataset.label??"Continue to WhatsApp")}function d(){o?.classList.add("hidden"),document.body.style.overflow="",I(),f?.focus()}h?.addEventListener("click",B);w?.addEventListener("click",d);p?.addEventListener("click",d);document.addEventListener("keydown",e=>{if(!o?.classList.contains("hidden")){if(e.key==="Escape"){d();return}if(e.key==="Tab"){const n=v();if(!n.length)return;const a=n[0],s=n[n.length-1];e.shiftKey&&document.activeElement===a?(e.preventDefault(),s.focus()):!e.shiftKey&&document.activeElement===s&&(e.preventDefault(),a.focus())}}});r?.addEventListener("submit",async e=>{e.preventDefault();const n=document.getElementById("wa-name"),a=document.getElementById("wa-phone"),s=document.getElementById("wa-email"),c=n?.value.trim()??"",l=a?.value.trim()??"",i=s?.value.trim()??"";if(!c||!l){c?a?.focus():n?.focus();return}t&&(t.disabled=!0,t.textContent=t.dataset.submitting??"Sendingâ¦");try{await fetch(E,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({api_token:b,contact_name:c,contact_phone:l,...i?{contact_email:i}:{}})})}catch{}try{typeof window.gtag=="function"&&window.gtag("event","whatsapp_submit",{lang:document.documentElement.lang||"unknown",has_email:!!i})}catch{}m?.classList.add("hidden"),u?.classList.remove("hidden"),setTimeout(()=>{window.open(g,"_blank","noopener,noreferrer"),d()},2e3)});</script>
115 <button id="wa-trigger" type="button" data-track="whatsapp_open" data-track-source="floating_button" aria-label="Chat on WhatsApp" class="fixed bottom-6 right-6 z-50 w-14 h-14 rounded-full shadow-elevated flex items-center justify-center transition-transform hover:scale-110 active:scale-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-green-500" style="background-color: #25D366;"> <svg width="28" height="28" viewBox="0 0 24 24" fill="white" aria-hidden="true"> <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"></path> </svg> </button> <!-- Modal overlay --><div id="wa-modal" data-wa-href="https://wa.me/917204249531?text=Hi%20JanaVaidya%2C%20I%20have%20an%20enquiry." role="dialog" aria-modal="true" aria-labelledby="wa-modal-title" class="fixed inset-0 z-[60] flex items-end sm:items-center justify-center p-4 hidden"> <!-- Backdrop -->
115 <div id="wa-backdrop" class="absolute inset-0 bg-foreground/40 backdrop-blur-sm"></div> <!-- Panel --> <div class="relative w-full max-w-md bg-card rounded-2xl shadow-elevated overflow-hidden"> <!-- Close button --> <button id="wa-close" type="button" aria-label="Close" class="absolute top-4 right-4 text-muted-foreground hover:text-foreground transition-colors z-10"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> </button> <!-- Form state --> <div id="wa-form-state" class="p-6 sm:p-8"> <!-- Heading --> <div class="flex items-center gap-3 mb-5"> <div class="w-10 h-10 rounded-xl flex-shrink-0 flex items-center justify-center" style="background-color: #25D366;"> <svg width="20" height="20" viewBox="0 0 24 24" fill="white" aria-hidden="true"> <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"></path> </svg> </div> <div> <h2 id="wa-modal-title" class="font-serif font-semibold text-lg text-foreground leading-snug"> Chat with us on WhatsApp </h2> <p class="text-xs text-muted-foreground font-sans mt-0.5"> Share your details and we will open WhatsApp for you. </p> </div> </div> <form id="wa-form" novalidate class="space-y-4 font-sans"> <!-- Name --> <div> <label for="wa-name" class="block text-sm font-medium text-foreground mb-1.5"> Your name <span class="text-primary" aria-hidden="true"> *</span> </label> <input type="text" id="wa-name" name="contact_name" required autocomplete="name" placeholder="Full name" class="w-full rounded-lg border border-border bg-background px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-secondary/40"> </div> <!-- Phone --> <div> <label for="wa-phone" class="block text-sm font-medium text-foreground mb-1.5"> Mobile number <span class="text-primary" aria-hidden="true"> *</span> </label> <input type="tel" id="wa-phone" name="contact_phone" required autocomplete="tel" placeholder="+91 98765 43210" class="w-full rounded-lg border border-border bg-background px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-secondary/40"> </div> <!-- Email (optional) --> <div> <label for="wa-email" class="block text-sm font-medium text-foreground mb-1.5"> Email address (optional) </label> <input type="email" id="wa-email" name="contact_email" autocomplete="email" placeholder="[email protected]" class="w-full rounded-lg border border-border bg-background px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-secondary/40"> </div> <!-- Submit --> <button type="submit" id="wa-submit" data-label="Continue to WhatsApp" data-submitting="Sendingâ¦" class="w-full rounded-lg bg-primary text-primary-foreground px-6 py-3 text-sm font-medium shadow-card hover-elevate active-elevate-2 disabled:opacity-60 disabled:cursor-not-allowed font-sans"> Continue to WhatsApp </button> </form> </div> <!-- Thank you state (hidden initially) --> <div id="wa-thanks-state" class="hidden p-6 sm:p-8 text-center py-14">
115 <div class="w-14 h-14 rounded-full mx-auto mb-4 flex items-center justify-center" style="background-color: #25D366;"> <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"></polyline></svg> </div> <h3 class="font-serif font-semibold text-xl text-foreground mb-2"> Thank you! </h3> <p class="text-sm text-muted-foreground font-sans leading-relaxed"> Opening WhatsApp now⦠</p> </div> </div> </div> <!-- 116 Sticky app-install bar (mobile, Android only â see the component). 117 Rendered last so it sits above the WhatsApp button in the stacking order. 118 --> <div id="jv-app-install-bar" data-android-href="https://play.google.com/store/apps/details?id=com.janavaidya.app&utm_source=website&utm_medium=referral&utm_campaign=sticky_bar" data-ios-href data-ios-available="0" hidden class="md:hidden fixed bottom-0 inset-x-0 z-50 border-t border-border bg-background/95 backdrop-blur shadow-[0_-4px_20px_rgba(0,0,0,0.06)]"> <div class="flex items-center gap-3 px-4 py-3 pb-[calc(0.75rem+env(safe-area-inset-bottom))]"> <img src="/logo-icon-hq.png" alt="" width="36" height="36" class="h-9 w-9 rounded-lg flex-shrink-0 object-contain" aria-hidden="true"> <div class="flex-1 leading-tight min-w-0"> <div class="text-sm font-semibold text-foreground font-sans truncate">Book in 60 seconds</div> <div class="text-xs text-muted-foreground font-sans truncate">Get the JanaVaidya app</div> </div> <a id="jv-install-bar-cta" href="https://play.google.com/store/apps/details?id=com.janavaidya.app&utm_source=website&utm_medium=referral&utm_campaign=sticky_bar" target="_blank" rel="noopener" data-track="app_install_click" data-track-store="google_play" data-track-source="sticky_bar" class="rounded-lg bg-primary text-primary-foreground px-4 py-2.5 text-sm font-semibold font-sans shadow-card active-elevate-2 flex-shrink-0">Install</a> <button type="button" aria-label="Dismiss" data-jv-dismiss-install-bar class="text-muted-foreground hover:text-foreground p-2 flex-shrink-0"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <line x1="18" y1="6" x2="6" y2="18"></line> <line x1="6" y1="6" x2="18" y2="18"></line> </svg> </button> </div> </div>
118<script> 119 (function setupInstallBar() { 120 var KEY = 'jv-install-bar-dismissed'; 121 var bar = document.getElementById('jv-app-install-bar'); 122 if (!bar) return; 123 124 function remove() { 125 bar.remove(); 126 document.body.classList.remove('jv-install-bar-visible'); 127 } 128 129 try { 130 if (localStorage.getItem(KEY) === '1') { 131 remove(); 132 return; 133 } 134 } catch (_) { 135 // localStorage blocked (private mode). Showing the bar is the safe 136 // default â worst case the visitor dismisses it again. 137 } 138 139 var ua = navigator.userAgent || ''; 140 var isAndroid = /Android/.test(ua); 141 var isIOS = 142 /iPad|iPhone|iPod/.test(ua) || 143 // iPadOS 13+ reports as Macintosh; the touch check disambiguates. 144 (/Macintosh/.test(ua) && navigator.maxTouchPoints > 1); 145 var iosAvailable = bar.getAttribute('data-ios-available') === '1'; 146 147 // Only ever offer a store that actually has the app. 148 if (isIOS && iosAvailable) { 149 var cta = document.getElementById('jv-install-bar-cta'); 150 var href = bar.getAttribute('data-ios-href'); 151 if (cta && href) { 152 cta.href = href; 153 cta.setAttribute('data-track-store', 'app_store'); 154 } 155 } else if (!isAndroid) { 156 // iOS with no app yet, or any other platform: nothing to install. 157 remove(); 158 return; 159 } 160 161 bar.hidden = false; 162 document.body.classList.add('jv-install-bar-visible'); 163 164 var dismiss = bar.querySelector('[data-jv-dismiss-install-bar]'); 165 if (dismiss) { 166 dismiss.addEventListener('click', function () { 167 try { localStorage.setItem(KEY, '1'); } catch (_) {} 168 if (typeof window.gtag === 'function') { 169 try { window.gtag('event', 'app_install_bar_dismiss'); } catch (_) {} 170 } 171 remove(); 172 }); 173 } 174 })(); 175</script>
175 <!-- Auth modal (signup / login). Mounted globally so any page can trigger it. --> <div id="jv-auth-modal" data-api-base="https://app.janavaidya.com/api" role="dialog" aria-modal="true" aria-labelledby="jv-auth-modal-title" class="fixed inset-0 z-[80] flex items-end sm:items-center justify-center p-4 hidden"> <!-- Backdrop --> <div id="jv-auth-backdrop" class="absolute inset-0 bg-foreground/40 backdrop-blur-sm"></div> <!-- Panel --> <div class="relative w-full max-w-md bg-card rounded-2xl shadow-elevated overflow-hidden"> <!-- Close button --> <button id="jv-auth-close" type="button" aria-label="Close" class="absolute top-4 right-4 text-muted-foreground hover:text-foreground transition-colors z-10"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <line x1="18" y1="6" x2="6" y2="18"></line> <line x1="6" y1="6" x2="18" y2="18"></line> </svg> </button> <div class="p-6 sm:p-8"> <!-- Heading area, updates per mode --> <div class="mb-5"> <h2 id="jv-auth-modal-title" class="font-serif font-semibold text-xl text-foreground" data-signup-text="Sign up to keep reading" data-login-text="Log in"> 176Sign up to keep reading 177</h2> <p class="text-sm text-muted-foreground font-sans mt-1" data-signup-text="Free for doctors. Tracks your progress across the 7 Foundation modules so you can take the Foundation Quiz when ready." data-login-text="Welcome back."> 178Free for doctors. Tracks your progress across the 7 Foundation modules so you can take the Foundation Quiz when ready. 179</p> </div> <!-- Error banner --> <div id="jv-auth-error" class="hidden rounded-lg bg-red-50 border border-red-200 text-red-800 text-sm font-sans px-3 py-2 mb-4"></div> <form id="jv-auth-form" novalidate class="space-y-4 font-sans"> <!-- Name â signup only --> <div class="jv-signup-only"> <label for="jv-auth-name" class="block text-sm font-medium text-foreground mb-1.5"> 180Full name <span class="text-primary" aria-hidden="true">*</span> </label> <input type="text" id="jv-auth-name" name="full_name" autocomplete="name" placeholder="Dr. Ramesh Iyer" class="w-full rounded-lg border border-border bg-background px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-secondary/40"> </div> <!-- Phone (used as login id) --> <div> <label for="jv-auth-phone" class="block text-sm font-medium text-foreground mb-1.5"> 181Mobile number <span class="text-primary" aria-hidden="true">*</span> </label> <input type="tel" id="jv-auth-phone" name="phone" required autocomplete="tel" placeholder="9876543210" class="w-full rounded-lg border border-border bg-background px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-secondary/40"> </div> <!-- Email â signup only, optional --> <div class="jv-signup-only"> <label for="jv-auth-email" class="block text-sm font-medium text-foreground mb-1.5"> 182Email <span class="text-muted-foreground text-xs">(optional)</span> </label> <input type="email" id="jv-auth-email" name="email" autocomplete="email" placeholder="[email protected]" class="w-full rounded-lg border border-border bg-background px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-secondary/40"> </div> <!-- Password --> <div> <label for="jv-auth-password" class="block text-sm font-medium text-foreground mb-1.5"> 183Password <span class="text-primary" aria-hidden="true">*</span> </label> <input type="password" id="jv-auth-password" name="password" required autocomplete="current-password" placeholder="Minimum 8 characters" class="w-full rounded-lg border border-border bg-background px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-secondary/40"> </div> <!-- Referral code â signup only, optional, pre-filled from URL/storage --> <div class="jv-signup-only"> <label for="jv-auth-referral" class="block text-sm font-medium text-foreground mb-1.5"> 184Referral code <span class="text-muted-foreground text-xs">(optional)</span> </label> <input type="text" id="jv-auth-referral" name="referral_code" placeholder="From another doctor" class="w-full rounded-lg border border-border bg-background px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-secondary/40"> </div> <!-- Submit --> <button type="submit" id="jv-auth-submit" data-signup-text="Sign up â 30 seconds" data-login-text="Log in" class="w-full rounded-lg bg-primary text-primary-foreground px-6 py-3 text-sm font-medium shadow-card hover-elevate active-elevate-2 disabled:opacity-60 disabled:cursor-not-allowed font-sans"> 185Sign up â 30 seconds 186</button> </form> <!-- Mode toggle --> <p class="mt-4 text-sm text-muted-foreground font-sans text-center"> <span class="jv-signup-only"> 187Already have an account? 188<button type="button" id="jv-auth-toggle-login" class="text-primary hover:underline font-medium">Log in</button>
188 </span> <span class="jv-login-only hidden"> 189New to JanaVaidya? 190<button type="button" id="jv-auth-toggle-signup" class="text-primary hover:underline font-medium">Create an account</button> </span> </p> </div> </div> </div>
190<script> 191 (function setupAuthModal() { 192 const modal = document.getElementById('jv-auth-modal'); 193 if (!modal) return; 194 const apiBase = modal.getAttribute('data-api-base') || ''; 195 const backdrop = document.getElementById('jv-auth-backdrop'); 196 const closeBtn = document.getElementById('jv-auth-close'); 197 const form = document.getElementById('jv-auth-form'); 198 const submitBtn = document.getElementById('jv-auth-submit'); 199 const titleEl = document.getElementById('jv-auth-modal-title'); 200 const subtitleEl = titleEl ? titleEl.nextElementSibling : null; 201 const errBox = document.getElementById('jv-auth-error'); 202 const toggleToLogin = document.getElementById('jv-auth-toggle-login'); 203 const toggleToSignup = document.getElementById('jv-auth-toggle-signup'); 204 const refInput = document.getElementById('jv-auth-referral'); 205 let mode = 'signup'; 206 let authLastFocused = null; 207 function authFocusable() { 208 return Array.prototype.slice.call(modal.querySelectorAll( 209 'a[href],button:not([disabled]),input:not([disabled]),textarea:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])' 210 )).filter(function (el) { return el.offsetParent !== null; }); 211 } 212 213 function applyMode() { 214 modal.querySelectorAll('.jv-signup-only').forEach((el) => { 215 if (mode === 'signup') el.classList.remove('hidden'); 216 else el.classList.add('hidden'); 217 }); 218 modal.querySelectorAll('.jv-login-only').forEach((el) => { 219 if (mode === 'login') el.classList.remove('hidden'); 220 else el.classList.add('hidden'); 221 }); 222 if (titleEl) titleEl.textContent = titleEl.getAttribute('data-' + mode + '-text') || titleEl.textContent; 223 if (subtitleEl) subtitleEl.textContent = subtitleEl.getAttribute('data-' + mode + '-text') || subtitleEl.textContent; 224 if (submitBtn) submitBtn.textContent = submitBtn.getAttribute('data-' + mode + '-text') || submitBtn.textContent; 225 hideError(); 226 } 227 228 function open(initialMode) { 229 mode = initialMode === 'login' ? 'login' : 'signup'; 230 applyMode(); 231 // Pre-fill referral from storage / cookie 232 if (refInput && mode === 'signup' && !refInput.value) { 233 let stored = ''; 234 try { stored = localStorage.getItem('jv_referral_code') || ''; } catch (_) {} 235 if (!stored) { 236 const m = document.cookie.match(/jv_ref=([^;]+)/); 237 if (m) stored = decodeURIComponent(m[1]); 238 } 239 if (stored) refInput.value = stored; 240 } 241 authLastFocused = document.activeElement; 242 modal.classList.remove('hidden'); 243 document.body.style.overflow = 'hidden'; 244 setTimeout(function () { var el = document.getElementById('jv-auth-phone'); if (el) el.focus(); }, 60); 245 } 246 247 function close() { 248 modal.classList.add('hidden'); 249 document.body.style.overflow = ''; 250 if (authLastFocused && authLastFocused.focus) authLastFocused.focus(); 251 } 252 253 function showError(msg) { 254 if (!errBox) return; 255 errBox.textContent = msg; 256 errBox.classList.remove('hidden'); 257 } 258 259 function hideError() { 260 if (!errBox) return; 261 errBox.classList.add('hidden'); 262 errBox.textContent = ''; 263 } 264 265 backdrop && backdrop.addEventListener('click', close); 266 closeBtn && closeBtn.addEventListener('click', close); 267 document.addEventListener('keydown', (e) => { 268 if (modal.classList.contains('hidden')) return; 269 if (e.key === 'Escape') { close(); return; } 270 if (e.key === 'Tab') { 271 var f = authFocusable(); 272 if (!f.length) return; 273 var first = f[0]; 274 var last = f[f.length - 1]; 275 if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } 276 else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } 277 } 278 }); 279 280 toggleToLogin && toggleToLogin.addEventListener('click', () => { mode = 'login'; applyMode(); }); 281 toggleToSignup && toggleToSignup.addEventListener('click', () => { mode = 'signup'; applyMode(); }); 282 283 // Anchor click handlers â anything with data-auth-trigger opens the modal 284 document.addEventListener('click', (e) => { 285 const target = e.target; 286 if (!(target instanceof Element)) return; 287 const trigger = target.closest('[data-auth-trigger]'); 288 if (!trigger) return; 289 e.preventDefault(); 290 const desiredMode = trigger.getAttribute('data-auth-trigger'); 291 open(desiredMode); 292 }); 293 294 form && form.addEventListener('submit', async (e) => { 295 e.preventDefault(); 296 hideError(); 297 submitBtn.disabled = true; 298 const orig = submitBtn.textContent; 299 submitBtn.textContent = mode === 'signup' ? 'Creating accountâ¦' : 'Logging inâ¦'; 300 301 const phone = document.getElementById('jv-auth-phone').value.trim(); 302 const password = document.getElementById('jv-auth-password').value; 303 const full_name = document.getElementById('jv-auth-name').value.trim(); 304 const email = document.getElementById('jv-auth-email').value.trim(); 305 const referral_code = (document.getElementById('jv-auth-referral').value || '').trim(); 306
307 try { 308 let res, data; 309 if (mode === 'signup') { 310 if (!full_name) { showError('Please enter your full name.'); throw new Error('validation'); } 311 if (!phone) { showError('Please enter your mobile number.'); throw new Error('validation'); } 312 if (!password || password.length < 8) { showError('Password must be at least 8 characters.'); throw new Error('validation'); } 313 314 res = await fetch(apiBase + '/auth/signup', { 315 method: 'POST', 316 credentials: 'include', 317 headers: { 'Content-Type': 'application/json' }, 318 body: JSON.stringify({ 319 phone, password, full_name, 320 role: 'doctor', 321 ...(email ? { email } : {}), 322 ...(referral_code ? { referral_code } : {}), 323 }), 324 }); 325 } else { 326 if (!phone || !password) { showError('Please enter your phone and password.'); throw new Error('validation'); } 327 res = await fetch(apiBase + '/auth/login', { 328 method: 'POST', 329 credentials: 'include', 330 headers: { 'Content-Type': 'application/json' }, 331 body: JSON.stringify({ phone, password }), 332 }); 333 } 334 data = await res.json().catch(() => ({})); 335 if (!res.ok) { 336 showError(data.error || 'Something went wrong. Please try again.'); 337 throw new Error('api'); 338 } 339 // Persist token to localStorage as a backup to the cookie. 340 if (data.token) { 341 try { localStorage.setItem('jv_token', data.token); } catch (_) {} 342 } 343 // Clear the referral code from storage now that the signup is complete. 344 try { localStorage.removeItem('jv_referral_code'); } catch (_) {} 345 try { localStorage.removeItem('jv_referral_captured_at'); } catch (_) {} 346 // Reload the page so the paywall logic sees the new auth state. 347 window.location.reload(); 348 } catch (_) { 349 submitBtn.disabled = false; 350 submitBtn.textContent = orig; 351 } 352 }); 353 354 // Expose for paywall to open the modal directly 355 window.openJvAuthModal = open; 356 })(); 357</script>
357 <!-- 358 Foundation Academy paywall. 359 360 For 6 of the 7 Foundation modules (everything except scope-and-safety, 361 which is the fully-free hook), show only the introduction (up to and 362 including the first <h2>) to anonymous visitors, then a soft signup 363 overlay. Signed-in users see the full article. 364 365 The full article HTML is server-rendered so Google can index it. The 366 paywall is purely visual â hidden via CSS for anonymous browsers. 367 368 The 7 module slugs are at /for-doctors/academy/[slug]. 369 -->
369<script> 370 (function setupFoundationPaywall() { 371 // Only engages on Foundation Academy module pages other than the 372 // first one (scope-and-safety stays fully free as the hook). 373 const FREE_MODULE = 'scope-and-safety'; 374 const m = window.location.pathname.match(/^\/for-doctors\/academy\/([a-z0-9-]+)\/?$/); 375 if (!m) return; 376 const slug = m[1]; 377 if (slug === FREE_MODULE) return; 378 379 // Check auth state â cookie first, then localStorage backup. 380 const hasCookie = document.cookie.indexOf('jv_auth=') !== -1; 381 let hasToken = false; 382 try { hasToken = !!localStorage.getItem('jv_token'); } catch (_) {} 383 if (hasCookie || hasToken) return; // signed in â show everything 384 385 // Engage paywall. 386 document.body.classList.add('jv-paywall-active'); 387 388 // Insert the signup overlay after the article body so it appears 389 // immediately below the visible portion. 390 const article = document.querySelector('article'); 391 if (!article) return; 392 393 const overlay = document.createElement('div'); 394 overlay.className = 'jv-paywall-overlay'; 395 overlay.innerHTML = [ 396 '<div class="jv-paywall-meta"></div>', 397 '<h3>Sign up to keep reading</h3>', 398 '<p>Free for doctors. Finish this article â plus the rest of the Foundation programme and the Foundation Quiz. 30 seconds.</p>', 399 '<button type="button" class="jv-paywall-button" data-auth-trigger="signup" data-track="paywall_signup_click" data-track-source="academy_paywall">Sign up â 30 seconds</button>', 400 '<div class="jv-paywall-login">Already have an account? <button type="button" data-auth-trigger="login">Log in</button></div>', 401 ].join(''); 402 403 // Populate the meta pill with a concrete count of what remains. 404 // Tells the reader: "there's a lot more article below, not just one 405 // paragraph." Uses the next hidden H2's title as a teaser. 406 try { 407 const h2s = article.querySelectorAll('.prose-jv h2'); 408 const hiddenCount = Math.max(0, h2s.length - 1); 409 const metaEl = overlay.querySelector('.jv-paywall-meta'); 410 if (metaEl && hiddenCount > 0) { 411 const nextTitle = h2s[1] ? h2s[1].textContent.trim() : ''; 412 if (hiddenCount > 1 && nextTitle) { 413 metaEl.textContent = hiddenCount + ' more sections â including "' + nextTitle + '"'; 414 } else if (nextTitle) { 415 metaEl.textContent = '1 more section: "' + nextTitle + '"'; 416 } else { 417 metaEl.textContent = hiddenCount + ' more section' + (hiddenCount === 1 ? '' : 's') + ' below'; 418 } 419 } 420 } catch (_) { 421 // Best-effort â paywall still works without the count badge. 422 } 423 424 article.appendChild(overlay); 425 })(); 426 </script>
426 <!-- 427 Reading progress + visible reading meter. 428 429 For signed-in doctors on Foundation Academy module pages: show a live 430 reading-time meter (progress bar + countdown) and a "Mark as read" button 431 that activates once they've spent the per-article minimum reading time AND 432 scrolled to the end â mirroring the in-app reader. The minimum-time gate is 433 the same anti-skim rule the server enforces; this just makes it VISIBLE so 434 a fast reader isn't left wondering why a module won't mark. 435 436 The server (POST /api/website-academy/track) is the source of truth: it 437 accumulates validated active-reading seconds (capped by real wall-clock) 438 and returns { completed, accumulated_seconds, min_read_seconds }. 439 -->
439<script> 440 (function setupReadingProgress() { 441 const m = window.location.pathname.match(/^\/for-doctors\/academy\/([a-z0-9-]+)\/?$/); 442 if (!m) return; 443 const slug = m[1]; 444 445 // Auth check â same as paywall (the meter is for signed-in doctors). 446 const hasCookie = document.cookie.indexOf('jv_auth=') !== -1; 447 let hasToken = false; 448 try { hasToken = !!localStorage.getItem('jv_token'); } catch (_) {} 449 if (!hasCookie && !hasToken) return; 450 451 // Pick the API base. Production: website on janavaidya.com, API on 452 // app.janavaidya.com. Dev: localhost:3000. 453 const apiBase = location.hostname.endsWith('janavaidya.com') 454 ? 'https://app.janavaidya.com/api' 455 : 'http://localhost:3000/api'; 456 457 let fired = false; // completed (server confirmed) 458 let reachedEnd = false; // scrolled through to the end 459 let visibleSeconds = 0; // active seconds accrued since the last send 460 let serverAccum = 0; // last server-confirmed accumulated seconds 461 let floor = null; // per-article minimum (from the server) 462 let sending = false; 463 const token = (function () { try { return localStorage.getItem('jv_token'); } catch (_) { return null; } })(); 464 465 function fmt(s) { 466 s = Math.max(0, Math.round(s || 0)); 467 const mm = Math.floor(s / 60); const ss = s % 60; 468 return mm + ':' + (ss < 10 ? '0' : '') + ss; 469 } 470 function atEnd() { 471 const article = document.querySelector('article'); 472 if (!article) return false; 473 return article.getBoundingClientRect().bottom <= window.innerHeight * 1.2; // 20% margin 474 } 475 476 // ââ The meter card (injected after the article) ââ 477 let cardEl = null; 478 function buildCard() { 479 if (cardEl || document.getElementById('jv-reading-meter')) return; 480 const article = document.querySelector('article'); 481 if (!article) return; 482 const wrap = document.createElement('div'); 483 wrap.id = 'jv-reading-meter'; 484 wrap.className = 'mx-auto max-w-3xl px-4 sm:px-6 mt-8'; 485 wrap.innerHTML = 486 '<div class="rounded-2xl border border-border bg-white p-5 font-sans">' + 487 '<div id="jv-meter-live">' + 488 '<div class="flex items-center justify-between gap-3 mb-2">' + 489 '<p id="jv-meter-status" class="text-sm font-medium text-foreground">Readingâ¦</p>' + 490 '<span id="jv-meter-time" class="text-xs text-muted-foreground tabular-nums">0:00</span>' + 491 '</div>' + 492 '<div class="h-1.5 w-full rounded-full bg-muted overflow-hidden">' + 493 '<div id="jv-meter-bar" class="h-full bg-secondary" style="width:0%;transition:width 400ms ease"></div>' + 494 '</div>' + 495 '<p id="jv-meter-hint" class="mt-3 text-xs text-muted-foreground">A minimum reading time keeps the Academy meaningful â the button below activates once you have read the minimum and reached the end.</p>' + 496 '</div>' + 497 '<button id="jv-meter-btn" type="button" disabled ' + 498 'class="mt-4 inline-flex items-center justify-center rounded-lg bg-secondary text-white px-5 py-2.5 text-sm font-medium transition-opacity disabled:opacity-50 disabled:cursor-not-allowed">' + 499 'Keep readingâ¦' + 500 '</button>' + 501 '</div>'; 502 article.parentNode.insertBefore(wrap, article.nextSibling); 503 cardEl = wrap; 504 document.getElementById('jv-meter-btn').addEventListener('click', onMarkClick); 505 } 506 507 function renderReadState() { 508 buildCard(); 509 if (!cardEl) return; 510 cardEl.querySelector('div').innerHTML = 511 '<div class="inline-flex items-center gap-2 rounded-full bg-green-50 border border-green-200 text-green-800 px-4 py-2 text-sm font-medium">' + 512 '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
512' + 513 'Marked as read · ' + 514 '<a href="/for-doctors/academy" class="underline hover:no-underline">see your progress</a>' + 515 '</div>'; 516 } 517 518 function updateUI() { 519 if (fired || !cardEl) return; 520 const display = serverAccum + visibleSeconds; 521 const timeMet = floor != null && display >= floor; 522 const ready = timeMet && reachedEnd; 523 const pct = floor && floor > 0 ? Math.min(100, Math.round((display / floor) * 100)) : 0; 524 525 const bar = document.getElementById('jv-meter-bar'); 526 const time = document.getElementById('jv-meter-time'); 527 const status = document.getElementById('jv-meter-status'); 528 const hint = document.getElementById('jv-meter-hint'); 529 const btn = document.getElementById('jv-meter-btn'); 530 if (bar) bar.style.width = pct + '%'; 531 if (time) time.textContent = fmt(display) + (floor != null ? ' / ' + fmt(floor) : ''); 532 if (status) status.textContent = timeMet ? (reachedEnd ? 'You are ready to mark this read.' : 'Minimum reading time met â scroll to the end.') : 'Readingâ¦'; 533 if (hint) hint.style.display = timeMet ? 'none' : ''; 534 if (btn) { 535 btn.disabled = !ready; 536 btn.textContent = !timeMet 537 ? ('Keep reading â ' + (floor != null ? fmt(floor - display) : 'â¦') + ' left') 538 : !reachedEnd ? 'Scroll to the end to finish' 539 : 'Mark as read'; 540 } 541 } 542 543 function flush(force) { 544 if (fired || sending) return; 545 if (!force && visibleSeconds <= 0 && !reachedEnd) return; 546 const secs = visibleSeconds; 547 visibleSeconds = 0; 548 sending = true; 549 fetch(apiBase + '/website-academy/track', { 550 method: 'POST', 551 credentials: 'include', 552 headers: { 553 'Content-Type': 'application/json', 554 ...(token ? { 'Authorization': 'Bearer ' + token } : {}), 555 }, 556 body: JSON.stringify({ module_slug: slug, additional_seconds: secs, reached_end: reachedEnd }), 557 keepalive: true, 558 }).then(function (res) { return res && res.ok ? res.json() : null; }) 559 .then(function (data) { 560 sending = false; 561 if (data) { 562 if (typeof data.accumulated_seconds === 'number') serverAccum = data.accumulated_seconds; 563 if (typeof data.min_read_seconds === 'number') floor = data.min_read_seconds; 564 } 565 if (data && data.completed) { fired = true; renderReadState(); } 566 else updateUI(); 567 }) 568 .catch(function () { sending = false; visibleSeconds += secs; }); // re-queue on failure 569 } 570 571 function onMarkClick() { 572 if (fired) { window.location.href = '/for-doctors/academy'; return; } 573 const btn = document.getElementById('jv-meter-btn'); 574 if (btn) { btn.disabled = true; btn.textContent = 'Markingâ¦'; } 575 // Force a final heartbeat; the server confirms completion. 576 flush(true); 577 // If the wall-clock cap is a hair short, the next auto-heartbeat 578 // catches it; re-enable so they can tap again in a moment. 579 setTimeout(function () { if (!fired) updateUI(); }, 1500); 580 } 581 582 // 1s ticker â accrue ACTIVE reading time, detect reaching the end, drive the meter. 583 const tick = setInterval(function () { 584 if (fired) { clearInterval(tick); return; } 585 if (document.visibilityState === 'visible') { 586 visibleSeconds += 1; 587 if (!reachedEnd && atEnd()) reachedEnd = true; 588 updateUI(); 589 } 590 }, 1000); 591 592 // Heartbeat every 30s + flush on tab-hide / page close. 593 const beat = setInterval(function () { if (fired) { clearInterval(beat); return; } flush(false); }, 30000); 594 document.addEventListener('visibilitychange', function () { if (document.visibilityState === 'hidden') flush(false); }); 595 window.addEventListener('beforeunload', function () { flush(false); }); 596 597 // Seed from the server: resume the countdown + show "read" immediately if 598 // already completed on a prior visit. 599 buildCard(); 600 (function preflightStatus() { 601 let preflightToken = null; 602 try { preflightToken = localStorage.getItem('jv_token'); } catch (_) {} 603 fetch(apiBase + '/website-academy/progress', { 604 credentials: 'include', 605 headers: preflightToken ? { 'Authorization': 'Bearer ' + preflightToken } : {}, 606 }).then(function (r) { return r && r.ok ? r.json() : null; }) 607 .then(function (data) { 608 if (!data || !Array.isArray(data.modules)) return; 609 const me = data.modules.find(function (x) { return x.slug === slug; }); 610 if (!me) return; 611 if (typeof me.accumulatedSeconds === 'number') serverAccum = me.accumulatedSeconds; 612 if (typeof me.minReadSeconds === 'number') floor = me.minReadSeconds; 613 if (me.completedAt || me.status === 'read') { fired = true; renderReadState(); } 614 else updateUI(); 615 }) 616 .catch(function () { /* silent */ }); 617 })(); 618 })(); 619 </script>
619 <!-- 620 Global event tracking for Google Analytics 4. 621 622 Any element with `data-track="event_name"` fires a GA4 event of that 623 name when clicked. Optional `data-track-{key}` attributes become 624 event parameters (e.g. `data-track-source="homepage_hero"` sends a 625 `source` parameter). `lang` (en/kn) is auto-added to every event. 626 627 Also auto-fires `academy_module_view` on Foundation Academy module 628 pages so you can see which modules anonymous visitors are reading. 629 -->
629<script> 630 (function setupTracking() { 631 function fire(name, params) { 632 if (typeof window.gtag !== 'function') return; 633 try { 634 params = params || {}; 635 params.lang = document.documentElement.lang || 'unknown'; 636 window.gtag('event', name, params); 637 } catch (_) {} 638 } 639 640 // Click-tracked elements (anything with data-track="...") 641 document.addEventListener('click', function (e) { 642 try { 643 var el = e.target.closest('[data-track]'); 644 if (!el) return; 645 var event = el.getAttribute('data-track'); 646 if (!event) return; 647 var params = {}; 648 for (var i = 0; i < el.attributes.length; i++) { 649 var attr = el.attributes[i]; 650 if (attr.name.indexOf('data-track-') === 0) { 651 var key = attr.name.slice('data-track-'.length).replace(/-/g, '_'); 652 if (key) params[key] = attr.value; 653 } 654 } 655 fire(event, params); 656 } catch (_) {} 657 }, true); 658 659 // Academy module view â fires once per page load on 660 // /for-doctors/academy/[slug] 661 try { 662 var m = window.location.pathname.match(/^\/for-doctors\/academy\/([a-z0-9-]+)\/?$/); 663 if (m) fire('academy_module_view', { module: m[1] }); 664 } catch (_) {} 665 })(); 666 </script>
666 <!-- 667 Scroll-reveal. Adds `jv-in` to `.jv-reveal` / `.jv-stagger` elements as 668 they enter the viewport, which plays a calm rise animation (defined in 669 global.css, gated by prefers-reduced-motion). Elements are fully visible 670 by default, so with no JS or reduced motion the page reads normally. 671 -->
671<script> 672 (function setupReveal() { 673 var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; 674 // No JS-driven animation for reduced-motion or old browsers: leave the 675 // content in its default (fully visible) state and do nothing. 676 if (reduce || !('IntersectionObserver' in window)) return; 677 // Pages that don't mark their own reveal targets get a tasteful 678 // default: every top-level content section except the first (the hero) 679 // rises in on scroll. The homepage marks its own targets, so it is 680 // left exactly as authored. 681 if (!document.querySelector('.jv-reveal, .jv-stagger')) { 682 var secs = document.querySelectorAll('.jv-page-content > section'); 683 for (var s = 1; s < secs.length; s++) secs[s].classList.add('jv-reveal'); 684 } 685 var els = document.querySelectorAll('.jv-reveal, .jv-stagger'); 686 if (!els.length) return; 687 // Opt in to the hidden-then-rise CSS now that we can animate + reveal. 688 document.documentElement.classList.add('jv-armed'); 689 var io = new IntersectionObserver(function (entries) { 690 entries.forEach(function (e) { 691 if (!e.isIntersecting) return; 692 e.target.classList.add('jv-in'); 693 io.unobserve(e.target); 694 }); 695 }, { threshold: 0, rootMargin: '0px 0px -40px 0px' }); 696 els.forEach(function (el) { io.observe(el); }); 697 // Safety net: if anything is still un-revealed shortly after load 698 // (e.g. an observer edge case), reveal it so content is never stuck. 699 setTimeout(function () { 700 els.forEach(function (el) { 701 var r = el.getBoundingClientRect(); 702 if (r.top < window.innerHeight) el.classList.add('jv-in'); 703 }); 704 }, 1200); 705 })(); 706 </script>
706 </body> </html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.