PageSourceSearch

https://janavaidya.com/blog/

html janavaidya.com collected 2026-10-02 01:32:38 UTC 73,573 bytes, 706 lines download raw bytes

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&#39;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&#39;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&#39;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&#39;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.